diff --git a/src/adql/db/DBChecker.java b/src/adql/db/DBChecker.java
index 7dcbc62b104f70b3aa75c4d8a0eeb7d38b57004d..834d45abc5cf55a0005ad4b178941ac28469b586 100644
--- a/src/adql/db/DBChecker.java
+++ b/src/adql/db/DBChecker.java
@@ -17,16 +17,22 @@ package adql.db;
  * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2011,2013-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                            Astronomishes Rechen Institut (ARI)
+ *                            Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Stack;
 
+import adql.db.STCS.CoordSys;
+import adql.db.STCS.Region;
+import adql.db.STCS.RegionType;
 import adql.db.exception.UnresolvedColumnException;
+import adql.db.exception.UnresolvedFunction;
 import adql.db.exception.UnresolvedIdentifiersException;
 import adql.db.exception.UnresolvedTableException;
 import adql.parser.ParseException;
@@ -42,14 +48,37 @@ import adql.query.SelectItem;
 import adql.query.from.ADQLTable;
 import adql.query.from.FromContent;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.StringConstant;
+import adql.query.operand.UnknownType;
+import adql.query.operand.function.ADQLFunction;
+import adql.query.operand.function.DefaultUDF;
+import adql.query.operand.function.UserDefinedFunction;
+import adql.query.operand.function.geometry.BoxFunction;
+import adql.query.operand.function.geometry.CircleFunction;
+import adql.query.operand.function.geometry.GeometryFunction;
+import adql.query.operand.function.geometry.PointFunction;
+import adql.query.operand.function.geometry.PolygonFunction;
+import adql.query.operand.function.geometry.RegionFunction;
 import adql.search.ISearchHandler;
 import adql.search.SearchColumnHandler;
 import adql.search.SimpleSearchHandler;
 
 /**
+ * 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 whether all used coordinate systems are supported</li>
+ * 	<li>Check that types of columns and UDFs match with their context</li>
+ * </ol>
+ * 
+ * <h3>Check tables and columns</h3>
  * <p>
- * 	Checks the existence of tables and columns, but also adds database metadata
- * 	on {@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>
@@ -59,35 +88,239 @@ import adql.search.SimpleSearchHandler;
  * </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.PostgreSQLTranslator}.
+ * 	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&eacute;gory Mantelet (CDS;ARI)
- * @version 1.3 (08/2014)
+ * @version 1.3 (10/2014)
  */
 public class DBChecker implements QueryChecker {
 
 	/** List of all available tables ({@link DBTable}). */
 	protected SearchTableList lstTables;
 
+	/** <p>List of all allowed geometrical functions (i.e. CONTAINS, REGION, POINT, COORD2, ...).</p>
+	 * <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.
+	 * </p>
+	 * @since 1.3 */
+	protected String[] allowedGeo = null;
+
+	/** <p>List of all allowed coordinate systems.</p>
+	 * <p>
+	 * 	Each item of this list must be of the form: "{frame} {refpos} {flavor}".
+	 * 	Each of these 3 items can be either of value, a list of values expressed with the syntax "({value1}|{value2}|...)"
+	 * 	or a '*' to mean all possible values.
+	 * </p>
+	 * <p><i>Note: since a default value (corresponding to the empty string - '') should always be possible for each part of a coordinate system,
+	 * the checker will always add the default value (UNKNOWNFRAME, UNKNOWNREFPOS or SPHERICAL2) into the given list of possible values for each coord. sys. part.</i></p>
+	 * <p>
+	 * 	If this list is NULL, all coordinates systems are allowed.
+	 * 	However, if not, all items of this list must be the only allowed coordinate systems.
+	 * 	So, if the list is empty, none is allowed.
+	 * </p>
+	 * @since 1.3 */
+	protected String[] allowedCoordSys = null;
+
+	/** <p>A regular expression built using the list of allowed coordinate systems.
+	 * With this regex, it is possible to known whether a coordinate system expression is allowed or not.</p>
+	 * <p>If NULL, all coordinate systems are allowed.</p>
+	 * @since 1.3 */
+	protected String coordSysRegExp = null;
+
+	/** <p>List of all allowed User Defined Functions (UDFs).</p>
+	 * <p>
+	 * 	If this list is NULL, any encountered UDF will be allowed.
+	 * 	However, if not, all items of this list must be the only allowed UDFs.
+	 * 	So, if the list is empty, no UDF is allowed.
+	 * </p>
+	 * @since 1.3 */
+	protected FunctionDef[] allowedUdfs = null;
+
 	/* ************ */
 	/* CONSTRUCTORS */
 	/* ************ */
 	/**
-	 * Builds a {@link DBChecker} with an empty list of tables.
+	 * <p>Builds a {@link DBChecker} with an empty list of tables.</p>
+	 * 
+	 * <p>Verifications done by this object after creation:</p>
+	 * <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(){
-		lstTables = new SearchTableList();
+		this(null, null);
 	}
 
 	/**
-	 * Builds a {@link DBChecker} with the given list of tables.
+	 * <p>Builds a {@link DBChecker} with the given list of known tables.</p>
+	 * 
+	 * <p>Verifications done by this object after creation:</p>
+	 * <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){
+		this(tables, null);
+	}
+
+	/**
+	 * <p>Builds a {@link DBChecker} with the given list of known tables and with a restricted list of user defined functions.</p>
+	 * 
+	 * <p>Verifications done by this object after creation:</p>
+	 * <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>
+	 * 
+	 * @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).
+	 *                   	If empty list, no "unknown" (or UDF) is allowed.
+	 *                   	<i>Note: match with items of this list are done case insensitively.</i>
+	 * 
+	 * @since 1.3
+	 */
+	public DBChecker(final Collection<? extends DBTable> tables, final Collection<? extends FunctionDef> allowedUdfs){
+		// Sort and store the given tables:
 		setTables(tables);
+
+		Object[] tmp;
+		int cnt;
+
+		// Store all allowed UDFs in a sorted array:
+		if (allowedUdfs != null){
+			// Remove all NULL and empty strings:
+			tmp = new FunctionDef[allowedUdfs.size()];
+			cnt = 0;
+			for(FunctionDef udf : allowedUdfs){
+				if (udf != null && udf.name.trim().length() > 0)
+					tmp[cnt++] = udf;
+			}
+			// make a copy of the array:
+			this.allowedUdfs = Arrays.copyOf(tmp, cnt, FunctionDef[].class);
+			tmp = null;
+			// sort the values:
+			Arrays.sort(this.allowedUdfs);
+		}
+	}
+
+	/**
+	 * <p>Builds a {@link DBChecker} with the given list of known tables and with a restricted list of user defined functions.</p>
+	 * 
+	 * <p>Verifications done by this object after creation:</p>
+	 * <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>OK</b></li>
+	 * 	<li>Support of coordinate systems:              <b>OK</b></li>
+	 * </ul>
+	 * 
+	 * @param tables			List of all available tables.
+	 * @param allowedGeoFcts	List of all allowed geometrical functions (i.e. CONTAINS, POINT, UNION, CIRCLE, COORD1).
+	 *                      	If NULL, no verification will be done (and so, all geometries are allowed).
+	 *                      	If empty list, no geometry function is allowed.
+	 *                      	<i>Note: match with items of this list are done case insensitively.</i>
+	 * @param allowedCoordSys	List of all allowed coordinate system patterns. The syntax of a such pattern is the following:
+	 *                       	"{frame} {refpos} {flavor}" ; on the contrary to a coordinate system expression, here no part is optional.
+	 *                       	Each part of this pattern can be one the possible values (case insensitive), a list of possible values
+	 *                       	expressed with the syntax "({value1}|{value2}|...)", or a '*' for any valid value.
+	 *                       	For instance: "ICRS (GEOCENTER|heliocenter) *".
+	 *                       	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
+	 */
+	public DBChecker(final Collection<? extends DBTable> tables, final Collection<String> allowedGeoFcts, final Collection<String> allowedCoordSys) throws ParseException{
+		this(tables, null, allowedGeoFcts, allowedCoordSys);
+	}
+
+	/**
+	 * <p>Builds a {@link DBChecker}.</p>
+	 * 
+	 * <p>Verifications done by this object after creation:</p>
+	 * <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>
+	 * 
+	 * @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).
+	 *                   		If empty list, no "unknown" (or UDF) is allowed.
+	 *                   		<i>Note: match with items of this list are done case insensitively.</i>
+	 * @param allowedGeoFcts	List of all allowed geometrical functions (i.e. CONTAINS, POINT, UNION, CIRCLE, COORD1).
+	 *                      	If NULL, no verification will be done (and so, all geometries are allowed).
+	 *                      	If empty list, no geometry function is allowed.
+	 *                      	<i>Note: match with items of this list are done case insensitively.</i>
+	 * @param allowedCoordSys	List of all allowed coordinate system patterns. The syntax of a such pattern is the following:
+	 *                       	"{frame} {refpos} {flavor}" ; on the contrary to a coordinate system expression, here no part is optional.
+	 *                       	Each part of this pattern can be one the possible values (case insensitive), a list of possible values
+	 *                       	expressed with the syntax "({value1}|{value2}|...)", or a '*' for any valid value.
+	 *                       	For instance: "ICRS (GEOCENTER|heliocenter) *".
+	 *                       	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
+	 */
+	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);
+
+		// Set the list of allowed geometrical functions:
+		allowedGeo = specialSort(allowedGeoFcts);
+
+		// Set the list of allowed coordinate systems:
+		this.allowedCoordSys = specialSort(allowedCoordSys);
+		coordSysRegExp = STCS.buildCoordSysRegExp(this.allowedCoordSys);
+	}
+
+	/**
+	 * Transform the given collection of string elements in a sorted array.
+	 * Only non-NULL and non-empty strings are kept.
+	 * 
+	 * @param items	Items to copy and sort.
+	 * 
+	 * @return	A sorted array containing all - except NULL and empty strings - items of the given collection.
+	 * 
+	 * @since 1.3
+	 */
+	protected final static String[] specialSort(final Collection<String> items){
+		// Nothing to do if the array is NULL:
+		if (items == null)
+			return null;
+
+		// Keep only valid items (not NULL and not empty string):
+		String[] tmp = new String[items.size()];
+		int cnt = 0;
+		for(String item : items){
+			if (item != null && item.trim().length() > 0)
+				tmp[cnt++] = item;
+		}
+
+		// Make an adjusted array copy:
+		String[] copy = Arrays.copyOf(tmp, cnt);
+
+		// Sort the values:
+		Arrays.sort(copy);
+
+		return copy;
 	}
 
 	/* ****** */
@@ -98,7 +331,7 @@ public class DBChecker implements QueryChecker {
 	 * 
 	 * <p><i><u>Note:</u>
 	 * 	Only if the given collection is NOT an instance of {@link SearchTableList},
-	 * 	the collection will be copied inside a new {@link SearchTableList}.
+	 * 	the collection will be copied inside a new {@link SearchTableList}, otherwise it is used as provided.
 	 * </i></p>
 	 * 
 	 * @param tables	List of {@link DBTable}s.
@@ -130,63 +363,136 @@ public class DBChecker implements QueryChecker {
 	 * @see #check(ADQLQuery, Stack)
 	 */
 	@Override
-	public void check(final ADQLQuery query) throws ParseException{
+	public final void check(final ADQLQuery query) throws ParseException{
 		check(query, null);
 	}
 
 	/**
-	 * Followed algorithm:
-	 * <pre>
-	 * Map&lt;DBTable,ADQLTable&gt; mapTables;
-	 * 
-	 * For each ADQLTable t
-	 * 	if (t.isSubQuery())
-	 * 		dbTable = generateDBTable(t.getSubQuery, t.getAlias());
-	 * 	else
-	 * 		dbTable = resolveTable(t);
-	 * 	t.setDBLink(dbTable);
-	 * 	dbTables.put(t, dbTable);
-	 * End
-	 * 
-	 * For each SelectAllColumns c
-	 * 	table = c.getAdqlTable();
-	 * 	if (table != null){
-	 * 		dbTable = resolveTable(table);
-	 * 		if (dbTable == null)
-	 * 			dbTable = query.getFrom().getTablesByAlias(table.getTableName(), table.isCaseSensitive(IdentifierField.TABLE));
-	 *		if (dbTable == null)
-	 *			throw new UnresolvedTableException(table);
-	 * 		table.setDBLink(dbTable);
-	 * 	}
-	 * End
-	 * 
-	 * SearchColumnList list = query.getFrom().getDBColumns();
-	 * 
-	 * For each ADQLColumn c
-	 * 	dbColumn = resolveColumn(c, list);
-	 * 	c.setDBLink(dbColumn);
-	 * 	c.setAdqlTable(mapTables.get(dbColumn.getTable()));
-	 * End
-	 * 
-	 * For each ColumnReference colRef
-	 *	checkColumnReference(colRef, query.getSelect(), list);
-	 * End
-	 * </pre>
+	 * <p>Process several (semantic) verifications in the given ADQL query.</p>
+	 * 
+	 * <p>Main verifications done in this function:</p>
+	 * <ol>
+	 * 	<li>Existence of DB items (tables and columns)</li>
+	 * 	<li>Semantic verification of sub-queries</li>
+	 * 	<li>Support of every encountered User Defined Functions (UDFs - functions unknown by the syntactic parser)</li>
+	 * 	<li>Support of every encountered geometries (functions, coordinate systems and STC-S expressions)</li>
+	 * 	<li>Consistency of types still unknown (because the syntactic parser could not yet resolve them)</li>
+	 * </ol>
 	 * 
 	 * @param query			The query to check.
-	 * @param fathersList	List of all columns available in the father query.
+	 * @param fathersList	List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                   	Each item of this stack is a list of columns available in each father-level query.
+	 *                   	<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
 	 * 
-	 * @throws UnresolvedIdentifiersException	An {@link UnresolvedIdentifiersException} if some tables or columns can not be resolved.
+	 * @throws UnresolvedIdentifiersException	An {@link UnresolvedIdentifiersException} if one or several of the above listed tests have detected
+	 *                                       	some semantic errors (i.e. unresolved table, columns, function).
 	 * 
 	 * @since 1.2
 	 * 
-	 * @see #resolveTable(ADQLTable)
-	 * @see #generateDBTable(ADQLQuery, String)
-	 * @see #resolveColumn(ADQLColumn, SearchColumnList, Stack)
-	 * @see #checkColumnReference(ColumnReference, ClauseSelect, SearchColumnList)
+	 * @see #checkDBItems(ADQLQuery, Stack, UnresolvedIdentifiersException)
+	 * @see #checkSubQueries(ADQLQuery, Stack, SearchColumnList, UnresolvedIdentifiersException)
+	 * @see #checkUDFs(ADQLQuery, UnresolvedIdentifiersException)
+	 * @see #checkGeometries(ADQLQuery, UnresolvedIdentifiersException)
+	 * @see #checkTypes(ADQLQuery, UnresolvedIdentifiersException)
 	 */
-	protected void check(final ADQLQuery query, 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):
+		SearchColumnList availableColumns = checkDBItems(query, fathersList, errors);
+
+		// B. Check UDFs:
+		if (allowedUdfs != null)
+			checkUDFs(query, errors);
+
+		// C. Check geometries:
+		checkGeometries(query, errors);
+
+		// D. Check types:
+		checkTypes(query, errors);
+
+		// E. Check sub-queries:
+		checkSubQueries(query, fathersList, availableColumns, errors);
+
+		// Throw all errors, if any:
+		if (errors.getNbErrors() > 0)
+			throw errors;
+	}
+
+	/* ************************************************ */
+	/* CHECKING METHODS FOR DB ITEMS (TABLES & COLUMNS) */
+	/* ************************************************ */
+
+	/**
+	 * <p>Check DB items (tables and columns) used in the given ADQL query.</p>
+	 * 
+	 * <p>Operations done in this function:</p>
+	 * <ol>
+	 * 	<li>Resolve all found tables</li>
+	 * 	<li>Get the whole list of all available columns <i>Note: this list is returned by this function.</i></li>
+	 * 	<li>Resolve all found columns</li>
+	 * </ol>
+	 * 
+	 * @param query			Query in which the existence of DB items must be checked.
+	 * @param fathersList	List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                   	Each item of this stack is a list of columns available in each father-level query.
+	 *                   	<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
+	 * @param errors		List of errors to complete in this function each time an unknown table or column is encountered.
+	 * 
+	 * @return	List of all columns available in the given query.
+	 * 
+	 * @see #resolveTables(ADQLQuery, Stack, UnresolvedIdentifiersException)
+	 * @see FromContent#getDBColumns()
+	 * @see #resolveColumns(ADQLQuery, Stack, Map, SearchColumnList, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	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);
+
+		// b. Get the list of all columns made available in the clause FROM:
+		SearchColumnList availableColumns;
+		try{
+			availableColumns = query.getFrom().getDBColumns();
+		}catch(ParseException pe){
+			errors.addException(pe);
+			availableColumns = new SearchColumnList();
+		}
+
+		// c. Resolve all columns:
+		resolveColumns(query, fathersList, mapTables, availableColumns, errors);
+
+		return availableColumns;
+	}
+
+	/**
+	 * <p>Search all table references inside the given query, resolve them against the available tables, and if there is only one match,
+	 * attach the matching metadata to them.</p>
+	 * 
+	 * <b>Management of sub-query tables</b>
+	 * <p>
+	 * 	If a table is not a DB table reference but a sub-query, this latter is first checked (using {@link #check(ADQLQuery, Stack)} ;
+	 * 	but the father list must not contain tables of the given query, because on the same level) and then corresponding table metadata
+	 * 	are generated (using {@link #generateDBTable(ADQLQuery, String)}) and attached to it.
+	 * </p>
+	 * 
+	 * <b>Management of "{table}.*" in the SELECT clause</b>
+	 * <p>
+	 * 	For each of this SELECT item, this function tries to resolve the table name. If only one match is found, the corresponding ADQL table object
+	 * 	is got from the list of resolved tables and attached to this SELECT item (thus, the joker item will also have the good metadata,
+	 * 	particularly if the referenced table is a sub-query).
+	 * </p>
+	 * 
+	 * @param query			Query in which the existence of tables must be checked.
+	 * @param fathersList	List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                      Each item of this stack is a list of columns available in each father-level query.
+	 *                   	<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
+	 * @param errors		List of errors to complete in this function each time an unknown table or column is encountered.
+	 * 
+	 * @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>();
 		ISearchHandler sHandler;
 
@@ -200,7 +506,7 @@ public class DBChecker implements QueryChecker {
 				// resolve the table:
 				DBTable dbTable = null;
 				if (table.isSubQuery()){
-					// check the subquery tables:
+					// check the sub-query tables:
 					check(table.getSubQuery(), fathersList);
 					// generate its DBTable:
 					dbTable = generateDBTable(table.getSubQuery(), table.getAlias());
@@ -220,9 +526,9 @@ public class DBChecker implements QueryChecker {
 
 		// Attach table information on wildcards with the syntax "{tableName}.*" of the SELECT clause:
 		/* Note: no need to check the table name among the father tables, because there is
-		 *       no interest to select a father column in a subquery
-		 *       (which can return only one column ; besides, no aggregate is not allowed
-		 *       in subqueries).*/
+		 *       no interest to select a father column in a sub-query
+		 *       (which can return only one column ; besides, no aggregate is allowed
+		 *       in sub-queries).*/
 		sHandler = new SearchWildCardHandler();
 		sHandler.search(query.getSelect());
 		for(ADQLObject result : sHandler){
@@ -230,31 +536,77 @@ public class DBChecker implements QueryChecker {
 				SelectAllColumns wildcard = (SelectAllColumns)result;
 				ADQLTable table = wildcard.getAdqlTable();
 				DBTable dbTable = null;
-				// First, try to resolve the table by table alias:
+
+				// first, try to resolve the table by table alias:
 				if (table.getTableName() != null && table.getSchemaName() == null){
 					ArrayList<ADQLTable> tables = query.getFrom().getTablesByAlias(table.getTableName(), table.isCaseSensitive(IdentifierField.TABLE));
 					if (tables.size() == 1)
 						dbTable = tables.get(0).getDBLink();
 				}
-				// Then try to resolve the table reference by table name:
+
+				// then try to resolve the table reference by table name:
 				if (dbTable == null)
 					dbTable = resolveTable(table);
 
-				//			table.setDBLink(dbTable);
+				// set the corresponding tables among the list of resolved tables:
 				wildcard.setAdqlTable(mapTables.get(dbTable));
 			}catch(ParseException pe){
 				errors.addException(pe);
 			}
 		}
 
-		// Get the list of all columns made available in the clause FROM:
-		SearchColumnList list;
-		try{
-			list = query.getFrom().getDBColumns();
-		}catch(ParseException pe){
-			errors.addException(pe);
-			list = new SearchColumnList();
-		}
+		return mapTables;
+	}
+
+	/**
+	 * Resolve the given table, that's to say search for the corresponding {@link DBTable}.
+	 * 
+	 * @param table	The table to resolve.
+	 * 
+	 * @return		The corresponding {@link DBTable} if found, <i>null</i> otherwise.
+	 * 
+	 * @throws ParseException	An {@link UnresolvedTableException} if the given table can't be resolved.
+	 */
+	protected DBTable resolveTable(final ADQLTable table) throws ParseException{
+		ArrayList<DBTable> tables = lstTables.search(table);
+
+		// good if only one table has been found:
+		if (tables.size() == 1)
+			return tables.get(0);
+		// but if more than one: ambiguous table name !
+		else if (tables.size() > 1)
+			throw new UnresolvedTableException(table, tables.get(0).getADQLSchemaName() + "." + tables.get(0).getADQLName(), tables.get(1).getADQLSchemaName() + "." + tables.get(1).getADQLName());
+		// otherwise (no match): unknown table !
+		else
+			throw new UnresolvedTableException(table);
+	}
+
+	/**
+	 * <p>Search all column references inside the given query, resolve them thanks to the given tables' metadata,
+	 * and if there is only one match, attach the matching metadata to them.</p>
+	 * 
+	 * <b>Management of selected columns' references</b>
+	 * <p>
+	 * 	A column reference is not only a direct reference to a table column using a column name.
+	 * 	It can also be a reference to an item of the SELECT clause (which will then call a "selected column").
+	 * 	That kind of reference can be either an index (an unsigned integer starting from 1 to N, where N is the
+	 * 	number selected columns), or the name/alias of the column.
+	 * </p>
+	 * <p>
+	 * 	These references are also checked, in a second step, in this function. Thus, column metadata are
+	 * 	also attached to them, as common columns.
+	 * </p>
+	 * 
+	 * @param query			Query in which the existence of tables must be checked.
+	 * @param fathersList	List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                      Each item of this stack is a list of columns available in each father-level query.
+	 *                   	<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
+	 * @param mapTables		List of all resolved tables.
+	 * @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){
+		ISearchHandler sHandler;
 
 		// Check the existence of all columns:
 		sHandler = new SearchColumnHandler();
@@ -272,7 +624,7 @@ public class DBChecker implements QueryChecker {
 			}
 		}
 
-		// Check the correctness of all column references:
+		// Check the correctness of all column references (= references to selected columns):
 		/* Note: no need to provide the father tables when resolving column references,
 		 *       because no father column can be used in ORDER BY and/or GROUP BY. */
 		sHandler = new SearchColReferenceHandler();
@@ -291,70 +643,22 @@ public class DBChecker implements QueryChecker {
 				errors.addException(pe);
 			}
 		}
-
-		// Check subqueries outside the clause FROM:
-		sHandler = new SearchSubQueryHandler();
-		sHandler.search(query);
-		if (sHandler.getNbMatch() > 0){
-
-			// Push the list of columns in the father columns stack:
-			if (fathersList == null)
-				fathersList = new Stack<SearchColumnList>();
-			fathersList.push(list);
-
-			// Check each found subquery (except the first one because it is the current query):
-			for(ADQLObject result : sHandler){
-				try{
-					check((ADQLQuery)result, fathersList);
-				}catch(UnresolvedIdentifiersException uie){
-					Iterator<ParseException> itPe = uie.getErrors();
-					while(itPe.hasNext())
-						errors.addException(itPe.next());
-				}
-			}
-
-			// Pop the list of columns from the father columns stack:
-			fathersList.pop();
-
-		}
-
-		// Throw all errors if any:
-		if (errors.getNbErrors() > 0)
-			throw errors;
 	}
 
 	/**
-	 * Resolves the given table, that's to say searches for the corresponding {@link DBTable}.
-	 * 
-	 * @param table	The table to resolve.
+	 * <p>Resolve the given column, that's to say search for the corresponding {@link DBColumn}.</p>
 	 * 
-	 * @return		The corresponding {@link DBTable} if found, <i>null</i> otherwise.
-	 * 
-	 * @throws ParseException	An {@link UnresolvedTableException} if the given table can't be resolved.
-	 */
-	protected DBTable resolveTable(final ADQLTable table) throws ParseException{
-		ArrayList<DBTable> tables = lstTables.search(table);
-
-		// good if only one table has been found:
-		if (tables.size() == 1)
-			return tables.get(0);
-		// but if more than one: ambiguous table name !
-		else if (tables.size() > 1)
-			throw new UnresolvedTableException(table, tables.get(0).getADQLSchemaName() + "." + tables.get(0).getADQLName(), tables.get(1).getADQLSchemaName() + "." + tables.get(1).getADQLName());
-		// otherwise (no match): unknown table !
-		else
-			throw new UnresolvedTableException(table);
-	}
-
-	/**
-	 * <p>Resolves the given column, that's to say searches for the corresponding {@link DBColumn}.</p>
-	 * <p>The third parameter is used only if this function is called inside a subquery. In this case,
-	 * column is tried to be resolved with the first list (dbColumns). If no match is found,
-	 * the resolution is tried with the father columns list (fatherColumns).</p>
+	 * <p>
+	 * 	The third parameter is used only if this function is called inside a sub-query. In this case,
+	 * 	the column is tried to be resolved with the first list (dbColumns). If no match is found,
+	 * 	the resolution is tried with the father columns list (fathersList).
+	 * </p>
 	 * 
 	 * @param column		The column to resolve.
 	 * @param dbColumns		List of all available {@link DBColumn}s.
-	 * @param fathersList	List of all columns available in the father query ; a list for each father-level.
+	 * @param fathersList	List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                      Each item of this stack is a list of columns available in each father-level query.
+	 *                   	<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
 	 * 
 	 * @return 				The corresponding {@link DBColumn} if found. Otherwise an exception is thrown.
 	 * 
@@ -386,14 +690,14 @@ public class DBChecker implements QueryChecker {
 	}
 
 	/**
-	 * Checks whether the given column reference corresponds to a selected item (column or an expression with an alias)
+	 * Check whether the given column reference corresponds to a selected item (column or an expression with an alias)
 	 * or to an existing column.
 	 * 
-	 * @param colRef			The column reference which must be checked.
-	 * @param select			The SELECT clause of the ADQL query.
-	 * @param dbColumns			The list of all available {@link DBColumn}s.
+	 * @param colRef		The column reference which must be checked.
+	 * @param select		The SELECT clause of the ADQL query.
+	 * @param dbColumns		The list of all available columns.
 	 * 
-	 * @return 					The corresponding {@link DBColumn} if this reference is actually the name of a column, <i>null</i> otherwise.
+	 * @return 		The corresponding {@link DBColumn} if this reference is actually the name of a column, <i>null</i> otherwise.
 	 * 
 	 * @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.
@@ -431,19 +735,16 @@ public class DBChecker implements QueryChecker {
 		}
 	}
 
-	/* ************************************* */
-	/* DBTABLE & DBCOLUMN GENERATION METHODS */
-	/* ************************************* */
 	/**
-	 * Generates a {@link DBTable} corresponding to the given sub-query with the given table name.
-	 * This {@link DBTable} which contains all {@link DBColumn} returned by {@link ADQLQuery#getResultingColumns()}.
+	 * Generate a {@link DBTable} corresponding to the given sub-query with the given table name.
+	 * This {@link DBTable} will contain all {@link DBColumn} returned by {@link ADQLQuery#getResultingColumns()}.
 	 * 
 	 * @param subQuery	Sub-query in which the specified table must be searched.
 	 * @param tableName	Name of the table to search.
 	 * 
-	 * @return			The corresponding {@link DBTable} if the table has been found in the given sub-query, <i>null</i> otherwise.
+	 * @return	The corresponding {@link DBTable} if the table has been found in the given sub-query, <i>null</i> otherwise.
 	 * 
-	 * @throws ParseException	Can be used to explain why the table has not been found.
+	 * @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{
 		DefaultDBTable dbTable = new DefaultDBTable(tableName);
@@ -455,6 +756,469 @@ public class DBChecker implements QueryChecker {
 		return dbTable;
 	}
 
+	/* ************************* */
+	/* CHECKING METHODS FOR UDFs */
+	/* ************************* */
+
+	/**
+	 * <p>Search all UDFs (User Defined Functions) inside the given query, and then
+	 * check their signature against the list of allowed UDFs.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	When more than one allowed function match, the function is considered as correct
+	 * 	and no error is added.
+	 * 	However, in case of multiple matches, the return type of matching functions could
+	 * 	be different and in this case, there would be an error while checking later
+	 * 	the types. In such case, throwing an error could make sense, but the user would
+	 * 	then need to cast some parameters to help the parser identifying the right function.
+	 * 	But the type-casting ability is not yet possible in ADQL.
+	 * </i></p>
+	 * 
+	 * @param query		Query in which UDFs must be checked.
+	 * @param errors	List of errors to complete in this function each time a UDF does not match to any of the allowed UDFs.
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkUDFs(final ADQLQuery query, final UnresolvedIdentifiersException errors){
+		// Search all UDFs:
+		ISearchHandler sHandler = new SearchUDFHandler();
+		sHandler.search(query);
+
+		// If no UDF are allowed, throw immediately an error:
+		if (allowedUdfs.length == 0){
+			for(ADQLObject result : sHandler)
+				errors.addException(new UnresolvedFunction((UserDefinedFunction)result));
+		}
+		// Otherwise, try to resolve all of them:
+		else{
+			ArrayList<UserDefinedFunction> toResolveLater = new ArrayList<UserDefinedFunction>();
+			UserDefinedFunction udf;
+			int match;
+			BinarySearch<FunctionDef,UserDefinedFunction> binSearch = new BinarySearch<FunctionDef,UserDefinedFunction>(){
+				@Override
+				protected int compare(UserDefinedFunction searchItem, FunctionDef arrayItem){
+					return arrayItem.compareTo(searchItem) * -1;
+				}
+			};
+
+			// Try to resolve all the found UDFs:
+			/* 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){
+				udf = (UserDefinedFunction)result;
+				// search for a match:
+				match = binSearch.search(udf, allowedUdfs);
+				// if no match...
+				if (match < 0){
+					// ...if the type of all parameters is resolved, add an error (no match is possible):
+					if (isAllParamTypesResolved(udf))
+						errors.addException(new UnresolvedFunction(udf));	// TODO Add the ADQLOperand position!
+					// ...otherwise, try to resolved it later (when other UDFs will be mostly resolved):
+					else
+						toResolveLater.add(udf);
+				}
+				// if there is a match, metadata may be attached (particularly if the function is built automatically by the syntactic parser):
+				else if (udf instanceof DefaultUDF)
+					((DefaultUDF)udf).setDefinition(allowedUdfs[match]);
+			}
+
+			// Try to resolve UDFs whose some parameter types are depending of other UDFs:
+			for(int i = 0; i < toResolveLater.size(); i++){
+				udf = toResolveLater.get(i);
+				// search for a match:
+				match = binSearch.search(udf, allowedUdfs);
+				// if no match, add an error:
+				if (match < 0)
+					errors.addException(new UnresolvedFunction(udf));	// TODO Add the ADQLOperand position!
+				// otherwise, metadata may be attached (particularly if the function is built automatically by the syntactic parser):
+				else if (udf instanceof DefaultUDF)
+					((DefaultUDF)udf).setDefinition(allowedUdfs[match]);
+			}
+		}
+	}
+
+	/**
+	 * <p>Tell whether the type of all parameters of the given ADQL function
+	 * is resolved.</p>
+	 * 
+	 * <p>A parameter type may not be resolved for 2 main reasons:</p>
+	 * <ul>
+	 * 	<li>the parameter is a <b>column</b>, but this column has not been successfully resolved. Thus its type is still unknown.</li>
+	 * 	<li>the parameter is a <b>UDF</b>, but this UDF has not been already resolved. Thus, as for the column, its return type is still unknown.
+	 * 		But it could be known later if the UDF is resolved later ; a second try should be done afterwards.</li>
+	 * </ul>
+	 * 
+	 * @param fct	ADQL function whose the parameters' type should be checked.
+	 * 
+	 * @return	<i>true</i> if the type of all parameters is known, <i>false</i> otherwise.
+	 * 
+	 * @since 1.3
+	 */
+	protected final boolean isAllParamTypesResolved(final ADQLFunction fct){
+		for(ADQLOperand op : fct.getParameters()){
+			if (op.isNumeric() == op.isString())
+				return false;
+		}
+		return true;
+	}
+
+	/* ************************************************************************************************* */
+	/* METHODS CHECKING THE GEOMETRIES (geometrical functions, coordinate systems and STC-S expressions) */
+	/* ************************************************************************************************* */
+
+	/**
+	 * <p>Check all geometries.</p>
+	 * 
+	 * <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>
+	 * 
+	 * @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>(){
+			@Override
+			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:
+		if (allowedCoordSys != null)
+			resolveCoordinateSystems(query, errors);
+
+		// c. 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);
+	}
+
+	/**
+	 * Search for all geometrical functions and check whether they are allowed.
+	 * 
+	 * @param query		Query in which geometrical functions must be checked.
+	 * @param errors	List of errors to complete in this function each time a geometrical function is not supported.
+	 * 
+	 * @see #checkGeometryFunction(String, ADQLFunction, BinarySearch, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected 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){
+			fctName = result.getName();
+			checkGeometryFunction(fctName, (ADQLFunction)result, binSearch, errors);
+		}
+	}
+
+	/**
+	 * <p>Check whether the specified geometrical function is allowed by this implementation.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	If the list of allowed geometrical functions is empty, this function will always add an errors to the given list.
+	 * 	Indeed, it means that no geometrical function is allowed and so that the specified function is automatically not supported.
+	 * </i></p>
+	 * 
+	 * @param fctName		Name of the geometrical function to test.
+	 * @param fct			The function instance being or containing the geometrical function to check. <i>Note: this function can be the function to test or a function embedding the function under test (i.e. RegionFunction).
+	 * @param binSearch		The object to use in order to search a function name inside the list of allowed functions.
+	 *                 		It is able to perform a binary search inside a sorted array of String objects. The interest of
+	 *                 		this object is its compare function which must be overridden and tells how to compare the item
+	 *                 		to search and the items of the array (basically, a non-case-sensitive comparison between 2 strings).
+	 * @param errors		List of errors to complete in this function each time a geometrical function is not supported.
+	 * 
+	 * @since 1.3
+	 */
+	protected 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);
+		if (match < 0)
+			errors.addException(new UnresolvedFunction("The geometrical function \"" + fctName + "\" is not available in this implementation!", fct));
+	}
+
+	/**
+	 * <p>Search all explicit coordinate system declarations, check their syntax and whether they are allowed by this implementation.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	"explicit" means here that all {@link StringConstant} instances. Only coordinate systems expressed as string can
+	 * 	be parsed and so checked. So if a coordinate system is specified by a column, no check can be done at this stage...
+	 * 	it will be possible to perform such test only at the execution.
+	 * </i></p>
+	 * 
+	 * @param query		Query in which coordinate systems must be checked.
+	 * @param errors	List of errors to complete in this function each time a coordinate system has a wrong syntax or is not supported.
+	 * 
+	 * @see #checkCoordinateSystem(StringConstant, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected void resolveCoordinateSystems(final ADQLQuery query, final UnresolvedIdentifiersException errors){
+		ISearchHandler sHandler = new SearchCoordSysHandler();
+		sHandler.search(query);
+		for(ADQLObject result : sHandler)
+			checkCoordinateSystem((StringConstant)result, errors);
+	}
+
+	/**
+	 * Parse and then check the coordinate system contained in the given {@link StringConstant} instance.
+	 * 
+	 * @param adqlCoordSys	The {@link StringConstant} object containing the coordinate system to check.
+	 * @param errors		List of errors to complete in this function each time a coordinate system has a wrong syntax or is not supported.
+	 * 
+	 * @see STCS#parseCoordSys(String)
+	 * @see #checkCoordinateSystem(CoordSys, ADQLOperand, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkCoordinateSystem(final StringConstant adqlCoordSys, final UnresolvedIdentifiersException errors){
+		String coordSysStr = adqlCoordSys.getValue();
+		try{
+			checkCoordinateSystem(STCS.parseCoordSys(coordSysStr), adqlCoordSys, errors);
+		}catch(ParseException pe){
+			errors.addException(new ParseException(pe.getMessage())); // TODO Missing object position!
+		}
+	}
+
+	/**
+	 * Check whether the given coordinate system is allowed by this implementation. 
+	 * 
+	 * @param coordSys	Coordinate system to test.
+	 * @param operand	The operand representing or containing the coordinate system under test.
+	 * @param errors	List of errors to complete in this function each time a coordinate system is not supported.
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkCoordinateSystem(final CoordSys coordSys, final ADQLOperand operand, final UnresolvedIdentifiersException errors){
+		if (coordSysRegExp != null && coordSys != null && !coordSys.toFullSTCS().matches(coordSysRegExp))
+			errors.addException(new ParseException("Coordinate system \"" + ((operand instanceof StringConstant) ? ((StringConstant)operand).getValue() : coordSys.toString()) + "\" (= \"" + coordSys.toFullSTCS() + "\") not allowed in this implementation."));	// TODO Missing object position! + List of accepted coordinate systems
+	}
+
+	/**
+	 * <p>Search all STC-S expressions inside the given query, parse them (and so check their syntax) and then determine
+	 * whether the declared coordinate system and the expressed region are allowed in this implementation.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	In the current ADQL language definition, STC-S expressions can be found only as only parameter of the REGION function.
+	 * </i></p>
+	 * 
+	 * @param query			Query in which STC-S expressions must be checked.
+	 * @param binSearch		The object to use in order to search a region name inside the list of allowed functions/regions.
+	 *                 		It is able to perform a binary search inside a sorted array of String objects. The interest of
+	 *                 		this object is its compare function which must be overridden and tells how to compare the item
+	 *                 		to search and the items of the array (basically, a non-case-sensitive comparison between 2 strings).
+	 * @param errors		List of errors to complete in this function each time the STC-S syntax is wrong or each time the declared coordinate system or region is not supported.
+	 * 
+	 * @see STCS#parseRegion(String)
+	 * @see #checkRegion(Region, RegionFunction, BinarySearch, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected void resolveSTCSExpressions(final ADQLQuery query, final BinarySearch<String,String> binSearch, final UnresolvedIdentifiersException errors){
+		// Search REGION functions:
+		ISearchHandler sHandler = new SearchRegionHandler();
+		sHandler.search(query);
+
+		// Parse and check their STC-S expression:
+		String stcs;
+		Region region;
+		for(ADQLObject result : sHandler){
+			try{
+				// get the STC-S expression:
+				stcs = ((StringConstant)((RegionFunction)result).getParameter(0)).getValue();
+
+				// parse the STC-S expression (and so check the syntax):
+				region = STCS.parseRegion(stcs);
+
+				// 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){
+				errors.addException(new ParseException(pe.getMessage())); // TODO Missing object position!
+			}
+		}
+	}
+
+	/**
+	 * <p>Check the given region.</p>
+	 * 
+	 * <p>The following points are checked in this function:</p>
+	 * <ul>
+	 * 	<li>whether the coordinate system is allowed</li>
+	 * 	<li>whether the type of region is allowed</li>
+	 * 	<li>whether the inner regions are correct (here this function is called recursively on each inner region).</li>
+	 * </ul>
+	 * 
+	 * @param r			The region to check.
+	 * @param fct		The REGION function containing the region to check.
+	 * @param errors	List of errors to complete in this function if the given region or its inner regions are not supported.
+	 * 
+	 * @see #checkCoordinateSystem(CoordSys, ADQLOperand, UnresolvedIdentifiersException)
+	 * @see #checkGeometryFunction(String, ADQLFunction, BinarySearch, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkRegion(final Region r, final RegionFunction fct, final BinarySearch<String,String> binSearch, final UnresolvedIdentifiersException errors){
+		if (r == null)
+			return;
+
+		// Check the coordinate system (if any):
+		if (r.coordSys != null)
+			checkCoordinateSystem(r.coordSys, fct, errors);
+
+		// Check that the region type is allowed:
+		if (allowedGeo != null){
+			if (allowedGeo.length == 0)
+				errors.addException(new UnresolvedFunction("The region type \"" + r.type + "\" is not available in this implementation!", fct));
+			else
+				checkGeometryFunction((r.type == RegionType.POSITION) ? "POINT" : r.type.toString(), fct, binSearch, errors);
+		}
+
+		// Check all the inner regions:
+		if (r.regions != null){
+			for(Region innerR : r.regions)
+				checkRegion(innerR, fct, binSearch, errors);
+		}
+	}
+
+	/* **************************************************** */
+	/* METHODS CHECKING TYPES UNKNOWN WHILE CHECKING SYNTAX */
+	/* **************************************************** */
+
+	/**
+	 * <p>Search all operands whose the type is not yet known and try to resolve it now
+	 * and to check whether it matches the type expected by the syntactic parser.</p>
+	 * 
+	 * <p>
+	 * 	Only two operands may have an unresolved type: columns and user defined functions.
+	 * 	Indeed, their type can be resolved only if the list of available columns and UDFs is known,
+	 * 	and if columns and UDFs used in the query are resolved successfully.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	When an operand type is still unknown, they will own the three kinds of type and
+	 * 	so this function won't raise an error: it is thus automatically on the expected type.
+	 * 	This behavior is perfectly correct because if the type is not resolved
+	 * 	that means the item/operand has not been resolved in the previous steps and so that
+	 * 	an error about this item has already been raised.
+	 * </p>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	This function does not check the types exactly, but just roughly by considering only three categories:
+	 * 	string, numeric and geometry.
+	 * </i></p>
+	 * 
+	 * @param query		Query in which unknown types must be resolved and checked.
+	 * @param errors	List of errors to complete in this function each time a types does not match to the expected one.
+	 * 
+	 * @see UnknownType
+	 * 
+	 * @since 1.3
+	 */
+	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){
+			unknown = (UnknownType)result;
+			switch(unknown.getExpectedType()){
+				case 'G':
+				case 'g':
+					if (!unknown.isGeometry())
+						errors.addException(new ParseException("Type mismatch! A geometry was expected instead of \"" + unknown.toADQL() + "\"."));	// TODO Add the ADQLOperand position!
+					break;
+				case 'N':
+				case 'n':
+					if (!unknown.isNumeric())
+						errors.addException(new ParseException("Type mismatch! A numeric value was expected instead of \"" + unknown.toADQL() + "\"."));	// TODO Add the ADQLOperand position!
+					break;
+				case 'S':
+				case 's':
+					if (!unknown.isString())
+						errors.addException(new ParseException("Type mismatch! A string value was expected instead of \"" + unknown.toADQL() + "\"."));	// TODO Add the ADQLOperand position!
+					break;
+			}
+		}
+	}
+
+	/* ******************************** */
+	/* METHODS CHECKING THE SUB-QUERIES */
+	/* ******************************** */
+
+	/**
+	 * <p>Search all sub-queries found in the given query but not in the clause FROM.
+	 * These sub-queries are then checked using {@link #check(ADQLQuery, Stack)}.</p>
+	 * 
+	 * <b>Fathers stack</b>
+	 * <p>
+	 * 	Each time a sub-query must be checked with {@link #check(ADQLQuery, Stack)},
+	 * 	the list of all columns available in each of its father queries must be provided.
+	 * 	This function is composing itself this stack by adding the given list of available
+	 * 	columns (= all columns resolved in the given query) at the end of the given stack.
+	 * 	If this stack is given empty, then a new stack is created.
+	 * </p>
+	 * <p>
+	 * 	This modification of the given stack is just the execution time of this function.
+	 * 	Before returning, this function removes the last item of the stack.
+	 * </p>
+	 * 
+	 * 
+	 * @param query				Query in which sub-queries must be checked.
+	 * @param fathersList		List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                      	Each item of this stack is a list of columns available in each father-level query.
+	 *                   		<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
+	 * @param availableColumns	List of all columns resolved in the given query.
+	 * @param errors			List of errors to complete in this function each time a semantic error is encountered.
+	 * 
+	 * @since 1.3
+	 */
+	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){
+
+			// Push the list of columns into the father columns stack:
+			if (fathersList == null)
+				fathersList = new Stack<SearchColumnList>();
+			fathersList.push(availableColumns);
+
+			// Check each found sub-query:
+			for(ADQLObject result : sHandler){
+				try{
+					check((ADQLQuery)result, fathersList);
+				}catch(UnresolvedIdentifiersException uie){
+					Iterator<ParseException> itPe = uie.getErrors();
+					while(itPe.hasNext())
+						errors.addException(itPe.next());
+				}
+			}
+
+			// Pop the list of columns from the father columns stack:
+			fathersList.pop();
+		}
+	}
+
 	/* *************** */
 	/* SEARCH HANDLERS */
 	/* *************** */
@@ -462,7 +1226,7 @@ public class DBChecker implements QueryChecker {
 	 * Lets searching all tables.
 	 * 
 	 * @author Gr&eacute;gory Mantelet (CDS)
-	 * @version 07/2011
+	 * @version 1.0 (07/2011)
 	 */
 	private static class SearchTableHandler extends SimpleSearchHandler {
 		@Override
@@ -475,7 +1239,7 @@ public class DBChecker implements QueryChecker {
 	 * Lets searching all wildcards.
 	 * 
 	 * @author Gr&eacute;gory Mantelet (CDS)
-	 * @version 09/2011
+	 * @version 1.0 (09/2011)
 	 */
 	private static class SearchWildCardHandler extends SimpleSearchHandler {
 		@Override
@@ -488,7 +1252,7 @@ public class DBChecker implements QueryChecker {
 	 * Lets searching column references.
 	 * 
 	 * @author Gr&eacute;gory Mantelet (CDS)
-	 * @version 11/2011
+	 * @version 1.0 (11/2011)
 	 */
 	private static class SearchColReferenceHandler extends SimpleSearchHandler {
 		@Override
@@ -527,4 +1291,168 @@ public class DBChecker implements QueryChecker {
 		}
 	}
 
+	/**
+	 * Let searching user defined functions.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class SearchUDFHandler extends SimpleSearchHandler {
+		@Override
+		protected boolean match(ADQLObject obj){
+			return (obj instanceof UserDefinedFunction);
+		}
+	}
+
+	/**
+	 * Let searching geometrical functions.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class SearchGeometryHandler extends SimpleSearchHandler {
+		@Override
+		protected boolean match(ADQLObject obj){
+			return (obj instanceof GeometryFunction);
+		}
+	}
+
+	/**
+	 * <p>Let searching all ADQL objects whose the type was not known while checking the syntax of the ADQL query.
+	 * These objects are {@link ADQLColumn}s and {@link UserDefinedFunction}s.</p>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	Only {@link UnknownType} instances having an expected type equals to 'S' (or 's' ; for string) or 'N' (or 'n' ; for numeric)
+	 * 	are kept by this handler. Others are ignored.
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class SearchUnknownTypeHandler extends SimpleSearchHandler {
+		@Override
+		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
+				return false;
+		}
+	}
+
+	/**
+	 * Let searching all explicit declaration of coordinate systems.
+	 * So, only {@link StringConstant} objects will be returned.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class SearchCoordSysHandler extends SimpleSearchHandler {
+		@Override
+		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
+				return false;
+		}
+
+		@Override
+		protected void addMatch(ADQLObject matchObj, ADQLIterator it){
+			results.add(((GeometryFunction)matchObj).getCoordinateSystem());
+		}
+
+	}
+
+	/**
+	 * Let searching all {@link RegionFunction}s.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class SearchRegionHandler extends SimpleSearchHandler {
+		@Override
+		protected boolean match(ADQLObject obj){
+			if (obj instanceof RegionFunction)
+				return (((RegionFunction)obj).getParameter(0) instanceof StringConstant);
+			else
+				return false;
+		}
+
+	}
+
+	/**
+	 * <p>Implement the binary search algorithm over a sorted array.</p>
+	 * 
+	 * <p>
+	 * 	The only difference with the standard implementation of Java is
+	 * 	that this object lets perform research with a different type
+	 * 	of object than the types of array items.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	For that reason, the "compare" function must always be implemented.
+	 * </p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * 
+	 * @param <T>	Type of items stored in the array.
+	 * @param <S>	Type of the item to search.
+	 * 
+	 * @since 1.3
+	 */
+	protected static abstract class BinarySearch< T, S > {
+		private int s, e, m, comp;
+
+		/**
+		 * <p>Search the given item in the given array.</p>
+		 * 
+		 * <p>
+		 * 	In case the given object matches to several items of the array,
+		 * 	this function will return the smallest index, pointing thus to the first
+		 * 	of all matches.
+		 * </p>
+		 * 
+		 * @param searchItem	Object for which a corresponding array item must be searched.
+		 * @param array			Array in which the given object must be searched.
+		 * 
+		 * @return	The array index of the first item of all matches.
+		 */
+		public int search(final S searchItem, final T[] array){
+			s = 0;
+			e = array.length - 1;
+			while(s < e){
+				// middle of the sorted array:
+				m = s + ((e - s) / 2);
+				// compare the fct with the middle item of the array:
+				comp = compare(searchItem, array[m]);
+				// if the fct is after, trigger the inspection of the right part of the array: 
+				if (comp > 0)
+					s = m + 1;
+				// otherwise, the left part:
+				else
+					e = m;
+			}
+			if (s != e || compare(searchItem, array[s]) != 0)
+				return -1;
+			else
+				return s;
+		}
+
+		/**
+		 * Compare the search item and the array item.
+		 * 
+		 * @param searchItem	Item whose a corresponding value must be found in the array.
+		 * @param arrayItem		An item of the array.
+		 * 
+		 * @return	Negative value if searchItem is less than arrayItem, 0 if they are equals, or a positive value if searchItem is greater.
+		 */
+		protected abstract int compare(final S searchItem, final T arrayItem);
+	}
+
 }
diff --git a/src/adql/db/DBColumn.java b/src/adql/db/DBColumn.java
index a803717e449bb7d1bf8594a3aee51a1b632eb2f9..c987e062cc161f5b20488c8e4652accc00455463 100644
--- a/src/adql/db/DBColumn.java
+++ b/src/adql/db/DBColumn.java
@@ -16,7 +16,8 @@ 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2011,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 /**
@@ -27,8 +28,8 @@ package adql.db;
  * 	and corresponds to a real column in the "database" with its DB name ({@link #getDBName()}).
  * </p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 08/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public interface DBColumn {
 
@@ -46,6 +47,19 @@ public interface DBColumn {
 	 */
 	public String getDBName();
 
+	/**
+	 * <p>Get the type of this column (as closed as possible from the "database" type).</p>
+	 * 
+	 * <p><i>Note:
+	 * 	The returned type should be as closed as possible from a type listed by the IVOA in the TAP protocol description into the section UPLOAD.
+	 * </i></p>
+	 * 
+	 * @return	Its type.
+	 * 
+	 * @since 1.3
+	 */
+	public DBType getDatatype();
+
 	/**
 	 * Gets the table which contains this {@link DBColumn}.
 	 * 
diff --git a/src/adql/db/DBCommonColumn.java b/src/adql/db/DBCommonColumn.java
index cab30f6f86cf8be463679d0fbf78fdb1c8c22257..8c5d3ba8e3c9ce5a8f4569c3c575060933adbfdf 100644
--- a/src/adql/db/DBCommonColumn.java
+++ b/src/adql/db/DBCommonColumn.java
@@ -16,12 +16,13 @@ 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 2014 - Astronomishes Rechen Institut (ARI)
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
 import java.util.Iterator;
 
+import adql.db.exception.UnresolvedJoin;
 import adql.query.ADQLQuery;
 
 /**
@@ -33,7 +34,7 @@ import adql.query.ADQLQuery;
  * in case of several JOINs.
  * 
  * @author Gr&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
- * @version 1.2 (11/2013)
+ * @version 1.3 (10/2014)
  * @since 1.2
  */
 public class DBCommonColumn implements DBColumn {
@@ -54,8 +55,13 @@ public class DBCommonColumn implements DBColumn {
 	 * 
 	 * @param leftCol	Column of the left join table. May be a {@link DBCommonColumn}.
 	 * @param rightCol	Column of the right join table. May be a {@link DBCommonColumn}.
+	 * 
+	 * @throws UnresolvedJoin	If the type of the two given columns are not roughly (just testing numeric, string or geometry) compatible.
 	 */
-	public DBCommonColumn(final DBColumn leftCol, final DBColumn rightCol){
+	public DBCommonColumn(final DBColumn leftCol, final DBColumn rightCol) throws UnresolvedJoin{
+		// Test whether type of both columns are compatible:
+		if (leftCol.getDatatype() != null && rightCol.getDatatype() != null && !leftCol.getDatatype().isCompatible(rightCol.getDatatype()))
+			throw new UnresolvedJoin("JOIN impossible: incompatible column types when trying to join the columns " + leftCol.getADQLName() + " (" + leftCol.getDatatype() + ") and " + rightCol.getADQLName() + " (" + rightCol.getDatatype() + ")!");
 
 		// LEFT COLUMN:
 		if (leftCol instanceof DBCommonColumn){
@@ -83,7 +89,6 @@ public class DBCommonColumn implements DBColumn {
 			// add the table to cover:
 			addCoveredTable(rightCol.getTable());
 		}
-
 	}
 
 	/**
@@ -112,6 +117,11 @@ public class DBCommonColumn implements DBColumn {
 		return generalColumnDesc.getDBName();
 	}
 
+	@Override
+	public final DBType getDatatype(){
+		return generalColumnDesc.getDatatype();
+	}
+
 	@Override
 	public final DBTable getTable(){
 		return null;
diff --git a/src/tap/metadata/TAPType.java b/src/adql/db/DBType.java
similarity index 51%
rename from src/tap/metadata/TAPType.java
rename to src/adql/db/DBType.java
index 1990d56b98206001bac4ee2c79076b368ed89aec..87f6c05c81674a98bed60cc17ec67a7c2947eb0a 100644
--- a/src/tap/metadata/TAPType.java
+++ b/src/adql/db/DBType.java
@@ -1,14 +1,14 @@
-package tap.metadata;
+package adql.db;
 
 /*
- * This file is part of TAPLibrary.
+ * This file is part of ADQLLibrary.
  * 
- * TAPLibrary is free software: you can redistribute it and/or modify
+ * ADQLLibrary is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  * 
- * TAPLibrary is distributed in the hope that it will be useful,
+ * ADQLLibrary is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU Lesser General Public License for more details.
@@ -16,35 +16,35 @@ package tap.metadata;
  * You should have received a copy of the GNU Lesser General Public License
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
- * Copyright 2014 - Astronomishes Rechen Institut (ARI)
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
  */
 
 /**
  * 
  * <p>
- * 	Describe a full TAP column type as it is described in the IVOA document.
+ * 	Describe a full column type as it is described in the IVOA document of TAP.
  * 	Thus, this object contains 2 attributes: <code>type</code> (or datatype) and <code>length</code> (or size).
  * </p>
  * 
  * <p>The length/size may be not defined ; in this case, its value is set to {@link #NO_LENGTH} or is negative or null.</p>
  * 
- * <p>All datatypes declared in the IVOA recommendation document of TAP are listed in an enumeration type: {@link TAPDatatype}.
+ * <p>All datatypes declared in the IVOA recommendation document of TAP are listed in an enumeration type: {@link DBDatatype}.
  * It is used to set the attribute type/datatype of this class.</p>
  *  
- * @author Gr&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
- * @version 2.0 (06/2014)
- * @since 2.0
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 1.3 (10/2014)
+ * @since 1.3
  */
-public class TAPType {
+public class DBType {
 
 	/**
 	 * List of all datatypes declared in the IVOA recommendation of TAP (in the section UPLOAD).
 	 * 
-	 * @author Gr&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
-	 * @version 2.0 (06/2014)
-	 * @since 2.0
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
 	 */
-	public static enum TAPDatatype{
+	public static enum DBDatatype{
 		SMALLINT, INTEGER, BIGINT, REAL, DOUBLE, BINARY, VARBINARY, CHAR, VARCHAR, BLOB, CLOB, TIMESTAMP, POINT, REGION;
 	}
 
@@ -52,7 +52,7 @@ public class TAPType {
 	public static final int NO_LENGTH = -1;
 
 	/** Datatype of a column. */
-	public final TAPDatatype type;
+	public final DBDatatype type;
 
 	/** The length parameter (only few datatypes need this parameter: char, varchar, binary and varbinary). */
 	public final int length;
@@ -62,7 +62,7 @@ public class TAPType {
 	 * 
 	 * @param datatype	Column datatype.
 	 */
-	public TAPType(final TAPDatatype datatype){
+	public DBType(final DBDatatype datatype){
 		this(datatype, NO_LENGTH);
 	}
 
@@ -72,22 +72,71 @@ public class TAPType {
 	 * @param datatype	Column datatype.
 	 * @param length	Length of the column value (needed only for datatypes like char, varchar, binary and varbinary).
 	 */
-	public TAPType(final TAPDatatype datatype, final int length){
+	public DBType(final DBDatatype datatype, final int length){
 		if (datatype == null)
 			throw new NullPointerException("Missing TAP column datatype !");
 		this.type = datatype;
 		this.length = length;
 	}
 
-	/**
-	 * Convert this TAP column type into a VOTable field type.
-	 * 
-	 * @return	The corresponding VOTable field type.
-	 * 
-	 * @see VotType#VotType(TAPType)
-	 */
-	public VotType toVotType(){
-		return new VotType(this);
+	public boolean isNumeric(){
+		switch(type){
+			case SMALLINT:
+			case INTEGER:
+			case BIGINT:
+			case REAL:
+			case DOUBLE:
+				/* Note: binaries are also included here because they can also be considered as Numeric,
+				 * but not for JOINs. */
+			case BINARY:
+			case VARBINARY:
+			case BLOB:
+				return true;
+			default:
+				return false;
+		}
+	}
+
+	public boolean isBinary(){
+		switch(type){
+			case BINARY:
+			case VARBINARY:
+			case BLOB:
+				return true;
+			default:
+				return false;
+		}
+	}
+
+	public boolean isString(){
+		switch(type){
+			case CHAR:
+			case VARCHAR:
+			case CLOB:
+			case TIMESTAMP:
+				return true;
+			default:
+				return false;
+		}
+	}
+
+	public boolean isGeometry(){
+		return (type == DBDatatype.POINT || type == DBDatatype.REGION);
+	}
+
+	public boolean isCompatible(final DBType t){
+		if (t == null)
+			return false;
+		else if (isBinary() == t.isBinary())
+			return (type == DBDatatype.BLOB && t.type == DBDatatype.BLOB) || (type != DBDatatype.BLOB && t.type != DBDatatype.BLOB);
+		else if (isNumeric() == t.isNumeric())
+			return true;
+		else if (isGeometry() == t.isGeometry())
+			return (type == t.type);
+		else if (isString())
+			return (type == DBDatatype.CLOB && t.type == DBDatatype.CLOB) || (type != DBDatatype.CLOB && t.type != DBDatatype.CLOB);
+		else
+			return (type == t.type);
 	}
 
 	@Override
diff --git a/src/adql/db/DefaultDBColumn.java b/src/adql/db/DefaultDBColumn.java
index 8496501aa3ee7ecc3fb15619c8bddecafb98e3ef..a4ed9e3bc557bc8cba391bc68c35e269dbf27ea9 100644
--- a/src/adql/db/DefaultDBColumn.java
+++ b/src/adql/db/DefaultDBColumn.java
@@ -16,20 +16,27 @@ 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 /**
  * Default implementation of {@link DBColumn}.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 08/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class DefaultDBColumn implements DBColumn {
 
+	/** Name of the column in the "database". */
 	protected String dbName;
+	/** Type of the column in the "database".
+	 * <i>Note: This should be one of the types listed by the IVOA in the TAP description.</i>
+	 * @since 1.3 */
+	protected DBType type;
+	/** Table in which this column exists. */
 	protected DBTable table;
-
+	/** Name that this column must have in ADQL queries. */
 	protected String adqlName = null;
 
 	/**
@@ -40,10 +47,30 @@ public class DefaultDBColumn implements DBColumn {
 	 * 					if a whole column reference is given, no split will be done.</b>
 	 * @param table		DB table which contains this column.
 	 * 
-	 * @see #DefaultDBColumn(String, String, DBTable)
+	 * @see #DefaultDBColumn(String, String, DBType, DBTable)
 	 */
 	public DefaultDBColumn(final String dbName, final DBTable table){
-		this(dbName, dbName, table);
+		this(dbName, dbName, null, table);
+	}
+
+	/**
+	 * Builds a default {@link DBColumn} with the given DB name and DB table.
+	 * 
+	 * @param dbName	Database column name (it will be also used for the ADQL name).
+	 * 					<b>Only the column name is expected. Contrary to {@link DefaultDBTable},
+	 * 					if a whole column reference is given, no split will be done.</b>
+	 * @param type		Type of the column.
+	 *            		<i>Note: there is no default value. Consequently if this parameter is NULL,
+	 *            		the type should be considered as unknown. It means that any comparison with
+	 *            		any type will always return 'true'.</i>
+	 * @param table		DB table which contains this column.
+	 * 
+	 * @see #DefaultDBColumn(String, String, DBType, DBTable)
+	 * 
+	 * @since 1.3
+	 */
+	public DefaultDBColumn(final String dbName, final DBType type, final DBTable table){
+		this(dbName, dbName, type, table);
 	}
 
 	/**
@@ -56,13 +83,38 @@ public class DefaultDBColumn implements DBColumn {
 	 * 					<b>Only the column name is expected. Contrary to {@link DefaultDBTable},
 	 * 					if a whole column reference is given, no split will be done.</b>
 	 * @param table		DB table which contains this column.
+	 * 
+	 * @see #DefaultDBColumn(String, String, DBType, DBTable)
 	 */
 	public DefaultDBColumn(final String dbName, final String adqlName, final DBTable table){
+		this(dbName, adqlName, null, table);
+	}
+
+	/**
+	 * Builds a default {@link DBColumn} with the given DB name, DB table and ADQL name.
+	 * 
+	 * @param dbName	Database column name.
+	 * 					<b>Only the column name is expected. Contrary to {@link DefaultDBTable},
+	 * 					if a whole column reference is given, no split will be done.</b>
+	 * @param adqlName	Column name used in ADQL queries.
+	 * 					<b>Only the column name is expected. Contrary to {@link DefaultDBTable},
+	 * 					if a whole column reference is given, no split will be done.</b>
+	 * @param type		Type of the column.
+	 *            		<i>Note: there is no default value. Consequently if this parameter is NULL,
+	 *            		the type should be considered as unknown. It means that any comparison with
+	 *            		any type will always return 'true'.</i>
+	 * @param table		DB table which contains this column.
+	 * 
+	 * @since 1.3
+	 */
+	public DefaultDBColumn(final String dbName, final String adqlName, final DBType type, final DBTable table){
 		this.dbName = dbName;
 		this.adqlName = adqlName;
+		this.type = type;
 		this.table = table;
 	}
 
+	@Override
 	public final String getADQLName(){
 		return adqlName;
 	}
@@ -72,10 +124,38 @@ public class DefaultDBColumn implements DBColumn {
 			this.adqlName = adqlName;
 	}
 
+	@Override
+	public final DBType getDatatype(){
+		return type;
+	}
+
+	/**
+	 * <p>Set the type of this column.</p>
+	 * 
+	 * <p><i>Note 1:
+	 * 	The given type should be as closed as possible from a type listed by the IVOA in the TAP protocol description into the section UPLOAD.
+	 * </i></p>
+	 * 
+	 * <p><i>Note 2:
+	 * 	there is no default value. Consequently if this parameter is NULL,
+	 * 	the type should be considered as unknown. It means that any comparison with
+	 * 	any type will always return 'true'.
+	 * </i></p>
+	 * 
+	 * @param type	New type of this column.
+	 * 
+	 * @since 1.3
+	 */
+	public final void setDatatype(final DBType type){
+		this.type = type;
+	}
+
+	@Override
 	public final String getDBName(){
 		return dbName;
 	}
 
+	@Override
 	public final DBTable getTable(){
 		return table;
 	}
@@ -84,8 +164,9 @@ public class DefaultDBColumn implements DBColumn {
 		this.table = table;
 	}
 
+	@Override
 	public DBColumn copy(final String dbName, final String adqlName, final DBTable dbTable){
-		return new DefaultDBColumn(dbName, adqlName, dbTable);
+		return new DefaultDBColumn(dbName, adqlName, type, dbTable);
 	}
 
 }
diff --git a/src/adql/db/FunctionDef.java b/src/adql/db/FunctionDef.java
new file mode 100644
index 0000000000000000000000000000000000000000..a8071eecb96545e846316d58c83a65de0e13eb43
--- /dev/null
+++ b/src/adql/db/FunctionDef.java
@@ -0,0 +1,476 @@
+package adql.db;
+
+/*
+ * This file is part of ADQLLibrary.
+ * 
+ * ADQLLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * ADQLLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * 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 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import adql.db.DBType.DBDatatype;
+import adql.parser.ParseException;
+import adql.query.operand.function.ADQLFunction;
+
+/**
+ * <p>Definition of any function that could be used in ADQL queries.</p>
+ * 
+ * <p>
+ * 	A such definition can be built manually thanks to the different constructors of this class,
+ * 	or by parsing a string function definition form using the static function {@link #parse(String)}.
+ * </p>
+ * 
+ * <p>
+ * 	The syntax of the expression expected by {@link #parse(String)} is the same as the one used to build
+ * 	the string returned by {@link #toString()}. Here is this syntax:
+ * </p>
+ * <pre>{fctName}([{param1Name} {param1Type}, ...])[ -> {returnType}]</pre>
+ * 
+ * <p>
+ * 	A description of this function may be set thanks to the public class attribute {@link #description}.
+ * </p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 1.3 (10/2014)
+ * 
+ * @since 1.3
+ */
+public class FunctionDef implements Comparable<FunctionDef> {
+	/** Regular expression for what should be a function or parameter name - a regular identifier. */
+	protected final static String regularIdentifierRegExp = "[a-zA-Z]+[0-9a-zA-Z_]*";
+	/** Rough regular expression for a function return type or a parameter type.
+	 * The exact type is not checked here ; just the type name syntax is tested, not its value.
+	 * This regular expression allows a type to have exactly one parameter (which is generally the length of a character or binary string. */
+	protected final static String typeRegExp = "([a-zA-Z]+[0-9a-zA-Z]*)(\\(\\s*([0-9]+)\\s*\\))?";
+	/** Rough regular expression for a function parameters' list. */
+	protected final static String fctParamsRegExp = "\\s*[^,]+\\s*(,\\s*[^,]+\\s*)*";
+	/** Rough regular expression for a function parameter: a name (see {@link #regularIdentifierRegExp}) and a type (see {@link #typeRegExp}). */
+	protected final static String fctParamRegExp = "\\s*(" + regularIdentifierRegExp + ")\\s+" + typeRegExp + "\\s*";
+	/** Rough regular expression for a whole function definition. */
+	protected final static String fctDefRegExp = "\\s*(" + regularIdentifierRegExp + ")\\s*\\(([a-zA-Z0-9,() \r\n\t]*)\\)(\\s*->\\s*(" + typeRegExp + "))?\\s*";
+
+	/** Pattern of a function definition. This object has been compiled with {@link #fctDefRegExp}. */
+	protected final static Pattern fctPattern = Pattern.compile(fctDefRegExp);
+	/** Pattern of a single parameter definition. This object has been compiled with {@link #fctParamRegExp}. */
+	protected final static Pattern paramPattern = Pattern.compile(fctParamRegExp);
+
+	/** Name of the function. */
+	public final String name;
+
+	/** Description of this function. */
+	public String description = null;
+
+	/** Type of the result returned by this function. */
+	public final DBType returnType;
+	/** Indicate whether the return type is a string. */
+	protected final boolean isString;
+	/** Indicate whether the return type is a numeric. */
+	protected final boolean isNumeric;
+	/** Indicate whether the return type is a geometry. */
+	protected final boolean isGeometry;
+
+	/** Total number of parameters. */
+	public final int nbParams;
+	/** List of all the parameters of this function. */
+	protected final FunctionParam[] params;
+
+	/** <p>String representation of this function.</p>
+	 * <p>The syntax of this representation is the following <i>(items between brackets are optional)</i>:</p>
+	 * <pre>{fctName}([{param1Name} {param1Type}, ...])[ -> {returnType}]</pre> */
+	private final String serializedForm;
+
+	/** <p>String representation of this function dedicated to comparison with any function signature.</p>
+	 * <p>This form is different from the serialized form on the following points:</p>
+	 * <ul>
+	 * 	<li>the function name is always in lower case.</li>
+	 * 	<li>each parameter is represented by a string of 3 characters, one for each kind of type (in the order): numeric, string, geometry.
+	 * 	    Each character is either a 0 or 1, so that indicating whether the parameter is of that kind of type.</li>
+	 * 	<li>no return type.</li>
+	 * </ul>
+	 * <p>So the syntax of this form is the following <i>(items between brackets are optional ; xxx is a string of 3 characters, each being either 0 or 1)</i>:</p>
+	 * <pre>{fctName}([xxx, ...])</pre> */
+	private final String compareForm;
+
+	/**
+	 * <p>Definition of a function parameter.</p>
+	 * 
+	 * <p>This definition is composed of two items: the name and the type of the parameter.</p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static final class FunctionParam {
+		/** Parameter name. <i>Ensured not null</i> */
+		public final String name;
+		/** Parameter type. <i>Ensured not null</i> */
+		public final DBType type;
+
+		/**
+		 * Create a function parameter.
+		 * 
+		 * @param paramName	Name of the parameter to create. <i>MUST NOT be NULL</i>
+		 * @param paramType	Type of the parameter to create. <i>MUST NOT be NULL</i>
+		 */
+		public FunctionParam(final String paramName, final DBType paramType){
+			if (paramName == null)
+				throw new NullPointerException("Missing name! The function parameter can not be created.");
+			if (paramType == null)
+				throw new NullPointerException("Missing type! The function parameter can not be created.");
+			this.name = paramName;
+			this.type = paramType;
+		}
+	}
+
+	/**
+	 * <p>Create a function definition.</p>
+	 * 
+	 * <p>The created function will have <b>no return type</b> and <b>no parameter</b>.</p> 
+	 * 
+	 * @param fctName	Name of the function.
+	 */
+	public FunctionDef(final String fctName){
+		this(fctName, null, null);
+	}
+
+	/**
+	 * <p>Create a function definition.</p>
+	 * 
+	 * <p>The created function will have a return type (if the provided one is not null) and <b>no parameter</b>.</p> 
+	 * 
+	 * @param fctName		Name of the function.
+	 * @param returnType	Return type of the function. <i>If NULL, this function will have no return type</i>
+	 */
+	public FunctionDef(final String fctName, final DBType returnType){
+		this(fctName, returnType, null);
+	}
+
+	/**
+	 * <p>Create a function definition.</p>
+	 * 
+	 * <p>The created function will have <b>no return type</b> and some parameters (except if the given array is NULL or empty).</p> 
+	 * 
+	 * @param fctName		Name of the function.
+	 * @param params		Parameters of this function. <i>If NULL or empty, this function will have no parameter.</i>
+	 */
+	public FunctionDef(final String fctName, final FunctionParam[] params){
+		this(fctName, null, params);
+	}
+
+	public FunctionDef(final String fctName, final DBType returnType, final FunctionParam[] params){
+		// Set the name:
+		if (fctName == null)
+			throw new NullPointerException("Missing name! Can not create this function definition.");
+		this.name = fctName;
+
+		// Set the parameters:
+		this.params = (params == null || params.length == 0) ? null : params;
+		this.nbParams = (params == null) ? 0 : params.length;
+
+		// Set the return type;
+		this.returnType = returnType;
+		if (returnType != null){
+			isNumeric = returnType.isNumeric();
+			isString = returnType.isString();
+			isGeometry = returnType.isGeometry();
+		}else
+			isNumeric = isString = isGeometry = false;
+
+		// Serialize in Strings (serializedForm and compareForm) this function definition:
+		StringBuffer bufSer = new StringBuffer(name), bufCmp = new StringBuffer(name.toLowerCase());
+		bufSer.append('(');
+		for(int i = 0; i < nbParams; i++){
+			bufSer.append(params[i].name).append(' ').append(params[i].type);
+			bufCmp.append(params[i].type.isNumeric() ? '1' : '0').append(params[i].type.isString() ? '1' : '0').append(params[i].type.isGeometry() ? '1' : '0');
+			if (i + 1 < nbParams)
+				bufSer.append(", ");
+		}
+		bufSer.append(')');
+		if (returnType != null)
+			bufSer.append(" -> ").append(returnType);
+		serializedForm = bufSer.toString();
+		compareForm = bufCmp.toString();
+	}
+
+	/**
+	 * Tell whether this function returns a numeric.
+	 * 
+	 * @return	<i>true</i> if this function returns a numeric, <i>false</i> otherwise.
+	 */
+	public final boolean isNumeric(){
+		return isNumeric;
+	}
+
+	/**
+	 * Tell whether this function returns a string.
+	 * 
+	 * @return	<i>true</i> if this function returns a string, <i>false</i> otherwise.
+	 */
+	public final boolean isString(){
+		return isString;
+	}
+
+	/**
+	 * Tell whether this function returns a geometry.
+	 * 
+	 * @return	<i>true</i> if this function returns a geometry, <i>false</i> otherwise.
+	 */
+	public final boolean isGeometry(){
+		return isGeometry;
+	}
+
+	/**
+	 * Get the number of parameters required by this function.
+	 * 
+	 * @return	Number of required parameters.
+	 */
+	public final int getNbParams(){
+		return nbParams;
+	}
+
+	/**
+	 * Get the definition of the indParam-th parameter of this function.
+	 * 
+	 * @param indParam	Index of the parameter whose the definition must be returned.
+	 * 
+	 * @return	Definition of the specified parameter.
+	 * 
+	 * @throws ArrayIndexOutOfBoundsException	If the given index is negative or bigger than the number of parameters.
+	 */
+	public final FunctionParam getParam(final int indParam) throws ArrayIndexOutOfBoundsException{
+		if (indParam < 0 || indParam >= nbParams)
+			throw new ArrayIndexOutOfBoundsException(indParam);
+		else
+			return params[indParam];
+	}
+
+	/**
+	 * <p>Let parsing the serialized form of a function definition.</p>
+	 * 
+	 * <p>The expected syntax is <i>(items between brackets are optional)</i>:</p>
+	 * <pre>{fctName}([{param1Name} {param1Type}, ...])[ -> {returnType}]</pre>
+	 * 
+	 * <p>
+	 * 	Allowed parameter types and return types should be one the types listed by the UPLOAD section of the TAP recommendation document.
+	 * 	These types are listed in the enumeration object {@link DBType}.
+	 * 	However, other types should be accepted like the common database types...but it should be better to not rely on that
+	 * 	since the conversion of those types to TAP types should not be exactly what is expected.
+	 * </p>
+	 * 
+	 * @param strDefinition	Serialized function definition to parse.
+	 * 
+	 * @return	The object representation of the given string definition.
+	 * 
+	 * @throws ParseException	If the given string has a wrong syntax or uses unknown types.
+	 */
+	public static FunctionDef parse(final String strDefinition) throws ParseException{
+		if (strDefinition == null)
+			throw new NullPointerException("Missing string definition to build a FunctionDef!");
+
+		// Check the global syntax of the function definition:
+		Matcher m = fctPattern.matcher(strDefinition);
+		if (m.matches()){
+
+			// Get the function name:
+			String fctName = m.group(1);
+
+			// Parse and get the return type:
+			DBType returnType = null;
+			if (m.group(3) != null){
+				returnType = parseType(m.group(5), (m.group(7) == null) ? DBType.NO_LENGTH : Integer.parseInt(m.group(7)));
+				if (returnType == null)
+					throw new ParseException("Unknown return type: \"" + m.group(4).trim() + "\"!");
+			}
+
+			// Get the parameters, if any:
+			String paramsList = m.group(2);
+			FunctionParam[] params = null;
+			if (paramsList != null && paramsList.trim().length() > 0){
+
+				// Check the syntax of the parameters' list:
+				if (!paramsList.matches(fctParamsRegExp))
+					throw new ParseException("Wrong parameters syntax! Expected syntax: \"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"()\", \"(param INTEGER)\", \"(param1 INTEGER, param2 DOUBLE)\"");
+
+				// Split all the parameter definitions:
+				String[] paramsSplit = paramsList.split(",");
+				params = new FunctionParam[paramsSplit.length];
+				DBType paramType;
+
+				// For each parameter definition...
+				for(int i = 0; i < params.length; i++){
+					m = paramPattern.matcher(paramsSplit[i]);
+					if (m.matches()){
+
+						// ...parse and get the parameter type:
+						paramType = parseType(m.group(2), (m.group(4) == null) ? DBType.NO_LENGTH : Integer.parseInt(m.group(4)));
+
+						// ...build the parameter definition object:
+						if (paramType == null)
+							throw new ParseException("Unknown type for the parameter \"" + m.group(1) + "\": \"" + m.group(2) + ((m.group(3) == null) ? "" : m.group(3)) + "\"!");
+						else
+							params[i] = new FunctionParam(m.group(1), paramType);
+					}else
+						// note: should never happen because we have already check the syntax of the whole parameters list before parsing each individual parameter.
+						throw new ParseException("Wrong syntax for the " + (i + 1) + "-th parameter: \"" + paramsSplit[i].trim() + "\"! Expected syntax: \"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"()\", \"(param INTEGER)\", \"(param1 INTEGER, param2 DOUBLE)\"");
+				}
+			}
+
+			// Build the function definition object:
+			return new FunctionDef(fctName, returnType, params);
+		}else
+			throw new ParseException("Wrong function definition syntax! Expected syntax: \"<regular_identifier>(<parameters>?) <return_type>?\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <return_type>=\" -> <type_name>\", <parameters>=\"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"foo()\", \"foo() -> VARCHAR\", \"foo(param INTEGER)\", \"foo(param1 INTEGER, param2 DOUBLE) -> DOUBLE\"");
+	}
+
+	/**
+	 * Parse the given string representation of a datatype.
+	 * 
+	 * @param datatype	String representation of a datatype.
+	 *                	<i>Note: This string must not contain the length parameter or any other parameter.
+	 *                	These latter should have been separated from the datatype before calling this function.</i>
+	 * @param length	Length of this datatype.
+	 *              	<i>Note: This length will be used only for binary (BINARY and VARBINARY)
+	 *              	and character (CHAR and VARCHAR) types.</i> 
+	 * 
+	 * @return	The object representation of the specified datatype.
+	 */
+	private static DBType parseType(String datatype, int length){
+		if (datatype == null)
+			return null;
+
+		try{
+			// Try to find a corresponding DBType item:
+			DBDatatype dbDatatype = DBDatatype.valueOf(datatype.toUpperCase());
+
+			// If there's a match, build the type object representation:
+			length = (length <= 0) ? DBType.NO_LENGTH : length;
+			switch(dbDatatype){
+				case CHAR:
+				case VARCHAR:
+				case BINARY:
+				case VARBINARY:
+					return new DBType(dbDatatype, length);
+				default:
+					return new DBType(dbDatatype);
+			}
+		}catch(IllegalArgumentException iae){
+			// If there's no corresponding DBType item, try to find a match among the most used DB types:
+			datatype = datatype.toLowerCase();
+			if (datatype.equals("bool") || datatype.equals("boolean") || datatype.equals("short"))
+				return new DBType(DBDatatype.SMALLINT);
+			else if (datatype.equals("int2"))
+				return new DBType(DBDatatype.SMALLINT);
+			else if (datatype.equals("int") || datatype.equals("int4"))
+				return new DBType(DBDatatype.INTEGER);
+			else if (datatype.equals("long") || datatype.equals("number") || datatype.equals("bigint") || datatype.equals("int8"))
+				return new DBType(DBDatatype.BIGINT);
+			else if (datatype.equals("float") || datatype.equals("float4"))
+				return new DBType(DBDatatype.REAL);
+			else if (datatype.equals("numeric") || datatype.equals("float8"))
+				return new DBType(DBDatatype.DOUBLE);
+			else if (datatype.equals("byte") || datatype.equals("raw"))
+				return new DBType(DBDatatype.BINARY, length);
+			else if (datatype.equals("unsignedByte"))
+				return new DBType(DBDatatype.VARBINARY, length);
+			else if (datatype.equals("character"))
+				return new DBType(DBDatatype.CHAR, length);
+			else if (datatype.equals("string") || datatype.equals("varchar2"))
+				return new DBType(DBDatatype.VARCHAR, length);
+			else if (datatype.equals("bytea"))
+				return new DBType(DBDatatype.BLOB);
+			else if (datatype.equals("text"))
+				return new DBType(DBDatatype.CLOB);
+			else if (datatype.equals("date") || datatype.equals("time"))
+				return new DBType(DBDatatype.TIMESTAMP);
+			else if (datatype.equals("position"))
+				return new DBType(DBDatatype.POINT);
+			else if (datatype.equals("polygon") || datatype.equals("box") || datatype.equals("circle"))
+				return new DBType(DBDatatype.REGION);
+			else
+				return null;
+		}
+	}
+
+	@Override
+	public String toString(){
+		return serializedForm;
+	}
+
+	@Override
+	public int compareTo(final FunctionDef def){
+		return compareForm.compareTo(def.compareForm);
+	}
+
+	/**
+	 * <p>Compare this function definition with the given ADQL function item.</p>
+	 * 
+	 * <p>
+	 * 	The comparison is done only on the function name and on rough type of the parameters.
+	 * 	"Rough type" means here that just the kind of type is tested: numeric, string or geometry.
+	 * 	Anyway, the return type is never tested by this function, since such information is usually
+	 * 	not part of a function signature.
+	 * </p>
+	 * 
+	 * <p>The notion of "greater" and "less" are defined here according to the three following test steps:</p>
+	 * <ol>
+	 * 	<li><b>Name test:</b> if the name of both function are equals, next steps are evaluated, otherwise the standard string comparison (case insensitive) result is returned.</li>
+	 * 	<li><b>Parameters test:</b> parameters are compared individually. Each time parameters (at the same position in both functions) are equals the next parameter can be tested,
+	 * 	                            and so on until two parameters are different or the end of the parameters' list is reached.
+	 * 	                            Just the kind of type is used for parameter comparison. Each kind of type is tested in the following order: numeric, string and geometry.
+	 * 	                            When a kind of type is not equal for both parameters, the function exits with the appropriate value
+	 * 	                            (1 if the parameter of this function definition is of the kind of type, -1 otherwise).</li>
+	 * 	<li><b>Number of parameters test:</b> in the case where this function definition has N parameters and the given ADQL function has M parameters,
+	 * 	                                      and that the L (= min(N,M)) first parameters have the same type in both functions, the value returns by this function
+	 * 	                                      will be N-M. Thus, if this function definition has more parameters than the given function, a positive value will be
+	 * 	                                      returned. Otherwise a negative value will be returned, or 0 if the number of parameters is the same.</li>
+	 * </ol>
+	 * 
+	 * @param fct	ADQL function item to compare with this function definition.
+	 * 
+	 * @return	A positive value if this function definition is "greater" than the given {@link ADQLFunction},
+	 * 			0 if they are perfectly matching,
+	 * 			or a negative value if this function definition is "less" than the given {@link ADQLFunction}.
+	 */
+	public int compareTo(final ADQLFunction fct){
+		if (fct == null)
+			throw new NullPointerException("Missing ADQL function with which comparing this function definition!");
+
+		// Names comparison:
+		int comp = name.compareToIgnoreCase(fct.getName());
+
+		// If equals, compare the parameters' type:
+		if (comp == 0){
+			for(int i = 0; comp == 0 && i < nbParams && i < fct.getNbParameters(); i++){
+				if (params[i].type.isNumeric() == fct.getParameter(i).isNumeric()){
+					if (params[i].type.isString() == fct.getParameter(i).isString()){
+						if (params[i].type.isGeometry() == fct.getParameter(i).isGeometry())
+							comp = 0;
+						else
+							comp = params[i].type.isGeometry() ? 1 : -1;
+					}else
+						comp = params[i].type.isString() ? 1 : -1;
+				}else
+					comp = params[i].type.isNumeric() ? 1 : -1;
+			}
+
+			// If the first min(N,M) parameters are of the same type, do the last comparison on the number of parameters:
+			if (comp == 0 && nbParams != fct.getNbParameters())
+				comp = nbParams - fct.getNbParameters();
+		}
+
+		return comp;
+	}
+}
diff --git a/src/adql/db/STCS.java b/src/adql/db/STCS.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4a34e9eb4e3a64dfb08e62a10db0a123e742789
--- /dev/null
+++ b/src/adql/db/STCS.java
@@ -0,0 +1,1685 @@
+package adql.db;
+
+/*
+ * This file is part of ADQLLibrary.
+ * 
+ * ADQLLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * ADQLLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * 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 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import adql.parser.ADQLQueryFactory;
+import adql.parser.ParseException;
+import adql.query.TextPosition;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.NegativeOperand;
+import adql.query.operand.NumericConstant;
+import adql.query.operand.StringConstant;
+import adql.query.operand.function.ADQLFunction;
+import adql.query.operand.function.geometry.BoxFunction;
+import adql.query.operand.function.geometry.CircleFunction;
+import adql.query.operand.function.geometry.GeometryFunction;
+import adql.query.operand.function.geometry.PointFunction;
+import adql.query.operand.function.geometry.PolygonFunction;
+import adql.query.operand.function.geometry.RegionFunction;
+
+/**
+ * <p>This class helps dealing with the subset of STC-S expressions described by the section "6 Use of STC-S in TAP (informative)"
+ * of the TAP Recommendation 1.0 (27th March 2010). This subset is limited to the most common coordinate systems and regions.</p>
+ * 
+ * <p><i>Note:
+ * 	No instance of this class can be created. Its usage is only limited to its static functions and classes.
+ * </i></p>
+ * 
+ * <h3>Coordinate system</h3>
+ * <p>
+ * 	The function {@link #parseCoordSys(String)} is able to parse a string containing only the STC-S expression of a coordinate system
+ * 	(or an empty string or null which would be interpreted as the default coordinate system - UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2).
+ * 	When successful, this parsing returns an object representation of the coordinate system: {@link CoordSys}.
+ * </p>
+ * <p>
+ * 	To serialize into STC-S a coordinate system, you have to create a {@link CoordSys} instance with the desired values
+ * 	and to call the function {@link CoordSys#toSTCS()}. The static function {@link #toSTCS(CoordSys)} is just calling the
+ * 	{@link CoordSys#toSTCS()} on the given coordinate system.
+ * </p>
+ * 
+ * <h3>Geometrical region</h3>
+ * <p>
+ * 	As for the coordinate system, there is a static function to parse the STC-S representation of a geometrical region: {@link #parseRegion(String)}.
+ * 	Here again, when the parsing is successful an object representation is returned: {@link Region}.
+ * </p>
+ * <p>
+ * 	This class lets also serializing into STC-S a region. The procedure is the same as with a coordinate system: create a {@link Region} and then
+ * 	call {@link Region#toString()}.
+ * </p>
+ * <p>
+ * 	The class {@link Region} lets also dealing with the {@link ADQLFunction} implementing a region. It is then possible to create a {@link Region}
+ * 	object from a such {@link ADQLFunction} and to get the corresponding STC-S representation. The static function {@link #toSTCS(GeometryFunction)}
+ * 	is a helpful function which do these both actions in once.
+ * </p>
+ * <p><i>Note:
+ * 	The conversion from {@link ADQLFunction} to {@link Region} or STC-S is possible only if the {@link ADQLFunction} contains constants as parameter.
+ * 	Thus, a such function using a column, a concatenation, a math operation or using another function can not be converted into STC-S using this class.
+ * </i></p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 1.3 (10/2014)
+ * @since 1.3
+ */
+public final class STCS {
+
+	/**
+	 * Empty private constructor ; in order to prevent any instance creation.
+	 */
+	private STCS(){}
+
+	/* ***************** */
+	/* COORDINATE SYSTEM */
+	/* ***************** */
+
+	/** Regular expression for a STC-S representation of a coordinate system. It takes into account the fact that each part of
+	 * a coordinate system is optional and so that a full coordinate system expression can be reduced to an empty string. */
+	private final static String coordSysRegExp = Frame.regexp + "?\\s*" + RefPos.regexp + "?\\s*" + Flavor.regexp + "?";
+	/** Regular expression of an expression exclusively limited to a coordinate system. */
+	private final static String onlyCoordSysRegExp = "^\\s*" + coordSysRegExp + "\\s*$";
+	/** Regular expression of a default coordinate system: either an empty string or a string containing only default values. */
+	private final static String defaultCoordSysRegExp = "^\\s*" + Frame.DEFAULT + "?\\s*" + RefPos.DEFAULT + "?\\s*" + Flavor.DEFAULT + "?\\s*$";
+	/** Regular expression of a pattern describing a set of allowed coordinate systems. <i>See {@link #buildAllowedRegExp(String)} for more details.</i> */
+	/* With this regular expression, we get the following matching groups:
+	 *       0: All the expression
+	 * 1+(6*N): The N-th part of the coordinate system (N is an unsigned integer between 0 and 2 (included) ; it is reduced to '*' if the two following groups are NULL
+	 * 2+(6*N): A single value for the N-th part
+	 * 3+(6*N): A list of values for the N-th part
+	 * 4+(6*N): First value of the list for the N-th part
+	 * 5+(6*N): All the other values (starting with a |) of the list for the N-th part
+	 * 6+(6*N): Last value of the list for the N-th part.
+	 */
+	private final static String allowedCoordSysRegExp = "^\\s*" + buildAllowedRegExp(Frame.regexp) + "\\s+" + buildAllowedRegExp(RefPos.regexp) + "\\s+" + buildAllowedRegExp(Flavor.regexp) + "\\s*$";
+
+	/** Pattern of an allowed coordinate system pattern. This object has been compiled with {@link #allowedCoordSysRegExp}. */
+	private final static Pattern allowedCoordSysPattern = Pattern.compile(allowedCoordSysRegExp);
+
+	/** Human description of the syntax of a full coordinate system expression. */
+	private final static String COORD_SYS_SYNTAX = "\"[" + Frame.regexp + "] [" + RefPos.regexp + "] [" + Flavor.regexp + "]\" ; an empty string is also allowed and will be interpreted as the coordinate system locally used";
+
+	/**
+	 * Build the regular expression of a string defining the allowed values for one part of the whole coordinate system.
+	 * 
+	 * @param rootRegExp	All allowed part values.
+	 * 
+	 * @return	The corresponding regular expression.
+	 */
+	private static String buildAllowedRegExp(final String rootRegExp){
+		return "(" + rootRegExp + "|\\*|(\\(\\s*" + rootRegExp + "\\s*(\\|\\s*" + rootRegExp + "\\s*)*\\)))";
+	}
+
+	/**
+	 * <p>List of all possible frames in an STC expression.</p>
+	 * 
+	 * <p>
+	 * 	When no value is specified, the default one is {@link #UNKNOWNFRAME}.
+	 * 	The default value is also accessible through the attribute {@link #DEFAULT}
+	 * 	and it is possible to test whether a frame is the default with the function {@link #isDefault()}.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)"
+	 * 	of the TAP Recommendation 1.0 (27th March 2010).
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static enum Frame{
+		ECLIPTIC, FK4, FK5, GALACTIC, ICRS, UNKNOWNFRAME;
+
+		/** Default value for a frame: {@link #UNKNOWNFRAME}. */
+		public static final Frame DEFAULT = UNKNOWNFRAME;
+
+		/** Regular expression to test whether a string is a valid frame or not. This regular expression does not take into account
+		 * the case of an empty string (which means "default frame"). */
+		public static final String regexp = buildRegexp(Frame.class);
+
+		/**
+		 * Tell whether this frame is the default one.
+		 * 
+		 * @return	<i>true</i> if this is the default frame, <i>false</i>
+		 */
+		public final boolean isDefault(){
+			return this == DEFAULT;
+		}
+	}
+
+	/**
+	 * <p>List of all possible reference positions in an STC expression.</p>
+	 * 
+	 * <p>
+	 * 	When no value is specified, the default one is {@link #UNKNOWNREFPOS}.
+	 * 	The default value is also accessible through the attribute {@link #DEFAULT}
+	 * 	and it is possible to test whether a reference position is the default with the function {@link #isDefault()}.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)"
+	 * 	of the TAP Recommendation 1.0 (27th March 2010).
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static enum RefPos{
+		BARYCENTER, GEOCENTER, HELIOCENTER, LSR, TOPOCENTER, RELOCATABLE, UNKNOWNREFPOS;
+
+		/** Default value for a reference position: {@link #UNKNOWNREFPOS}. */
+		public static final RefPos DEFAULT = UNKNOWNREFPOS;
+
+		/** Regular expression to test whether a string is a valid reference position or not. This regular expression does not take into account
+		 * the case of an empty string (which means "default reference position"). */
+		public static final String regexp = buildRegexp(RefPos.class);
+
+		/**
+		 * Tell whether this reference position is the default one.
+		 * 
+		 * @return	<i>true</i> if this is the default reference position, <i>false</i>
+		 */
+		public final boolean isDefault(){
+			return this == DEFAULT;
+		}
+	}
+
+	/**
+	 * <p>List of all possible flavors in an STC expression.</p>
+	 * 
+	 * <p>
+	 * 	When no value is specified, the default one is {@link #SPHERICAL2}.
+	 * 	The default value is also accessible through the attribute {@link #DEFAULT}
+	 * 	and it is possible to test whether a flavor is the default with the function {@link #isDefault()}.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)"
+	 * 	of the TAP Recommendation 1.0 (27th March 2010).
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static enum Flavor{
+		CARTESIAN2, CARTESIAN3, SPHERICAL2;
+
+		/** Default value for a flavor: {@link #SPHERICAL2}. */
+		public static final Flavor DEFAULT = SPHERICAL2;
+
+		/** Regular expression to test whether a string is a valid flavor or not. This regular expression does not take into account
+		 * the case of an empty string (which means "default flavor"). */
+		public static final String regexp = buildRegexp(Flavor.class);
+
+		/**
+		 * Tell whether this flavor is the default one.
+		 * 
+		 * @return	<i>true</i> if this is the default flavor, <i>false</i>
+		 */
+		public final boolean isDefault(){
+			return this == DEFAULT;
+		}
+	}
+
+	/**
+	 * Build a regular expression covering all possible values of the given enumeration.
+	 * 
+	 * @param enumType	Class of an enumeration type.
+	 * 
+	 * @return	The build regular expression or "\s*" if the given enumeration contains no constants/values.
+	 * 
+	 * @throws IllegalArgumentException	If the given class is not an enumeration type.
+	 */
+	private static String buildRegexp(final Class<?> enumType) throws IllegalArgumentException{
+		// The given class must be an enumeration type:
+		if (!enumType.isEnum())
+			throw new IllegalArgumentException("An enum class was expected, but a " + enumType.getName() + " has been given!");
+
+		// Get the enumeration constants/values:
+		Object[] constants = enumType.getEnumConstants();
+		if (constants == null || constants.length == 0)
+			return "\\s*";
+
+		// Concatenate all constants with pipe to build a choice regular expression:
+		StringBuffer buf = new StringBuffer("(");
+		for(int i = 0; i < constants.length; i++){
+			buf.append(constants[i]);
+			if ((i + 1) < constants.length)
+				buf.append('|');
+		}
+		return buf.append(')').toString();
+	}
+
+	/**
+	 * <p>Object representation of an STC coordinate system.</p>
+	 * 
+	 * <p>
+	 * 	A coordinate system is composed of three parts: a frame ({@link #frame}),
+	 * 	a reference position ({@link #refpos}) and a flavor ({@link #flavor}).
+	 * </p>
+	 * 
+	 * <p>
+	 * 	The default value - also corresponding to an empty string - should be:
+	 * 	{@link Frame#UNKNOWNFRAME} {@link RefPos#UNKNOWNREFPOS} {@link Flavor#SPHERICAL2}.
+	 * 	Once built, it is possible to know whether the coordinate system is the default one
+	 * 	or not thanks to function {@link #isDefault()}.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	An instance of this class can be easily serialized into STC-S using {@link #toSTCS()}, {@link #toFullSTCS()}
+	 * 	or {@link #toString()}. {@link #toFullSTCS()} will display default values explicitly
+	 * 	on the contrary to {@link #toSTCS()} which will replace them by empty strings.
+	 * </p>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	The flavors CARTESIAN2 and CARTESIAN3 can not be used with other frame and reference position than
+	 * 	UNKNOWNFRAME and UNKNOWNREFPOS. In the contrary case an {@link IllegalArgumentException} is throw.
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static class CoordSys {
+		/** First item of a coordinate system expression: the frame. */
+		public final Frame frame;
+
+		/** Second item of a coordinate system expression: the reference position. */
+		public final RefPos refpos;
+
+		/** Third and last item of a coordinate system expression: the flavor. */
+		public final Flavor flavor;
+
+		/** Indicate whether all parts of the coordinate system are set to their default value. */
+		private final boolean isDefault;
+
+		/** STC-S representation of this coordinate system. Default items are not written (that's to say, they are replaced by an empty string). */
+		private final String stcs;
+
+		/** STC-S representation of this coordinate system. Default items are explicitly written. */
+		private final String fullStcs;
+
+		/**
+		 * Build a default coordinate system (UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2).
+		 */
+		public CoordSys(){
+			this(null, null, null);
+		}
+
+		/**
+		 * Build a coordinate system with the given parts.
+		 * 
+		 * @param fr	Frame part.
+		 * @param rp	Reference position part.
+		 * @param fl	Flavor part.
+		 * 
+		 * @throws IllegalArgumentException	If a cartesian flavor is used with a frame and reference position other than UNKNOWNFRAME and UNKNOWNREFPOS.
+		 */
+		public CoordSys(final Frame fr, final RefPos rp, final Flavor fl) throws IllegalArgumentException{
+			frame = (fr == null) ? Frame.DEFAULT : fr;
+			refpos = (rp == null) ? RefPos.DEFAULT : rp;
+			flavor = (fl == null) ? Flavor.DEFAULT : fl;
+
+			if (flavor != Flavor.SPHERICAL2 && (frame != Frame.UNKNOWNFRAME || refpos != RefPos.UNKNOWNREFPOS))
+				throw new IllegalArgumentException("a coordinate system expressed with a cartesian flavor MUST have an UNKNOWNFRAME and UNKNOWNREFPOS!");
+
+			isDefault = frame.isDefault() && refpos.isDefault() && flavor.isDefault();
+
+			stcs = ((!frame.isDefault() ? frame + " " : "") + (!refpos.isDefault() ? refpos + " " : "") + (!flavor.isDefault() ? flavor : "")).trim();
+			fullStcs = frame + " " + refpos + " " + flavor;
+		}
+
+		/**
+		 * Build a coordinate system by parsing the given STC-S expression.
+		 * 
+		 * @param coordsys	STC-S expression representing a coordinate system. <i>Empty string and NULL are allowed values ; they correspond to a default coordinate system.</i>
+		 * 
+		 * @throws ParseException	If the syntax of the given STC-S expression is wrong or if it is not a coordinate system only.
+		 */
+		public CoordSys(final String coordsys) throws ParseException{
+			CoordSys tmp = new STCSParser().parseCoordSys(coordsys);
+			frame = tmp.frame;
+			refpos = tmp.refpos;
+			flavor = tmp.flavor;
+			isDefault = tmp.isDefault;
+			stcs = tmp.stcs;
+			fullStcs = tmp.fullStcs;
+		}
+
+		/**
+		 * Tell whether this is the default coordinate system (UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2).
+		 * 
+		 * @return	<i>true</i> if it is the default coordinate system, <i>false</i> otherwise.
+		 */
+		public final boolean isDefault(){
+			return isDefault;
+		}
+
+		/**
+		 * Get the STC-S expression of this coordinate system,
+		 * in which default values are not written (they are replaced by empty strings).
+		 * 
+		 * @return	STC-S representation of this coordinate system.
+		 */
+		public String toSTCS(){
+			return stcs;
+		}
+
+		/**
+		 * Get the STC-S expression of this coordinate system,
+		 * in which default values are explicitly written.
+		 * 
+		 * @return	STC-S representation of this coordinate system.
+		 */
+		public String toFullSTCS(){
+			return fullStcs;
+		}
+
+		/**
+		 * Convert this coordinate system into a STC-S expression.
+		 * 
+		 * @see java.lang.Object#toString()
+		 * @see #toSTCS()
+		 */
+		@Override
+		public String toString(){
+			return stcs;
+		}
+	}
+
+	/**
+	 * Parse the given STC-S representation of a coordinate system.
+	 * 
+	 * @param stcs	STC-S expression of a coordinate system. <i>Note: a NULL or empty string will be interpreted as a default coordinate system.</i>
+	 * 
+	 * @return	The object representation of the specified coordinate system.
+	 * 
+	 * @throws ParseException	If the given expression has a wrong STC-S syntax.
+	 */
+	public static CoordSys parseCoordSys(final String stcs) throws ParseException{
+		return (new STCSParser().parseCoordSys(stcs));
+	}
+
+	/**
+	 * <p>Convert an object representation of a coordinate system into an STC-S expression.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	A NULL object will be interpreted as the default coordinate system and so an empty string will be returned.
+	 * 	Otherwise, this function is equivalent to {@link CoordSys#toSTCS()} (in which default values for each
+	 * 	coordinate system part is not displayed).
+	 * </i></p>
+	 * 
+	 * @param coordSys	The object representation of the coordinate system to convert into STC-S.
+	 * 
+	 * @return	The corresponding STC-S expression.
+	 * 
+	 * @see CoordSys#toSTCS()
+	 * @see CoordSys#toFullSTCS()
+	 */
+	public static String toSTCS(final CoordSys coordSys){
+		if (coordSys == null)
+			return "";
+		else
+			return coordSys.toSTCS();
+	}
+
+	/**
+	 * <p>Build a big regular expression gathering all of the given coordinate system syntaxes.</p>
+	 * 
+	 * <p>
+	 * 	Each item of the given list must respect a strict syntax. Each part of the coordinate system
+	 * 	may be a single value, a list of values or a '*' (meaning all values are allowed).
+	 * 	A list of values must have the following syntax: <code>({value1}|{value2}|...)</code>.
+	 * 	An empty string is NOT here accepted.
+	 * </p>
+	 * 
+	 * <p><i>Example:
+	 * 	<code>(ICRS|FK4|FK5) * SPHERICAL2</code> is OK,
+	 * 	but <code>(ICRS|FK4|FK5) *</code> is not valid because the flavor value is not defined.
+	 * </i></p>
+	 * 
+	 * <p>
+	 * 	Since the default value of each part of a coordinate system should always be possible,
+	 * 	this function ensure these default values are always possible in the returned regular expression.
+	 * 	Thus, if some values except the default one are specified, the default value is automatically appended.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	If the given array is NULL, all coordinate systems are allowed.
+	 * 	But if the given array is empty, none except an empty string or the default value will be allowed.
+	 * </i></p>
+	 * 
+	 * @param allowedCoordSys	List of all coordinate systems that are allowed.
+	 * 
+	 * @return	The corresponding regular expression.
+	 * 
+	 * @throws ParseException	If the syntax of one of the given allowed coordinate system is wrong.
+	 */
+	public static String buildCoordSysRegExp(final String[] allowedCoordSys) throws ParseException{
+		// NULL array => all coordinate systems are allowed:
+		if (allowedCoordSys == null)
+			return onlyCoordSysRegExp;
+		// Empty array => no coordinate system (except the default one) is allowed:
+		else if (allowedCoordSys.length == 0)
+			return defaultCoordSysRegExp;
+
+		// The final regular expression must be reduced to a coordinate system and nothing else before:
+		StringBuffer finalRegExp = new StringBuffer("^\\s*(");
+
+		// For each allowed coordinate system:
+		Matcher m;
+		int nbCoordSys = 0;
+		for(int i = 0; i < allowedCoordSys.length; i++){
+
+			// NULL item => skipped!
+			if (allowedCoordSys[i] == null)
+				continue;
+			else{
+				if (nbCoordSys > 0)
+					finalRegExp.append('|');
+				nbCoordSys++;
+			}
+
+			// Check its syntax and identify all of its parts:
+			m = allowedCoordSysPattern.matcher(allowedCoordSys[i].toUpperCase());
+			if (m.matches()){
+				finalRegExp.append('(');
+				for(int g = 0; g < 3; g++){	// See the comment after the Javadoc of #allowedCoordSysRegExp for a complete list of available groups returned by the pattern.
+
+					// SINGLE VALUE:
+					if (m.group(2 + (6 * g)) != null)
+						finalRegExp.append('(').append(defaultChoice(g, m.group(2 + (6 * g)))).append(m.group(2 + (6 * g))).append(')');
+
+					// LIST OF VALUES:
+					else if (m.group(3 + (6 * g)) != null)
+						finalRegExp.append('(').append(defaultChoice(g, m.group(3 + (6 * g)))).append(m.group(3 + (6 * g)).replaceAll("\\s", "").substring(1));
+
+					// JOKER (*):
+					else{
+						switch(g){
+							case 0:
+								finalRegExp.append(Frame.regexp);
+								break;
+							case 1:
+								finalRegExp.append(RefPos.regexp);
+								break;
+							case 2:
+								finalRegExp.append(Flavor.regexp);
+								break;
+						}
+						finalRegExp.append('?');
+					}
+					finalRegExp.append("\\s*");
+				}
+				finalRegExp.append(')');
+			}else
+				throw new ParseException("Wrong allowed coordinate system syntax for the " + (i + 1) + "-th item: \"" + allowedCoordSys[i] + "\"! Expected: \"frameRegExp refposRegExp flavorRegExp\" ; where each xxxRegExp = (xxx | '*' | '('xxx ('|' xxx)*')'), frame=\"" + Frame.regexp + "\", refpos=\"" + RefPos.regexp + "\" and flavor=\"" + Flavor.regexp + "\" ; an empty string is also allowed and will be interpreted as '*' (so all possible values).");
+		}
+
+		// The final regular expression must be reduced to a coordinate system and nothing else after:
+		finalRegExp.append(")\\s*");
+
+		return (nbCoordSys > 0) ? finalRegExp.append(")$").toString() : defaultCoordSysRegExp;
+	}
+
+	/**
+	 * Get the default value appended by a '|' character, ONLY IF the given value does not already contain the default value.
+	 * 
+	 * @param g		Index of the coordinate system part (0: Frame, 1: RefPos, 2: Flavor, another value will return an empty string).
+	 * @param value	Value in which the default value must prefix.
+	 * 
+	 * @return	A prefix for the given value (the default value and a '|' if the default value is not already in the given value, "" otherwise).
+	 */
+	private static String defaultChoice(final int g, final String value){
+		switch(g){
+			case 0:
+				return value.contains(Frame.DEFAULT.toString()) ? "" : Frame.DEFAULT + "|";
+			case 1:
+				return value.contains(RefPos.DEFAULT.toString()) ? "" : RefPos.DEFAULT + "|";
+			case 2:
+				return value.contains(Flavor.DEFAULT.toString()) ? "" : Flavor.DEFAULT + "|";
+			default:
+				return "";
+		}
+	}
+
+	/* ****** */
+	/* REGION */
+	/* ****** */
+
+	/**
+	 * <p>List all possible region types allowed in an STC-S expression.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)"
+	 * 	of the TAP Recommendation 1.0 (27th March 2010).
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static enum RegionType{
+		POSITION, CIRCLE, BOX, POLYGON, UNION, INTERSECTION, NOT;
+	}
+
+	/**
+	 * <p>Object representation of an STC region.</p>
+	 * 
+	 * <p>
+	 * 	This class contains a field for each possible parameter of a region. Depending of the region type
+	 * 	some are not used. In such case, these unused fields are set to NULL.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	An instance of this class can be easily serialized into STC-S using {@link #toSTCS()}, {@link #toFullSTCS()}
+	 * 	or {@link #toString()}. {@link #toFullSTCS()} will display default value explicit
+	 * 	on the contrary to {@link #toSTCS()} which will replace them by empty strings.
+	 * </p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static class Region {
+		/** Type of the region. */
+		public final RegionType type;
+
+		/** Coordinate system used by this region.
+		 * <i>Note: only the NOT region does not declare a coordinate system ; so only for this region this field is NULL.</i> */
+		public final CoordSys coordSys;
+
+		/** List of coordinates' pairs. The second dimension of this array represents a pair of coordinates ; it is then an array of two elements.
+		 * <i>Note: this field is used by POINT, BOX, CIRCLE and POLYGON.</i> */
+		public final double[][] coordinates;
+
+		/** Width of the BOX region. */
+		public final double width;
+
+		/** Height of the BOX region. */
+		public final double height;
+
+		/** Radius of the CIRCLE region. */
+		public final double radius;
+
+		/** List of regions unified (UNION), intersected (INTERSECTION) or avoided (NOT). */
+		public final Region[] regions;
+
+		/** STC-S representation of this region, in which default values of the coordinate system (if any) are not written (they are replaced by empty strings).
+		 * <i>Note: This attribute is NULL until the first call of the function {@link #toSTCS()} where it is built.</i> */
+		private String stcs = null;
+
+		/** STC-S representation of this region, in which default values of the coordinate system (if any) are explicitly written.
+		 * <i>Note: This attribute is NULL until the first call of the function {@link #toFullSTCS()} where it is built.</i> */
+		private String fullStcs = null;
+
+		/** The ADQL function object representing this region.
+		 * <i>Note: this attribute is NULL until the first call of the function {@link #toGeometry()} or {@link #toGeometry(ADQLQueryFactory)}.</i> */
+		private GeometryFunction geometry = null;
+
+		/**
+		 * <p>Constructor for a POINT/POSITION region.</p>
+		 * 
+		 * <p><i><b>Important note:</b>
+		 * 	The array of coordinates is used like that. No copy is done.
+		 * </i></p>
+		 * 
+		 * @param coordSys		Coordinate system. <i>note: It MAY BE null ; if so, the default coordinate system will be chosen</li>
+		 * @param coordinates	A pair of coordinates ; coordinates[0] and coordinates[1].
+		 */
+		public Region(final CoordSys coordSys, final double[] coordinates){
+			this(coordSys, new double[][]{coordinates});
+		}
+
+		/**
+		 * <p>Constructor for a POINT/POSITION or a POLYGON region.</p>
+		 * 
+		 * <p>Whether it is a polygon or a point depends on the number of given coordinates:</p>
+		 * <ul>
+		 * 	<li>1 item => POINT/POSITION</li>
+		 * 	<li>more items => POLYGON</li>
+		 * </ul>
+		 * 
+		 * <p><i><b>Important note:</b>
+		 * 	The array of coordinates is used like that. No copy is done.
+		 * </i></p>
+		 * 
+		 * @param coordSys		Coordinate system. <i>note: It MAY BE null ; if so, the default coordinate system will be chosen</li>
+		 * @param coordinates	List of coordinates' pairs ; coordinates[n] = 1 pair = 2 items (coordinates[n][0] and coordinates[n][1]) ; if 1 pair, it is a POINT/POSITION, but if more, it is a POLYGON.
+		 */
+		public Region(final CoordSys coordSys, final double[][] coordinates){
+			// Check roughly the coordinates:
+			if (coordinates == null || coordinates.length == 0)
+				throw new NullPointerException("Missing coordinates!");
+			else if (coordinates[0].length != 2)
+				throw new IllegalArgumentException("Wrong number of coordinates! Expected at least 2 pairs of coordinates (so coordinates[0], coordinates[1] and coordinates[n].length = 2).");
+
+			// Decide of the region type in function of the number of coordinates' pairs:
+			type = (coordinates.length > 1) ? RegionType.POLYGON : RegionType.POSITION;
+
+			// Set the coordinate system (if NULL, choose the default one):
+			this.coordSys = (coordSys == null ? new CoordSys() : coordSys);
+
+			// Set the coordinates:
+			this.coordinates = coordinates;
+
+			// Set the other fields as not used:
+			width = Double.NaN;
+			height = Double.NaN;
+			radius = Double.NaN;
+			regions = null;
+		}
+
+		/**
+		 * <p>Constructor for a CIRCLE region.</p>
+		 * 
+		 * <p><i><b>Important note:</b>
+		 * 	The array of coordinates is used like that. No copy is done.
+		 * </i></p>
+		 * 
+		 * @param coordSys		Coordinate system. <i>note: It MAY BE null ; if so, the default coordinate system will be chosen</li>
+		 * @param coordinates	A pair of coordinates ; coordinates[0] and coordinates[1].
+		 * @param radius		The circle radius.
+		 */
+		public Region(final CoordSys coordSys, final double[] coordinates, final double radius){
+			// Check roughly the coordinates:
+			if (coordinates == null || coordinates.length == 0)
+				throw new NullPointerException("Missing coordinates!");
+			else if (coordinates.length != 2)
+				throw new IllegalArgumentException("Wrong number of coordinates! Expected exactly 2 values.");
+
+			// Set the region type:
+			type = RegionType.CIRCLE;
+
+			// Set the coordinate system (if NULL, choose the default one):
+			this.coordSys = (coordSys == null ? new CoordSys() : coordSys);
+
+			// Set the coordinates:
+			this.coordinates = new double[][]{coordinates};
+
+			// Set the radius:
+			this.radius = radius;
+
+			// Set the other fields as not used:
+			width = Double.NaN;
+			height = Double.NaN;
+			regions = null;
+		}
+
+		/**
+		 * <p>Constructor for a BOX region.</p>
+		 * 
+		 * <p><i><b>Important note:</b>
+		 * 	The array of coordinates is used like that. No copy is done.
+		 * </i></p>
+		 * 
+		 * @param coordSys		Coordinate system. <i>note: It MAY BE null ; if so, the default coordinate system will be chosen</li>
+		 * @param coordinates	A pair of coordinates ; coordinates[0] and coordinates[1].
+		 * @param width			Width of the box.
+		 * @param height		Height of the box.
+		 */
+		public Region(final CoordSys coordSys, final double[] coordinates, final double width, final double height){
+			// Check roughly the coordinates:
+			if (coordinates == null || coordinates.length == 0)
+				throw new NullPointerException("Missing coordinates!");
+			else if (coordinates.length != 2)
+				throw new IllegalArgumentException("Wrong number of coordinates! Expected exactly 2 values.");
+
+			// Set the region type:
+			type = RegionType.BOX;
+
+			// Set the coordinate system (if NULL, choose the default one):
+			this.coordSys = (coordSys == null ? new CoordSys() : coordSys);
+
+			// Set the coordinates:
+			this.coordinates = new double[][]{coordinates};
+
+			// Set the size of the box:
+			this.width = width;
+			this.height = height;
+
+			// Set the other fields as not used:
+			radius = Double.NaN;
+			regions = null;
+		}
+
+		/**
+		 * <p>Constructor for a UNION or INTERSECTION region.</p>
+		 * 
+		 * <p><i><b>Important note:</b>
+		 * 	The array of regions is used like that. No copy is done.
+		 * </i></p>
+		 * 
+		 * @param unionOrIntersection	Type of the region to create. <i>Note: It can be ONLY a UNION or INTERSECTION. Another value will throw an IllegalArgumentException).</i>
+		 * @param coordSys				Coordinate system. <i>note: It MAY BE null ; if so, the default coordinate system will be chosen</li>
+		 * @param regions				Regions to unite or to intersect. <i>Note: At least two regions must be provided.</i>
+		 */
+		public Region(final RegionType unionOrIntersection, final CoordSys coordSys, final Region[] regions){
+			// Check the type:
+			if (unionOrIntersection == null)
+				throw new NullPointerException("Missing type of region (UNION or INTERSECTION here)!");
+			else if (unionOrIntersection != RegionType.UNION && unionOrIntersection != RegionType.INTERSECTION)
+				throw new IllegalArgumentException("Wrong region type: \"" + unionOrIntersection + "\"! This constructor lets create only an UNION or INTERSECTION region.");
+
+			// Check the list of regions:
+			if (regions == null || regions.length == 0)
+				throw new NullPointerException("Missing regions to " + (unionOrIntersection == RegionType.UNION ? "unite" : "intersect") + "!");
+			else if (regions.length < 2)
+				throw new IllegalArgumentException("Wrong number of regions! Expected at least 2 regions.");
+
+			// Set the region type:
+			type = unionOrIntersection;
+
+			// Set the coordinate system (if NULL, choose the default one):
+			this.coordSys = (coordSys == null ? new CoordSys() : coordSys);
+
+			// Set the regions:
+			this.regions = regions;
+
+			// Set the other fields as not used:
+			coordinates = null;
+			radius = Double.NaN;
+			width = Double.NaN;
+			height = Double.NaN;
+		}
+
+		/**
+		 * Constructor for a NOT region.
+		 * 
+		 * @param region	Any region to not select.
+		 */
+		public Region(final Region region){
+			// Check the region parameter:
+			if (region == null)
+				throw new NullPointerException("Missing region to NOT select!");
+
+			// Set the region type:
+			type = RegionType.NOT;
+
+			// Set the regions:
+			this.regions = new Region[]{region};
+
+			// Set the other fields as not used:
+			coordSys = null;
+			coordinates = null;
+			radius = Double.NaN;
+			width = Double.NaN;
+			height = Double.NaN;
+		}
+
+		/**
+		 * <p>Build a Region from the given ADQL representation.</p>
+		 * 
+		 * <p><i>Note:
+		 * 	Only {@link PointFunction}, {@link CircleFunction}, {@link BoxFunction}, {@link PolygonFunction} and {@link RegionFunction}
+		 * 	are accepted here. Other extensions of {@link GeometryFunction} will throw an {@link IllegalArgumentException}.
+		 * </i></p>
+		 * 
+		 * @param geometry	The ADQL representation of the region to create here.
+		 * 
+		 * @throws IllegalArgumentException	If the given geometry is neither of {@link PointFunction}, {@link BoxFunction}, {@link PolygonFunction} and {@link RegionFunction}.
+		 * @throws ParseException			If the declared coordinate system, the coordinates or the STC-S definition has a wrong syntax.
+		 */
+		public Region(final GeometryFunction geometry) throws IllegalArgumentException, ParseException{
+			if (geometry == null)
+				throw new NullPointerException("Missing geometry to convert into STCS.Region!");
+
+			if (geometry instanceof PointFunction){
+				type = RegionType.POSITION;
+				coordSys = STCS.parseCoordSys(extractString(geometry.getCoordinateSystem()));
+				coordinates = new double[][]{{extractNumeric(((PointFunction)geometry).getCoord1()),extractNumeric(((PointFunction)geometry).getCoord2())}};
+				width = Double.NaN;
+				height = Double.NaN;
+				radius = Double.NaN;
+				regions = null;
+			}else if (geometry instanceof CircleFunction){
+				type = RegionType.CIRCLE;
+				coordSys = STCS.parseCoordSys(extractString(geometry.getCoordinateSystem()));
+				coordinates = new double[][]{{extractNumeric(((CircleFunction)geometry).getCoord1()),extractNumeric(((CircleFunction)geometry).getCoord2())}};
+				radius = extractNumeric(((CircleFunction)geometry).getRadius());
+				width = Double.NaN;
+				height = Double.NaN;
+				regions = null;
+			}else if (geometry instanceof BoxFunction){
+				type = RegionType.BOX;
+				coordSys = STCS.parseCoordSys(extractString(geometry.getCoordinateSystem()));
+				coordinates = new double[][]{{extractNumeric(((BoxFunction)geometry).getCoord1()),extractNumeric(((BoxFunction)geometry).getCoord2())}};
+				width = extractNumeric(((BoxFunction)geometry).getWidth());
+				height = extractNumeric(((BoxFunction)geometry).getHeight());
+				radius = Double.NaN;
+				regions = null;
+			}else if (geometry instanceof PolygonFunction){
+				PolygonFunction poly = (PolygonFunction)geometry;
+				type = RegionType.POLYGON;
+				coordSys = STCS.parseCoordSys(extractString(poly.getCoordinateSystem()));
+				coordinates = new double[(poly.getNbParameters() - 1) / 2][2];
+				for(int i = 0; i < coordinates.length; i++)
+					coordinates[i] = new double[]{extractNumeric(poly.getParameter(1 + i * 2)),extractNumeric(poly.getParameter(2 + i * 2))};
+				width = Double.NaN;
+				height = Double.NaN;
+				radius = Double.NaN;
+				regions = null;
+			}else if (geometry instanceof RegionFunction){
+				Region r = STCS.parseRegion(extractString(((RegionFunction)geometry).getParameter(0)));
+				type = r.type;
+				coordSys = r.coordSys;
+				coordinates = r.coordinates;
+				width = r.width;
+				height = r.height;
+				radius = r.radius;
+				regions = r.regions;
+			}else
+				throw new IllegalArgumentException("Unknown region type! Only geometrical function PointFunction, CircleFunction, BoxFunction, PolygonFunction and RegionFunction are allowed.");
+		}
+
+		/**
+		 * Extract a string value from the given {@link ADQLOperand}
+		 * which is <b>expected to be a {@link StringConstant} instance</b>.
+		 * 
+		 * @param op	A string operand.
+		 * 
+		 * @return	The string value embedded in the given operand.
+		 * 
+		 * @throws ParseException	If the given operand is not an instance of {@link StringConstant}.
+		 */
+		private static String extractString(final ADQLOperand op) throws ParseException{
+			if (op == null)
+				throw new NullPointerException("Missing operand!");
+			else if (op instanceof StringConstant)
+				return ((StringConstant)op).getValue();
+			else
+				throw new ParseException("Can not convert into STC-S a non string argument (including ADQLColumn and Concatenation)!");
+		}
+
+		/**
+		 * Extract a numeric value from the given {@link ADQLOperand}
+		 * which is <b>expected to be a {@link NumericConstant} instance</b>
+		 * or a {@link NegativeOperand} embedding a {@link NumericConstant}.
+		 * 
+		 * @param op	A numeric operand.
+		 * 
+		 * @return	The numeric value embedded in the given operand.
+		 * 
+		 * @throws ParseException	If the given operand is not an instance of {@link NumericConstant} or a {@link NegativeOperand}.
+		 */
+		private static double extractNumeric(final ADQLOperand op) throws ParseException{
+			if (op == null)
+				throw new NullPointerException("Missing operand!");
+			else if (op instanceof NumericConstant)
+				return Double.parseDouble(((NumericConstant)op).getValue());
+			else if (op instanceof NegativeOperand)
+				return extractNumeric(((NegativeOperand)op).getOperand()) * -1;
+			else
+				throw new ParseException("Can not convert into STC-S a non numeric argument (including ADQLColumn and Operation)!");
+		}
+
+		/**
+		 * <p>Get the STC-S representation of this region (in which default values
+		 * of the coordinate system are not written ; they are replaced by empty strings).</p>
+		 * 
+		 * <p><i>Note:
+		 * 	This function build the STC-S just once and store it in a class attribute.
+		 * 	The value of this attribute is then returned at next calls of this function.
+		 * </i></p>
+		 * 
+		 * @return	Its STC-S representation.
+		 */
+		public String toSTCS(){
+			if (stcs != null)
+				return stcs;
+			else{
+				// Write the region type:
+				StringBuffer buf = new StringBuffer(type.toString());
+
+				// Write the coordinate system (except for NOT):
+				if (type != RegionType.NOT){
+					String coordSysStr = coordSys.toSTCS();
+					if (coordSysStr != null && coordSysStr.length() > 0)
+						buf.append(' ').append(coordSysStr);
+					buf.append(' ');
+				}
+
+				// Write the other parameters (coordinates, regions, ...):
+				switch(type){
+					case POSITION:
+					case POLYGON:
+						appendCoordinates(buf, coordinates);
+						break;
+					case CIRCLE:
+						appendCoordinates(buf, coordinates);
+						buf.append(' ').append(radius);
+						break;
+					case BOX:
+						appendCoordinates(buf, coordinates);
+						buf.append(' ').append(width).append(' ').append(height);
+						break;
+					case UNION:
+					case INTERSECTION:
+					case NOT:
+						buf.append('(');
+						appendRegions(buf, regions, false);
+						buf.append(')');
+						break;
+				}
+
+				// Return the built STC-S:
+				return (stcs = buf.toString());
+			}
+		}
+
+		/**
+		 * <p>Get the STC-S representation of this region (in which default values
+		 * of the coordinate system are explicitly written).</p>
+		 * 
+		 * <p><i>Note:
+		 * 	This function build the STC-S just once and store it in a class attribute.
+		 * 	The value of this attribute is then returned at next calls of this function.
+		 * </i></p>
+		 * 
+		 * @return	Its STC-S representation.
+		 */
+		public String toFullSTCS(){
+			if (fullStcs != null)
+				return fullStcs;
+			else{
+				// Write the region type:
+				StringBuffer buf = new StringBuffer(type.toString());
+
+				// Write the coordinate system (except for NOT):
+				if (type != RegionType.NOT){
+					String coordSysStr = coordSys.toFullSTCS();
+					if (coordSysStr != null && coordSysStr.length() > 0)
+						buf.append(' ').append(coordSysStr);
+					buf.append(' ');
+				}
+
+				// Write the other parameters (coordinates, regions, ...):
+				switch(type){
+					case POSITION:
+					case POLYGON:
+						appendCoordinates(buf, coordinates);
+						break;
+					case CIRCLE:
+						appendCoordinates(buf, coordinates);
+						buf.append(' ').append(radius);
+						break;
+					case BOX:
+						appendCoordinates(buf, coordinates);
+						buf.append(' ').append(width).append(' ').append(height);
+						break;
+					case UNION:
+					case INTERSECTION:
+					case NOT:
+						buf.append('(');
+						appendRegions(buf, regions, true);
+						buf.append(')');
+						break;
+				}
+
+				// Return the built STC-S:
+				return (fullStcs = buf.toString());
+			}
+		}
+
+		/**
+		 * Append all the given coordinates to the given buffer.
+		 * 
+		 * @param buf		Buffer in which coordinates must be appended.
+		 * @param coords	Coordinates to append.
+		 */
+		private static void appendCoordinates(final StringBuffer buf, final double[][] coords){
+			for(int i = 0; i < coords.length; i++){
+				if (i > 0)
+					buf.append(' ');
+				buf.append(coords[i][0]).append(' ').append(coords[i][1]);
+			}
+		}
+
+		/**
+		 * Append all the given regions in the given buffer.
+		 * 
+		 * @param buf			Buffer in which regions must be appended.
+		 * @param regions		Regions to append.
+		 * @param fullCoordSys	Indicate whether the coordinate system of the regions must explicitly display the default values.
+		 */
+		private static void appendRegions(final StringBuffer buf, final Region[] regions, final boolean fullCoordSys){
+			for(int i = 0; i < regions.length; i++){
+				if (i > 0)
+					buf.append(' ');
+				if (fullCoordSys)
+					buf.append(regions[i].toFullSTCS());
+				else
+					buf.append(regions[i].toSTCS());
+			}
+		}
+
+		@Override
+		public String toString(){
+			return toSTCS();
+		}
+
+		/**
+		 * <p>Convert this region into its corresponding ADQL representation.</p>
+		 * 
+		 * <ul>
+		 * 	<li><b>POSITION:</b> {@link PointFunction}</li>
+		 * 	<li><b>CIRCLE:</b> {@link CircleFunction}</li>
+		 * 	<li><b>BOX:</b> {@link BoxFunction}</li>
+		 * 	<li><b>POLYGON:</b> {@link PolygonFunction}</li>
+		 * 	<li><b>UNION, INTERSECTION, NOT:</b> {@link RegionFunction}</li>
+		 * </ul>
+		 * 
+		 * <p><i>Note:
+		 * 	This function is using the default ADQL factory, built using {@link ADQLQueryFactory#ADQLQueryFactory()}.
+		 * </i></p>
+		 * 
+		 * @return	The corresponding ADQL representation.
+		 * 
+		 * @see #toGeometry(ADQLQueryFactory)
+		 */
+		public GeometryFunction toGeometry(){
+			return toGeometry(null);
+		}
+
+		/**
+		 * <p>Convert this region into its corresponding ADQL representation.</p>
+		 * 
+		 * <ul>
+		 * 	<li><b>POSITION:</b> {@link PointFunction}</li>
+		 * 	<li><b>CIRCLE:</b> {@link CircleFunction}</li>
+		 * 	<li><b>BOX:</b> {@link BoxFunction}</li>
+		 * 	<li><b>POLYGON:</b> {@link PolygonFunction}</li>
+		 * 	<li><b>UNION, INTERSECTION, NOT:</b> {@link RegionFunction}</li>
+		 * </ul>
+		 * 
+		 * <p><i>Note:
+		 * 	This function build the ADQL representation just once and store it in a class attribute.
+		 * 	The value of this attribute is then returned at next calls of this function.
+		 * </i></p>
+		 * 
+		 * @param factory	The factory of ADQL objects to use.
+		 * 
+		 * @return	The corresponding ADQL representation.
+		 */
+		public GeometryFunction toGeometry(ADQLQueryFactory factory){
+			if (factory == null)
+				factory = new ADQLQueryFactory();
+
+			try{
+				if (geometry != null)
+					return geometry;
+				else{
+					StringConstant coordSysObj = factory.createStringConstant(coordSys == null ? "" : coordSys.toString());
+					switch(type){
+						case POSITION:
+							return (geometry = factory.createPoint(coordSysObj, toNumericObj(coordinates[0][0], factory), toNumericObj(coordinates[0][1], factory)));
+						case CIRCLE:
+							return (geometry = factory.createCircle(coordSysObj, toNumericObj(coordinates[0][0], factory), toNumericObj(coordinates[0][1], factory), toNumericObj(radius, factory)));
+						case BOX:
+							return (geometry = factory.createBox(coordSysObj, toNumericObj(coordinates[0][0], factory), toNumericObj(coordinates[0][1], factory), toNumericObj(width, factory), toNumericObj(height, factory)));
+						case POLYGON:
+							ArrayList<ADQLOperand> coords = new ArrayList<ADQLOperand>(coordinates.length * 2);
+							for(int i = 0; i < coordinates.length; i++){
+								coords.add(toNumericObj(coordinates[i][0], factory));
+								coords.add(toNumericObj(coordinates[i][1], factory));
+							}
+							return (geometry = factory.createPolygon(coordSysObj, coords));
+						default:
+							return (geometry = factory.createRegion(factory.createStringConstant(toString())));
+					}
+				}
+			}catch(Exception pe){
+				return null;
+			}
+		}
+
+		/**
+		 * <p>Convert a numeric value into an ADQL representation:</p>
+		 * 
+		 * <ul>
+		 * 	<li>If negative: NegativeOperand(NumericConstant(val))</li>
+		 * 	<li>Otherwise: NumericConstant(val)</li>
+		 * </ul>
+		 * 
+		 * @param val		The value to embed in an ADQL object.
+		 * @param factory	The factory to use to created ADQL objects.
+		 * 
+		 * @return	The representing ADQL representation.
+		 * 
+		 * @throws Exception	If an error occurs while creating the ADQL object.
+		 */
+		private ADQLOperand toNumericObj(final double val, final ADQLQueryFactory factory) throws Exception{
+			if (val >= 0)
+				return factory.createNumericConstant("" + val);
+			else
+				return factory.createNegativeOperand(factory.createNumericConstant("" + (val * -1)));
+		}
+	}
+
+	/**
+	 * Parse the given STC-S expression representing a geometrical region.
+	 * 
+	 * @param stcsRegion	STC-S expression of a region. <i>Note: MUST be different from NULL.</i>
+	 * 
+	 * @return	The object representation of the specified geometrical region.
+	 * 
+	 * @throws ParseException	If the given expression is NULL, empty string or if the STC-S syntax is wrong.
+	 */
+	public static Region parseRegion(final String stcsRegion) throws ParseException{
+		if (stcsRegion == null || stcsRegion.trim().length() == 0)
+			throw new ParseException("Missing STC-S expression to parse!");
+		return (new STCSParser().parseRegion(stcsRegion));
+	}
+
+	/**
+	 * Convert into STC-S the given object representation of a geometrical region.
+	 * 
+	 * @param region	Region to convert into STC-S.
+	 * 
+	 * @return	The corresponding STC-S expression.
+	 */
+	public static String toSTCS(final Region region){
+		if (region == null)
+			throw new NullPointerException("Missing region to serialize into STC-S!");
+		return region.toSTCS();
+	}
+
+	/**
+	 * <p>Convert into STC-S the given ADQL representation of a geometrical function.</p>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	Only {@link PointFunction}, {@link CircleFunction}, {@link BoxFunction}, {@link PolygonFunction}
+	 * 	and {@link RegionFunction} are accepted here. Other extensions of {@link GeometryFunction} will
+	 * 	throw an {@link IllegalArgumentException}.
+	 * </i></p>
+	 * 
+	 * @param region	ADQL representation of the region to convert into STC-S.
+	 * 
+	 * @return	The corresponding STC-S expression.
+	 * 
+	 * @throws ParseException	If the given object is NULL or not of the good type.
+	 * 
+	 * @see {@link Region#Region(GeometryFunction)}
+	 */
+	public static String toSTCS(final GeometryFunction region) throws ParseException{
+		if (region == null)
+			throw new NullPointerException("Missing region to serialize into STC-S!");
+		return (new Region(region)).toSTCS();
+	}
+
+	/* *************************** */
+	/* PARSER OF STC-S EXPRESSIONS */
+	/* *************************** */
+
+	/**
+	 * Let parse any STC-S expression.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class STCSParser {
+		/** Regular expression of a numerical value. */
+		private final static String numericRegExp = "(\\+|-)?(\\d+(\\.\\d*)?|\\.\\d+)([Ee](\\+|-)?\\d+)?";
+
+		/** Position of the next characters to read in the STC-S expression to parse. */
+		private int pos;
+		/** Full STC-S expression to parse. */
+		private String stcs;
+		/** Last read token (can be a numeric, a string, a region type, ...). */
+		private String token;
+		/** Buffer used to read tokens. */
+		private StringBuffer buffer;
+
+		/**
+		 * Exception sent when the end of the expression
+		 * (EOE = End Of Expression) is reached.
+		 * 
+		 * @author Gr&eacute;gory Mantelet (ARI)
+		 * @version 1.3 (10/2014)
+		 * @since 1.3
+		 */
+		private static class EOEException extends ParseException {
+			private static final long serialVersionUID = 1L;
+
+			/** Build a simple EOEException. */
+			public EOEException(){
+				super("Unexpected End Of Expression!");
+			}
+		}
+
+		/**
+		 * Build the STC-S parser.
+		 */
+		public STCSParser(){}
+
+		/**
+		 * Parse the given STC-S expression, expected as a coordinate system.
+		 * 
+		 * @param stcs	The STC-S expression to parse.
+		 * 
+		 * @return	The corresponding object representation of the specified coordinate system.
+		 * 
+		 * @throws ParseException	If the syntax of the given STC-S expression is wrong or if it is not a coordinate system.
+		 */
+		public CoordSys parseCoordSys(final String stcs) throws ParseException{
+			init(stcs);
+			CoordSys coordsys = null;
+			try{
+				coordsys = coordSys();
+				end(COORD_SYS_SYNTAX);
+				return coordsys;
+			}catch(EOEException ex){
+				ex.printStackTrace();
+				return new CoordSys();
+			}
+		}
+
+		/**
+		 * Parse the given STC-S expression, expected as a geometrical region.
+		 * 
+		 * @param stcs	The STC-S expression to parse.
+		 * 
+		 * @return	The corresponding object representation of the specified geometrical region.
+		 * 
+		 * @throws ParseException	If the syntax of the given STC-S expression is wrong or if it is not a geometrical region.
+		 */
+		public Region parseRegion(final String stcs) throws ParseException{
+			init(stcs);
+			Region region = region();
+			end("\"POSITION <coordsys> <coordPair>\", \"CIRCLE <coordSys> <coordPair> <numeric>\", \"BOX <coordSys> <coordPair> <coordPair>\", \"POLYGON <coordSys> <coordPair> <coordPair> <coordPair> [<coordPair> ...]\", \"UNION <coordSys> ( <region> <region> [<region> ...] )\", \"INTERSECTION [<coordSys>] ( <region> <region> [<region> ...] )\" or \"NOT ( <region> )\"");
+			return region;
+		}
+
+		/**
+		 * Prepare the parser in order to read the given STC-S expression.
+		 * 
+		 * @param newStcs	New STC-S expression to parse from now.
+		 */
+		private void init(final String newStcs){
+			stcs = (newStcs == null) ? "" : newStcs.replaceAll("\\s", " ");
+			token = null;
+			buffer = new StringBuffer();
+			pos = 0;
+		}
+
+		/**
+		 * Finalize the parsing.
+		 * No more characters (except eventually some space characters) should remain in the STC-S expression to parse.
+		 * 
+		 * @param expectedSyntax	Description of the good syntax expected. This description is used only to write the
+		 *                      	{@link ParseException} in case other non-space characters are found among the remaining characters.
+		 * 
+		 * @throws ParseException	If other non-space characters remains. 
+		 */
+		private void end(final String expectedSyntax) throws ParseException{
+			// Skip all spaces:
+			skipSpaces();
+
+			// If there is still some characters, they are not expected, and so throw an exception:
+			if (stcs.length() > 0 && pos < stcs.length())
+				throw new ParseException("Incorrect syntax: \"" + stcs.substring(pos) + "\" was unexpected! Expected syntax: " + expectedSyntax + ".", new TextPosition(1, pos, 1, stcs.length()));
+
+			// Reset the buffer, token and the STC-S expression to parse:
+			buffer = null;
+			stcs = null;
+			token = null;
+		}
+
+		/**
+		 * Tool function which skip all next space characters until the next meaningful characters. 
+		 */
+		private void skipSpaces(){
+			while(pos < stcs.length() && stcs.charAt(pos) == ' ')
+				pos++;
+		}
+
+		/**
+		 * <p>Get the next meaningful word. This word can be a numeric, any string constant or a region type.</p>
+		 * 
+		 * <p>
+		 * 	In case the end of the expression is reached before getting any meaningful character, an {@link EOEException} is thrown.
+		 * </p>
+		 * 
+		 * @return	The full read word/token.
+		 * 
+		 * @throws EOEException	If the end of the STC-S expression is reached before getting any meaningful character.
+		 */
+		private String nextToken() throws EOEException{
+			// Skip all spaces:
+			skipSpaces();
+
+			// Fetch all characters until word separator (a space or a open/close parenthesis):
+			while(pos < stcs.length() && stcs.charAt(pos) != ' ' && stcs.charAt(pos) != '(' && stcs.charAt(pos) != ')')
+				buffer.append(stcs.charAt(pos++));
+
+			// If no character has been fetched while at least one was expected, throw an exception:
+			if (buffer.length() == 0)
+				throw new EOEException();
+
+			// Save the read token and reset the buffer:
+			token = buffer.toString();
+			buffer.delete(0, token.length());
+
+			return token;
+		}
+
+		/**
+		 * Read the next token as a numeric.
+		 * If not a numeric, a {@link ParseException} is thrown.
+		 * 
+		 * @return	The read numerical value.
+		 * 
+		 * @throws ParseException	If the next token is not a numerical expression.
+		 */
+		private double numeric() throws ParseException{
+			if (nextToken().matches(numericRegExp))
+				return Double.parseDouble(token);
+			else
+				throw new ParseException("a numeric was expected!", new TextPosition(1, pos - token.length(), 1, pos));	// TODO Check the begin and end!
+		}
+
+		/**
+		 * Read the next 2 tokens as a coordinate pairs (so as 2 numerical values).
+		 * If not 2 numeric, a {@link ParseException} is thrown.
+		 * 
+		 * @return	The read coordinate pairs.
+		 * 
+		 * @throws ParseException	If the next 2 tokens are not 2 numerical expressions.
+		 */
+		private double[] coordPair() throws ParseException{
+			skipSpaces();
+			int startPos = pos;
+			try{
+				return new double[]{numeric(),numeric()};
+			}catch(ParseException pe){
+				if (pe instanceof EOEException)
+					throw pe;
+				else
+					throw new ParseException("a coordinates pair (2 numerics separated by one or more spaces) was expected!", new TextPosition(1, startPos, 1, pos));	// TODO Check the begin and end!
+			}
+		}
+
+		/**
+		 * Read and parse the next tokens as a coordinate system expression.
+		 * If they do not match, a {@link ParseException} is thrown.
+		 * 
+		 * @return	The object representation of the read coordinate system.
+		 * 
+		 * @throws ParseException	If the next tokens are not representing a valid coordinate system.
+		 */
+		private CoordSys coordSys() throws ParseException{
+			// Skip all spaces:
+			skipSpaces();
+
+			// Backup the current position:
+			/* (because every parts of a coordinate system are optional ;
+			 * like this, it will be possible to go back in the expression
+			 * to parse if optional parts are not written) */
+			String oldToken = token;
+			int startPos = pos;
+
+			Frame fr = null;
+			RefPos rp = null;
+			Flavor fl = null;
+
+			try{
+				// Read the token:
+				nextToken();
+				// Try to parse it as a frame:
+				if ((fr = frame()) != null){
+					// if success, go the next token:
+					startPos = pos;
+					oldToken = token;
+					nextToken();
+				}
+				// Try to parse the last read token as a reference position:
+				if ((rp = refpos()) != null){
+					// if success, go the next token:
+					startPos = pos;
+					oldToken = token;
+					nextToken();
+				}
+				// Try to parse the last read token as a flavor:
+				if ((fl = flavor()) == null){
+					// if NOT a success, go back "in time" (go back to the position before reading the token):
+					pos = startPos;
+					token = oldToken;
+				}
+			}catch(EOEException ex){
+				/* End Of Expression may happen here since all parts of a coordinate system are optional.
+				 * So, there is no need to treat the error. */
+			}
+
+			// Build the object representation of the read coordinate system:
+			/* Note: if nothing has been read for one or all parts of the coordinate system,
+			 * the NULL value will be replaced automatically in the constructor
+			 * by the default value of the corresponding part(s). */
+			try{
+				return new CoordSys(fr, rp, fl);
+			}catch(IllegalArgumentException iae){
+				throw new ParseException(iae.getMessage(), new TextPosition(1, startPos, 1, pos));
+			}
+		}
+
+		/**
+		 * Parse the last read token as FRAME.
+		 * 
+		 * @return	The corresponding enumeration item, or NULL if the last token is not a valid FRAME item.
+		 */
+		private Frame frame(){
+			try{
+				return Frame.valueOf(token.toUpperCase());
+			}catch(IllegalArgumentException iae){
+				return null;
+			}
+		}
+
+		/**
+		 * Parse the last read token as REFERENCE POSITION.
+		 * 
+		 * @return	The corresponding enumeration item, or NULL if the last token is not a valid REFERENCE POSITION item.
+		 */
+		private RefPos refpos(){
+			try{
+				return RefPos.valueOf(token.toUpperCase());
+			}catch(IllegalArgumentException iae){
+				return null;
+			}
+		}
+
+		/**
+		 * Parse the last read token as FLAVOR.
+		 * 
+		 * @return	The corresponding enumeration item, or NULL if the last token is not a valid FLAVOR item.
+		 */
+		private Flavor flavor(){
+			try{
+				return Flavor.valueOf(token.toUpperCase());
+			}catch(IllegalArgumentException iae){
+				return null;
+			}
+		}
+
+		/**
+		 * Read and parse the next tokens as a geometrical region.
+		 * If they do not match, a {@link ParseException} is thrown.
+		 * 
+		 * @return	The object representation of the read geometrical region.
+		 * 
+		 * @throws ParseException	If the next tokens are not representing a valid geometrical region.
+		 */
+		private Region region() throws ParseException{
+			// Skip all spaces:
+			skipSpaces();
+
+			// Read the next token (it should be the region type):
+			int startPos = pos;
+			token = nextToken().toUpperCase();
+
+			/* Identify the region type, next the expected parameters and finally build the corresponding object representation */
+			// POSITION case:
+			if (token.equals("POSITION")){
+				try{
+					CoordSys coordSys = coordSys();
+					double[] coords = coordPair();
+					return new Region(coordSys, coords);
+				}catch(Exception e){
+					throw buildException(e, "\"POSITION <coordSys> <coordPair>\", where coordPair=\"<numeric> <numeric>\" and coordSys=" + COORD_SYS_SYNTAX, startPos);
+				}
+			}
+			// CIRCLE case:
+			else if (token.equals("CIRCLE")){
+				try{
+					CoordSys coordSys = coordSys();
+					double[] coords = coordPair();
+					double radius = numeric();
+					return new Region(coordSys, coords, radius);
+				}catch(Exception e){
+					throw buildException(e, "\"CIRCLE <coordSys> <coordPair> <radius>\", where coordPair=\"<numeric> <numeric>\", radius=\"<numeric>\" and coordSys=" + COORD_SYS_SYNTAX, startPos);
+				}
+			}
+			// BOX case:
+			else if (token.equals("BOX")){
+				try{
+					CoordSys coordSys = coordSys();
+					double[] coords = coordPair();
+					double width = numeric(), height = numeric();
+					return new Region(coordSys, coords, width, height);
+				}catch(Exception e){
+					throw buildException(e, "\"BOX <coordSys> <coordPair> <width> <height>\", where coordPair=\"<numeric> <numeric>\", width and height=\"<numeric>\" and coordSys=" + COORD_SYS_SYNTAX, startPos);
+				}
+			}
+			// POLYGON case:
+			else if (token.equals("POLYGON")){
+				try{
+					CoordSys coordSys = coordSys();
+					ArrayList<Double> coordinates = new ArrayList<Double>(6);
+					double[] coords;
+					for(int i = 0; i < 3; i++){
+						coords = coordPair();
+						coordinates.add(coords[0]);
+						coordinates.add(coords[1]);
+					}
+					boolean moreCoord = true;
+					int posBackup;
+					do{
+						posBackup = pos;
+						try{
+							coords = coordPair();
+							coordinates.add(coords[0]);
+							coordinates.add(coords[1]);
+						}catch(ParseException pe){
+							moreCoord = false;
+							pos = posBackup;
+						}
+					}while(moreCoord);
+					double[][] allCoords = new double[coordinates.size() / 2][2];
+					for(int i = 0; i < coordinates.size() && i + 1 < coordinates.size(); i += 2)
+						allCoords[i / 2] = new double[]{coordinates.get(i),coordinates.get(i + 1)};
+					return new Region(coordSys, allCoords);
+				}catch(Exception e){
+					throw buildException(e, "\"POLYGON <coordSys> <coordPair> <coordPair> <coordPair> [<coordPair> ...]\", where coordPair=\"<numeric> <numeric>\" and coordSys=" + COORD_SYS_SYNTAX, startPos);
+				}
+			}
+			// UNION & INTERSECTION cases:
+			else if (token.equals("UNION") || token.equals("INTERSECTION")){
+				RegionType type = (token.equals("UNION") ? RegionType.UNION : RegionType.INTERSECTION);
+				try{
+					CoordSys coordSys = coordSys();
+					ArrayList<Region> regions = new ArrayList<Region>(2);
+
+					skipSpaces();
+					if (stcs.charAt(pos) != '(')
+						throw buildException(new ParseException("a opening parenthesis - ( - was expected!", new TextPosition(1, pos, 1, pos + 1)), "\"" + type + " <coordSys> ( <region> <region> [<region> ...] )\", where coordSys=" + COORD_SYS_SYNTAX, startPos);
+					else
+						pos++;
+
+					// parse and add the FIRST region:
+					regions.add(region());
+
+					// parse and add the SECOND region:
+					regions.add(region());
+
+					skipSpaces();
+					while(stcs.charAt(pos) != ')'){
+						regions.add(region());
+						skipSpaces();
+					}
+					pos++;
+
+					return new Region(type, coordSys, regions.toArray(new Region[regions.size()]));
+				}catch(Exception e){
+					if (e instanceof ParseException && e.getMessage().startsWith("Incorrect syntax: \""))
+						throw (ParseException)e;
+					else
+						throw buildException(e, "\"" + type + " <coordSys> ( <region> <region> [<region> ...] )\", where coordSys=" + COORD_SYS_SYNTAX, startPos);
+				}
+			}
+			// NOT case:
+			else if (token.equals("NOT")){
+				try{
+					skipSpaces();
+					if (stcs.charAt(pos) != '(')
+						throw buildException(new ParseException("an opening parenthesis - ( - was expected!", new TextPosition(1, pos, 1, pos + 1)), "\"NOT ( <region> )\"", startPos);
+					else
+						pos++;
+					Region region = region();
+					skipSpaces();
+					if (stcs.charAt(pos) != ')')
+						throw buildException(new ParseException("a closing parenthesis - ) - was expected!", new TextPosition(1, pos, 1, pos + 1)), "\"NOT ( <region> )\"", startPos);
+					else
+						pos++;
+					return new Region(region);
+				}catch(Exception e){
+					if (e instanceof ParseException && e.getMessage().startsWith("Incorrect syntax: "))
+						throw (ParseException)e;
+					else
+						throw buildException(e, "\"NOT ( <region> )\"", startPos);
+				}
+			}
+			// Otherwise, the region type is not known and so a ParseException is thrown:
+			else
+				throw new ParseException("Unknown STC region type: \"" + token + "\"!", new TextPosition(1, startPos, 1, pos));
+		}
+
+		/**
+		 * Build a {@link ParseException} based on the given one and by adding the human description of what was expected, if needed.
+		 * 
+		 * @param ex				Root exception.
+		 * @param expectedSyntax	Human description of what was expected.
+		 * @param startPos			Position of the first character of the wrong part of expression.
+		 * 
+		 * @return	The build exception.
+		 */
+		private ParseException buildException(final Exception ex, final String expectedSyntax, int startPos){
+			if (ex instanceof EOEException)
+				return new ParseException("Unexpected End Of Expression! Expected syntax: " + expectedSyntax + ".", new TextPosition(1, startPos, 1, pos));
+			else if (ex instanceof ParseException)
+				return new ParseException("Incorrect syntax: " + ex.getMessage() + " Expected syntax: " + expectedSyntax + ".", (((ParseException)ex).getPosition() != null ? ((ParseException)ex).getPosition() : new TextPosition(1, startPos, 1, pos)));
+			else
+				return new ParseException(ex.getMessage(), new TextPosition(1, startPos, 1, pos));
+		}
+	}
+}
diff --git a/src/adql/db/SearchColumnList.java b/src/adql/db/SearchColumnList.java
index 8411e4be31d939e6fc6670230eb1210b8fc33fcb..e385cb7890b2f632018538f2060d666da64528e5 100644
--- a/src/adql/db/SearchColumnList.java
+++ b/src/adql/db/SearchColumnList.java
@@ -17,7 +17,7 @@ package adql.db;
  * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
diff --git a/src/adql/db/exception/UnresolvedFunction.java b/src/adql/db/exception/UnresolvedFunction.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6eb4597a2dcbb7f5c83b606386df762b07f37a0
--- /dev/null
+++ b/src/adql/db/exception/UnresolvedFunction.java
@@ -0,0 +1,124 @@
+package adql.db.exception;
+
+/*
+ * This file is part of ADQLLibrary.
+ * 
+ * ADQLLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * ADQLLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * 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 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import adql.parser.ParseException;
+import adql.query.operand.function.ADQLFunction;
+
+/**
+ * Exception thrown when a function can not be resolved by the library.
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 1.3 (10/2014)
+ * @since 1.3
+ */
+public class UnresolvedFunction extends ParseException {
+	private static final long serialVersionUID = 1L;
+
+	/** Function which can not be resolved. */
+	protected final ADQLFunction functionInError;
+
+	/**
+	 * Build the exception with just a message.
+	 * 
+	 * @param message	Description of the error.
+	 */
+	public UnresolvedFunction(final String message){
+		super(message);
+		functionInError = null;
+	}
+
+	/**
+	 * Build the exception with the unresolved function in parameter.
+	 * The position of this function in the ADQL query can be retrieved and used afterwards.
+	 * 
+	 * @param fct	The unresolved function.
+	 */
+	public UnresolvedFunction(final ADQLFunction fct){
+		super("Unresolved function: \"" + fct.toADQL() + "\"! No UDF has been defined or found with the signature: " + getFctSignature(fct) + "."); // TODO Add the position of the function in the ADQL query!
+		functionInError = fct;
+	}
+
+	/**
+	 * Build the exception with a message but also with the unresolved function in parameter.
+	 * The position of this function in the ADQL query can be retrieved and used afterwards.
+	 * 
+	 * @param message	Description of the error.
+	 * @param fct		The unresolved function.
+	 */
+	public UnresolvedFunction(final String message, final ADQLFunction fct){
+		super(message); // TODO Add the position of the function in the ADQL query!
+		functionInError = fct;
+	}
+
+	/**
+	 * Get the unresolved function at the origin of this exception.
+	 * 
+	 * @return	The unresolved function. <i>Note: MAY be NULL</i>
+	 */
+	public final ADQLFunction getFunction(){
+		return functionInError;
+	}
+
+	/**
+	 * <p>Get the signature of the function given in parameter.</p>
+	 * 
+	 * <p>
+	 * 	In this signature, just the name and the type of all the parameters are written.
+	 * 	The return type is never part of a function signature.
+	 * </p>
+	 * 
+	 * <p><i>Note 1:
+	 * 	A parameter type can be either "NUMERIC", "STRING" or "GEOMETRY". In order to be the most generic has possible,
+	 * 	no more precision about a type is returned here. If the parameter is none of these type kinds, "???" is returned.
+	 * </i></p>
+	 * 
+	 * <p><i>Note 2:
+	 * 	If the given object is NULL, an empty string is returned.
+	 * </i></p>
+	 * 
+	 * @param fct	Function whose the signature must be returned.
+	 * 
+	 * @return	The corresponding signature.
+	 */
+	public static String getFctSignature(final ADQLFunction fct){
+		if (fct == null)
+			return "";
+
+		StringBuffer buf = new StringBuffer(fct.getName().toLowerCase());
+		buf.append('(');
+		for(int i = 0; i < fct.getNbParameters(); i++){
+			if (fct.getParameter(i).isNumeric())
+				buf.append("NUMERIC");
+			else if (fct.getParameter(i).isString())
+				buf.append("STRING");
+			else if (fct.getParameter(i).isGeometry())
+				buf.append("GEOMETRY");
+			else
+				buf.append("???");
+
+			if ((i + 1) < fct.getNbParameters())
+				buf.append(", ");
+		}
+		buf.append(')');
+		return buf.toString();
+	}
+
+}
diff --git a/src/adql/db/exception/UnresolvedIdentifiersException.java b/src/adql/db/exception/UnresolvedIdentifiersException.java
index b141befb55700ffa2a51f0d39fcba06d58d61d0f..8c201871deaee87e6a31245c461edab207b34a59 100644
--- a/src/adql/db/exception/UnresolvedIdentifiersException.java
+++ b/src/adql/db/exception/UnresolvedIdentifiersException.java
@@ -71,6 +71,10 @@ public class UnresolvedIdentifiersException extends ParseException implements It
 				String tableName = ((UnresolvedTableException)pe).getTableName();
 				if (tableName != null && !tableName.trim().isEmpty())
 					addIdentifierName(tableName + " " + pe.getPosition());
+				/*}else if (pe instanceof UnresolvedFunction){	// TODO MANAGE ALSO THE UNRESOLVED_FUNCTIONs!
+					String fctName = (((UnresolvedFunction)pe).getFunction() == null) ? null : ((UnresolvedFunction)pe).getFunction().getName();
+					if (fctName != null && !fctName.trim().isEmpty())
+						addIdentifierName(fctName + " " + pe.getPosition());*/
 			}else if (pe instanceof UnresolvedIdentifiersException)
 				addIdentifierName(((UnresolvedIdentifiersException)pe).unresolvedIdentifiers);
 		}
@@ -109,6 +113,7 @@ public class UnresolvedIdentifiersException extends ParseException implements It
 		return exceptions.iterator();
 	}
 
+	@Override
 	public final Iterator<ParseException> iterator(){
 		return getErrors();
 	}
@@ -120,7 +125,11 @@ public class UnresolvedIdentifiersException extends ParseException implements It
 	 */
 	@Override
 	public String getMessage(){
-		return exceptions.size() + " unresolved identifiers" + ((unresolvedIdentifiers != null) ? (": " + unresolvedIdentifiers) : "") + " !";
+		StringBuffer buf = new StringBuffer();
+		buf.append(exceptions.size()).append(" unresolved identifiers").append(((unresolvedIdentifiers != null) ? (": " + unresolvedIdentifiers) : "")).append('!');
+		for(ParseException pe : exceptions)
+			buf.append("\n  - ").append(pe.getMessage());
+		return buf.toString();
 	}
 
 }
diff --git a/src/adql/db/exception/UnresolvedJoin.java b/src/adql/db/exception/UnresolvedJoin.java
index c92440ece0b61b79c525f697c642783ccfc6049c..819beaa17211c3496ad0faacfc6957298b6c9470 100644
--- a/src/adql/db/exception/UnresolvedJoin.java
+++ b/src/adql/db/exception/UnresolvedJoin.java
@@ -16,7 +16,7 @@ package adql.db.exception;
  * 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 2013-2014 - Astronomishes Rechen Institut (ARI)
+ * Copyright 2013-2014 - Astronomisches Rechen Institut (ARI)
  */
 
 import adql.parser.ParseException;
diff --git a/src/adql/parser/ADQLParser.java b/src/adql/parser/ADQLParser.java
index b1649f8a3a5e508e0ee2b99ab910b198977687c1..b454aadd5a94c66214739a5d97528e934f5e2a8a 100644
--- a/src/adql/parser/ADQLParser.java
+++ b/src/adql/parser/ADQLParser.java
@@ -4,7 +4,6 @@ package adql.parser;
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Stack;
 import java.util.Vector;
 
@@ -32,7 +31,6 @@ import adql.query.operand.ADQLColumn;
 import adql.query.operand.ADQLOperand;
 import adql.query.operand.Concatenation;
 import adql.query.operand.OperationType;
-import adql.query.operand.StringConstant;
 import adql.query.operand.function.ADQLFunction;
 import adql.query.operand.function.MathFunction;
 import adql.query.operand.function.MathFunctionType;
@@ -61,7 +59,7 @@ import adql.translator.TranslationException;
 * @see ADQLQueryFactory
 *
 * @author Gr&eacute;gory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de
-* @version 1.2 (03/2014)
+* @version 1.3 (10/2014)
 */
 public class ADQLParser implements ADQLParserConstants {
 
@@ -80,9 +78,6 @@ public class ADQLParser implements ADQLParserConstants {
 	/** The first token of a table/column name. This token is extracted by {@link #Identifier()}. */
 	private Token currentIdentifierToken = null;
 
-	/** List of all allowed coordinate systems. */
-	private ArrayList<String> allowedCoordSys = new ArrayList<String>();
-
 	/**
 	* Builds an ADQL parser without a query to parse.
 	*/
@@ -320,24 +315,6 @@ public class ADQLParser implements ADQLParserConstants {
 		return Query();
 	}
 
-	public final void addCoordinateSystem(final String coordSys){
-		allowedCoordSys.add(coordSys);
-	}
-
-	public final void setCoordinateSystems(final Collection<String> coordSys){
-		allowedCoordSys.clear();
-		if (coordSys != null)
-			allowedCoordSys.addAll(coordSys);
-	}
-
-	public final boolean isAllowedCoordSys(final String coordSys){
-		for(String cs : allowedCoordSys){
-			if (cs.equalsIgnoreCase(coordSys))
-				return true;
-		}
-		return false;
-	}
-
 	public final void setDebug(boolean debug){
 		if (debug)
 			enable_tracing();
@@ -493,1190 +470,1598 @@ public class ADQLParser implements ADQLParserConstants {
 	* @throws ParseException	If the query syntax is incorrect.
 	*/
 	final public ADQLQuery Query() throws ParseException{
-		trace_call("Query");
+		ADQLQuery q = null;
+		q = QueryExpression();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case 0:
+				jj_consume_token(0);
+				break;
+			case EOQ:
+				jj_consume_token(EOQ);
+				break;
+			default:
+				jj_la1[0] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		// check the query:
+		if (queryChecker != null)
+			queryChecker.check(q);
+
+		{
+			if (true)
+				return q;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public ADQLQuery QueryExpression() throws ParseException{
 		try{
-			ADQLQuery q = null;
-			q = QueryExpression();
+			// create the query:
+			query = queryFactory.createQuery();
+			stackQuery.push(query);
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		Select();
+		From();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case WHERE:
+				Where();
+				break;
+			default:
+				jj_la1[1] = jj_gen;
+				;
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case GROUP_BY:
+				GroupBy();
+				break;
+			default:
+				jj_la1[2] = jj_gen;
+				;
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case HAVING:
+				Having();
+				break;
+			default:
+				jj_la1[3] = jj_gen;
+				;
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ORDER_BY:
+				OrderBy();
+				break;
+			default:
+				jj_la1[4] = jj_gen;
+				;
+		}
+		// get the previous query (!= null if the current query is a sub-query):
+		ADQLQuery previousQuery = stackQuery.pop();
+		if (stackQuery.isEmpty())
+			query = null;
+		else
+			query = stackQuery.peek();
+
+		{
+			if (true)
+				return previousQuery;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public ADQLQuery SubQueryExpression() throws ParseException{
+		ADQLQuery q = null;
+		jj_consume_token(LEFT_PAR);
+		q = QueryExpression();
+		jj_consume_token(RIGHT_PAR);
+		{
+			if (true)
+				return q;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public void Select() throws ParseException{
+		ClauseSelect select = query.getSelect();
+		SelectItem item = null;
+		Token t = null;
+		jj_consume_token(SELECT);
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case QUANTIFIER:
+				t = jj_consume_token(QUANTIFIER);
+				select.setDistinctColumns(t.image.equalsIgnoreCase("DISTINCT"));
+				break;
+			default:
+				jj_la1[5] = jj_gen;
+				;
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case TOP:
+				jj_consume_token(TOP);
+				t = jj_consume_token(UNSIGNED_INTEGER);
+				try{
+					select.setLimit(Integer.parseInt(t.image));
+				}catch(NumberFormatException nfe){
+					{
+						if (true)
+							throw new ParseException("[l." + t.beginLine + ";c." + t.beginColumn + "] The TOP limit (\u005c"" + t.image + "\u005c") isn't a regular unsigned integer !");
+					}
+				}
+				break;
+			default:
+				jj_la1[6] = jj_gen;
+				;
+		}
+		item = SelectItem();
+		select.add(item);
+		label_1: while(true){
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case 0:
-					jj_consume_token(0);
-					break;
-				case EOQ:
-					jj_consume_token(EOQ);
+				case COMMA:
+					;
 					break;
 				default:
-					jj_la1[0] = jj_gen;
-					jj_consume_token(-1);
-					throw new ParseException();
+					jj_la1[7] = jj_gen;
+					break label_1;
 			}
-			// check the query:
-			if (queryChecker != null)
-				queryChecker.check(q);
+			jj_consume_token(COMMA);
+			item = SelectItem();
+			select.add(item);
+		}
+	}
 
+	final public SelectItem SelectItem() throws ParseException{
+		IdentifierItems identifiers = new IdentifierItems(true);
+		IdentifierItem id = null, label = null;
+		ADQLOperand op = null;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ASTERISK:
+				jj_consume_token(ASTERISK);
+				{
+					if (true)
+						return new SelectAllColumns(query);
+				}
+				break;
+			default:
+				jj_la1[11] = jj_gen;
+				if (jj_2_1(7)){
+					id = Identifier();
+					jj_consume_token(DOT);
+					identifiers.append(id);
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							id = Identifier();
+							jj_consume_token(DOT);
+							identifiers.append(id);
+							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+								case DELIMITED_IDENTIFIER:
+								case REGULAR_IDENTIFIER:
+									id = Identifier();
+									jj_consume_token(DOT);
+									identifiers.append(id);
+									break;
+								default:
+									jj_la1[8] = jj_gen;
+									;
+							}
+							break;
+						default:
+							jj_la1[9] = jj_gen;
+							;
+					}
+					jj_consume_token(ASTERISK);
+					try{
+						;
+						{
+							if (true)
+								return new SelectAllColumns(queryFactory.createTable(identifiers, null));
+						}
+					}catch(Exception ex){
+						{
+							if (true)
+								throw generateParseException(ex);
+						}
+					}
+				}else{
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case LEFT_PAR:
+						case PLUS:
+						case MINUS:
+						case AVG:
+						case MAX:
+						case MIN:
+						case SUM:
+						case COUNT:
+						case BOX:
+						case CENTROID:
+						case CIRCLE:
+						case POINT:
+						case POLYGON:
+						case REGION:
+						case CONTAINS:
+						case INTERSECTS:
+						case AREA:
+						case COORD1:
+						case COORD2:
+						case COORDSYS:
+						case DISTANCE:
+						case ABS:
+						case CEILING:
+						case DEGREES:
+						case EXP:
+						case FLOOR:
+						case LOG:
+						case LOG10:
+						case MOD:
+						case PI:
+						case POWER:
+						case RADIANS:
+						case RAND:
+						case ROUND:
+						case SQRT:
+						case TRUNCATE:
+						case ACOS:
+						case ASIN:
+						case ATAN:
+						case ATAN2:
+						case COS:
+						case COT:
+						case SIN:
+						case TAN:
+						case STRING_LITERAL:
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+						case SCIENTIFIC_NUMBER:
+						case UNSIGNED_FLOAT:
+						case UNSIGNED_INTEGER:
+							op = ValueExpression();
+							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+								case AS:
+									jj_consume_token(AS);
+									label = Identifier();
+									break;
+								default:
+									jj_la1[10] = jj_gen;
+									;
+							}
+							break;
+						default:
+							jj_la1[12] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
+					}
+				}
+		}
+		try{
+			SelectItem item = queryFactory.createSelectItem(op, (label == null) ? null : label.identifier);
+			if (label != null)
+				item.setCaseSensitive(label.caseSensitivity);
 			{
 				if (true)
-					return q;
+					return item;
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Query");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLQuery QueryExpression() throws ParseException{
-		trace_call("QueryExpression");
+	final public void From() throws ParseException{
+		FromContent content = null, content2 = null;
 		try{
-			try{
-				// create the query:
-				query = queryFactory.createQuery();
-				stackQuery.push(query);
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
+			jj_consume_token(FROM);
+			content = TableRef();
+			label_2: while(true){
+				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+					case COMMA:
+						;
+						break;
+					default:
+						jj_la1[13] = jj_gen;
+						break label_2;
 				}
+				jj_consume_token(COMMA);
+				content2 = TableRef();
+				content = queryFactory.createJoin(JoinType.CROSS, content, content2);
 			}
-			Select();
-			From();
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case WHERE:
-					Where();
-					break;
-				default:
-					jj_la1[1] = jj_gen;
-					;
+			query.setFrom(content);
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
+		}
+	}
+
+	final public void Where() throws ParseException{
+		ClauseConstraints where = query.getWhere();
+		ADQLConstraint condition;
+		jj_consume_token(WHERE);
+		ConditionsList(where);
+	}
+
+	final public void GroupBy() throws ParseException{
+		ClauseADQL<ColumnReference> groupBy = query.getGroupBy();
+		ColumnReference colRef = null;
+		jj_consume_token(GROUP_BY);
+		colRef = ColumnRef();
+		groupBy.add(colRef);
+		label_3: while(true){
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case GROUP_BY:
-					GroupBy();
-					break;
-				default:
-					jj_la1[2] = jj_gen;
+				case COMMA:
 					;
-			}
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case HAVING:
-					Having();
 					break;
 				default:
-					jj_la1[3] = jj_gen;
-					;
+					jj_la1[14] = jj_gen;
+					break label_3;
 			}
+			jj_consume_token(COMMA);
+			colRef = ColumnRef();
+			groupBy.add(colRef);
+		}
+	}
+
+	final public void Having() throws ParseException{
+		ClauseConstraints having = query.getHaving();
+		jj_consume_token(HAVING);
+		ConditionsList(having);
+	}
+
+	final public void OrderBy() throws ParseException{
+		ClauseADQL<ADQLOrder> orderBy = query.getOrderBy();
+		ADQLOrder order = null;
+		jj_consume_token(ORDER_BY);
+		order = OrderItem();
+		orderBy.add(order);
+		label_4: while(true){
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ORDER_BY:
-					OrderBy();
+				case COMMA:
+					;
 					break;
 				default:
-					jj_la1[4] = jj_gen;
-					;
+					jj_la1[15] = jj_gen;
+					break label_4;
 			}
-			// get the previous query (!= null if the current query is a sub-query):
-			ADQLQuery previousQuery = stackQuery.pop();
-			if (stackQuery.isEmpty())
-				query = null;
-			else
-				query = stackQuery.peek();
+			jj_consume_token(COMMA);
+			order = OrderItem();
+			orderBy.add(order);
+		}
+	}
 
-			{
-				if (true)
-					return previousQuery;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("QueryExpression");
+	/* *************************** */
+	/* COLUMN AND TABLE REFERENCES */
+	/* *************************** */
+	final public IdentifierItem Identifier() throws ParseException{
+		Token t;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case REGULAR_IDENTIFIER:
+				t = jj_consume_token(REGULAR_IDENTIFIER);
+				{
+					if (true)
+						return new IdentifierItem(t, false);
+				}
+				break;
+			case DELIMITED_IDENTIFIER:
+				t = jj_consume_token(DELIMITED_IDENTIFIER);
+				{
+					if (true)
+						return new IdentifierItem(t, true);
+				}
+				break;
+			default:
+				jj_la1[16] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLQuery SubQueryExpression() throws ParseException{
-		trace_call("SubQueryExpression");
+	/**
+	 * Extracts the name of a table with its possible catalog and schema prefixes.
+	 * 
+	 * @return A {@link IdentifierItems} which contains at most three items: catalogName, schemaName and tableName.
+	 */
+	final public IdentifierItems TableName() throws ParseException{
+		IdentifierItems identifiers = new IdentifierItems(true);
+		IdentifierItem id = null;
+		id = Identifier();
+		identifiers.append(id);
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DOT:
+				jj_consume_token(DOT);
+				id = Identifier();
+				identifiers.append(id);
+				break;
+			default:
+				jj_la1[17] = jj_gen;
+				;
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DOT:
+				jj_consume_token(DOT);
+				id = Identifier();
+				identifiers.append(id);
+				break;
+			default:
+				jj_la1[18] = jj_gen;
+				;
+		}
+		{
+			if (true)
+				return identifiers;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	/**
+	 * Extracts the name of a column with its possible catalog, schema and table prefixes.
+	 * 
+	 * @return A {@link IdentifierItems} which contains at most four items: catalogName, schemaName, tableName and columnName.
+	 */
+	final public IdentifierItems ColumnName() throws ParseException{
+		IdentifierItem id;
+		IdentifierItems table = null, identifiers = new IdentifierItems(false);
+		id = Identifier();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DOT:
+				jj_consume_token(DOT);
+				table = TableName();
+				break;
+			default:
+				jj_la1[19] = jj_gen;
+				;
+		}
+		identifiers.append(id);
+		if (table != null){
+			for(int i = 0; i < table.size(); i++)
+				identifiers.append(table.get(i));
+		}
+		{
+			if (true)
+				return identifiers;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public ADQLColumn Column() throws ParseException{
+		IdentifierItems identifiers;
+		identifiers = ColumnName();
 		try{
-			ADQLQuery q = null;
-			jj_consume_token(LEFT_PAR);
-			q = QueryExpression();
-			jj_consume_token(RIGHT_PAR);
 			{
 				if (true)
-					return q;
+					return queryFactory.createColumn(identifiers);
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("SubQueryExpression");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public void Select() throws ParseException{
-		trace_call("Select");
+	final public ColumnReference ColumnRef() throws ParseException{
+		IdentifierItems identifiers = null;
+		Token ind = null;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DELIMITED_IDENTIFIER:
+			case REGULAR_IDENTIFIER:
+				identifiers = ColumnName();
+				break;
+			case UNSIGNED_INTEGER:
+				ind = jj_consume_token(UNSIGNED_INTEGER);
+				break;
+			default:
+				jj_la1[20] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
 		try{
-			ClauseSelect select = query.getSelect();
-			SelectItem item = null;
-			Token t = null;
-			jj_consume_token(SELECT);
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case QUANTIFIER:
-					t = jj_consume_token(QUANTIFIER);
-					select.setDistinctColumns(t.image.equalsIgnoreCase("DISTINCT"));
-					break;
-				default:
-					jj_la1[5] = jj_gen;
-					;
+			ColumnReference colRef = null;
+			if (identifiers != null)
+				colRef = queryFactory.createColRef(identifiers);
+			else
+				colRef = queryFactory.createColRef(Integer.parseInt(ind.image), new TextPosition(ind));
+			{
+				if (true)
+					return colRef;
 			}
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case TOP:
-					jj_consume_token(TOP);
-					t = jj_consume_token(UNSIGNED_INTEGER);
-					try{
-						select.setLimit(Integer.parseInt(t.image));
-					}catch(NumberFormatException nfe){
-						{
-							if (true)
-								throw new ParseException("[l." + t.beginLine + ";c." + t.beginColumn + "] The TOP limit (\u005c"" + t.image + "\u005c") isn't a regular unsigned integer !");
-						}
-					}
-					break;
-				default:
-					jj_la1[6] = jj_gen;
-					;
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			item = SelectItem();
-			select.add(item);
-			label_1: while(true){
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public ADQLOrder OrderItem() throws ParseException{
+		IdentifierItems identifiers = null;
+		Token ind = null, desc = null;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DELIMITED_IDENTIFIER:
+			case REGULAR_IDENTIFIER:
+				identifiers = ColumnName();
+				break;
+			case UNSIGNED_INTEGER:
+				ind = jj_consume_token(UNSIGNED_INTEGER);
+				break;
+			default:
+				jj_la1[21] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ASC:
+			case DESC:
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case COMMA:
-						;
+					case ASC:
+						jj_consume_token(ASC);
+						break;
+					case DESC:
+						desc = jj_consume_token(DESC);
 						break;
 					default:
-						jj_la1[7] = jj_gen;
-						break label_1;
+						jj_la1[22] = jj_gen;
+						jj_consume_token(-1);
+						throw new ParseException();
 				}
-				jj_consume_token(COMMA);
-				item = SelectItem();
-				select.add(item);
+				break;
+			default:
+				jj_la1[23] = jj_gen;
+				;
+		}
+		try{
+			ADQLOrder order = null;
+			if (identifiers != null)
+				order = queryFactory.createOrder(identifiers, desc != null);
+			else
+				order = queryFactory.createOrder(Integer.parseInt(ind.image), desc != null, new TextPosition(ind));
+			{
+				if (true)
+					return order;
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-		}finally{
-			trace_return("Select");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public SelectItem SelectItem() throws ParseException{
-		trace_call("SelectItem");
+	final public FromContent SimpleTableRef() throws ParseException{
+		IdentifierItem alias = null;
+		IdentifierItems identifiers = null;
+		ADQLQuery subQuery = null;
+		FromContent content = null;
 		try{
-			IdentifierItems identifiers = new IdentifierItems(true);
-			IdentifierItem id = null, label = null;
-			ADQLOperand op = null;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ASTERISK:
-					jj_consume_token(ASTERISK);
+				case DELIMITED_IDENTIFIER:
+				case REGULAR_IDENTIFIER:
+					identifiers = TableName();
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case AS:
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+								case AS:
+									jj_consume_token(AS);
+									break;
+								default:
+									jj_la1[24] = jj_gen;
+									;
+							}
+							alias = Identifier();
+							break;
+						default:
+							jj_la1[25] = jj_gen;
+							;
+					}
 					{
 						if (true)
-							return new SelectAllColumns(query);
+							return queryFactory.createTable(identifiers, alias);
 					}
 					break;
 				default:
-					jj_la1[11] = jj_gen;
-					if (jj_2_1(7)){
-						id = Identifier();
-						jj_consume_token(DOT);
-						identifiers.append(id);
+					jj_la1[27] = jj_gen;
+					if (jj_2_2(2)){
+						subQuery = SubQueryExpression();
 						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								id = Identifier();
-								jj_consume_token(DOT);
-								identifiers.append(id);
-								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-									case DELIMITED_IDENTIFIER:
-									case REGULAR_IDENTIFIER:
-										id = Identifier();
-										jj_consume_token(DOT);
-										identifiers.append(id);
-										break;
-									default:
-										jj_la1[8] = jj_gen;
-										;
-								}
+							case AS:
+								jj_consume_token(AS);
 								break;
 							default:
-								jj_la1[9] = jj_gen;
+								jj_la1[26] = jj_gen;
 								;
 						}
-						jj_consume_token(ASTERISK);
-						try{
-							;
-							{
-								if (true)
-									return new SelectAllColumns(queryFactory.createTable(identifiers, null));
-							}
-						}catch(Exception ex){
-							{
-								if (true)
-									throw generateParseException(ex);
-							}
+						alias = Identifier();
+						{
+							if (true)
+								return queryFactory.createTable(subQuery, alias);
 						}
 					}else{
 						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
 							case LEFT_PAR:
-							case PLUS:
-							case MINUS:
-							case AVG:
-							case MAX:
-							case MIN:
-							case SUM:
-							case COUNT:
-							case BOX:
-							case CENTROID:
-							case CIRCLE:
-							case POINT:
-							case POLYGON:
-							case REGION:
-							case CONTAINS:
-							case INTERSECTS:
-							case AREA:
-							case COORD1:
-							case COORD2:
-							case COORDSYS:
-							case DISTANCE:
-							case ABS:
-							case CEILING:
-							case DEGREES:
-							case EXP:
-							case FLOOR:
-							case LOG:
-							case LOG10:
-							case MOD:
-							case PI:
-							case POWER:
-							case RADIANS:
-							case RAND:
-							case ROUND:
-							case SQRT:
-							case TRUNCATE:
-							case ACOS:
-							case ASIN:
-							case ATAN:
-							case ATAN2:
-							case COS:
-							case COT:
-							case SIN:
-							case TAN:
-							case STRING_LITERAL:
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-							case SCIENTIFIC_NUMBER:
-							case UNSIGNED_FLOAT:
-							case UNSIGNED_INTEGER:
-								op = ValueExpression();
-								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-									case AS:
-										jj_consume_token(AS);
-										label = Identifier();
-										break;
-									default:
-										jj_la1[10] = jj_gen;
-										;
+								jj_consume_token(LEFT_PAR);
+								content = JoinedTable();
+								jj_consume_token(RIGHT_PAR);
+								{
+									if (true)
+										return content;
 								}
 								break;
 							default:
-								jj_la1[12] = jj_gen;
+								jj_la1[28] = jj_gen;
 								jj_consume_token(-1);
 								throw new ParseException();
 						}
 					}
 			}
-			try{
-				SelectItem item = queryFactory.createSelectItem(op, (label == null) ? null : label.identifier);
-				if (label != null)
-					item.setCaseSensitive(label.caseSensitivity);
-				{
-					if (true)
-						return item;
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("SelectItem");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public void From() throws ParseException{
-		trace_call("From");
-		try{
-			FromContent content = null, content2 = null;
-			try{
-				jj_consume_token(FROM);
-				content = TableRef();
-				label_2: while(true){
-					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case COMMA:
-							;
-							break;
-						default:
-							jj_la1[13] = jj_gen;
-							break label_2;
-					}
-					jj_consume_token(COMMA);
-					content2 = TableRef();
-					content = queryFactory.createJoin(JoinType.CROSS, content, content2);
-				}
-				query.setFrom(content);
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+	final public FromContent TableRef() throws ParseException{
+		FromContent content;
+		content = SimpleTableRef();
+		label_5: while(true){
+			if (jj_2_3(2)){
+				;
+			}else{
+				break label_5;
 			}
-		}finally{
-			trace_return("From");
+			content = JoinSpecification(content);
 		}
-	}
-
-	final public void Where() throws ParseException{
-		trace_call("Where");
-		try{
-			ClauseConstraints where = query.getWhere();
-			ADQLConstraint condition;
-			jj_consume_token(WHERE);
-			ConditionsList(where);
-		}finally{
-			trace_return("Where");
+		{
+			if (true)
+				return content;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public void GroupBy() throws ParseException{
-		trace_call("GroupBy");
-		try{
-			ClauseADQL<ColumnReference> groupBy = query.getGroupBy();
-			ColumnReference colRef = null;
-			jj_consume_token(GROUP_BY);
-			colRef = ColumnRef();
-			groupBy.add(colRef);
-			label_3: while(true){
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case COMMA:
-						;
-						break;
-					default:
-						jj_la1[14] = jj_gen;
-						break label_3;
-				}
-				jj_consume_token(COMMA);
-				colRef = ColumnRef();
-				groupBy.add(colRef);
+	final public FromContent JoinedTable() throws ParseException{
+		FromContent content;
+		content = SimpleTableRef();
+		label_6: while(true){
+			content = JoinSpecification(content);
+			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case NATURAL:
+				case INNER:
+				case RIGHT:
+				case LEFT:
+				case FULL:
+				case JOIN:
+					;
+					break;
+				default:
+					jj_la1[29] = jj_gen;
+					break label_6;
 			}
-		}finally{
-			trace_return("GroupBy");
-		}
-	}
-
-	final public void Having() throws ParseException{
-		trace_call("Having");
-		try{
-			ClauseConstraints having = query.getHaving();
-			jj_consume_token(HAVING);
-			ConditionsList(having);
-		}finally{
-			trace_return("Having");
 		}
-	}
-
-	final public void OrderBy() throws ParseException{
-		trace_call("OrderBy");
-		try{
-			ClauseADQL<ADQLOrder> orderBy = query.getOrderBy();
-			ADQLOrder order = null;
-			jj_consume_token(ORDER_BY);
-			order = OrderItem();
-			orderBy.add(order);
-			label_4: while(true){
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case COMMA:
-						;
-						break;
-					default:
-						jj_la1[15] = jj_gen;
-						break label_4;
-				}
-				jj_consume_token(COMMA);
-				order = OrderItem();
-				orderBy.add(order);
-			}
-		}finally{
-			trace_return("OrderBy");
+		{
+			if (true)
+				return content;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/* *************************** */
-	/* COLUMN AND TABLE REFERENCES */
-	/* *************************** */
-	final public IdentifierItem Identifier() throws ParseException{
-		trace_call("Identifier");
+	final public ADQLJoin JoinSpecification(FromContent leftTable) throws ParseException{
+		boolean natural = false;
+		JoinType type = JoinType.INNER;
+		ClauseConstraints condition = new ClauseConstraints("ON");
+		ArrayList<ADQLColumn> lstColumns = new ArrayList<ADQLColumn>();
+		IdentifierItem id;
+		FromContent rightTable;
 		try{
-			Token t;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case REGULAR_IDENTIFIER:
-					t = jj_consume_token(REGULAR_IDENTIFIER);
+				case NATURAL:
+					jj_consume_token(NATURAL);
+					natural = true;
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case INNER:
+						case RIGHT:
+						case LEFT:
+						case FULL:
+							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+								case INNER:
+									jj_consume_token(INNER);
+									break;
+								case RIGHT:
+								case LEFT:
+								case FULL:
+									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+										case LEFT:
+											jj_consume_token(LEFT);
+											type = JoinType.OUTER_LEFT;
+											break;
+										case RIGHT:
+											jj_consume_token(RIGHT);
+											type = JoinType.OUTER_RIGHT;
+											break;
+										case FULL:
+											jj_consume_token(FULL);
+											type = JoinType.OUTER_FULL;
+											break;
+										default:
+											jj_la1[30] = jj_gen;
+											jj_consume_token(-1);
+											throw new ParseException();
+									}
+									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+										case OUTER:
+											jj_consume_token(OUTER);
+											break;
+										default:
+											jj_la1[31] = jj_gen;
+											;
+									}
+									break;
+								default:
+									jj_la1[32] = jj_gen;
+									jj_consume_token(-1);
+									throw new ParseException();
+							}
+							break;
+						default:
+							jj_la1[33] = jj_gen;
+							;
+					}
+					jj_consume_token(JOIN);
+					rightTable = TableRef();
 					{
 						if (true)
-							return new IdentifierItem(t, false);
+							return queryFactory.createJoin(type, leftTable, rightTable);
 					}
 					break;
-				case DELIMITED_IDENTIFIER:
-					t = jj_consume_token(DELIMITED_IDENTIFIER);
-					{
-						if (true)
-							return new IdentifierItem(t, true);
+				case INNER:
+				case RIGHT:
+				case LEFT:
+				case FULL:
+				case JOIN:
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case INNER:
+						case RIGHT:
+						case LEFT:
+						case FULL:
+							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+								case INNER:
+									jj_consume_token(INNER);
+									break;
+								case RIGHT:
+								case LEFT:
+								case FULL:
+									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+										case LEFT:
+											jj_consume_token(LEFT);
+											type = JoinType.OUTER_LEFT;
+											break;
+										case RIGHT:
+											jj_consume_token(RIGHT);
+											type = JoinType.OUTER_RIGHT;
+											break;
+										case FULL:
+											jj_consume_token(FULL);
+											type = JoinType.OUTER_FULL;
+											break;
+										default:
+											jj_la1[34] = jj_gen;
+											jj_consume_token(-1);
+											throw new ParseException();
+									}
+									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+										case OUTER:
+											jj_consume_token(OUTER);
+											break;
+										default:
+											jj_la1[35] = jj_gen;
+											;
+									}
+									break;
+								default:
+									jj_la1[36] = jj_gen;
+									jj_consume_token(-1);
+									throw new ParseException();
+							}
+							break;
+						default:
+							jj_la1[37] = jj_gen;
+							;
+					}
+					jj_consume_token(JOIN);
+					rightTable = TableRef();
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case ON:
+							jj_consume_token(ON);
+							ConditionsList(condition);
+							{
+								if (true)
+									return queryFactory.createJoin(type, leftTable, rightTable, condition);
+							}
+							break;
+						case USING:
+							jj_consume_token(USING);
+							jj_consume_token(LEFT_PAR);
+							id = Identifier();
+							lstColumns.add(queryFactory.createColumn(id));
+							label_7: while(true){
+								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+									case COMMA:
+										;
+										break;
+									default:
+										jj_la1[38] = jj_gen;
+										break label_7;
+								}
+								jj_consume_token(COMMA);
+								id = Identifier();
+								lstColumns.add(queryFactory.createColumn(id));
+							}
+							jj_consume_token(RIGHT_PAR);
+							{
+								if (true)
+									return queryFactory.createJoin(type, leftTable, rightTable, lstColumns);
+							}
+							break;
+						default:
+							jj_la1[39] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
 					}
 					break;
 				default:
-					jj_la1[16] = jj_gen;
+					jj_la1[40] = jj_gen;
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Identifier");
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/**
-	 * Extracts the name of a table with its possible catalog and schema prefixes.
-	 * 
-	 * @return A {@link IdentifierItems} which contains at most three items: catalogName, schemaName and tableName.
-	 */
-	final public IdentifierItems TableName() throws ParseException{
-		trace_call("TableName");
-		try{
-			IdentifierItems identifiers = new IdentifierItems(true);
-			IdentifierItem id = null;
-			id = Identifier();
-			identifiers.append(id);
+	/* ****** */
+	/* STRING */
+	/* ****** */
+	final public String String() throws ParseException{
+		Token t;
+		String str = "";
+		label_8: while(true){
+			t = jj_consume_token(STRING_LITERAL);
+			str += t.image.substring(1, t.image.length() - 1).replaceAll("''", "'");
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case DOT:
-					jj_consume_token(DOT);
-					id = Identifier();
-					identifiers.append(id);
-					break;
-				default:
-					jj_la1[17] = jj_gen;
+				case STRING_LITERAL:
 					;
-			}
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case DOT:
-					jj_consume_token(DOT);
-					id = Identifier();
-					identifiers.append(id);
 					break;
 				default:
-					jj_la1[18] = jj_gen;
-					;
-			}
-			{
-				if (true)
-					return identifiers;
+					jj_la1[41] = jj_gen;
+					break label_8;
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("TableName");
 		}
+		{
+			if (true)
+				return str;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/**
-	 * Extracts the name of a column with its possible catalog, schema and table prefixes.
-	 * 
-	 * @return A {@link IdentifierItems} which contains at most four items: catalogName, schemaName, tableName and columnName.
-	 */
-	final public IdentifierItems ColumnName() throws ParseException{
-		trace_call("ColumnName");
-		try{
-			IdentifierItem id;
-			IdentifierItems table = null, identifiers = new IdentifierItems(false);
-			id = Identifier();
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case DOT:
-					jj_consume_token(DOT);
-					table = TableName();
-					break;
-				default:
-					jj_la1[19] = jj_gen;
-					;
-			}
-			identifiers.append(id);
-			if (table != null){
-				for(int i = 0; i < table.size(); i++)
-					identifiers.append(table.get(i));
-			}
-			{
-				if (true)
-					return identifiers;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ColumnName");
+	/* ************* */
+	/* NUMERIC TYPES */
+	/* ************* */
+	final public String UnsignedNumeric() throws ParseException{
+		Token t;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case SCIENTIFIC_NUMBER:
+				t = jj_consume_token(SCIENTIFIC_NUMBER);
+				break;
+			case UNSIGNED_FLOAT:
+				t = jj_consume_token(UNSIGNED_FLOAT);
+				break;
+			case UNSIGNED_INTEGER:
+				t = jj_consume_token(UNSIGNED_INTEGER);
+				break;
+			default:
+				jj_la1[42] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		{
+			if (true)
+				return t.image;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLColumn Column() throws ParseException{
-		trace_call("Column");
-		try{
-			IdentifierItems identifiers;
-			identifiers = ColumnName();
-			try{
-				{
-					if (true)
-						return queryFactory.createColumn(identifiers);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
+	final public String UnsignedFloat() throws ParseException{
+		Token t;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case UNSIGNED_INTEGER:
+				t = jj_consume_token(UNSIGNED_INTEGER);
+				break;
+			case UNSIGNED_FLOAT:
+				t = jj_consume_token(UNSIGNED_FLOAT);
+				break;
+			default:
+				jj_la1[43] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		{
+			if (true)
+				return t.image;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public String SignedInteger() throws ParseException{
+		Token sign = null, number;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case PLUS:
+			case MINUS:
+				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+					case PLUS:
+						sign = jj_consume_token(PLUS);
+						break;
+					case MINUS:
+						sign = jj_consume_token(MINUS);
+						break;
+					default:
+						jj_la1[44] = jj_gen;
+						jj_consume_token(-1);
+						throw new ParseException();
 				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Column");
+				break;
+			default:
+				jj_la1[45] = jj_gen;
+				;
+		}
+		number = jj_consume_token(UNSIGNED_INTEGER);
+		{
+			if (true)
+				return ((sign == null) ? "" : sign.image) + number.image;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ColumnReference ColumnRef() throws ParseException{
-		trace_call("ColumnRef");
+	/* *********** */
+	/* EXPRESSIONS */
+	/* *********** */
+	final public ADQLOperand NumericValueExpressionPrimary() throws ParseException{
+		String expr;
+		ADQLColumn column;
+		ADQLOperand op;
 		try{
-			IdentifierItems identifiers = null;
-			Token ind = null;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case SCIENTIFIC_NUMBER:
+				case UNSIGNED_FLOAT:
+				case UNSIGNED_INTEGER:
+					// unsigned_value_specification
+					expr = UnsignedNumeric();
+					{
+						if (true)
+							return queryFactory.createNumericConstant(expr);
+					}
+					break;
 				case DELIMITED_IDENTIFIER:
 				case REGULAR_IDENTIFIER:
-					identifiers = ColumnName();
+					column = Column();
+					column.setExpectedType('N');
+					{
+						if (true)
+							return column;
+					}
 					break;
-				case UNSIGNED_INTEGER:
-					ind = jj_consume_token(UNSIGNED_INTEGER);
+				case AVG:
+				case MAX:
+				case MIN:
+				case SUM:
+				case COUNT:
+					op = SqlFunction();
+					{
+						if (true)
+							return op;
+					}
+					break;
+				case LEFT_PAR:
+					jj_consume_token(LEFT_PAR);
+					op = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					{
+						if (true)
+							return queryFactory.createWrappedOperand(op);
+					}
 					break;
 				default:
-					jj_la1[20] = jj_gen;
+					jj_la1[46] = jj_gen;
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
-			try{
-				ColumnReference colRef = null;
-				if (identifiers != null)
-					colRef = queryFactory.createColRef(identifiers);
-				else
-					colRef = queryFactory.createColRef(Integer.parseInt(ind.image), new TextPosition(ind));
-				{
-					if (true)
-						return colRef;
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ColumnRef");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOrder OrderItem() throws ParseException{
-		trace_call("OrderItem");
+	final public ADQLOperand StringValueExpressionPrimary() throws ParseException{
+		String expr;
+		ADQLColumn column;
+		ADQLOperand op;
 		try{
-			IdentifierItems identifiers = null;
-			Token ind = null, desc = null;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case STRING_LITERAL:
+					// string
+					expr = String();
+					{
+						if (true)
+							return queryFactory.createStringConstant(expr);
+					}
+					break;
 				case DELIMITED_IDENTIFIER:
 				case REGULAR_IDENTIFIER:
-					identifiers = ColumnName();
+					column = Column();
+					column.setExpectedType('S');
+					{
+						if (true)
+							return column;
+					}
 					break;
-				case UNSIGNED_INTEGER:
-					ind = jj_consume_token(UNSIGNED_INTEGER);
+				case LEFT_PAR:
+					jj_consume_token(LEFT_PAR);
+					op = StringExpression();
+					jj_consume_token(RIGHT_PAR);
+					{
+						if (true)
+							return queryFactory.createWrappedOperand(op);
+					}
 					break;
 				default:
-					jj_la1[21] = jj_gen;
+					jj_la1[47] = jj_gen;
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ASC:
-				case DESC:
-					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case ASC:
-							jj_consume_token(ASC);
-							break;
-						case DESC:
-							desc = jj_consume_token(DESC);
-							break;
-						default:
-							jj_la1[22] = jj_gen;
-							jj_consume_token(-1);
-							throw new ParseException();
-					}
-					break;
-				default:
-					jj_la1[23] = jj_gen;
-					;
-			}
-			try{
-				ADQLOrder order = null;
-				if (identifiers != null)
-					order = queryFactory.createOrder(identifiers, desc != null);
-				else
-					order = queryFactory.createOrder(Integer.parseInt(ind.image), desc != null, new TextPosition(ind));
-				{
-					if (true)
-						return order;
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("OrderItem");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public FromContent SimpleTableRef() throws ParseException{
-		trace_call("SimpleTableRef");
+	final public ADQLOperand ValueExpression() throws ParseException{
+		ADQLOperand valueExpr = null;
 		try{
-			IdentifierItem alias = null;
-			IdentifierItems identifiers = null;
-			ADQLQuery subQuery = null;
-			FromContent content = null;
-			try{
+			if (jj_2_4(2147483647)){
+				valueExpr = NumericExpression();
+			}else if (jj_2_5(2147483647)){
+				valueExpr = StringExpression();
+			}else if (jj_2_6(2147483647)){
+				jj_consume_token(LEFT_PAR);
+				valueExpr = ValueExpression();
+				jj_consume_token(RIGHT_PAR);
+				valueExpr = queryFactory.createWrappedOperand(valueExpr);
+			}else if (jj_2_7(2147483647)){
+				valueExpr = UserDefinedFunction();
+			}else{
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case DELIMITED_IDENTIFIER:
-					case REGULAR_IDENTIFIER:
-						identifiers = TableName();
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case AS:
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-									case AS:
-										jj_consume_token(AS);
-										break;
-									default:
-										jj_la1[24] = jj_gen;
-										;
-								}
-								alias = Identifier();
-								break;
-							default:
-								jj_la1[25] = jj_gen;
-								;
-						}
-						{
-							if (true)
-								return queryFactory.createTable(identifiers, alias);
-						}
+					case BOX:
+					case CENTROID:
+					case CIRCLE:
+					case POINT:
+					case POLYGON:
+					case REGION:
+						valueExpr = GeometryValueFunction();
 						break;
 					default:
-						jj_la1[27] = jj_gen;
-						if (jj_2_2(2)){
-							subQuery = SubQueryExpression();
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case AS:
-									jj_consume_token(AS);
-									break;
-								default:
-									jj_la1[26] = jj_gen;
-									;
-							}
-							alias = Identifier();
-							{
-								if (true)
-									return queryFactory.createTable(subQuery, alias);
-							}
+						jj_la1[48] = jj_gen;
+						if (jj_2_8(2147483647)){
+							valueExpr = Column();
+						}else if (jj_2_9(2147483647)){
+							valueExpr = StringFactor();
 						}else{
 							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
 								case LEFT_PAR:
-									jj_consume_token(LEFT_PAR);
-									content = JoinedTable();
-									jj_consume_token(RIGHT_PAR);
-									{
-										if (true)
-											return content;
-									}
+								case PLUS:
+								case MINUS:
+								case AVG:
+								case MAX:
+								case MIN:
+								case SUM:
+								case COUNT:
+								case CONTAINS:
+								case INTERSECTS:
+								case AREA:
+								case COORD1:
+								case COORD2:
+								case DISTANCE:
+								case ABS:
+								case CEILING:
+								case DEGREES:
+								case EXP:
+								case FLOOR:
+								case LOG:
+								case LOG10:
+								case MOD:
+								case PI:
+								case POWER:
+								case RADIANS:
+								case RAND:
+								case ROUND:
+								case SQRT:
+								case TRUNCATE:
+								case ACOS:
+								case ASIN:
+								case ATAN:
+								case ATAN2:
+								case COS:
+								case COT:
+								case SIN:
+								case TAN:
+								case DELIMITED_IDENTIFIER:
+								case REGULAR_IDENTIFIER:
+								case SCIENTIFIC_NUMBER:
+								case UNSIGNED_FLOAT:
+								case UNSIGNED_INTEGER:
+									valueExpr = Factor();
 									break;
 								default:
-									jj_la1[28] = jj_gen;
+									jj_la1[49] = jj_gen;
 									jj_consume_token(-1);
 									throw new ParseException();
 							}
 						}
 				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("SimpleTableRef");
-		}
-	}
-
-	final public FromContent TableRef() throws ParseException{
-		trace_call("TableRef");
-		try{
-			FromContent content;
-			content = SimpleTableRef();
-			label_5: while(true){
-				if (jj_2_3(2)){
-					;
-				}else{
-					break label_5;
-				}
-				content = JoinSpecification(content);
 			}
 			{
 				if (true)
-					return content;
+					return valueExpr;
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("TableRef");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public FromContent JoinedTable() throws ParseException{
-		trace_call("JoinedTable");
-		try{
-			FromContent content;
-			content = SimpleTableRef();
-			label_6: while(true){
-				content = JoinSpecification(content);
+	final public ADQLOperand NumericExpression() throws ParseException{
+		Token sign = null;
+		ADQLOperand leftOp, rightOp = null;
+		leftOp = NumericTerm();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case PLUS:
+			case MINUS:
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case NATURAL:
-					case INNER:
-					case RIGHT:
-					case LEFT:
-					case FULL:
-					case JOIN:
-						;
+					case PLUS:
+						sign = jj_consume_token(PLUS);
+						break;
+					case MINUS:
+						sign = jj_consume_token(MINUS);
 						break;
 					default:
-						jj_la1[29] = jj_gen;
-						break label_6;
+						jj_la1[50] = jj_gen;
+						jj_consume_token(-1);
+						throw new ParseException();
+				}
+				rightOp = NumericExpression();
+				break;
+			default:
+				jj_la1[51] = jj_gen;
+				;
+		}
+		if (sign == null){
+			if (true)
+				return leftOp;
+		}else{
+			try{
+				{
+					if (true)
+						return queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp);
+				}
+			}catch(Exception ex){
+				{
+					if (true)
+						throw generateParseException(ex);
 				}
 			}
-			{
-				if (true)
-					return content;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("JoinedTable");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLJoin JoinSpecification(FromContent leftTable) throws ParseException{
-		trace_call("JoinSpecification");
-		try{
-			boolean natural = false;
-			JoinType type = JoinType.INNER;
-			ClauseConstraints condition = new ClauseConstraints("ON");
-			ArrayList<ADQLColumn> lstColumns = new ArrayList<ADQLColumn>();
-			IdentifierItem id;
-			FromContent rightTable;
-			try{
+	final public ADQLOperand NumericTerm() throws ParseException{
+		Token sign = null;
+		ADQLOperand leftOp, rightOp = null;
+		leftOp = Factor();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ASTERISK:
+			case DIVIDE:
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case NATURAL:
-						jj_consume_token(NATURAL);
-						natural = true;
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case INNER:
-							case RIGHT:
-							case LEFT:
-							case FULL:
-								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-									case INNER:
-										jj_consume_token(INNER);
-										break;
-									case RIGHT:
-									case LEFT:
-									case FULL:
-										switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-											case LEFT:
-												jj_consume_token(LEFT);
-												type = JoinType.OUTER_LEFT;
-												break;
-											case RIGHT:
-												jj_consume_token(RIGHT);
-												type = JoinType.OUTER_RIGHT;
-												break;
-											case FULL:
-												jj_consume_token(FULL);
-												type = JoinType.OUTER_FULL;
-												break;
-											default:
-												jj_la1[30] = jj_gen;
-												jj_consume_token(-1);
-												throw new ParseException();
-										}
-										switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-											case OUTER:
-												jj_consume_token(OUTER);
-												break;
-											default:
-												jj_la1[31] = jj_gen;
-												;
-										}
-										break;
-									default:
-										jj_la1[32] = jj_gen;
-										jj_consume_token(-1);
-										throw new ParseException();
-								}
-								break;
-							default:
-								jj_la1[33] = jj_gen;
-								;
-						}
-						jj_consume_token(JOIN);
-						rightTable = TableRef();
-						{
-							if (true)
-								return queryFactory.createJoin(type, leftTable, rightTable);
-						}
+					case ASTERISK:
+						sign = jj_consume_token(ASTERISK);
 						break;
-					case INNER:
-					case RIGHT:
-					case LEFT:
-					case FULL:
-					case JOIN:
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case INNER:
-							case RIGHT:
-							case LEFT:
-							case FULL:
-								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-									case INNER:
-										jj_consume_token(INNER);
-										break;
-									case RIGHT:
-									case LEFT:
-									case FULL:
-										switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-											case LEFT:
-												jj_consume_token(LEFT);
-												type = JoinType.OUTER_LEFT;
-												break;
-											case RIGHT:
-												jj_consume_token(RIGHT);
-												type = JoinType.OUTER_RIGHT;
-												break;
-											case FULL:
-												jj_consume_token(FULL);
-												type = JoinType.OUTER_FULL;
-												break;
-											default:
-												jj_la1[34] = jj_gen;
-												jj_consume_token(-1);
-												throw new ParseException();
-										}
-										switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-											case OUTER:
-												jj_consume_token(OUTER);
-												break;
-											default:
-												jj_la1[35] = jj_gen;
-												;
-										}
-										break;
-									default:
-										jj_la1[36] = jj_gen;
-										jj_consume_token(-1);
-										throw new ParseException();
-								}
-								break;
-							default:
-								jj_la1[37] = jj_gen;
-								;
-						}
-						jj_consume_token(JOIN);
-						rightTable = TableRef();
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case ON:
-								jj_consume_token(ON);
-								ConditionsList(condition);
-								{
-									if (true)
-										return queryFactory.createJoin(type, leftTable, rightTable, condition);
-								}
-								break;
-							case USING:
-								jj_consume_token(USING);
-								jj_consume_token(LEFT_PAR);
-								id = Identifier();
-								lstColumns.add(queryFactory.createColumn(id));
-								label_7: while(true){
-									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-										case COMMA:
-											;
-											break;
-										default:
-											jj_la1[38] = jj_gen;
-											break label_7;
-									}
-									jj_consume_token(COMMA);
-									id = Identifier();
-									lstColumns.add(queryFactory.createColumn(id));
-								}
-								jj_consume_token(RIGHT_PAR);
-								{
-									if (true)
-										return queryFactory.createJoin(type, leftTable, rightTable, lstColumns);
-								}
-								break;
-							default:
-								jj_la1[39] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
+					case DIVIDE:
+						sign = jj_consume_token(DIVIDE);
 						break;
 					default:
-						jj_la1[40] = jj_gen;
+						jj_la1[52] = jj_gen;
 						jj_consume_token(-1);
 						throw new ParseException();
 				}
+				rightOp = NumericTerm();
+				break;
+			default:
+				jj_la1[53] = jj_gen;
+				;
+		}
+		if (sign == null){
+			if (true)
+				return leftOp;
+		}else{
+			try{
+				{
+					if (true)
+						return queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp);
+				}
 			}catch(Exception ex){
 				{
 					if (true)
 						throw generateParseException(ex);
 				}
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("JoinSpecification");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/* ****** */
-	/* STRING */
-	/* ****** */
-	final public String String() throws ParseException{
-		trace_call("String");
-		try{
-			Token t;
-			String str = "";
-			label_8: while(true){
-				t = jj_consume_token(STRING_LITERAL);
-				str += t.image;
+	final public ADQLOperand Factor() throws ParseException{
+		boolean negative = false;;
+		ADQLOperand op;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case PLUS:
+			case MINUS:
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case STRING_LITERAL:
-						;
+					case PLUS:
+						jj_consume_token(PLUS);
+						break;
+					case MINUS:
+						jj_consume_token(MINUS);
+						negative = true;
 						break;
 					default:
-						jj_la1[41] = jj_gen;
-						break label_8;
+						jj_la1[54] = jj_gen;
+						jj_consume_token(-1);
+						throw new ParseException();
 				}
-			}
-			{
-				if (true)
-					return (str != null) ? str.substring(1, str.length() - 1) : str;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("String");
+				break;
+			default:
+				jj_la1[55] = jj_gen;
+				;
 		}
-	}
-
-	/* ************* */
-	/* NUMERIC TYPES */
-	/* ************* */
-	final public String UnsignedNumeric() throws ParseException{
-		trace_call("UnsignedNumeric");
-		try{
-			Token t;
+		if (jj_2_10(2)){
+			op = NumericFunction();
+		}else{
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case LEFT_PAR:
+				case AVG:
+				case MAX:
+				case MIN:
+				case SUM:
+				case COUNT:
+				case DELIMITED_IDENTIFIER:
+				case REGULAR_IDENTIFIER:
 				case SCIENTIFIC_NUMBER:
-					t = jj_consume_token(SCIENTIFIC_NUMBER);
-					break;
 				case UNSIGNED_FLOAT:
-					t = jj_consume_token(UNSIGNED_FLOAT);
-					break;
 				case UNSIGNED_INTEGER:
-					t = jj_consume_token(UNSIGNED_INTEGER);
+					op = NumericValueExpressionPrimary();
 					break;
 				default:
-					jj_la1[42] = jj_gen;
+					jj_la1[56] = jj_gen;
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
-			{
-				if (true)
-					return t.image;
+		}
+		if (negative){
+			try{
+				op = queryFactory.createNegativeOperand(op);
+			}catch(Exception ex){
+				{
+					if (true)
+						throw generateParseException(ex);
+				}
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("UnsignedNumeric");
 		}
+
+		{
+			if (true)
+				return op;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public String UnsignedFloat() throws ParseException{
-		trace_call("UnsignedFloat");
-		try{
-			Token t;
+	final public ADQLOperand StringExpression() throws ParseException{
+		ADQLOperand leftOp;
+		ADQLOperand rightOp = null;
+		leftOp = StringFactor();
+		label_9: while(true){
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case UNSIGNED_INTEGER:
-					t = jj_consume_token(UNSIGNED_INTEGER);
-					break;
-				case UNSIGNED_FLOAT:
-					t = jj_consume_token(UNSIGNED_FLOAT);
+				case CONCAT:
+					;
 					break;
 				default:
-					jj_la1[43] = jj_gen;
-					jj_consume_token(-1);
-					throw new ParseException();
+					jj_la1[57] = jj_gen;
+					break label_9;
 			}
-			{
-				if (true)
-					return t.image;
+			jj_consume_token(CONCAT);
+			rightOp = StringFactor();
+			if (!(leftOp instanceof Concatenation)){
+				try{
+					ADQLOperand temp = leftOp;
+					leftOp = queryFactory.createConcatenation();
+					((Concatenation)leftOp).add(temp);
+				}catch(Exception ex){
+					{
+						if (true)
+							throw generateParseException(ex);
+					}
+				}
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("UnsignedFloat");
+			((Concatenation)leftOp).add(rightOp);
 		}
+		{
+			if (true)
+				return leftOp;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public String SignedInteger() throws ParseException{
-		trace_call("SignedInteger");
-		try{
-			Token sign = null, number;
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case PLUS:
-				case MINUS:
+	final public ADQLOperand StringFactor() throws ParseException{
+		ADQLOperand op;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case COORDSYS:
+				op = ExtractCoordSys();
+				break;
+			default:
+				jj_la1[58] = jj_gen;
+				if (jj_2_11(2)){
+					op = UserDefinedFunction();
+					((UserDefinedFunction)op).setExpectedType('S');
+				}else{
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case PLUS:
-							sign = jj_consume_token(PLUS);
-							break;
-						case MINUS:
-							sign = jj_consume_token(MINUS);
+						case LEFT_PAR:
+						case STRING_LITERAL:
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							op = StringValueExpressionPrimary();
 							break;
 						default:
-							jj_la1[44] = jj_gen;
+							jj_la1[59] = jj_gen;
 							jj_consume_token(-1);
 							throw new ParseException();
 					}
-					break;
-				default:
-					jj_la1[45] = jj_gen;
-					;
-			}
-			number = jj_consume_token(UNSIGNED_INTEGER);
+				}
+		}
+		{
+			if (true)
+				return op;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public GeometryValue<GeometryFunction> GeometryExpression() throws ParseException{
+		ADQLColumn col = null;
+		GeometryFunction gf = null;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DELIMITED_IDENTIFIER:
+			case REGULAR_IDENTIFIER:
+				col = Column();
+				break;
+			case BOX:
+			case CENTROID:
+			case CIRCLE:
+			case POINT:
+			case POLYGON:
+			case REGION:
+				gf = GeometryValueFunction();
+				break;
+			default:
+				jj_la1[60] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		if (col != null){
+			col.setExpectedType('G');
 			{
 				if (true)
-					return ((sign == null) ? "" : sign.image) + number.image;
+					return new GeometryValue<GeometryFunction>(col);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("SignedInteger");
+		}else{
+			if (true)
+				return new GeometryValue<GeometryFunction>(gf);
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/* *********** */
-	/* EXPRESSIONS */
-	/* *********** */
-	final public ADQLOperand ValueExpressionPrimary() throws ParseException{
-		trace_call("ValueExpressionPrimary");
+	/* ********************************** */
+	/* BOOLEAN EXPRESSIONS (WHERE clause) */
+	/* ********************************** */
+	final public ClauseConstraints ConditionsList(ClauseConstraints clause) throws ParseException{
+		ADQLConstraint constraint = null;
+		Token op = null;
+		boolean notOp = false;
 		try{
-			String expr;
-			ADQLColumn column;
-			ADQLOperand op;
-			try{
+			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case NOT:
+					jj_consume_token(NOT);
+					notOp = true;
+					break;
+				default:
+					jj_la1[61] = jj_gen;
+					;
+			}
+			constraint = Constraint();
+			if (notOp)
+				constraint = queryFactory.createNot(constraint);
+			notOp = false;
+			if (clause instanceof ADQLConstraint)
+				clause.add(constraint);
+			else
+				clause.add(constraint);
+			label_10: while(true){
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case SCIENTIFIC_NUMBER:
-					case UNSIGNED_FLOAT:
-					case UNSIGNED_INTEGER:
-						// unsigned_value_specification
-						expr = UnsignedNumeric();
-						{
-							if (true)
-								return queryFactory.createNumericConstant(expr);
-						}
-						break;
-					case STRING_LITERAL:
-						expr = String();
-						{
-							if (true)
-								return queryFactory.createStringConstant(expr);
-						}
-						break;
-					case DELIMITED_IDENTIFIER:
-					case REGULAR_IDENTIFIER:
-						column = Column();
-						{
-							if (true)
-								return column;
-						}
+					case AND:
+					case OR:
+						;
 						break;
-					case AVG:
-					case MAX:
-					case MIN:
-					case SUM:
-					case COUNT:
-						op = SqlFunction();
-						{
-							if (true)
-								return op;
-						}
+					default:
+						jj_la1[62] = jj_gen;
+						break label_10;
+				}
+				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+					case AND:
+						op = jj_consume_token(AND);
 						break;
-					case LEFT_PAR:
-						jj_consume_token(LEFT_PAR);
-						op = ValueExpression();
-						jj_consume_token(RIGHT_PAR);
-						{
-							if (true)
-								return queryFactory.createWrappedOperand(op);
-						}
+					case OR:
+						op = jj_consume_token(OR);
 						break;
 					default:
-						jj_la1[46] = jj_gen;
+						jj_la1[63] = jj_gen;
 						jj_consume_token(-1);
 						throw new ParseException();
 				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
+				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+					case NOT:
+						jj_consume_token(NOT);
+						notOp = true;
+						break;
+					default:
+						jj_la1[64] = jj_gen;
+						;
 				}
+				constraint = Constraint();
+				if (notOp)
+					constraint = queryFactory.createNot(constraint);
+				notOp = false;
+				if (clause instanceof ADQLConstraint)
+					clause.add(op.image, constraint);
+				else
+					clause.add(op.image, constraint);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ValueExpressionPrimary");
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		{
+			if (true)
+				return clause;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOperand ValueExpression() throws ParseException{
-		trace_call("ValueExpression");
-		try{
-			ADQLOperand valueExpr = null;
+	final public ADQLConstraint Constraint() throws ParseException{
+		ADQLConstraint constraint = null;
+		if (jj_2_12(2147483647)){
+			constraint = Predicate();
+		}else{
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case BOX:
-				case CENTROID:
-				case CIRCLE:
-				case POINT:
-				case POLYGON:
-				case REGION:
-					valueExpr = GeometryValueFunction();
-					break;
-				default:
-					jj_la1[47] = jj_gen;
-					if (jj_2_4(2147483647)){
-						valueExpr = NumericExpression();
-					}else if (jj_2_5(2147483647)){
-						valueExpr = StringExpression();
-					}else if (jj_2_6(2147483647)){
-						valueExpr = StringExpression();
-					}else{
+				case LEFT_PAR:
+					jj_consume_token(LEFT_PAR);
+					try{
+						constraint = queryFactory.createGroupOfConstraints();
+					}catch(Exception ex){
+						{
+							if (true)
+								throw generateParseException(ex);
+						}
+					}
+					ConditionsList((ConstraintsGroup)constraint);
+					jj_consume_token(RIGHT_PAR);
+					break;
+				default:
+					jj_la1[65] = jj_gen;
+					jj_consume_token(-1);
+					throw new ParseException();
+			}
+		}
+		{
+			if (true)
+				return constraint;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public ADQLConstraint Predicate() throws ParseException{
+		ADQLQuery q = null;
+		ADQLColumn column = null;
+		ADQLOperand strExpr1 = null, strExpr2 = null;
+		ADQLOperand op;
+		Token notToken = null;
+		ADQLConstraint constraint = null;
+		try{
+			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case EXISTS:
+					jj_consume_token(EXISTS);
+					q = SubQueryExpression();
+					{
+						if (true)
+							return queryFactory.createExists(q);
+					}
+					break;
+				default:
+					jj_la1[70] = jj_gen;
+					if (jj_2_14(2147483647)){
+						column = Column();
+						jj_consume_token(IS);
+						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+							case NOT:
+								notToken = jj_consume_token(NOT);
+								break;
+							default:
+								jj_la1[66] = jj_gen;
+								;
+						}
+						jj_consume_token(NULL);
+						{
+							if (true)
+								return queryFactory.createIsNull((notToken != null), column);
+						}
+					}else if (jj_2_15(2147483647)){
+						strExpr1 = StringExpression();
+						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+							case NOT:
+								notToken = jj_consume_token(NOT);
+								break;
+							default:
+								jj_la1[67] = jj_gen;
+								;
+						}
+						jj_consume_token(LIKE);
+						strExpr2 = StringExpression();
+						{
+							if (true)
+								return queryFactory.createComparison(strExpr1, (notToken == null) ? ComparisonOperator.LIKE : ComparisonOperator.NOTLIKE, strExpr2);
+						}
+					}else{
 						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
 							case LEFT_PAR:
 							case PLUS:
@@ -1686,11 +2071,18 @@ public class ADQLParser implements ADQLParserConstants {
 							case MIN:
 							case SUM:
 							case COUNT:
+							case BOX:
+							case CENTROID:
+							case CIRCLE:
+							case POINT:
+							case POLYGON:
+							case REGION:
 							case CONTAINS:
 							case INTERSECTS:
 							case AREA:
 							case COORD1:
 							case COORD2:
+							case COORDSYS:
 							case DISTANCE:
 							case ABS:
 							case CEILING:
@@ -1721,1548 +2113,866 @@ public class ADQLParser implements ADQLParserConstants {
 							case SCIENTIFIC_NUMBER:
 							case UNSIGNED_FLOAT:
 							case UNSIGNED_INTEGER:
-								valueExpr = NumericExpression();
+								op = ValueExpression();
+								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+									case EQUAL:
+									case NOT_EQUAL:
+									case LESS_THAN:
+									case LESS_EQUAL_THAN:
+									case GREATER_THAN:
+									case GREATER_EQUAL_THAN:
+										constraint = ComparisonEnd(op);
+										break;
+									default:
+										jj_la1[68] = jj_gen;
+										if (jj_2_13(2)){
+											constraint = BetweenEnd(op);
+										}else{
+											switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+												case NOT:
+												case IN:
+													constraint = InEnd(op);
+													break;
+												default:
+													jj_la1[69] = jj_gen;
+													jj_consume_token(-1);
+													throw new ParseException();
+											}
+										}
+								}
 								break;
 							default:
-								jj_la1[48] = jj_gen;
+								jj_la1[71] = jj_gen;
 								jj_consume_token(-1);
 								throw new ParseException();
 						}
 					}
 			}
+		}catch(Exception ex){
 			{
 				if (true)
-					return valueExpr;
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ValueExpression");
 		}
+		{
+			if (true)
+				return constraint;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOperand NumericExpression() throws ParseException{
-		trace_call("NumericExpression");
+	final public Comparison ComparisonEnd(ADQLOperand leftOp) throws ParseException{
+		Token comp;
+		ADQLOperand rightOp;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case EQUAL:
+				comp = jj_consume_token(EQUAL);
+				break;
+			case NOT_EQUAL:
+				comp = jj_consume_token(NOT_EQUAL);
+				break;
+			case LESS_THAN:
+				comp = jj_consume_token(LESS_THAN);
+				break;
+			case LESS_EQUAL_THAN:
+				comp = jj_consume_token(LESS_EQUAL_THAN);
+				break;
+			case GREATER_THAN:
+				comp = jj_consume_token(GREATER_THAN);
+				break;
+			case GREATER_EQUAL_THAN:
+				comp = jj_consume_token(GREATER_EQUAL_THAN);
+				break;
+			default:
+				jj_la1[72] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		rightOp = ValueExpression();
+		try{
+			{
+				if (true)
+					return queryFactory.createComparison(leftOp, ComparisonOperator.getOperator(comp.image), rightOp);
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public Between BetweenEnd(ADQLOperand leftOp) throws ParseException{
+		Token notToken = null;
+		ADQLOperand min, max;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case NOT:
+				notToken = jj_consume_token(NOT);
+				break;
+			default:
+				jj_la1[73] = jj_gen;
+				;
+		}
+		jj_consume_token(BETWEEN);
+		min = ValueExpression();
+		jj_consume_token(AND);
+		max = ValueExpression();
 		try{
-			Token sign = null;
-			ADQLOperand leftOp, rightOp = null;
-			leftOp = NumericTerm();
+			{
+				if (true)
+					return queryFactory.createBetween((notToken != null), leftOp, min, max);
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public In InEnd(ADQLOperand leftOp) throws ParseException{
+		Token not = null;
+		ADQLQuery q = null;
+		ADQLOperand item;
+		Vector<ADQLOperand> items = new Vector<ADQLOperand>();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case NOT:
+				not = jj_consume_token(NOT);
+				break;
+			default:
+				jj_la1[74] = jj_gen;
+				;
+		}
+		jj_consume_token(IN);
+		if (jj_2_16(2)){
+			q = SubQueryExpression();
+		}else{
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case PLUS:
-				case MINUS:
-					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case PLUS:
-							sign = jj_consume_token(PLUS);
-							break;
-						case MINUS:
-							sign = jj_consume_token(MINUS);
-							break;
-						default:
-							jj_la1[49] = jj_gen;
-							jj_consume_token(-1);
-							throw new ParseException();
+				case LEFT_PAR:
+					jj_consume_token(LEFT_PAR);
+					item = ValueExpression();
+					items.add(item);
+					label_11: while(true){
+						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+							case COMMA:
+								;
+								break;
+							default:
+								jj_la1[75] = jj_gen;
+								break label_11;
+						}
+						jj_consume_token(COMMA);
+						item = ValueExpression();
+						items.add(item);
 					}
-					rightOp = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
 					break;
 				default:
-					jj_la1[50] = jj_gen;
-					;
+					jj_la1[76] = jj_gen;
+					jj_consume_token(-1);
+					throw new ParseException();
 			}
-			if (sign == null){
+		}
+		try{
+			if (q != null){
 				if (true)
-					return leftOp;
+					return queryFactory.createIn(leftOp, q, not != null);
 			}else{
-				try{
-					{
-						if (true)
-							return queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp);
-					}
-				}catch(Exception ex){
-					{
-						if (true)
-							throw generateParseException(ex);
-					}
+				ADQLOperand[] list = new ADQLOperand[items.size()];
+				int i = 0;
+				for(ADQLOperand op : items)
+					list[i++] = op;
+				{
+					if (true)
+						return queryFactory.createIn(leftOp, list, not != null);
 				}
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("NumericExpression");
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOperand NumericTerm() throws ParseException{
-		trace_call("NumericTerm");
+	/* ************* */
+	/* SQL FUNCTIONS */
+	/* ************* */
+	final public SQLFunction SqlFunction() throws ParseException{
+		Token fct, all = null, distinct = null;
+		ADQLOperand op = null;
+		SQLFunction funct = null;
 		try{
-			Token sign = null;
-			ADQLOperand leftOp, rightOp = null;
-			leftOp = Factor();
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ASTERISK:
-				case DIVIDE:
+				case COUNT:
+					jj_consume_token(COUNT);
+					jj_consume_token(LEFT_PAR);
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case QUANTIFIER:
+							distinct = jj_consume_token(QUANTIFIER);
+							break;
+						default:
+							jj_la1[77] = jj_gen;
+							;
+					}
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
 						case ASTERISK:
-							sign = jj_consume_token(ASTERISK);
+							all = jj_consume_token(ASTERISK);
 							break;
-						case DIVIDE:
-							sign = jj_consume_token(DIVIDE);
+						case LEFT_PAR:
+						case PLUS:
+						case MINUS:
+						case AVG:
+						case MAX:
+						case MIN:
+						case SUM:
+						case COUNT:
+						case BOX:
+						case CENTROID:
+						case CIRCLE:
+						case POINT:
+						case POLYGON:
+						case REGION:
+						case CONTAINS:
+						case INTERSECTS:
+						case AREA:
+						case COORD1:
+						case COORD2:
+						case COORDSYS:
+						case DISTANCE:
+						case ABS:
+						case CEILING:
+						case DEGREES:
+						case EXP:
+						case FLOOR:
+						case LOG:
+						case LOG10:
+						case MOD:
+						case PI:
+						case POWER:
+						case RADIANS:
+						case RAND:
+						case ROUND:
+						case SQRT:
+						case TRUNCATE:
+						case ACOS:
+						case ASIN:
+						case ATAN:
+						case ATAN2:
+						case COS:
+						case COT:
+						case SIN:
+						case TAN:
+						case STRING_LITERAL:
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+						case SCIENTIFIC_NUMBER:
+						case UNSIGNED_FLOAT:
+						case UNSIGNED_INTEGER:
+							op = ValueExpression();
 							break;
 						default:
-							jj_la1[51] = jj_gen;
+							jj_la1[78] = jj_gen;
 							jj_consume_token(-1);
 							throw new ParseException();
 					}
-					rightOp = NumericTerm();
+					jj_consume_token(RIGHT_PAR);
+					funct = queryFactory.createSQLFunction((all != null) ? SQLFunctionType.COUNT_ALL : SQLFunctionType.COUNT, op, distinct != null && distinct.image.equalsIgnoreCase("distinct"));
 					break;
-				default:
-					jj_la1[52] = jj_gen;
-					;
-			}
-			if (sign == null){
-				if (true)
-					return leftOp;
-			}else{
-				try{
-					{
-						if (true)
-							return queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp);
-					}
-				}catch(Exception ex){
-					{
-						if (true)
-							throw generateParseException(ex);
-					}
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("NumericTerm");
-		}
-	}
-
-	final public ADQLOperand Factor() throws ParseException{
-		trace_call("Factor");
-		try{
-			boolean negative = false;;
-			ADQLOperand op;
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case PLUS:
-				case MINUS:
+				case AVG:
+				case MAX:
+				case MIN:
+				case SUM:
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case PLUS:
-							jj_consume_token(PLUS);
+						case AVG:
+							fct = jj_consume_token(AVG);
 							break;
-						case MINUS:
-							jj_consume_token(MINUS);
-							negative = true;
+						case MAX:
+							fct = jj_consume_token(MAX);
+							break;
+						case MIN:
+							fct = jj_consume_token(MIN);
+							break;
+						case SUM:
+							fct = jj_consume_token(SUM);
 							break;
 						default:
-							jj_la1[53] = jj_gen;
+							jj_la1[79] = jj_gen;
 							jj_consume_token(-1);
 							throw new ParseException();
 					}
+					jj_consume_token(LEFT_PAR);
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case QUANTIFIER:
+							distinct = jj_consume_token(QUANTIFIER);
+							break;
+						default:
+							jj_la1[80] = jj_gen;
+							;
+					}
+					op = ValueExpression();
+					jj_consume_token(RIGHT_PAR);
+					funct = queryFactory.createSQLFunction(SQLFunctionType.valueOf(fct.image.toUpperCase()), op, distinct != null && distinct.image.equalsIgnoreCase("distinct"));
 					break;
 				default:
-					jj_la1[54] = jj_gen;
-					;
-			}
-			if (jj_2_7(2)){
-				op = NumericFunction();
-			}else{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case LEFT_PAR:
-					case AVG:
-					case MAX:
-					case MIN:
-					case SUM:
-					case COUNT:
-					case STRING_LITERAL:
-					case DELIMITED_IDENTIFIER:
-					case REGULAR_IDENTIFIER:
-					case SCIENTIFIC_NUMBER:
-					case UNSIGNED_FLOAT:
-					case UNSIGNED_INTEGER:
-						op = ValueExpressionPrimary();
-						break;
-					default:
-						jj_la1[55] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}
-			if (negative){
-				try{
-					op = queryFactory.createNegativeOperand(op);
-				}catch(Exception ex){
-					{
-						if (true)
-							throw generateParseException(ex);
-					}
-				}
+					jj_la1[81] = jj_gen;
+					jj_consume_token(-1);
+					throw new ParseException();
 			}
-
+		}catch(Exception ex){
 			{
 				if (true)
-					return op;
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Factor");
 		}
+		{
+			if (true)
+				return funct;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOperand StringExpression() throws ParseException{
-		trace_call("StringExpression");
-		try{
-			ADQLOperand leftOp;
-			ADQLOperand rightOp = null;
-			leftOp = StringFactor();
-			label_9: while(true){
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case CONCAT:
-						;
-						break;
-					default:
-						jj_la1[56] = jj_gen;
-						break label_9;
-				}
-				jj_consume_token(CONCAT);
-				rightOp = StringFactor();
-				if (!(leftOp instanceof Concatenation)){
-					try{
-						ADQLOperand temp = leftOp;
-						leftOp = queryFactory.createConcatenation();
-						((Concatenation)leftOp).add(temp);
-					}catch(Exception ex){
-						{
-							if (true)
-								throw generateParseException(ex);
-						}
-					}
-				}
-				((Concatenation)leftOp).add(rightOp);
-			}
-			{
-				if (true)
-					return leftOp;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("StringExpression");
+	/* ************** */
+	/* ADQL FUNCTIONS */
+	/* ************** */
+	final public ADQLOperand[] Coordinates() throws ParseException{
+		ADQLOperand[] ops = new ADQLOperand[2];
+		ops[0] = NumericExpression();
+		jj_consume_token(COMMA);
+		ops[1] = NumericExpression();
+		{
+			if (true)
+				return ops;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOperand StringFactor() throws ParseException{
-		trace_call("StringFactor");
+	final public GeometryFunction GeometryFunction() throws ParseException{
+		Token t = null;
+		GeometryValue<GeometryFunction> gvf1, gvf2;
+		GeometryValue<PointFunction> gvp1, gvp2;
+		GeometryFunction gf = null;
+		PointFunction p1 = null, p2 = null;
+		ADQLColumn col1 = null, col2 = null;
 		try{
-			ADQLOperand op;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case COORDSYS:
-					op = ExtractCoordSys();
+				case CONTAINS:
+				case INTERSECTS:
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case CONTAINS:
+							t = jj_consume_token(CONTAINS);
+							break;
+						case INTERSECTS:
+							t = jj_consume_token(INTERSECTS);
+							break;
+						default:
+							jj_la1[82] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
+					}
+					jj_consume_token(LEFT_PAR);
+					gvf1 = GeometryExpression();
+					jj_consume_token(COMMA);
+					gvf2 = GeometryExpression();
+					jj_consume_token(RIGHT_PAR);
+					if (t.image.equalsIgnoreCase("contains"))
+						gf = queryFactory.createContains(gvf1, gvf2);
+					else
+						gf = queryFactory.createIntersects(gvf1, gvf2);
 					break;
-				default:
-					jj_la1[57] = jj_gen;
-					if (jj_2_8(2)){
-						op = UserDefinedFunction();
-					}else{
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case LEFT_PAR:
-							case AVG:
-							case MAX:
-							case MIN:
-							case SUM:
-							case COUNT:
-							case STRING_LITERAL:
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-							case SCIENTIFIC_NUMBER:
-							case UNSIGNED_FLOAT:
-							case UNSIGNED_INTEGER:
-								op = ValueExpressionPrimary();
-								break;
-							default:
-								jj_la1[58] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-					}
-			}
-			{
-				if (true)
-					return op;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("StringFactor");
-		}
-	}
-
-	final public GeometryValue<GeometryFunction> GeometryExpression() throws ParseException{
-		trace_call("GeometryExpression");
-		try{
-			ADQLColumn col = null;
-			GeometryFunction gf = null;
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case DELIMITED_IDENTIFIER:
-				case REGULAR_IDENTIFIER:
-					col = Column();
+				case AREA:
+					jj_consume_token(AREA);
+					jj_consume_token(LEFT_PAR);
+					gvf1 = GeometryExpression();
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createArea(gvf1);
 					break;
-				case BOX:
-				case CENTROID:
-				case CIRCLE:
-				case POINT:
-				case POLYGON:
-				case REGION:
-					gf = GeometryValueFunction();
+				case COORD1:
+					jj_consume_token(COORD1);
+					jj_consume_token(LEFT_PAR);
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case POINT:
+							p1 = Point();
+							gf = queryFactory.createCoord1(p1);
+							break;
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							col1 = Column();
+							col1.setExpectedType('G');
+							gf = queryFactory.createCoord1(col1);
+							break;
+						default:
+							jj_la1[83] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
+					}
+					jj_consume_token(RIGHT_PAR);
 					break;
-				default:
-					jj_la1[59] = jj_gen;
-					jj_consume_token(-1);
-					throw new ParseException();
-			}
-			if (col != null){
-				if (true)
-					return new GeometryValue<GeometryFunction>(col);
-			}else{
-				if (true)
-					return new GeometryValue<GeometryFunction>(gf);
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("GeometryExpression");
-		}
-	}
-
-	/* ********************************** */
-	/* BOOLEAN EXPRESSIONS (WHERE clause) */
-	/* ********************************** */
-	final public ClauseConstraints ConditionsList(ClauseConstraints clause) throws ParseException{
-		trace_call("ConditionsList");
-		try{
-			ADQLConstraint constraint = null;
-			Token op = null;
-			boolean notOp = false;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case NOT:
-						jj_consume_token(NOT);
-						notOp = true;
-						break;
-					default:
-						jj_la1[60] = jj_gen;
-						;
-				}
-				constraint = Constraint();
-				if (notOp)
-					constraint = queryFactory.createNot(constraint);
-				notOp = false;
-				if (clause instanceof ADQLConstraint)
-					clause.add(constraint);
-				else
-					clause.add(constraint);
-				label_10: while(true){
+				case COORD2:
+					jj_consume_token(COORD2);
+					jj_consume_token(LEFT_PAR);
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case AND:
-						case OR:
-							;
+						case POINT:
+							p1 = Point();
+							gf = queryFactory.createCoord2(p1);
+							break;
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							col1 = Column();
+							col1.setExpectedType('G');
+							gf = queryFactory.createCoord2(col1);
 							break;
 						default:
-							jj_la1[61] = jj_gen;
-							break label_10;
+							jj_la1[84] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
 					}
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case DISTANCE:
+					jj_consume_token(DISTANCE);
+					jj_consume_token(LEFT_PAR);
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case AND:
-							op = jj_consume_token(AND);
+						case POINT:
+							p1 = Point();
 							break;
-						case OR:
-							op = jj_consume_token(OR);
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							col1 = Column();
 							break;
 						default:
-							jj_la1[62] = jj_gen;
+							jj_la1[85] = jj_gen;
 							jj_consume_token(-1);
 							throw new ParseException();
 					}
+					if (p1 != null)
+						gvp1 = new GeometryValue<PointFunction>(p1);
+					else{
+						col1.setExpectedType('G');
+						gvp1 = new GeometryValue<PointFunction>(col1);
+					}
+					jj_consume_token(COMMA);
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case NOT:
-							jj_consume_token(NOT);
-							notOp = true;
+						case POINT:
+							p2 = Point();
+							break;
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							col2 = Column();
 							break;
 						default:
-							jj_la1[63] = jj_gen;
-							;
+							jj_la1[86] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
 					}
-					constraint = Constraint();
-					if (notOp)
-						constraint = queryFactory.createNot(constraint);
-					notOp = false;
-					if (clause instanceof ADQLConstraint)
-						clause.add(op.image, constraint);
-					else
-						clause.add(op.image, constraint);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+					if (p2 != null)
+						gvp2 = new GeometryValue<PointFunction>(p2);
+					else{
+						col2.setExpectedType('G');
+						gvp2 = new GeometryValue<PointFunction>(col2);
+					}
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createDistance(gvp1, gvp2);
+					break;
+				default:
+					jj_la1[87] = jj_gen;
+					jj_consume_token(-1);
+					throw new ParseException();
 			}
+		}catch(Exception ex){
 			{
 				if (true)
-					return clause;
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ConditionsList");
 		}
-	}
-
-	final public ADQLConstraint Constraint() throws ParseException{
-		trace_call("Constraint");
-		try{
-			ADQLConstraint constraint = null;
-			if (jj_2_9(2147483647)){
-				constraint = Predicate();
-			}else{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case LEFT_PAR:
-						jj_consume_token(LEFT_PAR);
-						try{
-							constraint = queryFactory.createGroupOfConstraints();
-						}catch(Exception ex){
-							{
-								if (true)
-									throw generateParseException(ex);
-							}
-						}
-						ConditionsList((ConstraintsGroup)constraint);
-						jj_consume_token(RIGHT_PAR);
-						break;
-					default:
-						jj_la1[64] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}
-			{
-				if (true)
-					return constraint;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Constraint");
+		{
+			if (true)
+				return gf;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLConstraint Predicate() throws ParseException{
-		trace_call("Predicate");
-		try{
-			ADQLQuery q = null;
-			ADQLColumn column = null;
-			ADQLOperand strExpr1 = null, strExpr2 = null;
-			ADQLOperand op;
-			Token notToken = null;
-			ADQLConstraint constraint = null;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case EXISTS:
-						jj_consume_token(EXISTS);
-						q = SubQueryExpression();
-						{
-							if (true)
-								return queryFactory.createExists(q);
-						}
-						break;
-					default:
-						jj_la1[69] = jj_gen;
-						if (jj_2_11(2147483647)){
-							column = Column();
-							jj_consume_token(IS);
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case NOT:
-									notToken = jj_consume_token(NOT);
-									break;
-								default:
-									jj_la1[65] = jj_gen;
-									;
-							}
-							jj_consume_token(NULL);
-							{
-								if (true)
-									return queryFactory.createIsNull((notToken != null), column);
-							}
-						}else if (jj_2_12(2147483647)){
-							strExpr1 = StringExpression();
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case NOT:
-									notToken = jj_consume_token(NOT);
-									break;
-								default:
-									jj_la1[66] = jj_gen;
-									;
-							}
-							jj_consume_token(LIKE);
-							strExpr2 = StringExpression();
-							{
-								if (true)
-									return queryFactory.createComparison(strExpr1, (notToken == null) ? ComparisonOperator.LIKE : ComparisonOperator.NOTLIKE, strExpr2);
-							}
-						}else{
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case LEFT_PAR:
-								case PLUS:
-								case MINUS:
-								case AVG:
-								case MAX:
-								case MIN:
-								case SUM:
-								case COUNT:
-								case BOX:
-								case CENTROID:
-								case CIRCLE:
-								case POINT:
-								case POLYGON:
-								case REGION:
-								case CONTAINS:
-								case INTERSECTS:
-								case AREA:
-								case COORD1:
-								case COORD2:
-								case COORDSYS:
-								case DISTANCE:
-								case ABS:
-								case CEILING:
-								case DEGREES:
-								case EXP:
-								case FLOOR:
-								case LOG:
-								case LOG10:
-								case MOD:
-								case PI:
-								case POWER:
-								case RADIANS:
-								case RAND:
-								case ROUND:
-								case SQRT:
-								case TRUNCATE:
-								case ACOS:
-								case ASIN:
-								case ATAN:
-								case ATAN2:
-								case COS:
-								case COT:
-								case SIN:
-								case TAN:
-								case STRING_LITERAL:
-								case DELIMITED_IDENTIFIER:
-								case REGULAR_IDENTIFIER:
-								case SCIENTIFIC_NUMBER:
-								case UNSIGNED_FLOAT:
-								case UNSIGNED_INTEGER:
-									op = ValueExpression();
-									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-										case EQUAL:
-										case NOT_EQUAL:
-										case LESS_THAN:
-										case LESS_EQUAL_THAN:
-										case GREATER_THAN:
-										case GREATER_EQUAL_THAN:
-											constraint = ComparisonEnd(op);
-											break;
-										default:
-											jj_la1[67] = jj_gen;
-											if (jj_2_10(2)){
-												constraint = BetweenEnd(op);
-											}else{
-												switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-													case NOT:
-													case IN:
-														constraint = InEnd(op);
-														break;
-													default:
-														jj_la1[68] = jj_gen;
-														jj_consume_token(-1);
-														throw new ParseException();
-												}
-											}
-									}
-									break;
-								default:
-									jj_la1[70] = jj_gen;
-									jj_consume_token(-1);
-									throw new ParseException();
-							}
-						}
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			{
-				if (true)
-					return constraint;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Predicate");
+	final public ADQLOperand CoordinateSystem() throws ParseException{
+		ADQLOperand coordSys = null;
+		coordSys = StringExpression();
+		{
+			if (true)
+				return coordSys;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public Comparison ComparisonEnd(ADQLOperand leftOp) throws ParseException{
-		trace_call("ComparisonEnd");
+	final public GeometryFunction GeometryValueFunction() throws ParseException{
+		ADQLOperand coordSys;
+		ADQLOperand width, height;
+		ADQLOperand[] coords, tmp;
+		Vector<ADQLOperand> vCoords;
+		ADQLOperand op = null;
+		GeometryValue<GeometryFunction> gvf = null;
+		GeometryFunction gf = null;
 		try{
-			Token comp;
-			ADQLOperand rightOp;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case EQUAL:
-					comp = jj_consume_token(EQUAL);
-					break;
-				case NOT_EQUAL:
-					comp = jj_consume_token(NOT_EQUAL);
-					break;
-				case LESS_THAN:
-					comp = jj_consume_token(LESS_THAN);
-					break;
-				case LESS_EQUAL_THAN:
-					comp = jj_consume_token(LESS_EQUAL_THAN);
-					break;
-				case GREATER_THAN:
-					comp = jj_consume_token(GREATER_THAN);
-					break;
-				case GREATER_EQUAL_THAN:
-					comp = jj_consume_token(GREATER_EQUAL_THAN);
+				case BOX:
+					jj_consume_token(BOX);
+					jj_consume_token(LEFT_PAR);
+					coordSys = CoordinateSystem();
+					jj_consume_token(COMMA);
+					coords = Coordinates();
+					jj_consume_token(COMMA);
+					width = NumericExpression();
+					jj_consume_token(COMMA);
+					height = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createBox(coordSys, coords[0], coords[1], width, height);
 					break;
-				default:
-					jj_la1[71] = jj_gen;
-					jj_consume_token(-1);
-					throw new ParseException();
-			}
-			rightOp = ValueExpression();
-			try{
-				{
-					if (true)
-						return queryFactory.createComparison(leftOp, ComparisonOperator.getOperator(comp.image), rightOp);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ComparisonEnd");
-		}
-	}
-
-	final public Between BetweenEnd(ADQLOperand leftOp) throws ParseException{
-		trace_call("BetweenEnd");
-		try{
-			Token notToken = null;
-			ADQLOperand min, max;
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case NOT:
-					notToken = jj_consume_token(NOT);
-					break;
-				default:
-					jj_la1[72] = jj_gen;
-					;
-			}
-			jj_consume_token(BETWEEN);
-			min = ValueExpression();
-			jj_consume_token(AND);
-			max = ValueExpression();
-			try{
-				{
-					if (true)
-						return queryFactory.createBetween((notToken != null), leftOp, min, max);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("BetweenEnd");
-		}
-	}
-
-	final public In InEnd(ADQLOperand leftOp) throws ParseException{
-		trace_call("InEnd");
-		try{
-			Token not = null;
-			ADQLQuery q = null;
-			ADQLOperand item;
-			Vector<ADQLOperand> items = new Vector<ADQLOperand>();
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case NOT:
-					not = jj_consume_token(NOT);
-					break;
-				default:
-					jj_la1[73] = jj_gen;
-					;
-			}
-			jj_consume_token(IN);
-			if (jj_2_13(2)){
-				q = SubQueryExpression();
-			}else{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case LEFT_PAR:
-						jj_consume_token(LEFT_PAR);
-						item = ValueExpression();
-						items.add(item);
-						label_11: while(true){
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case COMMA:
-									;
-									break;
-								default:
-									jj_la1[74] = jj_gen;
-									break label_11;
-							}
-							jj_consume_token(COMMA);
-							item = ValueExpression();
-							items.add(item);
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					default:
-						jj_la1[75] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}
-			try{
-				if (q != null){
-					if (true)
-						return queryFactory.createIn(leftOp, q, not != null);
-				}else{
-					ADQLOperand[] list = new ADQLOperand[items.size()];
-					int i = 0;
-					for(ADQLOperand op : items)
-						list[i++] = op;
-					{
-						if (true)
-							return queryFactory.createIn(leftOp, list, not != null);
-					}
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("InEnd");
-		}
-	}
-
-	/* ************* */
-	/* SQL FUNCTIONS */
-	/* ************* */
-	final public SQLFunction SqlFunction() throws ParseException{
-		trace_call("SqlFunction");
-		try{
-			Token fct, all = null, distinct = null;
-			ADQLOperand op = null;
-			SQLFunction funct = null;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case COUNT:
-						jj_consume_token(COUNT);
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case QUANTIFIER:
-								distinct = jj_consume_token(QUANTIFIER);
-								break;
-							default:
-								jj_la1[76] = jj_gen;
-								;
-						}
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case ASTERISK:
-								all = jj_consume_token(ASTERISK);
-								break;
-							case LEFT_PAR:
-							case PLUS:
-							case MINUS:
-							case AVG:
-							case MAX:
-							case MIN:
-							case SUM:
-							case COUNT:
-							case BOX:
-							case CENTROID:
-							case CIRCLE:
-							case POINT:
-							case POLYGON:
-							case REGION:
-							case CONTAINS:
-							case INTERSECTS:
-							case AREA:
-							case COORD1:
-							case COORD2:
-							case COORDSYS:
-							case DISTANCE:
-							case ABS:
-							case CEILING:
-							case DEGREES:
-							case EXP:
-							case FLOOR:
-							case LOG:
-							case LOG10:
-							case MOD:
-							case PI:
-							case POWER:
-							case RADIANS:
-							case RAND:
-							case ROUND:
-							case SQRT:
-							case TRUNCATE:
-							case ACOS:
-							case ASIN:
-							case ATAN:
-							case ATAN2:
-							case COS:
-							case COT:
-							case SIN:
-							case TAN:
-							case STRING_LITERAL:
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-							case SCIENTIFIC_NUMBER:
-							case UNSIGNED_FLOAT:
-							case UNSIGNED_INTEGER:
-								op = ValueExpression();
-								break;
-							default:
-								jj_la1[77] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						jj_consume_token(RIGHT_PAR);
-						funct = queryFactory.createSQLFunction((all != null) ? SQLFunctionType.COUNT_ALL : SQLFunctionType.COUNT, op, distinct != null && distinct.image.equalsIgnoreCase("distinct"));
-						break;
-					case AVG:
-					case MAX:
-					case MIN:
-					case SUM:
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case AVG:
-								fct = jj_consume_token(AVG);
-								break;
-							case MAX:
-								fct = jj_consume_token(MAX);
-								break;
-							case MIN:
-								fct = jj_consume_token(MIN);
-								break;
-							case SUM:
-								fct = jj_consume_token(SUM);
-								break;
-							default:
-								jj_la1[78] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case QUANTIFIER:
-								distinct = jj_consume_token(QUANTIFIER);
-								break;
-							default:
-								jj_la1[79] = jj_gen;
-								;
-						}
-						op = ValueExpression();
-						jj_consume_token(RIGHT_PAR);
-						funct = queryFactory.createSQLFunction(SQLFunctionType.valueOf(fct.image.toUpperCase()), op, distinct != null && distinct.image.equalsIgnoreCase("distinct"));
-						break;
-					default:
-						jj_la1[80] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			{
-				if (true)
-					return funct;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("SqlFunction");
-		}
-	}
-
-	/* ************** */
-	/* ADQL FUNCTIONS */
-	/* ************** */
-	final public ADQLOperand[] Coordinates() throws ParseException{
-		trace_call("Coordinates");
-		try{
-			ADQLOperand[] ops = new ADQLOperand[2];
-			ops[0] = NumericExpression();
-			jj_consume_token(COMMA);
-			ops[1] = NumericExpression();
-			{
-				if (true)
-					return ops;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Coordinates");
-		}
-	}
-
-	final public GeometryFunction GeometryFunction() throws ParseException{
-		trace_call("GeometryFunction");
-		try{
-			Token t = null;
-			GeometryValue<GeometryFunction> gvf1, gvf2;
-			GeometryValue<PointFunction> gvp1, gvp2;
-			GeometryFunction gf = null;
-			PointFunction p1 = null, p2 = null;
-			ADQLColumn col1 = null, col2 = null;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case CONTAINS:
-					case INTERSECTS:
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case CONTAINS:
-								t = jj_consume_token(CONTAINS);
-								break;
-							case INTERSECTS:
-								t = jj_consume_token(INTERSECTS);
-								break;
-							default:
-								jj_la1[81] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						jj_consume_token(LEFT_PAR);
-						gvf1 = GeometryExpression();
-						jj_consume_token(COMMA);
-						gvf2 = GeometryExpression();
-						jj_consume_token(RIGHT_PAR);
-						if (t.image.equalsIgnoreCase("contains"))
-							gf = queryFactory.createContains(gvf1, gvf2);
-						else
-							gf = queryFactory.createIntersects(gvf1, gvf2);
-						break;
-					case AREA:
-						jj_consume_token(AREA);
-						jj_consume_token(LEFT_PAR);
-						gvf1 = GeometryExpression();
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createArea(gvf1);
-						break;
-					case COORD1:
-						jj_consume_token(COORD1);
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case POINT:
-								p1 = Point();
-								gf = queryFactory.createCoord1(p1);
-								break;
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								col1 = Column();
-								gf = queryFactory.createCoord1(col1);
-								break;
-							default:
-								jj_la1[82] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case COORD2:
-						jj_consume_token(COORD2);
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case POINT:
-								p1 = Point();
-								gf = queryFactory.createCoord2(p1);
-								break;
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								col1 = Column();
-								gf = queryFactory.createCoord2(col1);
-								break;
-							default:
-								jj_la1[83] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case DISTANCE:
-						jj_consume_token(DISTANCE);
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case POINT:
-								p1 = Point();
-								break;
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								col1 = Column();
-								break;
-							default:
-								jj_la1[84] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						if (p1 != null)
-							gvp1 = new GeometryValue<PointFunction>(p1);
-						else
-							gvp1 = new GeometryValue<PointFunction>(col1);
-						jj_consume_token(COMMA);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case POINT:
-								p2 = Point();
-								break;
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								col2 = Column();
-								break;
-							default:
-								jj_la1[85] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						if (p2 != null)
-							gvp2 = new GeometryValue<PointFunction>(p2);
-						else
-							gvp2 = new GeometryValue<PointFunction>(col2);
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createDistance(gvp1, gvp2);
-						break;
-					default:
-						jj_la1[86] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			{
-				if (true)
-					return gf;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("GeometryFunction");
-		}
-	}
-
-	final public ADQLOperand CoordinateSystem() throws ParseException{
-		trace_call("CoordinateSystem");
-		try{
-			Token oldToken = token;
-			ADQLOperand coordSys = null;
-			coordSys = StringExpression();
-			if (allowedCoordSys.size() > 0){
-				TextPosition position = new TextPosition(oldToken.next, token);
-				if (coordSys == null){
-					if (true)
-						throw new ParseException("A coordinate system must always be provided !", position);
-				}
-				if (coordSys instanceof StringConstant && !isAllowedCoordSys(((StringConstant)coordSys).getValue())){
-					if (true)
-						throw new ParseException("\u005c"" + coordSys.toADQL() + "\u005c" is not an allowed coordinate systems !", position);
-				}
-			}
-
-			{
-				if (true)
-					return coordSys;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("CoordinateSystem");
-		}
-	}
-
-	final public GeometryFunction GeometryValueFunction() throws ParseException{
-		trace_call("GeometryValueFunction");
-		try{
-			ADQLOperand coordSys;
-			ADQLOperand width, height;
-			ADQLOperand[] coords, tmp;
-			Vector<ADQLOperand> vCoords;
-			ADQLOperand op = null;
-			GeometryValue<GeometryFunction> gvf = null;
-			GeometryFunction gf = null;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case BOX:
-						jj_consume_token(BOX);
-						jj_consume_token(LEFT_PAR);
-						coordSys = CoordinateSystem();
-						jj_consume_token(COMMA);
-						coords = Coordinates();
-						jj_consume_token(COMMA);
-						width = NumericExpression();
-						jj_consume_token(COMMA);
-						height = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createBox(coordSys, coords[0], coords[1], width, height);
-						break;
-					case CENTROID:
-						jj_consume_token(CENTROID);
-						jj_consume_token(LEFT_PAR);
-						gvf = GeometryExpression();
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createCentroid(gvf);
-						break;
-					case CIRCLE:
-						jj_consume_token(CIRCLE);
-						jj_consume_token(LEFT_PAR);
-						coordSys = CoordinateSystem();
-						jj_consume_token(COMMA);
-						coords = Coordinates();
-						jj_consume_token(COMMA);
-						width = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createCircle(coordSys, coords[0], coords[1], width);
-						break;
-					case POINT:
-						gf = Point();
-						break;
-					case POLYGON:
-						jj_consume_token(POLYGON);
-						jj_consume_token(LEFT_PAR);
-						coordSys = CoordinateSystem();
-						vCoords = new Vector<ADQLOperand>();
-						jj_consume_token(COMMA);
-						tmp = Coordinates();
-						vCoords.add(tmp[0]);
-						vCoords.add(tmp[1]);
-						jj_consume_token(COMMA);
-						tmp = Coordinates();
-						vCoords.add(tmp[0]);
-						vCoords.add(tmp[1]);
-						jj_consume_token(COMMA);
-						tmp = Coordinates();
-						vCoords.add(tmp[0]);
-						vCoords.add(tmp[1]);
-						label_12: while(true){
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case COMMA:
-									;
-									break;
-								default:
-									jj_la1[87] = jj_gen;
-									break label_12;
-							}
-							jj_consume_token(COMMA);
-							tmp = Coordinates();
-							vCoords.add(tmp[0]);
-							vCoords.add(tmp[1]);
-						}
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createPolygon(coordSys, vCoords);
-						break;
-					case REGION:
-						jj_consume_token(REGION);
-						jj_consume_token(LEFT_PAR);
-						op = StringExpression();
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createRegion(op);
-						break;
-					default:
-						jj_la1[88] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			{
-				if (true)
-					return gf;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("GeometryValueFunction");
-		}
-	}
-
-	final public PointFunction Point() throws ParseException{
-		trace_call("Point");
-		try{
-			ADQLOperand coordSys;
-			ADQLOperand[] coords;
-			jj_consume_token(POINT);
-			jj_consume_token(LEFT_PAR);
-			coordSys = CoordinateSystem();
-			jj_consume_token(COMMA);
-			coords = Coordinates();
-			jj_consume_token(RIGHT_PAR);
-			try{
-				{
-					if (true)
-						return queryFactory.createPoint(coordSys, coords[0], coords[1]);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Point");
-		}
-	}
-
-	final public GeometryFunction ExtractCoordSys() throws ParseException{
-		trace_call("ExtractCoordSys");
-		try{
-			GeometryValue<GeometryFunction> gvf;
-			jj_consume_token(COORDSYS);
-			jj_consume_token(LEFT_PAR);
-			gvf = GeometryExpression();
-			jj_consume_token(RIGHT_PAR);
-			try{
-				{
-					if (true)
-						return queryFactory.createExtractCoordSys(gvf);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ExtractCoordSys");
-		}
-	}
-
-	/* ***************** */
-	/* NUMERIC FUNCTIONS */
-	/* ***************** */
-	final public ADQLFunction NumericFunction() throws ParseException{
-		trace_call("NumericFunction");
-		try{
-			ADQLFunction fct;
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ABS:
-				case CEILING:
-				case DEGREES:
-				case EXP:
-				case FLOOR:
-				case LOG:
-				case LOG10:
-				case MOD:
-				case PI:
-				case POWER:
-				case RADIANS:
-				case RAND:
-				case ROUND:
-				case SQRT:
-				case TRUNCATE:
-					fct = MathFunction();
+				case CENTROID:
+					jj_consume_token(CENTROID);
+					jj_consume_token(LEFT_PAR);
+					gvf = GeometryExpression();
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createCentroid(gvf);
 					break;
-				case ACOS:
-				case ASIN:
-				case ATAN:
-				case ATAN2:
-				case COS:
-				case COT:
-				case SIN:
-				case TAN:
-					fct = TrigFunction();
+				case CIRCLE:
+					jj_consume_token(CIRCLE);
+					jj_consume_token(LEFT_PAR);
+					coordSys = CoordinateSystem();
+					jj_consume_token(COMMA);
+					coords = Coordinates();
+					jj_consume_token(COMMA);
+					width = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createCircle(coordSys, coords[0], coords[1], width);
 					break;
-				case CONTAINS:
-				case INTERSECTS:
-				case AREA:
-				case COORD1:
-				case COORD2:
-				case DISTANCE:
-					fct = GeometryFunction();
+				case POINT:
+					gf = Point();
 					break;
-				case REGULAR_IDENTIFIER:
-					fct = UserDefinedFunction();
+				case POLYGON:
+					jj_consume_token(POLYGON);
+					jj_consume_token(LEFT_PAR);
+					coordSys = CoordinateSystem();
+					vCoords = new Vector<ADQLOperand>();
+					jj_consume_token(COMMA);
+					tmp = Coordinates();
+					vCoords.add(tmp[0]);
+					vCoords.add(tmp[1]);
+					jj_consume_token(COMMA);
+					tmp = Coordinates();
+					vCoords.add(tmp[0]);
+					vCoords.add(tmp[1]);
+					jj_consume_token(COMMA);
+					tmp = Coordinates();
+					vCoords.add(tmp[0]);
+					vCoords.add(tmp[1]);
+					label_12: while(true){
+						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+							case COMMA:
+								;
+								break;
+							default:
+								jj_la1[88] = jj_gen;
+								break label_12;
+						}
+						jj_consume_token(COMMA);
+						tmp = Coordinates();
+						vCoords.add(tmp[0]);
+						vCoords.add(tmp[1]);
+					}
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createPolygon(coordSys, vCoords);
+					break;
+				case REGION:
+					jj_consume_token(REGION);
+					jj_consume_token(LEFT_PAR);
+					op = StringExpression();
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createRegion(op);
 					break;
 				default:
 					jj_la1[89] = jj_gen;
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
+		}catch(Exception ex){
 			{
 				if (true)
-					return fct;
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("NumericFunction");
 		}
+		{
+			if (true)
+				return gf;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public MathFunction MathFunction() throws ParseException{
-		trace_call("MathFunction");
+	final public PointFunction Point() throws ParseException{
+		ADQLOperand coordSys;
+		ADQLOperand[] coords;
+		jj_consume_token(POINT);
+		jj_consume_token(LEFT_PAR);
+		coordSys = CoordinateSystem();
+		jj_consume_token(COMMA);
+		coords = Coordinates();
+		jj_consume_token(RIGHT_PAR);
 		try{
-			Token fct = null;
-			ADQLOperand param1 = null, param2 = null;
-			String integerValue = null;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case ABS:
-						fct = jj_consume_token(ABS);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case CEILING:
-						fct = jj_consume_token(CEILING);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case DEGREES:
-						fct = jj_consume_token(DEGREES);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case EXP:
-						fct = jj_consume_token(EXP);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case FLOOR:
-						fct = jj_consume_token(FLOOR);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case LOG:
-						fct = jj_consume_token(LOG);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case LOG10:
-						fct = jj_consume_token(LOG10);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case MOD:
-						fct = jj_consume_token(MOD);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(COMMA);
-						param2 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case PI:
-						fct = jj_consume_token(PI);
-						jj_consume_token(LEFT_PAR);
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case POWER:
-						fct = jj_consume_token(POWER);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(COMMA);
-						param2 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case RADIANS:
-						fct = jj_consume_token(RADIANS);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case RAND:
-						fct = jj_consume_token(RAND);
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case LEFT_PAR:
-							case PLUS:
-							case MINUS:
-							case AVG:
-							case MAX:
-							case MIN:
-							case SUM:
-							case COUNT:
-							case CONTAINS:
-							case INTERSECTS:
-							case AREA:
-							case COORD1:
-							case COORD2:
-							case DISTANCE:
-							case ABS:
-							case CEILING:
-							case DEGREES:
-							case EXP:
-							case FLOOR:
-							case LOG:
-							case LOG10:
-							case MOD:
-							case PI:
-							case POWER:
-							case RADIANS:
-							case RAND:
-							case ROUND:
-							case SQRT:
-							case TRUNCATE:
-							case ACOS:
-							case ASIN:
-							case ATAN:
-							case ATAN2:
-							case COS:
-							case COT:
-							case SIN:
-							case TAN:
-							case STRING_LITERAL:
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-							case SCIENTIFIC_NUMBER:
-							case UNSIGNED_FLOAT:
-							case UNSIGNED_INTEGER:
-								param1 = NumericExpression();
-								break;
-							default:
-								jj_la1[90] = jj_gen;
-								;
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case ROUND:
-						fct = jj_consume_token(ROUND);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case COMMA:
-								jj_consume_token(COMMA);
-								integerValue = SignedInteger();
-								param2 = queryFactory.createNumericConstant(integerValue);
-								break;
-							default:
-								jj_la1[91] = jj_gen;
-								;
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case SQRT:
-						fct = jj_consume_token(SQRT);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case TRUNCATE:
-						fct = jj_consume_token(TRUNCATE);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case COMMA:
-								jj_consume_token(COMMA);
-								integerValue = SignedInteger();
-								param2 = queryFactory.createNumericConstant(integerValue);
-								break;
-							default:
-								jj_la1[92] = jj_gen;
-								;
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					default:
-						jj_la1[93] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-				if (param1 != null){
-					if (true)
-						return queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2);
-				}else{
-					if (true)
-						return null;
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+			{
+				if (true)
+					return queryFactory.createPoint(coordSys, coords[0], coords[1]);
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("MathFunction");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public MathFunction TrigFunction() throws ParseException{
-		trace_call("TrigFunction");
+	final public GeometryFunction ExtractCoordSys() throws ParseException{
+		GeometryValue<GeometryFunction> gvf;
+		jj_consume_token(COORDSYS);
+		jj_consume_token(LEFT_PAR);
+		gvf = GeometryExpression();
+		jj_consume_token(RIGHT_PAR);
+		try{
+			{
+				if (true)
+					return queryFactory.createExtractCoordSys(gvf);
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	/* ***************** */
+	/* NUMERIC FUNCTIONS */
+	/* ***************** */
+	final public ADQLFunction NumericFunction() throws ParseException{
+		ADQLFunction fct;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ABS:
+			case CEILING:
+			case DEGREES:
+			case EXP:
+			case FLOOR:
+			case LOG:
+			case LOG10:
+			case MOD:
+			case PI:
+			case POWER:
+			case RADIANS:
+			case RAND:
+			case ROUND:
+			case SQRT:
+			case TRUNCATE:
+				fct = MathFunction();
+				break;
+			case ACOS:
+			case ASIN:
+			case ATAN:
+			case ATAN2:
+			case COS:
+			case COT:
+			case SIN:
+			case TAN:
+				fct = TrigFunction();
+				break;
+			case CONTAINS:
+			case INTERSECTS:
+			case AREA:
+			case COORD1:
+			case COORD2:
+			case DISTANCE:
+				fct = GeometryFunction();
+				break;
+			case REGULAR_IDENTIFIER:
+				fct = UserDefinedFunction();
+				((UserDefinedFunction)fct).setExpectedType('N');
+				break;
+			default:
+				jj_la1[90] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		{
+			if (true)
+				return fct;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public MathFunction MathFunction() throws ParseException{
+		Token fct = null;
+		ADQLOperand param1 = null, param2 = null;
+		String integerValue = null;
 		try{
-			Token fct = null;
-			ADQLOperand param1 = null, param2 = null;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ACOS:
-					fct = jj_consume_token(ACOS);
+				case ABS:
+					fct = jj_consume_token(ABS);
+					jj_consume_token(LEFT_PAR);
+					param1 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case CEILING:
+					fct = jj_consume_token(CEILING);
+					jj_consume_token(LEFT_PAR);
+					param1 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case DEGREES:
+					fct = jj_consume_token(DEGREES);
+					jj_consume_token(LEFT_PAR);
+					param1 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case EXP:
+					fct = jj_consume_token(EXP);
+					jj_consume_token(LEFT_PAR);
+					param1 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case FLOOR:
+					fct = jj_consume_token(FLOOR);
+					jj_consume_token(LEFT_PAR);
+					param1 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case LOG:
+					fct = jj_consume_token(LOG);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case ASIN:
-					fct = jj_consume_token(ASIN);
+				case LOG10:
+					fct = jj_consume_token(LOG10);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case ATAN:
-					fct = jj_consume_token(ATAN);
+				case MOD:
+					fct = jj_consume_token(MOD);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
+					jj_consume_token(COMMA);
+					param2 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case PI:
+					fct = jj_consume_token(PI);
+					jj_consume_token(LEFT_PAR);
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case ATAN2:
-					fct = jj_consume_token(ATAN2);
+				case POWER:
+					fct = jj_consume_token(POWER);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
 					jj_consume_token(COMMA);
 					param2 = NumericExpression();
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case COS:
-					fct = jj_consume_token(COS);
+				case RADIANS:
+					fct = jj_consume_token(RADIANS);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case COT:
-					fct = jj_consume_token(COT);
+				case RAND:
+					fct = jj_consume_token(RAND);
+					jj_consume_token(LEFT_PAR);
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case LEFT_PAR:
+						case PLUS:
+						case MINUS:
+						case AVG:
+						case MAX:
+						case MIN:
+						case SUM:
+						case COUNT:
+						case CONTAINS:
+						case INTERSECTS:
+						case AREA:
+						case COORD1:
+						case COORD2:
+						case DISTANCE:
+						case ABS:
+						case CEILING:
+						case DEGREES:
+						case EXP:
+						case FLOOR:
+						case LOG:
+						case LOG10:
+						case MOD:
+						case PI:
+						case POWER:
+						case RADIANS:
+						case RAND:
+						case ROUND:
+						case SQRT:
+						case TRUNCATE:
+						case ACOS:
+						case ASIN:
+						case ATAN:
+						case ATAN2:
+						case COS:
+						case COT:
+						case SIN:
+						case TAN:
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+						case SCIENTIFIC_NUMBER:
+						case UNSIGNED_FLOAT:
+						case UNSIGNED_INTEGER:
+							param1 = NumericExpression();
+							break;
+						default:
+							jj_la1[91] = jj_gen;
+							;
+					}
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case ROUND:
+					fct = jj_consume_token(ROUND);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case COMMA:
+							jj_consume_token(COMMA);
+							integerValue = SignedInteger();
+							param2 = queryFactory.createNumericConstant(integerValue);
+							break;
+						default:
+							jj_la1[92] = jj_gen;
+							;
+					}
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case SIN:
-					fct = jj_consume_token(SIN);
+				case SQRT:
+					fct = jj_consume_token(SQRT);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case TAN:
-					fct = jj_consume_token(TAN);
+				case TRUNCATE:
+					fct = jj_consume_token(TRUNCATE);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case COMMA:
+							jj_consume_token(COMMA);
+							integerValue = SignedInteger();
+							param2 = queryFactory.createNumericConstant(integerValue);
+							break;
+						default:
+							jj_la1[93] = jj_gen;
+							;
+					}
 					jj_consume_token(RIGHT_PAR);
 					break;
 				default:
@@ -3270,131 +2980,203 @@ public class ADQLParser implements ADQLParserConstants {
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
-			try{
-				if (param1 != null){
-					if (true)
-						return queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2);
-				}else{
-					if (true)
-						return null;
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+			if (param1 != null){
+				if (true)
+					return queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2);
+			}else{
+				if (true)
+					return null;
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public MathFunction TrigFunction() throws ParseException{
+		Token fct = null;
+		ADQLOperand param1 = null, param2 = null;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ACOS:
+				fct = jj_consume_token(ACOS);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case ASIN:
+				fct = jj_consume_token(ASIN);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case ATAN:
+				fct = jj_consume_token(ATAN);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case ATAN2:
+				fct = jj_consume_token(ATAN2);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(COMMA);
+				param2 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case COS:
+				fct = jj_consume_token(COS);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case COT:
+				fct = jj_consume_token(COT);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case SIN:
+				fct = jj_consume_token(SIN);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case TAN:
+				fct = jj_consume_token(TAN);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			default:
+				jj_la1[95] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		try{
+			if (param1 != null){
+				if (true)
+					return queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2);
+			}else{
+				if (true)
+					return null;
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("TrigFunction");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/* /!\ WARNING: The function name may be prefixed by "udf_" but there is no way to check it here ! */
 	final public UserDefinedFunction UserDefinedFunction() throws ParseException{
-		trace_call("UserDefinedFunction");
-		try{
-			Token fct;
-			Vector<ADQLOperand> params = new Vector<ADQLOperand>();
-			ADQLOperand op;
-			fct = jj_consume_token(REGULAR_IDENTIFIER);
-			jj_consume_token(LEFT_PAR);
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case LEFT_PAR:
-				case PLUS:
-				case MINUS:
-				case AVG:
-				case MAX:
-				case MIN:
-				case SUM:
-				case COUNT:
-				case BOX:
-				case CENTROID:
-				case CIRCLE:
-				case POINT:
-				case POLYGON:
-				case REGION:
-				case CONTAINS:
-				case INTERSECTS:
-				case AREA:
-				case COORD1:
-				case COORD2:
-				case COORDSYS:
-				case DISTANCE:
-				case ABS:
-				case CEILING:
-				case DEGREES:
-				case EXP:
-				case FLOOR:
-				case LOG:
-				case LOG10:
-				case MOD:
-				case PI:
-				case POWER:
-				case RADIANS:
-				case RAND:
-				case ROUND:
-				case SQRT:
-				case TRUNCATE:
-				case ACOS:
-				case ASIN:
-				case ATAN:
-				case ATAN2:
-				case COS:
-				case COT:
-				case SIN:
-				case TAN:
-				case STRING_LITERAL:
-				case DELIMITED_IDENTIFIER:
-				case REGULAR_IDENTIFIER:
-				case SCIENTIFIC_NUMBER:
-				case UNSIGNED_FLOAT:
-				case UNSIGNED_INTEGER:
+		Token fct;
+		Vector<ADQLOperand> params = new Vector<ADQLOperand>();
+		ADQLOperand op;
+		fct = jj_consume_token(REGULAR_IDENTIFIER);
+		jj_consume_token(LEFT_PAR);
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case LEFT_PAR:
+			case PLUS:
+			case MINUS:
+			case AVG:
+			case MAX:
+			case MIN:
+			case SUM:
+			case COUNT:
+			case BOX:
+			case CENTROID:
+			case CIRCLE:
+			case POINT:
+			case POLYGON:
+			case REGION:
+			case CONTAINS:
+			case INTERSECTS:
+			case AREA:
+			case COORD1:
+			case COORD2:
+			case COORDSYS:
+			case DISTANCE:
+			case ABS:
+			case CEILING:
+			case DEGREES:
+			case EXP:
+			case FLOOR:
+			case LOG:
+			case LOG10:
+			case MOD:
+			case PI:
+			case POWER:
+			case RADIANS:
+			case RAND:
+			case ROUND:
+			case SQRT:
+			case TRUNCATE:
+			case ACOS:
+			case ASIN:
+			case ATAN:
+			case ATAN2:
+			case COS:
+			case COT:
+			case SIN:
+			case TAN:
+			case STRING_LITERAL:
+			case DELIMITED_IDENTIFIER:
+			case REGULAR_IDENTIFIER:
+			case SCIENTIFIC_NUMBER:
+			case UNSIGNED_FLOAT:
+			case UNSIGNED_INTEGER:
+				op = ValueExpression();
+				params.add(op);
+				label_13: while(true){
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case COMMA:
+							;
+							break;
+						default:
+							jj_la1[96] = jj_gen;
+							break label_13;
+					}
+					jj_consume_token(COMMA);
 					op = ValueExpression();
 					params.add(op);
-					label_13: while(true){
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case COMMA:
-								;
-								break;
-							default:
-								jj_la1[95] = jj_gen;
-								break label_13;
-						}
-						jj_consume_token(COMMA);
-						op = ValueExpression();
-						params.add(op);
-					}
-					break;
-				default:
-					jj_la1[96] = jj_gen;
-					;
-			}
-			jj_consume_token(RIGHT_PAR);
-			//System.out.println("INFO [ADQLParser]: \""+fct.image+"\" (from line "+fct.beginLine+" and column "+fct.beginColumn+" to line "+token.endLine+" and column "+(token.endColumn+1)+") is considered as an user defined function !");
-			try{
-				ADQLOperand[] parameters = new ADQLOperand[params.size()];
-				for(int i = 0; i < params.size(); i++)
-					parameters[i] = params.get(i);
-				{
-					if (true)
-						return queryFactory.createUserDefinedFunction(fct.image, parameters);
-				}
-			}catch(UnsupportedOperationException uoe){
-				{
-					if (true)
-						throw new ParseException(uoe.getMessage(), new TextPosition(fct, token));
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
 				}
+				break;
+			default:
+				jj_la1[97] = jj_gen;
+				;
+		}
+		jj_consume_token(RIGHT_PAR);
+		//System.out.println("INFO [ADQLParser]: \""+fct.image+"\" (from line "+fct.beginLine+" and column "+fct.beginColumn+" to line "+token.endLine+" and column "+(token.endColumn+1)+") is considered as an user defined function !");
+		try{
+			//  Build the parameters list:
+			ADQLOperand[] parameters = new ADQLOperand[params.size()];
+			for(int i = 0; i < params.size(); i++)
+				parameters[i] = params.get(i);
+
+			// Create the UDF function:
+			{
+				if (true)
+					return queryFactory.createUserDefinedFunction(fct.image, parameters);
+			}
+		}catch(UnsupportedOperationException uoe){
+			/* This catch clause is just for backward compatibility:
+			* if the createUserDefinedFunction(...) is overridden and
+			* the function can not be identified a such exception may be thrown). */
+			{
+				if (true)
+					throw new ParseException(uoe.getMessage(), new TextPosition(fct, token));
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("UserDefinedFunction");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
 	private boolean jj_2_1(int xla){
@@ -3553,32 +3335,94 @@ public class ADQLParser implements ADQLParserConstants {
 		}
 	}
 
-	private boolean jj_3R_16(){
-		if (jj_scan_token(LEFT_PAR))
+	private boolean jj_2_14(int xla){
+		jj_la = xla;
+		jj_lastpos = jj_scanpos = token;
+		try{
+			return !jj_3_14();
+		}catch(LookaheadSuccess ls){
 			return true;
-		if (jj_3R_28())
+		}finally{
+			jj_save(13, xla);
+		}
+	}
+
+	private boolean jj_2_15(int xla){
+		jj_la = xla;
+		jj_lastpos = jj_scanpos = token;
+		try{
+			return !jj_3_15();
+		}catch(LookaheadSuccess ls){
 			return true;
-		if (jj_scan_token(RIGHT_PAR))
+		}finally{
+			jj_save(14, xla);
+		}
+	}
+
+	private boolean jj_2_16(int xla){
+		jj_la = xla;
+		jj_lastpos = jj_scanpos = token;
+		try{
+			return !jj_3_16();
+		}catch(LookaheadSuccess ls){
+			return true;
+		}finally{
+			jj_save(15, xla);
+		}
+	}
+
+	private boolean jj_3R_185(){
+		if (jj_scan_token(NOT))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_113(){
-		if (jj_3R_101())
+	private boolean jj_3R_146(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_46())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_152(){
+	private boolean jj_3R_149(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_164())
+		if (jj_3R_161()){
+			jj_scanpos = xsp;
+			if (jj_3_1()){
+				jj_scanpos = xsp;
+				if (jj_3R_162())
+					return true;
+			}
+		}
+		return false;
+	}
+
+	private boolean jj_3R_150(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_149())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_145(){
+		if (jj_3R_109())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_163(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_185())
 			jj_scanpos = xsp;
-		if (jj_3R_165())
+		if (jj_3R_186())
 			return true;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_166()){
+			if (jj_3R_187()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -3586,113 +3430,138 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_122(){
-		if (jj_3R_144())
+	private boolean jj_3R_148(){
+		if (jj_scan_token(TOP))
+			return true;
+		if (jj_scan_token(UNSIGNED_INTEGER))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_121(){
-		if (jj_3R_143())
+	private boolean jj_3R_147(){
+		if (jj_scan_token(QUANTIFIER))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_120(){
-		if (jj_3R_142())
+	private boolean jj_3R_48(){
+		if (jj_scan_token(SELECT))
+			return true;
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_147())
+			jj_scanpos = xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_148())
+			jj_scanpos = xsp;
+		if (jj_3R_149())
 			return true;
+		while(true){
+			xsp = jj_scanpos;
+			if (jj_3R_150()){
+				jj_scanpos = xsp;
+				break;
+			}
+		}
 		return false;
 	}
 
-	private boolean jj_3R_119(){
-		if (jj_3R_141())
+	private boolean jj_3R_144(){
+		if (jj_3R_21())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_112(){
-		if (jj_3R_23())
+	private boolean jj_3R_16(){
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_31())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_162(){
-		if (jj_3R_49())
+	private boolean jj_3R_53(){
+		if (jj_3R_74())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_64(){
+	private boolean jj_3R_122(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_112()){
+		if (jj_3R_144()){
 			jj_scanpos = xsp;
-			if (jj_3R_113())
+			if (jj_3R_145())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_32(){
-		if (jj_3R_49())
+	private boolean jj_3R_55(){
+		if (jj_3R_76())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3_8(){
-		if (jj_3R_20())
+	private boolean jj_3_11(){
+		if (jj_3R_24())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_31(){
-		if (jj_3R_48())
+	private boolean jj_3R_54(){
+		if (jj_3R_75())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_18(){
+	private boolean jj_3R_35(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_31()){
+		if (jj_3R_54()){
 			jj_scanpos = xsp;
-			if (jj_3_8()){
+			if (jj_3_11()){
 				jj_scanpos = xsp;
-				if (jj_3R_32())
+				if (jj_3R_55())
 					return true;
 			}
 		}
 		return false;
 	}
 
-	private boolean jj_3R_28(){
-		if (jj_3R_44())
+	private boolean jj_3R_133(){
+		if (jj_3R_155())
 			return true;
-		if (jj_3R_118())
+		return false;
+	}
+
+	private boolean jj_3R_132(){
+		if (jj_3R_154())
 			return true;
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_119())
-			jj_scanpos = xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_120())
-			jj_scanpos = xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_121())
-			jj_scanpos = xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_122())
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_65(){
-		if (jj_3R_41())
+	private boolean jj_3R_131(){
+		if (jj_3R_153())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_130(){
+		if (jj_3R_152())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_123(){
+		if (jj_3R_46())
 			return true;
 		Token xsp;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_114()){
+			if (jj_3R_146()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -3700,23 +3569,15 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_182(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_191())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_43(){
+	private boolean jj_3R_47(){
 		if (jj_scan_token(CONCAT))
 			return true;
-		if (jj_3R_18())
+		if (jj_3R_35())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_181(){
+	private boolean jj_3R_174(){
 		if (jj_scan_token(COMMA))
 			return true;
 		if (jj_3R_191())
@@ -3724,25 +3585,42 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_106(){
-		if (jj_scan_token(FULL))
+	private boolean jj_3R_173(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_191())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_170(){
-		if (jj_scan_token(MINUS))
+	private boolean jj_3R_31(){
+		if (jj_3R_48())
 			return true;
+		if (jj_3R_129())
+			return true;
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_130())
+			jj_scanpos = xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_131())
+			jj_scanpos = xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_132())
+			jj_scanpos = xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_133())
+			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_24(){
-		if (jj_3R_18())
+	private boolean jj_3R_27(){
+		if (jj_3R_35())
 			return true;
 		Token xsp;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_43()){
+			if (jj_3R_47()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -3750,24 +3628,30 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3_7(){
-		if (jj_3R_19())
+	private boolean jj_3R_73(){
+		if (jj_scan_token(MINUS))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_161(){
+	private boolean jj_3_10(){
+		if (jj_3R_23())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_52(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(8)){
 			jj_scanpos = xsp;
-			if (jj_3R_170())
+			if (jj_3R_73())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_160(){
+	private boolean jj_3R_168(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(10)){
@@ -3775,198 +3659,284 @@ public class ADQLParser implements ADQLParserConstants {
 			if (jj_scan_token(11))
 				return true;
 		}
-		if (jj_3R_130())
+		if (jj_3R_135())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_148(){
+	private boolean jj_3R_112(){
+		if (jj_scan_token(FULL))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_34(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_161())
+		if (jj_3R_52())
 			jj_scanpos = xsp;
 		xsp = jj_scanpos;
-		if (jj_3_7()){
+		if (jj_3_10()){
 			jj_scanpos = xsp;
-			if (jj_3R_162())
+			if (jj_3R_53())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_20(){
+	private boolean jj_3R_24(){
 		if (jj_scan_token(REGULAR_IDENTIFIER))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_65())
+		if (jj_3R_123())
 			jj_scanpos = xsp;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_109(){
-		if (jj_scan_token(FULL))
+	private boolean jj_3R_156(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_scan_token(8)){
+			jj_scanpos = xsp;
+			if (jj_scan_token(9))
+				return true;
+		}
+		if (jj_3R_108())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_180(){
-		if (jj_3R_102())
+	private boolean jj_3R_19(){
+		if (jj_3R_34())
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_145(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(8)){
 			jj_scanpos = xsp;
-			if (jj_scan_token(9))
-				return true;
+			if (jj_scan_token(9)){
+				jj_scanpos = xsp;
+				if (jj_scan_token(10)){
+					jj_scanpos = xsp;
+					if (jj_scan_token(11))
+						return true;
+				}
+			}
 		}
-		if (jj_3R_102())
+		return false;
+	}
+
+	private boolean jj_3R_172(){
+		if (jj_3R_108())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_93(){
+	private boolean jj_3R_20(){
+		if (jj_3R_35())
+			return true;
+		if (jj_scan_token(CONCAT))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_135(){
+		if (jj_3R_34())
+			return true;
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_168())
+			jj_scanpos = xsp;
+		return false;
+	}
+
+	private boolean jj_3R_100(){
 		if (jj_scan_token(TAN))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_92(){
+	private boolean jj_3_9(){
+		if (jj_3R_22())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_99(){
 		if (jj_scan_token(SIN))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_130(){
-		if (jj_3R_148())
+	private boolean jj_3_8(){
+		if (jj_3R_21())
 			return true;
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_160())
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_91(){
+	private boolean jj_3R_98(){
 		if (jj_scan_token(COT))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_90(){
+	private boolean jj_3R_115(){
+		if (jj_scan_token(FULL))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_97(){
 		if (jj_scan_token(COS))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_89(){
+	private boolean jj_3_7(){
+		if (jj_scan_token(REGULAR_IDENTIFIER))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_96(){
 		if (jj_scan_token(ATAN2))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_88(){
+	private boolean jj_3_6(){
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_95(){
 		if (jj_scan_token(ATAN))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_105(){
-		if (jj_scan_token(RIGHT))
-			return true;
+	private boolean jj_3_5(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_scan_token(63)){
+			jj_scanpos = xsp;
+			if (jj_3R_20())
+				return true;
+		}
 		return false;
 	}
 
-	private boolean jj_3R_87(){
+	private boolean jj_3R_94(){
 		if (jj_scan_token(ASIN))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_86(){
+	private boolean jj_3_4(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_18()){
+			jj_scanpos = xsp;
+			if (jj_3R_19())
+				return true;
+		}
+		return false;
+	}
+
+	private boolean jj_3R_18(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_scan_token(8)){
+			jj_scanpos = xsp;
+			if (jj_scan_token(9))
+				return true;
+		}
+		return false;
+	}
+
+	private boolean jj_3R_93(){
 		if (jj_scan_token(ACOS))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_51(){
+	private boolean jj_3R_58(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_86()){
+		if (jj_3R_93()){
 			jj_scanpos = xsp;
-			if (jj_3R_87()){
+			if (jj_3R_94()){
 				jj_scanpos = xsp;
-				if (jj_3R_88()){
+				if (jj_3R_95()){
 					jj_scanpos = xsp;
-					if (jj_3R_89()){
+					if (jj_3R_96()){
 						jj_scanpos = xsp;
-						if (jj_3R_90()){
+						if (jj_3R_97()){
 							jj_scanpos = xsp;
-							if (jj_3R_91()){
+							if (jj_3R_98()){
 								jj_scanpos = xsp;
-								if (jj_3R_92()){
+								if (jj_3R_99()){
 									jj_scanpos = xsp;
-									if (jj_3R_93())
+									if (jj_3R_100())
 										return true;
 								}
 							}
@@ -3978,128 +3948,155 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3_6(){
-		if (jj_3R_18())
+	private boolean jj_3R_69(){
+		if (jj_3R_34())
 			return true;
-		if (jj_scan_token(CONCAT))
+		return false;
+	}
+
+	private boolean jj_3R_68(){
+		if (jj_3R_35())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3_5(){
-		if (jj_scan_token(COORDSYS))
+	private boolean jj_3R_67(){
+		if (jj_3R_21())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_85(){
+	private boolean jj_3R_108(){
+		if (jj_3R_135())
+			return true;
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_156())
+			jj_scanpos = xsp;
+		return false;
+	}
+
+	private boolean jj_3R_92(){
 		if (jj_scan_token(TRUNCATE))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_182())
+		if (jj_3R_174())
 			jj_scanpos = xsp;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3_4(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_scan_token(8)){
-			jj_scanpos = xsp;
-			if (jj_scan_token(9))
-				return true;
-		}
+	private boolean jj_3R_66(){
+		if (jj_3R_109())
+			return true;
 		return false;
 	}
 
-	private boolean jj_3R_84(){
+	private boolean jj_3R_91(){
 		if (jj_scan_token(SQRT))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_83(){
+	private boolean jj_3R_65(){
+		if (jj_3R_24())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_90(){
 		if (jj_scan_token(ROUND))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_181())
+		if (jj_3R_173())
 			jj_scanpos = xsp;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_82(){
+	private boolean jj_3R_64(){
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_46())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_89(){
 		if (jj_scan_token(RAND))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_180())
+		if (jj_3R_172())
 			jj_scanpos = xsp;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_81(){
+	private boolean jj_3R_63(){
+		if (jj_3R_27())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_88(){
 		if (jj_scan_token(RADIANS))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_102(){
-		if (jj_3R_130())
-			return true;
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_145())
-			jj_scanpos = xsp;
-		return false;
-	}
-
-	private boolean jj_3R_80(){
+	private boolean jj_3R_87(){
 		if (jj_scan_token(POWER))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_79(){
+	private boolean jj_3R_62(){
+		if (jj_3R_108())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_86(){
 		if (jj_scan_token(PI))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
@@ -4109,153 +4106,150 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_78(){
+	private boolean jj_3R_111(){
+		if (jj_scan_token(RIGHT))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_85(){
 		if (jj_scan_token(MOD))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_59(){
-		if (jj_3R_102())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_77(){
+	private boolean jj_3R_84(){
 		if (jj_scan_token(LOG10))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_58(){
-		if (jj_3R_24())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_76(){
+	private boolean jj_3R_83(){
 		if (jj_scan_token(LOG))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_57(){
-		if (jj_3R_24())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_75(){
+	private boolean jj_3R_82(){
 		if (jj_scan_token(FLOOR))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_70(){
+	private boolean jj_3R_81(){
+		if (jj_scan_token(EXP))
+			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_41())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_56(){
-		if (jj_3R_102())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_74(){
-		if (jj_scan_token(EXP))
+	private boolean jj_3R_80(){
+		if (jj_scan_token(DEGREES))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_73(){
-		if (jj_scan_token(DEGREES))
-			return true;
+	private boolean jj_3R_126(){
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_27())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_69(){
-		if (jj_3R_117())
+	private boolean jj_3R_79(){
+		if (jj_scan_token(CEILING))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_108())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_55(){
-		if (jj_3R_101())
+	private boolean jj_3R_125(){
+		if (jj_3R_21())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_72(){
-		if (jj_scan_token(CEILING))
+	private boolean jj_3R_78(){
+		if (jj_scan_token(ABS))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_41(){
+	private boolean jj_3R_46(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_55()){
+		if (jj_3R_62()){
 			jj_scanpos = xsp;
-			if (jj_3R_56()){
+			if (jj_3R_63()){
 				jj_scanpos = xsp;
-				if (jj_3R_57()){
+				if (jj_3R_64()){
 					jj_scanpos = xsp;
-					if (jj_3R_58()){
+					if (jj_3R_65()){
 						jj_scanpos = xsp;
-						if (jj_3R_59())
-							return true;
+						if (jj_3R_66()){
+							jj_scanpos = xsp;
+							if (jj_3R_67()){
+								jj_scanpos = xsp;
+								if (jj_3R_68()){
+									jj_scanpos = xsp;
+									if (jj_3R_69())
+										return true;
+								}
+							}
+						}
 					}
 				}
 			}
@@ -4263,74 +4257,44 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_68(){
-		if (jj_3R_23())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_71(){
-		if (jj_scan_token(ABS))
-			return true;
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_102())
-			return true;
-		if (jj_scan_token(RIGHT_PAR))
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_67(){
-		if (jj_3R_116())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_108(){
-		if (jj_scan_token(RIGHT))
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_66(){
-		if (jj_3R_115())
+	private boolean jj_3R_124(){
+		if (jj_3R_22())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_50(){
+	private boolean jj_3R_57(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_71()){
+		if (jj_3R_78()){
 			jj_scanpos = xsp;
-			if (jj_3R_72()){
+			if (jj_3R_79()){
 				jj_scanpos = xsp;
-				if (jj_3R_73()){
+				if (jj_3R_80()){
 					jj_scanpos = xsp;
-					if (jj_3R_74()){
+					if (jj_3R_81()){
 						jj_scanpos = xsp;
-						if (jj_3R_75()){
+						if (jj_3R_82()){
 							jj_scanpos = xsp;
-							if (jj_3R_76()){
+							if (jj_3R_83()){
 								jj_scanpos = xsp;
-								if (jj_3R_77()){
+								if (jj_3R_84()){
 									jj_scanpos = xsp;
-									if (jj_3R_78()){
+									if (jj_3R_85()){
 										jj_scanpos = xsp;
-										if (jj_3R_79()){
+										if (jj_3R_86()){
 											jj_scanpos = xsp;
-											if (jj_3R_80()){
+											if (jj_3R_87()){
 												jj_scanpos = xsp;
-												if (jj_3R_81()){
+												if (jj_3R_88()){
 													jj_scanpos = xsp;
-													if (jj_3R_82()){
+													if (jj_3R_89()){
 														jj_scanpos = xsp;
-														if (jj_3R_83()){
+														if (jj_3R_90()){
 															jj_scanpos = xsp;
-															if (jj_3R_84()){
+															if (jj_3R_91()){
 																jj_scanpos = xsp;
-																if (jj_3R_85())
+																if (jj_3R_92())
 																	return true;
 															}
 														}
@@ -4349,91 +4313,113 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_179(){
-		if (jj_scan_token(COMMA))
+	private boolean jj_3R_121(){
+		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_14())
+		if (jj_3R_108())
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_36(){
-		if (jj_3R_20())
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_35(){
-		if (jj_3R_52())
+	private boolean jj_3R_120(){
+		if (jj_3R_143())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_34(){
-		if (jj_3R_51())
+	private boolean jj_3R_41(){
+		if (jj_3R_24())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_49(){
+	private boolean jj_3R_76(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_66()){
+		if (jj_3R_124()){
 			jj_scanpos = xsp;
-			if (jj_3R_67()){
+			if (jj_3R_125()){
 				jj_scanpos = xsp;
-				if (jj_3R_68()){
-					jj_scanpos = xsp;
-					if (jj_3R_69()){
-						jj_scanpos = xsp;
-						if (jj_3R_70())
-							return true;
-					}
-				}
+				if (jj_3R_126())
+					return true;
 			}
 		}
 		return false;
 	}
 
-	private boolean jj_3R_104(){
-		if (jj_scan_token(LEFT))
+	private boolean jj_3R_40(){
+		if (jj_3R_59())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_33(){
-		if (jj_3R_50())
+	private boolean jj_3R_114(){
+		if (jj_scan_token(RIGHT))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_61(){
+	private boolean jj_3R_119(){
+		if (jj_3R_21())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_39(){
+		if (jj_3R_58())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_118(){
+		if (jj_3R_142())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_38(){
+		if (jj_3R_57())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_23(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_104()){
+		if (jj_3R_38()){
 			jj_scanpos = xsp;
-			if (jj_3R_105()){
+			if (jj_3R_39()){
 				jj_scanpos = xsp;
-				if (jj_3R_106())
-					return true;
+				if (jj_3R_40()){
+					jj_scanpos = xsp;
+					if (jj_3R_41())
+						return true;
+				}
 			}
 		}
-		xsp = jj_scanpos;
-		if (jj_scan_token(25))
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_19(){
+	private boolean jj_3R_201(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_14())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_74(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_33()){
+		if (jj_3R_118()){
 			jj_scanpos = xsp;
-			if (jj_3R_34()){
+			if (jj_3R_119()){
 				jj_scanpos = xsp;
-				if (jj_3R_35()){
+				if (jj_3R_120()){
 					jj_scanpos = xsp;
-					if (jj_3R_36())
+					if (jj_3R_121())
 						return true;
 				}
 			}
@@ -4441,7 +4427,62 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_192(){
+	private boolean jj_3R_110(){
+		if (jj_scan_token(LEFT))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_70(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_110()){
+			jj_scanpos = xsp;
+			if (jj_3R_111()){
+				jj_scanpos = xsp;
+				if (jj_3R_112())
+					return true;
+			}
+		}
+		xsp = jj_scanpos;
+		if (jj_scan_token(25))
+			jj_scanpos = xsp;
+		return false;
+	}
+
+	private boolean jj_3R_75(){
+		if (jj_scan_token(COORDSYS))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_122())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_178(){
+		if (jj_3R_21())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_171(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_170())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_176(){
+		if (jj_3R_21())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_197(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(8)){
@@ -4455,45 +4496,53 @@ public class ADQLParser implements ADQLParserConstants {
 	private boolean jj_3R_191(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_192())
+		if (jj_3R_197())
 			jj_scanpos = xsp;
 		if (jj_scan_token(UNSIGNED_INTEGER))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_45(){
+	private boolean jj_3R_49(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(24)){
 			jj_scanpos = xsp;
-			if (jj_3R_61())
+			if (jj_3R_70())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_48(){
-		if (jj_scan_token(COORDSYS))
+	private boolean jj_3R_141(){
+		if (jj_scan_token(REGION))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_64())
+		if (jj_3R_27())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_159(){
+	private boolean jj_3R_158(){
+		if (jj_scan_token(POINT))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_169())
+			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_158())
+		if (jj_3R_170())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_115(){
+	private boolean jj_3R_142(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(99)){
@@ -4507,13 +4556,7 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_186(){
-		if (jj_3R_23())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_178(){
+	private boolean jj_3R_200(){
 		if (jj_scan_token(USING))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
@@ -4523,7 +4566,7 @@ public class ADQLParser implements ADQLParserConstants {
 		Token xsp;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_179()){
+			if (jj_3R_201()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -4533,26 +4576,52 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_107(){
+	private boolean jj_3R_113(){
 		if (jj_scan_token(LEFT))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_184(){
-		if (jj_3R_23())
+	private boolean jj_3R_140(){
+		if (jj_scan_token(POLYGON))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_169())
+			return true;
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_170())
+			return true;
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_170())
+			return true;
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_170())
+			return true;
+		Token xsp;
+		while(true){
+			xsp = jj_scanpos;
+			if (jj_3R_171()){
+				jj_scanpos = xsp;
+				break;
+			}
+		}
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_62(){
+	private boolean jj_3R_71(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_107()){
+		if (jj_3R_113()){
 			jj_scanpos = xsp;
-			if (jj_3R_108()){
+			if (jj_3R_114()){
 				jj_scanpos = xsp;
-				if (jj_3R_109())
+				if (jj_3R_115())
 					return true;
 			}
 		}
@@ -4562,55 +4631,33 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_177(){
+	private boolean jj_3R_199(){
 		if (jj_scan_token(ON))
 			return true;
-		if (jj_3R_152())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_129(){
-		if (jj_scan_token(REGION))
-			return true;
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_24())
-			return true;
-		if (jj_scan_token(RIGHT_PAR))
+		if (jj_3R_163())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_147(){
-		if (jj_scan_token(POINT))
-			return true;
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_157())
-			return true;
-		if (jj_scan_token(COMMA))
-			return true;
+	private boolean jj_3R_139(){
 		if (jj_3R_158())
 			return true;
-		if (jj_scan_token(RIGHT_PAR))
-			return true;
 		return false;
 	}
 
-	private boolean jj_3R_133(){
+	private boolean jj_3R_37(){
 		if (jj_scan_token(STRING_LITERAL))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_116(){
+	private boolean jj_3R_22(){
 		Token xsp;
-		if (jj_3R_133())
+		if (jj_3R_37())
 			return true;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_133()){
+			if (jj_3R_37()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -4618,130 +4665,122 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_46(){
+	private boolean jj_3R_138(){
+		if (jj_scan_token(CIRCLE))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_169())
+			return true;
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_170())
+			return true;
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_108())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_50(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(24)){
 			jj_scanpos = xsp;
-			if (jj_3R_62())
+			if (jj_3R_71())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_30(){
+	private boolean jj_3R_33(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_46())
+		if (jj_3R_50())
 			jj_scanpos = xsp;
 		if (jj_scan_token(JOIN))
 			return true;
-		if (jj_3R_47())
+		if (jj_3R_51())
 			return true;
 		xsp = jj_scanpos;
-		if (jj_3R_177()){
+		if (jj_3R_199()){
 			jj_scanpos = xsp;
-			if (jj_3R_178())
+			if (jj_3R_200())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_128(){
-		if (jj_scan_token(POLYGON))
+	private boolean jj_3R_137(){
+		if (jj_scan_token(CENTROID))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_157())
-			return true;
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_158())
-			return true;
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_158())
-			return true;
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_158())
+		if (jj_3R_122())
 			return true;
-		Token xsp;
-		while(true){
-			xsp = jj_scanpos;
-			if (jj_3R_159()){
-				jj_scanpos = xsp;
-				break;
-			}
-		}
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_29(){
+	private boolean jj_3R_32(){
 		if (jj_scan_token(NATURAL))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_45())
+		if (jj_3R_49())
 			jj_scanpos = xsp;
 		if (jj_scan_token(JOIN))
 			return true;
-		if (jj_3R_47())
+		if (jj_3R_51())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_127(){
-		if (jj_3R_147())
+	private boolean jj_3R_136(){
+		if (jj_scan_token(BOX))
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_171(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_scan_token(22))
-			jj_scanpos = xsp;
-		if (jj_3R_14())
+		if (jj_scan_token(LEFT_PAR))
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_126(){
-		if (jj_scan_token(CIRCLE))
+		if (jj_3R_169())
 			return true;
-		if (jj_scan_token(LEFT_PAR))
+		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_157())
+		if (jj_3R_170())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_158())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_125(){
-		if (jj_scan_token(CENTROID))
-			return true;
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_64())
+	private boolean jj_3R_192(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_scan_token(22))
+			jj_scanpos = xsp;
+		if (jj_3R_14())
 			return true;
-		if (jj_scan_token(RIGHT_PAR))
+		return false;
+	}
+
+	private boolean jj_3R_182(){
+		if (jj_3R_21())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_169(){
+	private boolean jj_3R_190(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(45)){
@@ -4752,26 +4791,55 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_124(){
-		if (jj_scan_token(BOX))
-			return true;
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_157())
+	private boolean jj_3R_183(){
+		if (jj_3R_46())
 			return true;
-		if (jj_scan_token(COMMA))
+		return false;
+	}
+
+	private boolean jj_3R_180(){
+		if (jj_3R_21())
 			return true;
+		return false;
+	}
+
+	private boolean jj_3R_109(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_136()){
+			jj_scanpos = xsp;
+			if (jj_3R_137()){
+				jj_scanpos = xsp;
+				if (jj_3R_138()){
+					jj_scanpos = xsp;
+					if (jj_3R_139()){
+						jj_scanpos = xsp;
+						if (jj_3R_140()){
+							jj_scanpos = xsp;
+							if (jj_3R_141())
+								return true;
+						}
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	private boolean jj_3R_181(){
 		if (jj_3R_158())
 			return true;
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_102())
-			return true;
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_102())
+		return false;
+	}
+
+	private boolean jj_3R_177(){
+		if (jj_3R_158())
 			return true;
-		if (jj_scan_token(RIGHT_PAR))
+		return false;
+	}
+
+	private boolean jj_3R_175(){
+		if (jj_3R_158())
 			return true;
 		return false;
 	}
@@ -4779,39 +4847,45 @@ public class ADQLParser implements ADQLParserConstants {
 	private boolean jj_3R_17(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_29()){
+		if (jj_3R_32()){
 			jj_scanpos = xsp;
-			if (jj_3R_30())
+			if (jj_3R_33())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_176(){
+	private boolean jj_3R_169(){
+		if (jj_3R_27())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_198(){
 		if (jj_3R_17())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_111(){
+	private boolean jj_3R_117(){
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_172())
+		if (jj_3R_193())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_172(){
-		if (jj_3R_63())
+	private boolean jj_3R_193(){
+		if (jj_3R_72())
 			return true;
 		Token xsp;
-		if (jj_3R_176())
+		if (jj_3R_198())
 			return true;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_176()){
+			if (jj_3R_198()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -4819,59 +4893,30 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_190(){
-		if (jj_3R_23())
+	private boolean jj_3R_179(){
+		if (jj_3R_158())
 			return true;
 		return false;
 	}
 
 	private boolean jj_3_3(){
-		if (jj_3R_17())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_101(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_124()){
-			jj_scanpos = xsp;
-			if (jj_3R_125()){
-				jj_scanpos = xsp;
-				if (jj_3R_126()){
-					jj_scanpos = xsp;
-					if (jj_3R_127()){
-						jj_scanpos = xsp;
-						if (jj_3R_128()){
-							jj_scanpos = xsp;
-							if (jj_3R_129())
-								return true;
-						}
-					}
-				}
-			}
-		}
-		return false;
-	}
-
-	private boolean jj_3R_149(){
-		if (jj_3R_41())
+		if (jj_3R_17())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_110(){
-		if (jj_3R_103())
+	private boolean jj_3R_116(){
+		if (jj_3R_77())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_171())
+		if (jj_3R_192())
 			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_47(){
-		if (jj_3R_63())
+	private boolean jj_3R_51(){
+		if (jj_3R_72())
 			return true;
 		Token xsp;
 		while(true){
@@ -4884,18 +4929,6 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_188(){
-		if (jj_3R_23())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_185(){
-		if (jj_3R_147())
-			return true;
-		return false;
-	}
-
 	private boolean jj_3_2(){
 		if (jj_3R_16())
 			return true;
@@ -4908,62 +4941,24 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_183(){
-		if (jj_3R_147())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_189(){
-		if (jj_3R_147())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_157(){
-		if (jj_3R_24())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_187(){
-		if (jj_3R_147())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_63(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_110()){
-			jj_scanpos = xsp;
-			if (jj_3_2()){
-				jj_scanpos = xsp;
-				if (jj_3R_111())
-					return true;
-			}
-		}
-		return false;
-	}
-
-	private boolean jj_3R_98(){
+	private boolean jj_3R_105(){
 		if (jj_scan_token(DISTANCE))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_187()){
+		if (jj_3R_179()){
 			jj_scanpos = xsp;
-			if (jj_3R_188())
+			if (jj_3R_180())
 				return true;
 		}
 		if (jj_scan_token(COMMA))
 			return true;
 		xsp = jj_scanpos;
-		if (jj_3R_189()){
+		if (jj_3R_181()){
 			jj_scanpos = xsp;
-			if (jj_3R_190())
+			if (jj_3R_182())
 				return true;
 		}
 		if (jj_scan_token(RIGHT_PAR))
@@ -4971,16 +4966,16 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_97(){
+	private boolean jj_3R_104(){
 		if (jj_scan_token(COORD2))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_185()){
+		if (jj_3R_177()){
 			jj_scanpos = xsp;
-			if (jj_3R_186())
+			if (jj_3R_178())
 				return true;
 		}
 		if (jj_scan_token(RIGHT_PAR))
@@ -4988,16 +4983,16 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_96(){
+	private boolean jj_3R_103(){
 		if (jj_scan_token(COORD1))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_183()){
+		if (jj_3R_175()){
 			jj_scanpos = xsp;
-			if (jj_3R_184())
+			if (jj_3R_176())
 				return true;
 		}
 		if (jj_scan_token(RIGHT_PAR))
@@ -5005,39 +5000,33 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_95(){
+	private boolean jj_3R_102(){
 		if (jj_scan_token(AREA))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_64())
+		if (jj_3R_122())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_168(){
-		if (jj_3R_42())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_155(){
+	private boolean jj_3R_72(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_168()){
+		if (jj_3R_116()){
 			jj_scanpos = xsp;
-			if (jj_scan_token(101))
-				return true;
+			if (jj_3_2()){
+				jj_scanpos = xsp;
+				if (jj_3R_117())
+					return true;
+			}
 		}
-		xsp = jj_scanpos;
-		if (jj_3R_169())
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_94(){
+	private boolean jj_3R_101(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(58)){
@@ -5047,62 +5036,57 @@ public class ADQLParser implements ADQLParserConstants {
 		}
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_64())
+		if (jj_3R_122())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_64())
+		if (jj_3R_122())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_146(){
+	private boolean jj_3R_157(){
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_41())
+		if (jj_3R_46())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_167(){
-		if (jj_3R_42())
+	private boolean jj_3R_189(){
+		if (jj_3R_36())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_153(){
+	private boolean jj_3R_166(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_167()){
+		if (jj_3R_189()){
 			jj_scanpos = xsp;
 			if (jj_scan_token(101))
 				return true;
 		}
+		xsp = jj_scanpos;
+		if (jj_3R_190())
+			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_60(){
-		if (jj_scan_token(DOT))
-			return true;
-		if (jj_3R_103())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_52(){
+	private boolean jj_3R_59(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_94()){
+		if (jj_3R_101()){
 			jj_scanpos = xsp;
-			if (jj_3R_95()){
+			if (jj_3R_102()){
 				jj_scanpos = xsp;
-				if (jj_3R_96()){
+				if (jj_3R_103()){
 					jj_scanpos = xsp;
-					if (jj_3R_97()){
+					if (jj_3R_104()){
 						jj_scanpos = xsp;
-						if (jj_3R_98())
+						if (jj_3R_105())
 							return true;
 					}
 				}
@@ -5111,7 +5095,7 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_135(){
+	private boolean jj_3R_160(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(47)){
@@ -5130,30 +5114,24 @@ public class ADQLParser implements ADQLParserConstants {
 		xsp = jj_scanpos;
 		if (jj_scan_token(19))
 			jj_scanpos = xsp;
-		if (jj_3R_41())
+		if (jj_3R_46())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_158(){
-		if (jj_3R_102())
+	private boolean jj_3R_170(){
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_102())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_23(){
-		if (jj_3R_42())
+		if (jj_3R_108())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_134(){
+	private boolean jj_3R_159(){
 		if (jj_scan_token(COUNT))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
@@ -5165,7 +5143,7 @@ public class ADQLParser implements ADQLParserConstants {
 		xsp = jj_scanpos;
 		if (jj_scan_token(10)){
 			jj_scanpos = xsp;
-			if (jj_3R_149())
+			if (jj_3R_183())
 				return true;
 		}
 		if (jj_scan_token(RIGHT_PAR))
@@ -5173,77 +5151,83 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_42(){
-		if (jj_3R_14())
+	private boolean jj_3R_188(){
+		if (jj_3R_36())
 			return true;
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_60())
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_132(){
-		if (jj_scan_token(DOT))
-			return true;
-		if (jj_3R_14())
-			return true;
+	private boolean jj_3R_164(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_188()){
+			jj_scanpos = xsp;
+			if (jj_scan_token(101))
+				return true;
+		}
 		return false;
 	}
 
-	private boolean jj_3R_131(){
+	private boolean jj_3R_56(){
 		if (jj_scan_token(DOT))
 			return true;
-		if (jj_3R_14())
+		if (jj_3R_77())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_117(){
+	private boolean jj_3R_143(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_134()){
+		if (jj_3R_159()){
 			jj_scanpos = xsp;
-			if (jj_3R_135())
+			if (jj_3R_160())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_103(){
+	private boolean jj_3R_21(){
+		if (jj_3R_36())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_36(){
 		if (jj_3R_14())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_131())
-			jj_scanpos = xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_132())
+		if (jj_3R_56())
 			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_26(){
-		if (jj_scan_token(DELIMITED_IDENTIFIER))
+	private boolean jj_3R_128(){
+		if (jj_scan_token(DOT))
+			return true;
+		if (jj_3R_14())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_25(){
-		if (jj_scan_token(REGULAR_IDENTIFIER))
+	private boolean jj_3R_127(){
+		if (jj_scan_token(DOT))
+			return true;
+		if (jj_3R_14())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_123(){
+	private boolean jj_3R_134(){
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_41())
+		if (jj_3R_46())
 			return true;
 		Token xsp;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_146()){
+			if (jj_3R_157()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -5253,24 +5237,13 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_14(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_25()){
-			jj_scanpos = xsp;
-			if (jj_3R_26())
-				return true;
-		}
-		return false;
-	}
-
-	private boolean jj_3_13(){
+	private boolean jj_3_16(){
 		if (jj_3R_16())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_100(){
+	private boolean jj_3R_107(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(35))
@@ -5278,124 +5251,80 @@ public class ADQLParser implements ADQLParserConstants {
 		if (jj_scan_token(IN))
 			return true;
 		xsp = jj_scanpos;
-		if (jj_3_13()){
+		if (jj_3_16()){
 			jj_scanpos = xsp;
-			if (jj_3R_123())
+			if (jj_3R_134())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_156(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_155())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_144(){
-		if (jj_scan_token(ORDER_BY))
-			return true;
-		if (jj_3R_155())
+	private boolean jj_3R_77(){
+		if (jj_3R_14())
 			return true;
 		Token xsp;
-		while(true){
-			xsp = jj_scanpos;
-			if (jj_3R_156()){
-				jj_scanpos = xsp;
-				break;
-			}
-		}
+		xsp = jj_scanpos;
+		if (jj_3R_127())
+			jj_scanpos = xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_128())
+			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_143(){
-		if (jj_scan_token(HAVING))
-			return true;
-		if (jj_3R_152())
+	private boolean jj_3R_29(){
+		if (jj_scan_token(DELIMITED_IDENTIFIER))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_154(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_153())
+	private boolean jj_3R_28(){
+		if (jj_scan_token(REGULAR_IDENTIFIER))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_22(){
+	private boolean jj_3R_26(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(35))
 			jj_scanpos = xsp;
 		if (jj_scan_token(BETWEEN))
 			return true;
-		if (jj_3R_41())
+		if (jj_3R_46())
 			return true;
 		if (jj_scan_token(AND))
 			return true;
-		if (jj_3R_41())
+		if (jj_3R_46())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_163(){
-		if (jj_scan_token(AS))
-			return true;
-		if (jj_3R_14())
+	private boolean jj_3R_61(){
+		if (jj_3R_107())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_142(){
-		if (jj_scan_token(GROUP_BY))
-			return true;
-		if (jj_3R_153())
-			return true;
+	private boolean jj_3R_14(){
 		Token xsp;
-		while(true){
-			xsp = jj_scanpos;
-			if (jj_3R_154()){
-				jj_scanpos = xsp;
-				break;
-			}
-		}
-		return false;
-	}
-
-	private boolean jj_3R_54(){
-		if (jj_3R_100())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3_10(){
-		if (jj_3R_22())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_140(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_47())
-			return true;
+		xsp = jj_scanpos;
+		if (jj_3R_28()){
+			jj_scanpos = xsp;
+			if (jj_3R_29())
+				return true;
+		}
 		return false;
 	}
 
-	private boolean jj_3R_141(){
-		if (jj_scan_token(WHERE))
-			return true;
-		if (jj_3R_152())
+	private boolean jj_3_13(){
+		if (jj_3R_26())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3_12(){
-		if (jj_3R_24())
+	private boolean jj_3_15(){
+		if (jj_3R_27())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
@@ -5406,21 +5335,29 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_53(){
-		if (jj_3R_99())
+	private boolean jj_3R_60(){
+		if (jj_3R_106())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3_11(){
-		if (jj_3R_23())
+	private boolean jj_3R_167(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_166())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3_14(){
+		if (jj_3R_21())
 			return true;
 		if (jj_scan_token(IS))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_99(){
+	private boolean jj_3R_106(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(12)){
@@ -5440,28 +5377,20 @@ public class ADQLParser implements ADQLParserConstants {
 				}
 			}
 		}
-		if (jj_3R_41())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_27(){
-		if (jj_3R_14())
-			return true;
-		if (jj_scan_token(DOT))
+		if (jj_3R_46())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_118(){
-		if (jj_scan_token(FROM))
+	private boolean jj_3R_155(){
+		if (jj_scan_token(ORDER_BY))
 			return true;
-		if (jj_3R_47())
+		if (jj_3R_166())
 			return true;
 		Token xsp;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_140()){
+			if (jj_3R_167()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -5469,24 +5398,32 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_40(){
-		if (jj_3R_41())
+	private boolean jj_3R_154(){
+		if (jj_scan_token(HAVING))
+			return true;
+		if (jj_3R_163())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_45(){
+		if (jj_3R_46())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_53()){
+		if (jj_3R_60()){
 			jj_scanpos = xsp;
-			if (jj_3_10()){
+			if (jj_3_13()){
 				jj_scanpos = xsp;
-				if (jj_3R_54())
+				if (jj_3R_61())
 					return true;
 			}
 		}
 		return false;
 	}
 
-	private boolean jj_3R_39(){
-		if (jj_3R_24())
+	private boolean jj_3R_44(){
+		if (jj_3R_27())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
@@ -5494,13 +5431,21 @@ public class ADQLParser implements ADQLParserConstants {
 			jj_scanpos = xsp;
 		if (jj_scan_token(LIKE))
 			return true;
-		if (jj_3R_24())
+		if (jj_3R_27())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_38(){
-		if (jj_3R_23())
+	private boolean jj_3R_165(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_164())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_43(){
+		if (jj_3R_21())
 			return true;
 		if (jj_scan_token(IS))
 			return true;
@@ -5513,7 +5458,31 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_37(){
+	private boolean jj_3R_184(){
+		if (jj_scan_token(AS))
+			return true;
+		if (jj_3R_14())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_153(){
+		if (jj_scan_token(GROUP_BY))
+			return true;
+		if (jj_3R_164())
+			return true;
+		Token xsp;
+		while(true){
+			xsp = jj_scanpos;
+			if (jj_3R_165()){
+				jj_scanpos = xsp;
+				break;
+			}
+		}
+		return false;
+	}
+
+	private boolean jj_3R_42(){
 		if (jj_scan_token(EXISTS))
 			return true;
 		if (jj_3R_16())
@@ -5522,37 +5491,39 @@ public class ADQLParser implements ADQLParserConstants {
 	}
 
 	private boolean jj_3R_151(){
-		if (jj_3R_41())
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_51())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_152(){
+		if (jj_scan_token(WHERE))
 			return true;
-		Token xsp;
-		xsp = jj_scanpos;
 		if (jj_3R_163())
-			jj_scanpos = xsp;
+			return true;
 		return false;
 	}
 
-	private boolean jj_3R_15(){
+	private boolean jj_3R_30(){
 		if (jj_3R_14())
 			return true;
 		if (jj_scan_token(DOT))
 			return true;
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_27())
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_21(){
+	private boolean jj_3R_25(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_37()){
+		if (jj_3R_42()){
 			jj_scanpos = xsp;
-			if (jj_3R_38()){
+			if (jj_3R_43()){
 				jj_scanpos = xsp;
-				if (jj_3R_39()){
+				if (jj_3R_44()){
 					jj_scanpos = xsp;
-					if (jj_3R_40())
+					if (jj_3R_45())
 						return true;
 				}
 			}
@@ -5560,150 +5531,116 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3_9(){
-		if (jj_3R_21())
+	private boolean jj_3R_129(){
+		if (jj_scan_token(FROM))
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_150(){
-		if (jj_scan_token(ASTERISK))
+		if (jj_3R_51())
 			return true;
+		Token xsp;
+		while(true){
+			xsp = jj_scanpos;
+			if (jj_3R_151()){
+				jj_scanpos = xsp;
+				break;
+			}
+		}
 		return false;
 	}
 
-	private boolean jj_3R_174(){
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_152())
-			return true;
-		if (jj_scan_token(RIGHT_PAR))
+	private boolean jj_3_12(){
+		if (jj_3R_25())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_173(){
-		if (jj_3R_21())
+	private boolean jj_3R_162(){
+		if (jj_3R_46())
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_165(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_173()){
+		if (jj_3R_184())
 			jj_scanpos = xsp;
-			if (jj_3R_174())
-				return true;
-		}
-		return false;
-	}
-
-	private boolean jj_3R_175(){
-		if (jj_scan_token(NOT))
-			return true;
 		return false;
 	}
 
-	private boolean jj_3_1(){
+	private boolean jj_3R_15(){
 		if (jj_3R_14())
 			return true;
 		if (jj_scan_token(DOT))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_15())
+		if (jj_3R_30())
 			jj_scanpos = xsp;
-		if (jj_scan_token(ASTERISK))
+		return false;
+	}
+
+	private boolean jj_3R_195(){
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_163())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_166(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_scan_token(33)){
-			jj_scanpos = xsp;
-			if (jj_scan_token(34))
-				return true;
-		}
-		xsp = jj_scanpos;
-		if (jj_3R_175())
-			jj_scanpos = xsp;
-		if (jj_3R_165())
+	private boolean jj_3R_194(){
+		if (jj_3R_25())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_138(){
+	private boolean jj_3R_186(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_150()){
+		if (jj_3R_194()){
 			jj_scanpos = xsp;
-			if (jj_3_1()){
-				jj_scanpos = xsp;
-				if (jj_3R_151())
-					return true;
-			}
+			if (jj_3R_195())
+				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_139(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_138())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_114(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_41())
+	private boolean jj_3R_196(){
+		if (jj_scan_token(NOT))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_137(){
-		if (jj_scan_token(TOP))
-			return true;
-		if (jj_scan_token(UNSIGNED_INTEGER))
+	private boolean jj_3R_187(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_scan_token(33)){
+			jj_scanpos = xsp;
+			if (jj_scan_token(34))
+				return true;
+		}
+		xsp = jj_scanpos;
+		if (jj_3R_196())
+			jj_scanpos = xsp;
+		if (jj_3R_186())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_136(){
-		if (jj_scan_token(QUANTIFIER))
+	private boolean jj_3R_161(){
+		if (jj_scan_token(ASTERISK))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_164(){
-		if (jj_scan_token(NOT))
+	private boolean jj_3_1(){
+		if (jj_3R_14())
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_44(){
-		if (jj_scan_token(SELECT))
+		if (jj_scan_token(DOT))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_136())
-			jj_scanpos = xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_137())
+		if (jj_3R_15())
 			jj_scanpos = xsp;
-		if (jj_3R_138())
+		if (jj_scan_token(ASTERISK))
 			return true;
-		while(true){
-			xsp = jj_scanpos;
-			if (jj_3R_139()){
-				jj_scanpos = xsp;
-				break;
-			}
-		}
 		return false;
 	}
 
@@ -5718,7 +5655,7 @@ public class ADQLParser implements ADQLParserConstants {
 	private Token jj_scanpos, jj_lastpos;
 	private int jj_la;
 	private int jj_gen;
-	final private int[] jj_la1 = new int[97];
+	final private int[] jj_la1 = new int[98];
 	static private int[] jj_la1_0;
 	static private int[] jj_la1_1;
 	static private int[] jj_la1_2;
@@ -5731,22 +5668,22 @@ public class ADQLParser implements ADQLParserConstants {
 	}
 
 	private static void jj_la1_init_0(){
-		jj_la1_0 = new int[]{0x41,0x0,0x0,0x0,0x0,0x80000,0x100000,0x20,0x0,0x0,0x400000,0x400,0x304,0x20,0x20,0x20,0x0,0x10,0x10,0x10,0x0,0x0,0x0,0x0,0x400000,0x400000,0x400000,0x0,0x4,0x3d800000,0x1c000000,0x2000000,0x1d000000,0x1d000000,0x1c000000,0x2000000,0x1d000000,0x1d000000,0x20,0xc0000000,0x3d800000,0x0,0x0,0x0,0x300,0x300,0x4,0x0,0x304,0x300,0x300,0xc00,0xc00,0x300,0x300,0x4,0x80,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x3f000,0x0,0x0,0x304,0x3f000,0x0,0x0,0x20,0x4,0x80000,0x704,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x304,0x20,0x20,0x0,0x0,0x20,0x304,};
+		jj_la1_0 = new int[]{0x41,0x0,0x0,0x0,0x0,0x80000,0x100000,0x20,0x0,0x0,0x400000,0x400,0x304,0x20,0x20,0x20,0x0,0x10,0x10,0x10,0x0,0x0,0x0,0x0,0x400000,0x400000,0x400000,0x0,0x4,0x3d800000,0x1c000000,0x2000000,0x1d000000,0x1d000000,0x1c000000,0x2000000,0x1d000000,0x1d000000,0x20,0xc0000000,0x3d800000,0x0,0x0,0x0,0x300,0x300,0x4,0x4,0x0,0x304,0x300,0x300,0xc00,0xc00,0x300,0x300,0x4,0x80,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x3f000,0x0,0x0,0x304,0x3f000,0x0,0x0,0x20,0x4,0x80000,0x704,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x304,0x20,0x20,0x0,0x0,0x20,0x304,};
 	}
 
 	private static void jj_la1_init_1(){
-		jj_la1_1 = new int[]{0x0,0x1,0x400,0x800,0x1000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x6000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000,0x3f00000,0x7c0f8000,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000,0x0,0x80000000,0xf8000,0x3f00000,0x8,0x6,0x6,0x8,0x0,0x8,0x8,0x0,0x108,0x200,0xffff8000,0x0,0x8,0x8,0x0,0x0,0x0,0xffff8000,0x78000,0x0,0xf8000,0xc000000,0x800000,0x800000,0x800000,0x800000,0x7c000000,0x0,0x3f00000,0x7c000000,0x7c0f8000,0x0,0x0,0x0,0x0,0x0,0xffff8000,};
+		jj_la1_1 = new int[]{0x0,0x1,0x400,0x800,0x1000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x6000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000,0x0,0x3f00000,0x7c0f8000,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000,0x0,0x80000000,0x0,0x3f00000,0x8,0x6,0x6,0x8,0x0,0x8,0x8,0x0,0x108,0x200,0xffff8000,0x0,0x8,0x8,0x0,0x0,0x0,0xffff8000,0x78000,0x0,0xf8000,0xc000000,0x800000,0x800000,0x800000,0x800000,0x7c000000,0x0,0x3f00000,0x7c000000,0x7c0f8000,0x0,0x0,0x0,0x0,0x0,0xffff8000,};
 	}
 
 	private static void jj_la1_init_2(){
-		jj_la1_2 = new int[]{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x20000000,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0xffffff,0x20ffffff,0x0,0x0,0xfffe,0xff0000,0x0,0x20ffffff,};
+		jj_la1_2 = new int[]{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0xffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0xffffff,0xffffff,0x0,0x0,0xfffe,0xff0000,0x0,0x20ffffff,};
 	}
 
 	private static void jj_la1_init_3(){
-		jj_la1_3 = new int[]{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0x3b,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x23,0x23,0x0,0x0,0x0,0x3,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x30,0x0,0x0,0x3b,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x3b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x3,0x3,0x3,0x3,0x0,0x0,0x0,0x2,0x3b,0x0,0x0,0x0,0x0,0x0,0x3b,};
+		jj_la1_3 = new int[]{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0x3b,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x23,0x23,0x0,0x0,0x0,0x3,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x30,0x0,0x0,0x3b,0x3,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x3,0x3,0x3,0x3,0x0,0x0,0x0,0x2,0x3b,0x0,0x0,0x0,0x0,0x0,0x3b,};
 	}
 
-	final private JJCalls[] jj_2_rtns = new JJCalls[13];
+	final private JJCalls[] jj_2_rtns = new JJCalls[16];
 	private boolean jj_rescan = false;
 	private int jj_gc = 0;
 
@@ -5766,7 +5703,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5788,7 +5725,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5801,7 +5738,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5814,7 +5751,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5826,7 +5763,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5838,7 +5775,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5864,7 +5801,6 @@ public class ADQLParser implements ADQLParserConstants {
 					}
 				}
 			}
-			trace_token(token, "");
 			return token;
 		}
 		token = oldToken;
@@ -5912,7 +5848,6 @@ public class ADQLParser implements ADQLParserConstants {
 			token = token.next = token_source.getNextToken();
 		jj_ntk = -1;
 		jj_gen++;
-		trace_token(token, " (in getNextToken)");
 		return token;
 	}
 
@@ -5976,7 +5911,7 @@ public class ADQLParser implements ADQLParserConstants {
 			la1tokens[jj_kind] = true;
 			jj_kind = -1;
 		}
-		for(int i = 0; i < 97; i++){
+		for(int i = 0; i < 98; i++){
 			if (jj_la1[i] == jj_gen){
 				for(int j = 0; j < 32; j++){
 					if ((jj_la1_0[i] & (1 << j)) != 0){
@@ -6011,68 +5946,15 @@ public class ADQLParser implements ADQLParserConstants {
 		return new ParseException(token, exptokseq, tokenImage);
 	}
 
-	private int trace_indent = 0;
-	private boolean trace_enabled = true;
-
 	/** Enable tracing. */
-	final public void enable_tracing(){
-		trace_enabled = true;
-	}
+	final public void enable_tracing(){}
 
 	/** Disable tracing. */
-	final public void disable_tracing(){
-		trace_enabled = false;
-	}
-
-	private void trace_call(String s){
-		if (trace_enabled){
-			for(int i = 0; i < trace_indent; i++){
-				System.out.print(" ");
-			}
-			System.out.println("Call:   " + s);
-		}
-		trace_indent = trace_indent + 2;
-	}
-
-	private void trace_return(String s){
-		trace_indent = trace_indent - 2;
-		if (trace_enabled){
-			for(int i = 0; i < trace_indent; i++){
-				System.out.print(" ");
-			}
-			System.out.println("Return: " + s);
-		}
-	}
-
-	private void trace_token(Token t, String where){
-		if (trace_enabled){
-			for(int i = 0; i < trace_indent; i++){
-				System.out.print(" ");
-			}
-			System.out.print("Consumed token: <" + tokenImage[t.kind]);
-			if (t.kind != 0 && !tokenImage[t.kind].equals("\"" + t.image + "\"")){
-				System.out.print(": \"" + t.image + "\"");
-			}
-			System.out.println(" at line " + t.beginLine + " column " + t.beginColumn + ">" + where);
-		}
-	}
-
-	private void trace_scan(Token t1, int t2){
-		if (trace_enabled){
-			for(int i = 0; i < trace_indent; i++){
-				System.out.print(" ");
-			}
-			System.out.print("Visited token: <" + tokenImage[t1.kind]);
-			if (t1.kind != 0 && !tokenImage[t1.kind].equals("\"" + t1.image + "\"")){
-				System.out.print(": \"" + t1.image + "\"");
-			}
-			System.out.println(" at line " + t1.beginLine + " column " + t1.beginColumn + ">; Expected token: <" + tokenImage[t2] + ">");
-		}
-	}
+	final public void disable_tracing(){}
 
 	private void jj_rescan_token(){
 		jj_rescan = true;
-		for(int i = 0; i < 13; i++){
+		for(int i = 0; i < 16; i++){
 			try{
 				JJCalls p = jj_2_rtns[i];
 				do{
@@ -6119,6 +6001,15 @@ public class ADQLParser implements ADQLParserConstants {
 							case 12:
 								jj_3_13();
 								break;
+							case 13:
+								jj_3_14();
+								break;
+							case 14:
+								jj_3_15();
+								break;
+							case 15:
+								jj_3_16();
+								break;
 						}
 					}
 					p = p.next;
diff --git a/src/adql/parser/ADQLParserConstants.java b/src/adql/parser/ADQLParserConstants.java
index cf75565a791da7dda1c52e7a91f37ac6498c5a89..e69af55581bc59a5c723daf82e5f639616ced299 100644
--- a/src/adql/parser/ADQLParserConstants.java
+++ b/src/adql/parser/ADQLParserConstants.java
@@ -1,213 +1,318 @@
 /* Generated By:JavaCC: Do not edit this line. ADQLParserConstants.java */
 package adql.parser;
 
+
 /**
  * Token literal values and constants.
  * Generated by org.javacc.parser.OtherFilesGen#start()
  */
 public interface ADQLParserConstants {
 
-	/** End of File. */
-	int EOF = 0;
-	/** RegularExpression Id. */
-	int LEFT_PAR = 2;
-	/** RegularExpression Id. */
-	int RIGHT_PAR = 3;
-	/** RegularExpression Id. */
-	int DOT = 4;
-	/** RegularExpression Id. */
-	int COMMA = 5;
-	/** RegularExpression Id. */
-	int EOQ = 6;
-	/** RegularExpression Id. */
-	int CONCAT = 7;
-	/** RegularExpression Id. */
-	int PLUS = 8;
-	/** RegularExpression Id. */
-	int MINUS = 9;
-	/** RegularExpression Id. */
-	int ASTERISK = 10;
-	/** RegularExpression Id. */
-	int DIVIDE = 11;
-	/** RegularExpression Id. */
-	int EQUAL = 12;
-	/** RegularExpression Id. */
-	int NOT_EQUAL = 13;
-	/** RegularExpression Id. */
-	int LESS_THAN = 14;
-	/** RegularExpression Id. */
-	int LESS_EQUAL_THAN = 15;
-	/** RegularExpression Id. */
-	int GREATER_THAN = 16;
-	/** RegularExpression Id. */
-	int GREATER_EQUAL_THAN = 17;
-	/** RegularExpression Id. */
-	int SELECT = 18;
-	/** RegularExpression Id. */
-	int QUANTIFIER = 19;
-	/** RegularExpression Id. */
-	int TOP = 20;
-	/** RegularExpression Id. */
-	int FROM = 21;
-	/** RegularExpression Id. */
-	int AS = 22;
-	/** RegularExpression Id. */
-	int NATURAL = 23;
-	/** RegularExpression Id. */
-	int INNER = 24;
-	/** RegularExpression Id. */
-	int OUTER = 25;
-	/** RegularExpression Id. */
-	int RIGHT = 26;
-	/** RegularExpression Id. */
-	int LEFT = 27;
-	/** RegularExpression Id. */
-	int FULL = 28;
-	/** RegularExpression Id. */
-	int JOIN = 29;
-	/** RegularExpression Id. */
-	int ON = 30;
-	/** RegularExpression Id. */
-	int USING = 31;
-	/** RegularExpression Id. */
-	int WHERE = 32;
-	/** RegularExpression Id. */
-	int AND = 33;
-	/** RegularExpression Id. */
-	int OR = 34;
-	/** RegularExpression Id. */
-	int NOT = 35;
-	/** RegularExpression Id. */
-	int IS = 36;
-	/** RegularExpression Id. */
-	int NULL = 37;
-	/** RegularExpression Id. */
-	int BETWEEN = 38;
-	/** RegularExpression Id. */
-	int LIKE = 39;
-	/** RegularExpression Id. */
-	int IN = 40;
-	/** RegularExpression Id. */
-	int EXISTS = 41;
-	/** RegularExpression Id. */
-	int GROUP_BY = 42;
-	/** RegularExpression Id. */
-	int HAVING = 43;
-	/** RegularExpression Id. */
-	int ORDER_BY = 44;
-	/** RegularExpression Id. */
-	int ASC = 45;
-	/** RegularExpression Id. */
-	int DESC = 46;
-	/** RegularExpression Id. */
-	int AVG = 47;
-	/** RegularExpression Id. */
-	int MAX = 48;
-	/** RegularExpression Id. */
-	int MIN = 49;
-	/** RegularExpression Id. */
-	int SUM = 50;
-	/** RegularExpression Id. */
-	int COUNT = 51;
-	/** RegularExpression Id. */
-	int BOX = 52;
-	/** RegularExpression Id. */
-	int CENTROID = 53;
-	/** RegularExpression Id. */
-	int CIRCLE = 54;
-	/** RegularExpression Id. */
-	int POINT = 55;
-	/** RegularExpression Id. */
-	int POLYGON = 56;
-	/** RegularExpression Id. */
-	int REGION = 57;
-	/** RegularExpression Id. */
-	int CONTAINS = 58;
-	/** RegularExpression Id. */
-	int INTERSECTS = 59;
-	/** RegularExpression Id. */
-	int AREA = 60;
-	/** RegularExpression Id. */
-	int COORD1 = 61;
-	/** RegularExpression Id. */
-	int COORD2 = 62;
-	/** RegularExpression Id. */
-	int COORDSYS = 63;
-	/** RegularExpression Id. */
-	int DISTANCE = 64;
-	/** RegularExpression Id. */
-	int ABS = 65;
-	/** RegularExpression Id. */
-	int CEILING = 66;
-	/** RegularExpression Id. */
-	int DEGREES = 67;
-	/** RegularExpression Id. */
-	int EXP = 68;
-	/** RegularExpression Id. */
-	int FLOOR = 69;
-	/** RegularExpression Id. */
-	int LOG = 70;
-	/** RegularExpression Id. */
-	int LOG10 = 71;
-	/** RegularExpression Id. */
-	int MOD = 72;
-	/** RegularExpression Id. */
-	int PI = 73;
-	/** RegularExpression Id. */
-	int POWER = 74;
-	/** RegularExpression Id. */
-	int RADIANS = 75;
-	/** RegularExpression Id. */
-	int RAND = 76;
-	/** RegularExpression Id. */
-	int ROUND = 77;
-	/** RegularExpression Id. */
-	int SQRT = 78;
-	/** RegularExpression Id. */
-	int TRUNCATE = 79;
-	/** RegularExpression Id. */
-	int ACOS = 80;
-	/** RegularExpression Id. */
-	int ASIN = 81;
-	/** RegularExpression Id. */
-	int ATAN = 82;
-	/** RegularExpression Id. */
-	int ATAN2 = 83;
-	/** RegularExpression Id. */
-	int COS = 84;
-	/** RegularExpression Id. */
-	int COT = 85;
-	/** RegularExpression Id. */
-	int SIN = 86;
-	/** RegularExpression Id. */
-	int TAN = 87;
-	/** RegularExpression Id. */
-	int STRING_LITERAL = 93;
-	/** RegularExpression Id. */
-	int DELIMITED_IDENTIFIER = 96;
-	/** RegularExpression Id. */
-	int REGULAR_IDENTIFIER = 97;
-	/** RegularExpression Id. */
-	int Letter = 98;
-	/** RegularExpression Id. */
-	int SCIENTIFIC_NUMBER = 99;
-	/** RegularExpression Id. */
-	int UNSIGNED_FLOAT = 100;
-	/** RegularExpression Id. */
-	int UNSIGNED_INTEGER = 101;
-	/** RegularExpression Id. */
-	int DIGIT = 102;
+  /** End of File. */
+  int EOF = 0;
+  /** RegularExpression Id. */
+  int LEFT_PAR = 2;
+  /** RegularExpression Id. */
+  int RIGHT_PAR = 3;
+  /** RegularExpression Id. */
+  int DOT = 4;
+  /** RegularExpression Id. */
+  int COMMA = 5;
+  /** RegularExpression Id. */
+  int EOQ = 6;
+  /** RegularExpression Id. */
+  int CONCAT = 7;
+  /** RegularExpression Id. */
+  int PLUS = 8;
+  /** RegularExpression Id. */
+  int MINUS = 9;
+  /** RegularExpression Id. */
+  int ASTERISK = 10;
+  /** RegularExpression Id. */
+  int DIVIDE = 11;
+  /** RegularExpression Id. */
+  int EQUAL = 12;
+  /** RegularExpression Id. */
+  int NOT_EQUAL = 13;
+  /** RegularExpression Id. */
+  int LESS_THAN = 14;
+  /** RegularExpression Id. */
+  int LESS_EQUAL_THAN = 15;
+  /** RegularExpression Id. */
+  int GREATER_THAN = 16;
+  /** RegularExpression Id. */
+  int GREATER_EQUAL_THAN = 17;
+  /** RegularExpression Id. */
+  int SELECT = 18;
+  /** RegularExpression Id. */
+  int QUANTIFIER = 19;
+  /** RegularExpression Id. */
+  int TOP = 20;
+  /** RegularExpression Id. */
+  int FROM = 21;
+  /** RegularExpression Id. */
+  int AS = 22;
+  /** RegularExpression Id. */
+  int NATURAL = 23;
+  /** RegularExpression Id. */
+  int INNER = 24;
+  /** RegularExpression Id. */
+  int OUTER = 25;
+  /** RegularExpression Id. */
+  int RIGHT = 26;
+  /** RegularExpression Id. */
+  int LEFT = 27;
+  /** RegularExpression Id. */
+  int FULL = 28;
+  /** RegularExpression Id. */
+  int JOIN = 29;
+  /** RegularExpression Id. */
+  int ON = 30;
+  /** RegularExpression Id. */
+  int USING = 31;
+  /** RegularExpression Id. */
+  int WHERE = 32;
+  /** RegularExpression Id. */
+  int AND = 33;
+  /** RegularExpression Id. */
+  int OR = 34;
+  /** RegularExpression Id. */
+  int NOT = 35;
+  /** RegularExpression Id. */
+  int IS = 36;
+  /** RegularExpression Id. */
+  int NULL = 37;
+  /** RegularExpression Id. */
+  int BETWEEN = 38;
+  /** RegularExpression Id. */
+  int LIKE = 39;
+  /** RegularExpression Id. */
+  int IN = 40;
+  /** RegularExpression Id. */
+  int EXISTS = 41;
+  /** RegularExpression Id. */
+  int GROUP_BY = 42;
+  /** RegularExpression Id. */
+  int HAVING = 43;
+  /** RegularExpression Id. */
+  int ORDER_BY = 44;
+  /** RegularExpression Id. */
+  int ASC = 45;
+  /** RegularExpression Id. */
+  int DESC = 46;
+  /** RegularExpression Id. */
+  int AVG = 47;
+  /** RegularExpression Id. */
+  int MAX = 48;
+  /** RegularExpression Id. */
+  int MIN = 49;
+  /** RegularExpression Id. */
+  int SUM = 50;
+  /** RegularExpression Id. */
+  int COUNT = 51;
+  /** RegularExpression Id. */
+  int BOX = 52;
+  /** RegularExpression Id. */
+  int CENTROID = 53;
+  /** RegularExpression Id. */
+  int CIRCLE = 54;
+  /** RegularExpression Id. */
+  int POINT = 55;
+  /** RegularExpression Id. */
+  int POLYGON = 56;
+  /** RegularExpression Id. */
+  int REGION = 57;
+  /** RegularExpression Id. */
+  int CONTAINS = 58;
+  /** RegularExpression Id. */
+  int INTERSECTS = 59;
+  /** RegularExpression Id. */
+  int AREA = 60;
+  /** RegularExpression Id. */
+  int COORD1 = 61;
+  /** RegularExpression Id. */
+  int COORD2 = 62;
+  /** RegularExpression Id. */
+  int COORDSYS = 63;
+  /** RegularExpression Id. */
+  int DISTANCE = 64;
+  /** RegularExpression Id. */
+  int ABS = 65;
+  /** RegularExpression Id. */
+  int CEILING = 66;
+  /** RegularExpression Id. */
+  int DEGREES = 67;
+  /** RegularExpression Id. */
+  int EXP = 68;
+  /** RegularExpression Id. */
+  int FLOOR = 69;
+  /** RegularExpression Id. */
+  int LOG = 70;
+  /** RegularExpression Id. */
+  int LOG10 = 71;
+  /** RegularExpression Id. */
+  int MOD = 72;
+  /** RegularExpression Id. */
+  int PI = 73;
+  /** RegularExpression Id. */
+  int POWER = 74;
+  /** RegularExpression Id. */
+  int RADIANS = 75;
+  /** RegularExpression Id. */
+  int RAND = 76;
+  /** RegularExpression Id. */
+  int ROUND = 77;
+  /** RegularExpression Id. */
+  int SQRT = 78;
+  /** RegularExpression Id. */
+  int TRUNCATE = 79;
+  /** RegularExpression Id. */
+  int ACOS = 80;
+  /** RegularExpression Id. */
+  int ASIN = 81;
+  /** RegularExpression Id. */
+  int ATAN = 82;
+  /** RegularExpression Id. */
+  int ATAN2 = 83;
+  /** RegularExpression Id. */
+  int COS = 84;
+  /** RegularExpression Id. */
+  int COT = 85;
+  /** RegularExpression Id. */
+  int SIN = 86;
+  /** RegularExpression Id. */
+  int TAN = 87;
+  /** RegularExpression Id. */
+  int STRING_LITERAL = 93;
+  /** RegularExpression Id. */
+  int DELIMITED_IDENTIFIER = 96;
+  /** RegularExpression Id. */
+  int REGULAR_IDENTIFIER = 97;
+  /** RegularExpression Id. */
+  int Letter = 98;
+  /** RegularExpression Id. */
+  int SCIENTIFIC_NUMBER = 99;
+  /** RegularExpression Id. */
+  int UNSIGNED_FLOAT = 100;
+  /** RegularExpression Id. */
+  int UNSIGNED_INTEGER = 101;
+  /** RegularExpression Id. */
+  int DIGIT = 102;
 
-	/** Lexical state. */
-	int DEFAULT = 0;
-	/** Lexical state. */
-	int WithinComment = 1;
-	/** Lexical state. */
-	int WithinString = 2;
-	/** Lexical state. */
-	int WithinDelimitedId = 3;
+  /** Lexical state. */
+  int DEFAULT = 0;
+  /** Lexical state. */
+  int WithinComment = 1;
+  /** Lexical state. */
+  int WithinString = 2;
+  /** Lexical state. */
+  int WithinDelimitedId = 3;
 
-	/** Literal token values. */
-	String[] tokenImage = {"<EOF>","<token of kind 1>","\"(\"","\")\"","\".\"","\",\"","\";\"","\"||\"","\"+\"","\"-\"","\"*\"","\"/\"","\"=\"","<NOT_EQUAL>","\"<\"","\"<=\"","\">\"","\">=\"","\"SELECT\"","<QUANTIFIER>","\"TOP\"","\"FROM\"","\"AS\"","\"NATURAL\"","\"INNER\"","\"OUTER\"","\"RIGHT\"","\"LEFT\"","\"FULL\"","\"JOIN\"","\"ON\"","\"USING\"","\"WHERE\"","\"AND\"","\"OR\"","\"NOT\"","\"IS\"","\"NULL\"","\"BETWEEN\"","\"LIKE\"","\"IN\"","\"EXISTS\"","\"GROUP BY\"","\"HAVING\"","\"ORDER BY\"","\"ASC\"","\"DESC\"","\"AVG\"","\"MAX\"","\"MIN\"","\"SUM\"","\"COUNT\"","\"BOX\"","\"CENTROID\"","\"CIRCLE\"","\"POINT\"","\"POLYGON\"","\"REGION\"","\"CONTAINS\"","\"INTERSECTS\"","\"AREA\"","\"COORD1\"","\"COORD2\"","\"COORDSYS\"","\"DISTANCE\"","\"ABS\"","\"CEILING\"","\"DEGREES\"","\"EXP\"","\"FLOOR\"","\"LOG\"","\"LOG10\"","\"MOD\"","\"PI\"","\"POWER\"","\"RADIANS\"","\"RAND\"","\"ROUND\"","\"SQRT\"","\"TRUNCATE\"","\"ACOS\"","\"ASIN\"","\"ATAN\"","\"ATAN2\"","\"COS\"","\"COT\"","\"SIN\"","\"TAN\"","<token of kind 88>","<token of kind 89>","<token of kind 90>","\"\\\'\"","<token of kind 92>","\"\\\'\"","\"\\\"\"","<token of kind 95>","\"\\\"\"","<REGULAR_IDENTIFIER>","<Letter>","<SCIENTIFIC_NUMBER>","<UNSIGNED_FLOAT>","<UNSIGNED_INTEGER>","<DIGIT>",};
+  /** Literal token values. */
+  String[] tokenImage = {
+    "<EOF>",
+    "<token of kind 1>",
+    "\"(\"",
+    "\")\"",
+    "\".\"",
+    "\",\"",
+    "\";\"",
+    "\"||\"",
+    "\"+\"",
+    "\"-\"",
+    "\"*\"",
+    "\"/\"",
+    "\"=\"",
+    "<NOT_EQUAL>",
+    "\"<\"",
+    "\"<=\"",
+    "\">\"",
+    "\">=\"",
+    "\"SELECT\"",
+    "<QUANTIFIER>",
+    "\"TOP\"",
+    "\"FROM\"",
+    "\"AS\"",
+    "\"NATURAL\"",
+    "\"INNER\"",
+    "\"OUTER\"",
+    "\"RIGHT\"",
+    "\"LEFT\"",
+    "\"FULL\"",
+    "\"JOIN\"",
+    "\"ON\"",
+    "\"USING\"",
+    "\"WHERE\"",
+    "\"AND\"",
+    "\"OR\"",
+    "\"NOT\"",
+    "\"IS\"",
+    "\"NULL\"",
+    "\"BETWEEN\"",
+    "\"LIKE\"",
+    "\"IN\"",
+    "\"EXISTS\"",
+    "\"GROUP BY\"",
+    "\"HAVING\"",
+    "\"ORDER BY\"",
+    "\"ASC\"",
+    "\"DESC\"",
+    "\"AVG\"",
+    "\"MAX\"",
+    "\"MIN\"",
+    "\"SUM\"",
+    "\"COUNT\"",
+    "\"BOX\"",
+    "\"CENTROID\"",
+    "\"CIRCLE\"",
+    "\"POINT\"",
+    "\"POLYGON\"",
+    "\"REGION\"",
+    "\"CONTAINS\"",
+    "\"INTERSECTS\"",
+    "\"AREA\"",
+    "\"COORD1\"",
+    "\"COORD2\"",
+    "\"COORDSYS\"",
+    "\"DISTANCE\"",
+    "\"ABS\"",
+    "\"CEILING\"",
+    "\"DEGREES\"",
+    "\"EXP\"",
+    "\"FLOOR\"",
+    "\"LOG\"",
+    "\"LOG10\"",
+    "\"MOD\"",
+    "\"PI\"",
+    "\"POWER\"",
+    "\"RADIANS\"",
+    "\"RAND\"",
+    "\"ROUND\"",
+    "\"SQRT\"",
+    "\"TRUNCATE\"",
+    "\"ACOS\"",
+    "\"ASIN\"",
+    "\"ATAN\"",
+    "\"ATAN2\"",
+    "\"COS\"",
+    "\"COT\"",
+    "\"SIN\"",
+    "\"TAN\"",
+    "<token of kind 88>",
+    "<token of kind 89>",
+    "<token of kind 90>",
+    "\"\\\'\"",
+    "<token of kind 92>",
+    "\"\\\'\"",
+    "\"\\\"\"",
+    "<token of kind 95>",
+    "\"\\\"\"",
+    "<REGULAR_IDENTIFIER>",
+    "<Letter>",
+    "<SCIENTIFIC_NUMBER>",
+    "<UNSIGNED_FLOAT>",
+    "<UNSIGNED_INTEGER>",
+    "<DIGIT>",
+  };
 
 }
diff --git a/src/adql/parser/ADQLParserTokenManager.java b/src/adql/parser/ADQLParserTokenManager.java
index 4e4d9ccacac9e6978b879eca02d022d91348d0e9..15441617d4a598b379a86b537a7081e68d30579b 100644
--- a/src/adql/parser/ADQLParserTokenManager.java
+++ b/src/adql/parser/ADQLParserTokenManager.java
@@ -1,6 +1,5 @@
 /* Generated By:JavaCC: Do not edit this line. ADQLParserTokenManager.java */
 package adql.parser;
-
 import java.util.Stack;
 import java.util.Vector;
 import java.util.ArrayList;
@@ -21,1716 +20,1842 @@ import adql.translator.PostgreSQLTranslator;
 import adql.translator.TranslationException;
 
 /** Token Manager. */
-public class ADQLParserTokenManager implements ADQLParserConstants {
-
-	/** Debug output. */
-	public java.io.PrintStream debugStream = System.out;
-
-	/** Set debug output. */
-	public void setDebugStream(java.io.PrintStream ds){
-		debugStream = ds;
-	}
-
-	private final int jjStopStringLiteralDfa_3(int pos, long active0, long active1){
-		switch(pos){
-			default:
-				return -1;
-		}
-	}
-
-	private final int jjStartNfa_3(int pos, long active0, long active1){
-		return jjMoveNfa_3(jjStopStringLiteralDfa_3(pos, active0, active1), pos + 1);
-	}
-
-	private int jjStopAtPos(int pos, int kind){
-		jjmatchedKind = kind;
-		jjmatchedPos = pos;
-		return pos + 1;
-	}
-
-	private int jjMoveStringLiteralDfa0_3(){
-		switch(curChar){
-			case 34:
-				return jjStartNfaWithStates_3(0, 96, 1);
-			default:
-				return jjMoveNfa_3(0, 0);
-		}
-	}
-
-	private int jjStartNfaWithStates_3(int pos, int kind, int state){
-		jjmatchedKind = kind;
-		jjmatchedPos = pos;
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			return pos + 1;
-		}
-		return jjMoveNfa_3(state, pos + 1);
-	}
-
-	static final long[] jjbitVec0 = {0x0L,0x0L,0xffffffffffffffffL,0xffffffffffffffffL};
-
-	private int jjMoveNfa_3(int startState, int curPos){
-		int startsAt = 0;
-		jjnewStateCnt = 3;
-		int i = 1;
-		jjstateSet[0] = startState;
-		int kind = 0x7fffffff;
-		for(;;){
-			if (++jjround == 0x7fffffff)
-				ReInitRounds();
-			if (curChar < 64){
-				long l = 1L << curChar;
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							if ((0xfffffffbffffffffL & l) != 0L){
-								if (kind > 95)
-									kind = 95;
-							}else if (curChar == 34)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						case 1:
-							if (curChar == 34 && kind > 95)
-								kind = 95;
-							break;
-						case 2:
-							if (curChar == 34)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else if (curChar < 128){
-				long l = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							kind = 95;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else{
-				int i2 = (curChar & 0xff) >> 6;
-				long l2 = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							if ((jjbitVec0[i2] & l2) != 0L && kind > 95)
-								kind = 95;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}
-			if (kind != 0x7fffffff){
-				jjmatchedKind = kind;
-				jjmatchedPos = curPos;
-				kind = 0x7fffffff;
-			}
-			++curPos;
-			if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
-				return curPos;
-			try{
-				curChar = input_stream.readChar();
-			}catch(java.io.IOException e){
-				return curPos;
-			}
-		}
-	}
-
-	private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1){
-		switch(pos){
-			case 0:
-				if ((active0 & 0xefff1ffdffb40000L) != 0L || (active1 & 0xf0fff4L) != 0L){
-					jjmatchedKind = 97;
-					return 37;
-				}
-				if ((active0 & 0x1000a00200400000L) != 0L || (active1 & 0xf0002L) != 0L){
-					jjmatchedKind = 97;
-					return 16;
-				}
-				if ((active0 & 0x10L) != 0L)
-					return 38;
-				if ((active0 & 0x400000000000L) != 0L || (active1 & 0x9L) != 0L){
-					jjmatchedKind = 97;
-					return 13;
-				}
-				if ((active0 & 0xc000L) != 0L)
-					return 3;
-				if ((active0 & 0x200L) != 0L)
-					return 19;
-				return -1;
-			case 1:
-				if ((active0 & 0xf7ffceebbeb40000L) != 0L || (active1 & 0xfdfdfeL) != 0L){
-					if (jjmatchedPos != 1){
-						jjmatchedKind = 97;
-						jjmatchedPos = 1;
-					}
-					return 37;
-				}
-				if ((active0 & 0x800311441400000L) != 0L || (active1 & 0x20200L) != 0L)
-					return 37;
-				if ((active1 & 0x1L) != 0L){
-					if (jjmatchedPos != 1){
-						jjmatchedKind = 97;
-						jjmatchedPos = 1;
-					}
-					return 12;
-				}
-				return -1;
-			case 2:
-				if ((active1 & 0x1L) != 0L){
-					if (jjmatchedPos != 2){
-						jjmatchedKind = 97;
-						jjmatchedPos = 2;
-					}
-					return 11;
-				}
-				if ((active0 & 0x17a00a00100000L) != 0L || (active1 & 0xf001d2L) != 0L)
-					return 37;
-				if ((active0 & 0xffe85ee1bfa40000L) != 0L || (active1 & 0xffc2cL) != 0L){
-					if (jjmatchedPos != 2){
-						jjmatchedKind = 97;
-						jjmatchedPos = 2;
-					}
-					return 37;
-				}
-				return -1;
-			case 3:
-				if ((active0 & 0xefe81e4187840000L) != 0L || (active1 & 0xac2cL) != 0L){
-					if (jjmatchedPos != 3){
-						jjmatchedKind = 97;
-						jjmatchedPos = 3;
-					}
-					return 37;
-				}
-				if ((active0 & 0x100040a038200000L) != 0L || (active1 & 0xf5000L) != 0L)
-					return 37;
-				if ((active1 & 0x80L) != 0L){
-					if (jjmatchedPos != 3){
-						jjmatchedKind = 97;
-						jjmatchedPos = 3;
-					}
-					return 21;
-				}
-				if ((active1 & 0x1L) != 0L){
-					if (jjmatchedPos != 3){
-						jjmatchedKind = 97;
-						jjmatchedPos = 3;
-					}
-					return 10;
-				}
-				return -1;
-			case 4:
-				if ((active0 & 0xef601e4000840000L) != 0L || (active1 & 0x880dL) != 0L){
-					jjmatchedKind = 97;
-					jjmatchedPos = 4;
-					return 37;
-				}
-				if ((active0 & 0x88000187000000L) != 0L || (active1 & 0x2420L) != 0L)
-					return 37;
-				if ((active1 & 0x80080L) != 0L)
-					return 21;
-				return -1;
-			case 5:
-				if ((active0 & 0x2400a0000040000L) != 0L)
-					return 37;
-				if ((active0 & 0x6000000000000000L) != 0L)
-					return 21;
-				if ((active0 & 0x8d20004000800000L) != 0L || (active1 & 0x880dL) != 0L){
-					jjmatchedKind = 97;
-					jjmatchedPos = 5;
-					return 37;
-				}
-				if ((active0 & 0x140000000000L) != 0L){
-					if (jjmatchedPos < 4){
-						jjmatchedKind = 97;
-						jjmatchedPos = 4;
-					}
-					return -1;
-				}
-				return -1;
-			case 6:
-				if ((active0 & 0x8c20000000000000L) != 0L || (active1 & 0x8001L) != 0L){
-					jjmatchedKind = 97;
-					jjmatchedPos = 6;
-					return 37;
-				}
-				if ((active0 & 0x100004000800000L) != 0L || (active1 & 0x80cL) != 0L)
-					return 37;
-				if ((active0 & 0x140000000000L) != 0L){
-					if (jjmatchedPos < 4){
-						jjmatchedKind = 97;
-						jjmatchedPos = 4;
-					}
-					return -1;
-				}
-				return -1;
-			case 7:
-				if ((active0 & 0x800000000000000L) != 0L){
-					jjmatchedKind = 97;
-					jjmatchedPos = 7;
-					return 37;
-				}
-				if ((active0 & 0x8420000000000000L) != 0L || (active1 & 0x8001L) != 0L)
-					return 37;
-				if ((active0 & 0x140000000000L) != 0L){
-					if (jjmatchedPos < 4){
-						jjmatchedKind = 97;
-						jjmatchedPos = 4;
-					}
-					return -1;
-				}
-				return -1;
-			case 8:
-				if ((active0 & 0x800000000000000L) != 0L){
-					jjmatchedKind = 97;
-					jjmatchedPos = 8;
-					return 37;
-				}
-				return -1;
-			default:
-				return -1;
-		}
-	}
-
-	private final int jjStartNfa_0(int pos, long active0, long active1){
-		return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0, active1), pos + 1);
-	}
-
-	private int jjMoveStringLiteralDfa0_0(){
-		switch(curChar){
-			case 34:
-				return jjStopAtPos(0, 94);
-			case 39:
-				return jjStopAtPos(0, 91);
-			case 40:
-				return jjStopAtPos(0, 2);
-			case 41:
-				return jjStopAtPos(0, 3);
-			case 42:
-				return jjStopAtPos(0, 10);
-			case 43:
-				return jjStopAtPos(0, 8);
-			case 44:
-				return jjStopAtPos(0, 5);
-			case 45:
-				return jjStartNfaWithStates_0(0, 9, 19);
-			case 46:
-				return jjStartNfaWithStates_0(0, 4, 38);
-			case 47:
-				return jjStopAtPos(0, 11);
-			case 59:
-				return jjStopAtPos(0, 6);
-			case 60:
-				jjmatchedKind = 14;
-				return jjMoveStringLiteralDfa1_0(0x8000L, 0x0L);
-			case 61:
-				return jjStopAtPos(0, 12);
-			case 62:
-				jjmatchedKind = 16;
-				return jjMoveStringLiteralDfa1_0(0x20000L, 0x0L);
-			case 65:
-			case 97:
-				return jjMoveStringLiteralDfa1_0(0x1000a00200400000L, 0xf0002L);
-			case 66:
-			case 98:
-				return jjMoveStringLiteralDfa1_0(0x10004000000000L, 0x0L);
-			case 67:
-			case 99:
-				return jjMoveStringLiteralDfa1_0(0xe468000000000000L, 0x300004L);
-			case 68:
-			case 100:
-				return jjMoveStringLiteralDfa1_0(0x400000000000L, 0x9L);
-			case 69:
-			case 101:
-				return jjMoveStringLiteralDfa1_0(0x20000000000L, 0x10L);
-			case 70:
-			case 102:
-				return jjMoveStringLiteralDfa1_0(0x10200000L, 0x20L);
-			case 71:
-			case 103:
-				return jjMoveStringLiteralDfa1_0(0x40000000000L, 0x0L);
-			case 72:
-			case 104:
-				return jjMoveStringLiteralDfa1_0(0x80000000000L, 0x0L);
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa1_0(0x800011001000000L, 0x0L);
-			case 74:
-			case 106:
-				return jjMoveStringLiteralDfa1_0(0x20000000L, 0x0L);
-			case 76:
-			case 108:
-				return jjMoveStringLiteralDfa1_0(0x8008000000L, 0xc0L);
-			case 77:
-			case 109:
-				return jjMoveStringLiteralDfa1_0(0x3000000000000L, 0x100L);
-			case 78:
-			case 110:
-				return jjMoveStringLiteralDfa1_0(0x2800800000L, 0x0L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa1_0(0x100442000000L, 0x0L);
-			case 80:
-			case 112:
-				return jjMoveStringLiteralDfa1_0(0x180000000000000L, 0x600L);
-			case 82:
-			case 114:
-				return jjMoveStringLiteralDfa1_0(0x200000004000000L, 0x3800L);
-			case 83:
-			case 115:
-				return jjMoveStringLiteralDfa1_0(0x4000000040000L, 0x404000L);
-			case 84:
-			case 116:
-				return jjMoveStringLiteralDfa1_0(0x100000L, 0x808000L);
-			case 85:
-			case 117:
-				return jjMoveStringLiteralDfa1_0(0x80000000L, 0x0L);
-			case 87:
-			case 119:
-				return jjMoveStringLiteralDfa1_0(0x100000000L, 0x0L);
-			case 124:
-				return jjMoveStringLiteralDfa1_0(0x80L, 0x0L);
-			default:
-				return jjMoveNfa_0(0, 0);
-		}
-	}
-
-	private int jjMoveStringLiteralDfa1_0(long active0, long active1){
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(0, active0, active1);
-			return 1;
-		}
-		switch(curChar){
-			case 61:
-				if ((active0 & 0x8000L) != 0L)
-					return jjStopAtPos(1, 15);
-				else if ((active0 & 0x20000L) != 0L)
-					return jjStopAtPos(1, 17);
-				break;
-			case 65:
-			case 97:
-				return jjMoveStringLiteralDfa2_0(active0, 0x1080000800000L, active1, 0x801800L);
-			case 66:
-			case 98:
-				return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x2L);
-			case 67:
-			case 99:
-				return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x10000L);
-			case 69:
-			case 101:
-				return jjMoveStringLiteralDfa2_0(active0, 0x220404008040000L, active1, 0xcL);
-			case 72:
-			case 104:
-				return jjMoveStringLiteralDfa2_0(active0, 0x100000000L, active1, 0L);
-			case 73:
-			case 105:
-				if ((active1 & 0x200L) != 0L)
-					return jjStartNfaWithStates_0(1, 73, 37);
-				return jjMoveStringLiteralDfa2_0(active0, 0x42008004000000L, active1, 0x400001L);
-			case 76:
-			case 108:
-				return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x20L);
-			case 78:
-			case 110:
-				if ((active0 & 0x40000000L) != 0L)
-					return jjStartNfaWithStates_0(1, 30, 37);
-				else if ((active0 & 0x10000000000L) != 0L){
-					jjmatchedKind = 40;
-					jjmatchedPos = 1;
-				}
-				return jjMoveStringLiteralDfa2_0(active0, 0x800000201000000L, active1, 0L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa2_0(active0, 0xe598000820100000L, active1, 0x3025c0L);
-			case 81:
-			case 113:
-				return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x4000L);
-			case 82:
-			case 114:
-				if ((active0 & 0x400000000L) != 0L){
-					jjmatchedKind = 34;
-					jjmatchedPos = 1;
-				}
-				return jjMoveStringLiteralDfa2_0(active0, 0x1000140000200000L, active1, 0x8000L);
-			case 83:
-			case 115:
-				if ((active0 & 0x400000L) != 0L){
-					jjmatchedKind = 22;
-					jjmatchedPos = 1;
-				}else if ((active0 & 0x1000000000L) != 0L)
-					return jjStartNfaWithStates_0(1, 36, 37);
-				return jjMoveStringLiteralDfa2_0(active0, 0x200080000000L, active1, 0x20000L);
-			case 84:
-			case 116:
-				return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0xc0000L);
-			case 85:
-			case 117:
-				return jjMoveStringLiteralDfa2_0(active0, 0x4002012000000L, active1, 0L);
-			case 86:
-			case 118:
-				return jjMoveStringLiteralDfa2_0(active0, 0x800000000000L, active1, 0L);
-			case 88:
-			case 120:
-				return jjMoveStringLiteralDfa2_0(active0, 0x20000000000L, active1, 0x10L);
-			case 124:
-				if ((active0 & 0x80L) != 0L)
-					return jjStopAtPos(1, 7);
-				break;
-			default:
-				break;
-		}
-		return jjStartNfa_0(0, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa2_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(0, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(1, active0, active1);
-			return 2;
-		}
-		switch(curChar){
-			case 65:
-			case 97:
-				return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0xc0000L);
-			case 67:
-			case 99:
-				if ((active0 & 0x200000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 45, 37);
-				break;
-			case 68:
-			case 100:
-				if ((active0 & 0x200000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 33, 37);
-				else if ((active1 & 0x100L) != 0L)
-					return jjStartNfaWithStates_0(2, 72, 37);
-				return jjMoveStringLiteralDfa3_0(active0, 0x100000000000L, active1, 0x800L);
-			case 69:
-			case 101:
-				return jjMoveStringLiteralDfa3_0(active0, 0x1000000100000000L, active1, 0L);
-			case 70:
-			case 102:
-				return jjMoveStringLiteralDfa3_0(active0, 0x8000000L, active1, 0L);
-			case 71:
-			case 103:
-				if ((active0 & 0x800000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 47, 37);
-				else if ((active1 & 0x40L) != 0L){
-					jjmatchedKind = 70;
-					jjmatchedPos = 2;
-				}
-				return jjMoveStringLiteralDfa3_0(active0, 0x200000004000000L, active1, 0x88L);
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa3_0(active0, 0x800200a0000000L, active1, 0x20004L);
-			case 75:
-			case 107:
-				return jjMoveStringLiteralDfa3_0(active0, 0x8000000000L, active1, 0L);
-			case 76:
-			case 108:
-				return jjMoveStringLiteralDfa3_0(active0, 0x100002010040000L, active1, 0L);
-			case 77:
-			case 109:
-				if ((active0 & 0x4000000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 50, 37);
-				break;
-			case 78:
-			case 110:
-				if ((active0 & 0x2000000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 49, 37);
-				else if ((active1 & 0x400000L) != 0L)
-					return jjStartNfaWithStates_0(2, 86, 37);
-				else if ((active1 & 0x800000L) != 0L)
-					return jjStartNfaWithStates_0(2, 87, 37);
-				return jjMoveStringLiteralDfa3_0(active0, 0x420000001000000L, active1, 0x1000L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa3_0(active0, 0xe000040000200000L, active1, 0x10020L);
-			case 80:
-			case 112:
-				if ((active0 & 0x100000L) != 0L)
-					return jjStartNfaWithStates_0(2, 20, 37);
-				else if ((active1 & 0x10L) != 0L)
-					return jjStartNfaWithStates_0(2, 68, 37);
-				break;
-			case 82:
-			case 114:
-				return jjMoveStringLiteralDfa3_0(active0, 0x40000000000000L, active1, 0x4000L);
-			case 83:
-			case 115:
-				if ((active1 & 0x2L) != 0L)
-					return jjStartNfaWithStates_0(2, 65, 37);
-				else if ((active1 & 0x100000L) != 0L)
-					return jjStartNfaWithStates_0(2, 84, 37);
-				return jjMoveStringLiteralDfa3_0(active0, 0x400000000000L, active1, 0x1L);
-			case 84:
-			case 116:
-				if ((active0 & 0x800000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 35, 37);
-				else if ((active1 & 0x200000L) != 0L)
-					return jjStartNfaWithStates_0(2, 85, 37);
-				return jjMoveStringLiteralDfa3_0(active0, 0x800004002800000L, active1, 0L);
-			case 85:
-			case 117:
-				return jjMoveStringLiteralDfa3_0(active0, 0x8000000000000L, active1, 0xa000L);
-			case 86:
-			case 118:
-				return jjMoveStringLiteralDfa3_0(active0, 0x80000000000L, active1, 0L);
-			case 87:
-			case 119:
-				return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0x400L);
-			case 88:
-			case 120:
-				if ((active0 & 0x1000000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 48, 37);
-				else if ((active0 & 0x10000000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 52, 37);
-				break;
-			default:
-				break;
-		}
-		return jjStartNfa_0(1, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa3_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(1, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(2, active0, active1);
-			return 3;
-		}
-		switch(curChar){
-			case 49:
-				return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x80L);
-			case 65:
-			case 97:
-				if ((active0 & 0x1000000000000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 60, 37);
-				break;
-			case 67:
-			case 99:
-				if ((active0 & 0x400000000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 46, 37);
-				return jjMoveStringLiteralDfa4_0(active0, 0x40000000000000L, active1, 0L);
-			case 68:
-			case 100:
-				if ((active1 & 0x1000L) != 0L)
-					return jjStartNfaWithStates_0(3, 76, 37);
-				break;
-			case 69:
-			case 101:
-				if ((active0 & 0x8000000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 39, 37);
-				return jjMoveStringLiteralDfa4_0(active0, 0x800100003040000L, active1, 0x400L);
-			case 72:
-			case 104:
-				return jjMoveStringLiteralDfa4_0(active0, 0x4000000L, active1, 0L);
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa4_0(active0, 0x200080000000000L, active1, 0x800L);
-			case 76:
-			case 108:
-				if ((active0 & 0x10000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 28, 37);
-				else if ((active0 & 0x2000000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 37, 37);
-				return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x4L);
-			case 77:
-			case 109:
-				if ((active0 & 0x200000L) != 0L)
-					return jjStartNfaWithStates_0(3, 21, 37);
-				break;
-			case 78:
-			case 110:
-				if ((active0 & 0x20000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 29, 37);
-				else if ((active1 & 0x20000L) != 0L)
-					return jjStartNfaWithStates_0(3, 81, 37);
-				else if ((active1 & 0x40000L) != 0L){
-					jjmatchedKind = 82;
-					jjmatchedPos = 3;
-				}
-				return jjMoveStringLiteralDfa4_0(active0, 0x88000080000000L, active1, 0x8a000L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x20L);
-			case 82:
-			case 114:
-				return jjMoveStringLiteralDfa4_0(active0, 0xe000000100000000L, active1, 0x8L);
-			case 83:
-			case 115:
-				if ((active1 & 0x10000L) != 0L)
-					return jjStartNfaWithStates_0(3, 80, 37);
-				return jjMoveStringLiteralDfa4_0(active0, 0x20000000000L, active1, 0L);
-			case 84:
-			case 116:
-				if ((active0 & 0x8000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 27, 37);
-				else if ((active1 & 0x4000L) != 0L)
-					return jjStartNfaWithStates_0(3, 78, 37);
-				return jjMoveStringLiteralDfa4_0(active0, 0x420000000000000L, active1, 0x1L);
-			case 85:
-			case 117:
-				return jjMoveStringLiteralDfa4_0(active0, 0x40000800000L, active1, 0L);
-			case 87:
-			case 119:
-				return jjMoveStringLiteralDfa4_0(active0, 0x4000000000L, active1, 0L);
-			case 89:
-			case 121:
-				return jjMoveStringLiteralDfa4_0(active0, 0x100000000000000L, active1, 0L);
-			default:
-				break;
-		}
-		return jjStartNfa_0(2, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa4_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(2, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(3, active0, active1);
-			return 4;
-		}
-		switch(curChar){
-			case 48:
-				if ((active1 & 0x80L) != 0L)
-					return jjStartNfaWithStates_0(4, 71, 21);
-				break;
-			case 50:
-				if ((active1 & 0x80000L) != 0L)
-					return jjStartNfaWithStates_0(4, 83, 21);
-				break;
-			case 65:
-			case 97:
-				return jjMoveStringLiteralDfa5_0(active0, 0x400000000000000L, active1, 0x801L);
-			case 67:
-			case 99:
-				return jjMoveStringLiteralDfa5_0(active0, 0x40000L, active1, 0x8000L);
-			case 68:
-			case 100:
-				if ((active1 & 0x2000L) != 0L)
-					return jjStartNfaWithStates_0(4, 77, 37);
-				return jjMoveStringLiteralDfa5_0(active0, 0xe000000000000000L, active1, 0L);
-			case 69:
-			case 101:
-				if ((active0 & 0x100000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 32, 37);
-				return jjMoveStringLiteralDfa5_0(active0, 0x4000000000L, active1, 0x8L);
-			case 71:
-			case 103:
-				if ((active0 & 0x80000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 31, 37);
-				return jjMoveStringLiteralDfa5_0(active0, 0x100000000000000L, active1, 0L);
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x4L);
-			case 76:
-			case 108:
-				return jjMoveStringLiteralDfa5_0(active0, 0x40000000000000L, active1, 0L);
-			case 78:
-			case 110:
-				return jjMoveStringLiteralDfa5_0(active0, 0x80000000000L, active1, 0L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa5_0(active0, 0x200000000000000L, active1, 0L);
-			case 80:
-			case 112:
-				return jjMoveStringLiteralDfa5_0(active0, 0x40000000000L, active1, 0L);
-			case 82:
-			case 114:
-				if ((active0 & 0x1000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 24, 37);
-				else if ((active0 & 0x2000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 25, 37);
-				else if ((active1 & 0x20L) != 0L)
-					return jjStartNfaWithStates_0(4, 69, 37);
-				else if ((active1 & 0x400L) != 0L)
-					return jjStartNfaWithStates_0(4, 74, 37);
-				return jjMoveStringLiteralDfa5_0(active0, 0x820100000800000L, active1, 0L);
-			case 84:
-			case 116:
-				if ((active0 & 0x4000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 26, 37);
-				else if ((active0 & 0x8000000000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 51, 37);
-				else if ((active0 & 0x80000000000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 55, 37);
-				return jjMoveStringLiteralDfa5_0(active0, 0x20000000000L, active1, 0L);
-			default:
-				break;
-		}
-		return jjStartNfa_0(3, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa5_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(3, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(4, active0, active1);
-			return 5;
-		}
-		switch(curChar){
-			case 32:
-				return jjMoveStringLiteralDfa6_0(active0, 0x140000000000L, active1, 0L);
-			case 49:
-				if ((active0 & 0x2000000000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 61, 21);
-				break;
-			case 50:
-				if ((active0 & 0x4000000000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 62, 21);
-				break;
-			case 65:
-			case 97:
-				return jjMoveStringLiteralDfa6_0(active0, 0x800000L, active1, 0x8000L);
-			case 69:
-			case 101:
-				if ((active0 & 0x40000000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 54, 37);
-				return jjMoveStringLiteralDfa6_0(active0, 0x4000000000L, active1, 0x8L);
-			case 71:
-			case 103:
-				if ((active0 & 0x80000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 43, 37);
-				break;
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa6_0(active0, 0x400000000000000L, active1, 0L);
-			case 78:
-			case 110:
-				if ((active0 & 0x200000000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 57, 37);
-				return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x805L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa6_0(active0, 0x120000000000000L, active1, 0L);
-			case 83:
-			case 115:
-				if ((active0 & 0x20000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 41, 37);
-				return jjMoveStringLiteralDfa6_0(active0, 0x8800000000000000L, active1, 0L);
-			case 84:
-			case 116:
-				if ((active0 & 0x40000L) != 0L)
-					return jjStartNfaWithStates_0(5, 18, 37);
-				break;
-			default:
-				break;
-		}
-		return jjStartNfa_0(4, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa6_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(4, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(5, active0, active1);
-			return 6;
-		}
-		switch(curChar){
-			case 66:
-			case 98:
-				return jjMoveStringLiteralDfa7_0(active0, 0x140000000000L, active1, 0L);
-			case 67:
-			case 99:
-				return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x1L);
-			case 69:
-			case 101:
-				return jjMoveStringLiteralDfa7_0(active0, 0x800000000000000L, active1, 0L);
-			case 71:
-			case 103:
-				if ((active1 & 0x4L) != 0L)
-					return jjStartNfaWithStates_0(6, 66, 37);
-				break;
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa7_0(active0, 0x20000000000000L, active1, 0L);
-			case 76:
-			case 108:
-				if ((active0 & 0x800000L) != 0L)
-					return jjStartNfaWithStates_0(6, 23, 37);
-				break;
-			case 78:
-			case 110:
-				if ((active0 & 0x4000000000L) != 0L)
-					return jjStartNfaWithStates_0(6, 38, 37);
-				else if ((active0 & 0x100000000000000L) != 0L)
-					return jjStartNfaWithStates_0(6, 56, 37);
-				return jjMoveStringLiteralDfa7_0(active0, 0x400000000000000L, active1, 0L);
-			case 83:
-			case 115:
-				if ((active1 & 0x8L) != 0L)
-					return jjStartNfaWithStates_0(6, 67, 37);
-				else if ((active1 & 0x800L) != 0L)
-					return jjStartNfaWithStates_0(6, 75, 37);
-				break;
-			case 84:
-			case 116:
-				return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x8000L);
-			case 89:
-			case 121:
-				return jjMoveStringLiteralDfa7_0(active0, 0x8000000000000000L, active1, 0L);
-			default:
-				break;
-		}
-		return jjStartNfa_0(5, active0, active1);
-	}
+public class ADQLParserTokenManager implements ADQLParserConstants
+{
 
-	private int jjMoveStringLiteralDfa7_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(5, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(6, active0, active1);
-			return 7;
-		}
-		switch(curChar){
-			case 67:
-			case 99:
-				return jjMoveStringLiteralDfa8_0(active0, 0x800000000000000L, active1, 0L);
-			case 68:
-			case 100:
-				if ((active0 & 0x20000000000000L) != 0L)
-					return jjStartNfaWithStates_0(7, 53, 37);
-				break;
-			case 69:
-			case 101:
-				if ((active1 & 0x1L) != 0L)
-					return jjStartNfaWithStates_0(7, 64, 37);
-				else if ((active1 & 0x8000L) != 0L)
-					return jjStartNfaWithStates_0(7, 79, 37);
-				break;
-			case 83:
-			case 115:
-				if ((active0 & 0x400000000000000L) != 0L)
-					return jjStartNfaWithStates_0(7, 58, 37);
-				else if ((active0 & 0x8000000000000000L) != 0L)
-					return jjStartNfaWithStates_0(7, 63, 37);
-				break;
-			case 89:
-			case 121:
-				if ((active0 & 0x40000000000L) != 0L)
-					return jjStopAtPos(7, 42);
-				else if ((active0 & 0x100000000000L) != 0L)
-					return jjStopAtPos(7, 44);
-				break;
-			default:
-				break;
-		}
-		return jjStartNfa_0(6, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa8_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(6, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(7, active0, 0L);
-			return 8;
-		}
-		switch(curChar){
-			case 84:
-			case 116:
-				return jjMoveStringLiteralDfa9_0(active0, 0x800000000000000L);
-			default:
-				break;
-		}
-		return jjStartNfa_0(7, active0, 0L);
-	}
-
-	private int jjMoveStringLiteralDfa9_0(long old0, long active0){
-		if (((active0 &= old0)) == 0L)
-			return jjStartNfa_0(7, old0, 0L);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(8, active0, 0L);
-			return 9;
-		}
-		switch(curChar){
-			case 83:
-			case 115:
-				if ((active0 & 0x800000000000000L) != 0L)
-					return jjStartNfaWithStates_0(9, 59, 37);
-				break;
-			default:
-				break;
-		}
-		return jjStartNfa_0(8, active0, 0L);
-	}
-
-	private int jjStartNfaWithStates_0(int pos, int kind, int state){
-		jjmatchedKind = kind;
-		jjmatchedPos = pos;
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			return pos + 1;
-		}
-		return jjMoveNfa_0(state, pos + 1);
-	}
-
-	private int jjMoveNfa_0(int startState, int curPos){
-		int startsAt = 0;
-		jjnewStateCnt = 37;
-		int i = 1;
-		jjstateSet[0] = startState;
-		int kind = 0x7fffffff;
-		for(;;){
-			if (++jjround == 0x7fffffff)
-				ReInitRounds();
-			if (curChar < 64){
-				long l = 1L << curChar;
-				do{
-					switch(jjstateSet[--i]){
-						case 12:
-						case 21:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 38:
-							if ((0x3ff000000000000L & l) != 0L){
-								if (kind > 100)
-									kind = 100;
-								jjCheckNAdd(27);
-							}
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(23, 24);
-							break;
-						case 37:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 0:
-							if ((0x3ff000000000000L & l) != 0L){
-								if (kind > 101)
-									kind = 101;
-								jjCheckNAddStates(0, 6);
-							}else if ((0x100002600L & l) != 0L){
-								if (kind > 1)
-									kind = 1;
-							}else if (curChar == 46)
-								jjCheckNAddTwoStates(23, 27);
-							else if (curChar == 45)
-								jjCheckNAdd(19);
-							else if (curChar == 33)
-								jjstateSet[jjnewStateCnt++] = 5;
-							else if (curChar == 60)
-								jjstateSet[jjnewStateCnt++] = 3;
-							if (curChar == 13)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						case 11:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 16:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 13:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 10:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 1:
-							if (curChar == 10 && kind > 1)
-								kind = 1;
-							break;
-						case 2:
-							if (curChar == 13)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						case 3:
-							if (curChar == 62)
-								kind = 13;
-							break;
-						case 4:
-							if (curChar == 60)
-								jjstateSet[jjnewStateCnt++] = 3;
-							break;
-						case 5:
-							if (curChar == 61)
-								kind = 13;
-							break;
-						case 6:
-							if (curChar == 33)
-								jjstateSet[jjnewStateCnt++] = 5;
-							break;
-						case 18:
-							if (curChar == 45)
-								jjCheckNAdd(19);
-							break;
-						case 19:
-							if (curChar != 45)
-								break;
-							if (kind > 88)
-								kind = 88;
-							jjCheckNAdd(19);
-							break;
-						case 22:
-							if (curChar == 46)
-								jjCheckNAddTwoStates(23, 27);
-							break;
-						case 23:
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(23, 24);
-							break;
-						case 25:
-							if ((0x280000000000L & l) != 0L)
-								jjCheckNAdd(26);
-							break;
-						case 26:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 99)
-								kind = 99;
-							jjCheckNAdd(26);
-							break;
-						case 27:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 100)
-								kind = 100;
-							jjCheckNAdd(27);
-							break;
-						case 28:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 101)
-								kind = 101;
-							jjCheckNAddStates(0, 6);
-							break;
-						case 29:
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(29, 24);
-							break;
-						case 30:
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(30, 31);
-							break;
-						case 31:
-							if (curChar == 46)
-								jjCheckNAddTwoStates(32, 24);
-							break;
-						case 32:
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(32, 24);
-							break;
-						case 33:
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(33, 34);
-							break;
-						case 34:
-							if (curChar != 46)
-								break;
-							if (kind > 100)
-								kind = 100;
-							jjCheckNAdd(35);
-							break;
-						case 35:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 100)
-								kind = 100;
-							jjCheckNAdd(35);
-							break;
-						case 36:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 101)
-								kind = 101;
-							jjCheckNAdd(36);
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else if (curChar < 128){
-				long l = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						case 12:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x8000000080000L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 11;
-							break;
-						case 37:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							break;
-						case 0:
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x200000002L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 16;
-							else if ((0x1000000010L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 13;
-							break;
-						case 11:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x10000000100000L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 10;
-							break;
-						case 16:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x100000001000L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 15;
-							break;
-						case 13:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x20000000200L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 12;
-							break;
-						case 10:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x20000000200L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 9;
-							break;
-						case 7:
-							if ((0x10000000100000L & l) != 0L && kind > 19)
-								kind = 19;
-							break;
-						case 8:
-							if ((0x800000008L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 7;
-							break;
-						case 9:
-							if ((0x400000004000L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 8;
-							break;
-						case 14:
-							if ((0x1000000010L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 13;
-							break;
-						case 15:
-							if ((0x100000001000L & l) != 0L && kind > 19)
-								kind = 19;
-							break;
-						case 17:
-							if ((0x200000002L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 16;
-							break;
-						case 20:
-							if ((0x7fffffe07fffffeL & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAddTwoStates(20, 21);
-							break;
-						case 21:
-							if ((0x7fffffe87fffffeL & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 24:
-							if ((0x2000000020L & l) != 0L)
-								jjAddStates(7, 8);
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else{
-				int i2 = (curChar & 0xff) >> 6;
-				long l2 = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}
-			if (kind != 0x7fffffff){
-				jjmatchedKind = kind;
-				jjmatchedPos = curPos;
-				kind = 0x7fffffff;
-			}
-			++curPos;
-			if ((i = jjnewStateCnt) == (startsAt = 37 - (jjnewStateCnt = startsAt)))
-				return curPos;
-			try{
-				curChar = input_stream.readChar();
-			}catch(java.io.IOException e){
-				return curPos;
-			}
-		}
-	}
-
-	private final int jjStopStringLiteralDfa_2(int pos, long active0, long active1){
-		switch(pos){
-			default:
-				return -1;
-		}
-	}
-
-	private final int jjStartNfa_2(int pos, long active0, long active1){
-		return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0, active1), pos + 1);
-	}
-
-	private int jjMoveStringLiteralDfa0_2(){
-		switch(curChar){
-			case 39:
-				return jjStartNfaWithStates_2(0, 93, 1);
-			default:
-				return jjMoveNfa_2(0, 0);
-		}
-	}
-
-	private int jjStartNfaWithStates_2(int pos, int kind, int state){
-		jjmatchedKind = kind;
-		jjmatchedPos = pos;
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			return pos + 1;
-		}
-		return jjMoveNfa_2(state, pos + 1);
-	}
-
-	private int jjMoveNfa_2(int startState, int curPos){
-		int startsAt = 0;
-		jjnewStateCnt = 3;
-		int i = 1;
-		jjstateSet[0] = startState;
-		int kind = 0x7fffffff;
-		for(;;){
-			if (++jjround == 0x7fffffff)
-				ReInitRounds();
-			if (curChar < 64){
-				long l = 1L << curChar;
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							if ((0xffffff7fffffffffL & l) != 0L){
-								if (kind > 92)
-									kind = 92;
-							}else if (curChar == 39)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						case 1:
-							if (curChar == 39 && kind > 92)
-								kind = 92;
-							break;
-						case 2:
-							if (curChar == 39)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else if (curChar < 128){
-				long l = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							kind = 92;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else{
-				int i2 = (curChar & 0xff) >> 6;
-				long l2 = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							if ((jjbitVec0[i2] & l2) != 0L && kind > 92)
-								kind = 92;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}
-			if (kind != 0x7fffffff){
-				jjmatchedKind = kind;
-				jjmatchedPos = curPos;
-				kind = 0x7fffffff;
-			}
-			++curPos;
-			if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
-				return curPos;
-			try{
-				curChar = input_stream.readChar();
-			}catch(java.io.IOException e){
-				return curPos;
-			}
-		}
-	}
-
-	private int jjMoveStringLiteralDfa0_1(){
-		return jjMoveNfa_1(0, 0);
-	}
-
-	private int jjMoveNfa_1(int startState, int curPos){
-		int startsAt = 0;
-		jjnewStateCnt = 3;
-		int i = 1;
-		jjstateSet[0] = startState;
-		int kind = 0x7fffffff;
-		for(;;){
-			if (++jjround == 0x7fffffff)
-				ReInitRounds();
-			if (curChar < 64){
-				long l = 1L << curChar;
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							if ((0x2400L & l) != 0L){
-								if (kind > 89)
-									kind = 89;
-							}
-							if (curChar == 13)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						case 1:
-							if (curChar == 10 && kind > 89)
-								kind = 89;
-							break;
-						case 2:
-							if (curChar == 13)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else if (curChar < 128){
-				long l = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else{
-				int i2 = (curChar & 0xff) >> 6;
-				long l2 = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}
-			if (kind != 0x7fffffff){
-				jjmatchedKind = kind;
-				jjmatchedPos = curPos;
-				kind = 0x7fffffff;
-			}
-			++curPos;
-			if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
-				return curPos;
-			try{
-				curChar = input_stream.readChar();
-			}catch(java.io.IOException e){
-				return curPos;
-			}
-		}
-	}
-
-	static final int[] jjnextStates = {29,30,31,24,33,34,36,25,26,};
-
-	/** Token literal values. */
-	public static final String[] jjstrLiteralImages = {"",null,"\50","\51","\56","\54","\73","\174\174","\53","\55","\52","\57","\75",null,"\74","\74\75","\76","\76\75",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,};
-
-	/** Lexer state names. */
-	public static final String[] lexStateNames = {"DEFAULT","WithinComment","WithinString","WithinDelimitedId",};
-
-	/** Lex State array. */
-	public static final int[] jjnewLexState = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,0,-1,2,-1,0,3,-1,0,-1,-1,-1,-1,-1,-1,};
-	static final long[] jjtoToken = {0xfffffffffffffffdL,0x3b20ffffffL,};
-	static final long[] jjtoSkip = {0x2L,0x2000000L,};
-	static final long[] jjtoMore = {0x0L,0xdd000000L,};
-	protected SimpleCharStream input_stream;
-	private final int[] jjrounds = new int[37];
-	private final int[] jjstateSet = new int[74];
-	protected char curChar;
-
-	/** Constructor. */
-	public ADQLParserTokenManager(SimpleCharStream stream){
-		if (SimpleCharStream.staticFlag)
-			throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
-		input_stream = stream;
-	}
+  /** Debug output. */
+  public  java.io.PrintStream debugStream = System.out;
+  /** Set debug output. */
+  public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+private final int jjStopStringLiteralDfa_3(int pos, long active0, long active1)
+{
+   switch (pos)
+   {
+      default :
+         return -1;
+   }
+}
+private final int jjStartNfa_3(int pos, long active0, long active1)
+{
+   return jjMoveNfa_3(jjStopStringLiteralDfa_3(pos, active0, active1), pos + 1);
+}
+private int jjStopAtPos(int pos, int kind)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   return pos + 1;
+}
+private int jjMoveStringLiteralDfa0_3()
+{
+   switch(curChar)
+   {
+      case 34:
+         return jjStartNfaWithStates_3(0, 96, 1);
+      default :
+         return jjMoveNfa_3(0, 0);
+   }
+}
+private int jjStartNfaWithStates_3(int pos, int kind, int state)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) { return pos + 1; }
+   return jjMoveNfa_3(state, pos + 1);
+}
+static final long[] jjbitVec0 = {
+   0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+private int jjMoveNfa_3(int startState, int curPos)
+{
+   int startsAt = 0;
+   jjnewStateCnt = 3;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((0xfffffffbffffffffL & l) != 0L)
+                  {
+                     if (kind > 95)
+                        kind = 95;
+                  }
+                  else if (curChar == 34)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 1:
+                  if (curChar == 34 && kind > 95)
+                     kind = 95;
+                  break;
+               case 2:
+                  if (curChar == 34)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  kind = 95;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((jjbitVec0[i2] & l2) != 0L && kind > 95)
+                     kind = 95;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+         return curPos;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { return curPos; }
+   }
+}
+private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
+{
+   switch (pos)
+   {
+      case 0:
+         if ((active0 & 0xefff1ffdffb40000L) != 0L || (active1 & 0xf0fff4L) != 0L)
+         {
+            jjmatchedKind = 97;
+            return 37;
+         }
+         if ((active0 & 0x1000a00200400000L) != 0L || (active1 & 0xf0002L) != 0L)
+         {
+            jjmatchedKind = 97;
+            return 16;
+         }
+         if ((active0 & 0x10L) != 0L)
+            return 38;
+         if ((active0 & 0x400000000000L) != 0L || (active1 & 0x9L) != 0L)
+         {
+            jjmatchedKind = 97;
+            return 13;
+         }
+         if ((active0 & 0xc000L) != 0L)
+            return 3;
+         if ((active0 & 0x200L) != 0L)
+            return 19;
+         return -1;
+      case 1:
+         if ((active0 & 0xf7ffceebbeb40000L) != 0L || (active1 & 0xfdfdfeL) != 0L)
+         {
+            if (jjmatchedPos != 1)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 1;
+            }
+            return 37;
+         }
+         if ((active0 & 0x800311441400000L) != 0L || (active1 & 0x20200L) != 0L)
+            return 37;
+         if ((active1 & 0x1L) != 0L)
+         {
+            if (jjmatchedPos != 1)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 1;
+            }
+            return 12;
+         }
+         return -1;
+      case 2:
+         if ((active1 & 0x1L) != 0L)
+         {
+            if (jjmatchedPos != 2)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 2;
+            }
+            return 11;
+         }
+         if ((active0 & 0x17a00a00100000L) != 0L || (active1 & 0xf001d2L) != 0L)
+            return 37;
+         if ((active0 & 0xffe85ee1bfa40000L) != 0L || (active1 & 0xffc2cL) != 0L)
+         {
+            if (jjmatchedPos != 2)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 2;
+            }
+            return 37;
+         }
+         return -1;
+      case 3:
+         if ((active0 & 0xefe81e4187840000L) != 0L || (active1 & 0xac2cL) != 0L)
+         {
+            if (jjmatchedPos != 3)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 3;
+            }
+            return 37;
+         }
+         if ((active0 & 0x100040a038200000L) != 0L || (active1 & 0xf5000L) != 0L)
+            return 37;
+         if ((active1 & 0x80L) != 0L)
+         {
+            if (jjmatchedPos != 3)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 3;
+            }
+            return 21;
+         }
+         if ((active1 & 0x1L) != 0L)
+         {
+            if (jjmatchedPos != 3)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 3;
+            }
+            return 10;
+         }
+         return -1;
+      case 4:
+         if ((active0 & 0xef601e4000840000L) != 0L || (active1 & 0x880dL) != 0L)
+         {
+            jjmatchedKind = 97;
+            jjmatchedPos = 4;
+            return 37;
+         }
+         if ((active0 & 0x88000187000000L) != 0L || (active1 & 0x2420L) != 0L)
+            return 37;
+         if ((active1 & 0x80080L) != 0L)
+            return 21;
+         return -1;
+      case 5:
+         if ((active0 & 0x2400a0000040000L) != 0L)
+            return 37;
+         if ((active0 & 0x6000000000000000L) != 0L)
+            return 21;
+         if ((active0 & 0x8d20004000800000L) != 0L || (active1 & 0x880dL) != 0L)
+         {
+            jjmatchedKind = 97;
+            jjmatchedPos = 5;
+            return 37;
+         }
+         if ((active0 & 0x140000000000L) != 0L)
+         {
+            if (jjmatchedPos < 4)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 4;
+            }
+            return -1;
+         }
+         return -1;
+      case 6:
+         if ((active0 & 0x8c20000000000000L) != 0L || (active1 & 0x8001L) != 0L)
+         {
+            jjmatchedKind = 97;
+            jjmatchedPos = 6;
+            return 37;
+         }
+         if ((active0 & 0x100004000800000L) != 0L || (active1 & 0x80cL) != 0L)
+            return 37;
+         if ((active0 & 0x140000000000L) != 0L)
+         {
+            if (jjmatchedPos < 4)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 4;
+            }
+            return -1;
+         }
+         return -1;
+      case 7:
+         if ((active0 & 0x800000000000000L) != 0L)
+         {
+            jjmatchedKind = 97;
+            jjmatchedPos = 7;
+            return 37;
+         }
+         if ((active0 & 0x8420000000000000L) != 0L || (active1 & 0x8001L) != 0L)
+            return 37;
+         if ((active0 & 0x140000000000L) != 0L)
+         {
+            if (jjmatchedPos < 4)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 4;
+            }
+            return -1;
+         }
+         return -1;
+      case 8:
+         if ((active0 & 0x800000000000000L) != 0L)
+         {
+            jjmatchedKind = 97;
+            jjmatchedPos = 8;
+            return 37;
+         }
+         return -1;
+      default :
+         return -1;
+   }
+}
+private final int jjStartNfa_0(int pos, long active0, long active1)
+{
+   return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0, active1), pos + 1);
+}
+private int jjMoveStringLiteralDfa0_0()
+{
+   switch(curChar)
+   {
+      case 34:
+         return jjStopAtPos(0, 94);
+      case 39:
+         return jjStopAtPos(0, 91);
+      case 40:
+         return jjStopAtPos(0, 2);
+      case 41:
+         return jjStopAtPos(0, 3);
+      case 42:
+         return jjStopAtPos(0, 10);
+      case 43:
+         return jjStopAtPos(0, 8);
+      case 44:
+         return jjStopAtPos(0, 5);
+      case 45:
+         return jjStartNfaWithStates_0(0, 9, 19);
+      case 46:
+         return jjStartNfaWithStates_0(0, 4, 38);
+      case 47:
+         return jjStopAtPos(0, 11);
+      case 59:
+         return jjStopAtPos(0, 6);
+      case 60:
+         jjmatchedKind = 14;
+         return jjMoveStringLiteralDfa1_0(0x8000L, 0x0L);
+      case 61:
+         return jjStopAtPos(0, 12);
+      case 62:
+         jjmatchedKind = 16;
+         return jjMoveStringLiteralDfa1_0(0x20000L, 0x0L);
+      case 65:
+      case 97:
+         return jjMoveStringLiteralDfa1_0(0x1000a00200400000L, 0xf0002L);
+      case 66:
+      case 98:
+         return jjMoveStringLiteralDfa1_0(0x10004000000000L, 0x0L);
+      case 67:
+      case 99:
+         return jjMoveStringLiteralDfa1_0(0xe468000000000000L, 0x300004L);
+      case 68:
+      case 100:
+         return jjMoveStringLiteralDfa1_0(0x400000000000L, 0x9L);
+      case 69:
+      case 101:
+         return jjMoveStringLiteralDfa1_0(0x20000000000L, 0x10L);
+      case 70:
+      case 102:
+         return jjMoveStringLiteralDfa1_0(0x10200000L, 0x20L);
+      case 71:
+      case 103:
+         return jjMoveStringLiteralDfa1_0(0x40000000000L, 0x0L);
+      case 72:
+      case 104:
+         return jjMoveStringLiteralDfa1_0(0x80000000000L, 0x0L);
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa1_0(0x800011001000000L, 0x0L);
+      case 74:
+      case 106:
+         return jjMoveStringLiteralDfa1_0(0x20000000L, 0x0L);
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa1_0(0x8008000000L, 0xc0L);
+      case 77:
+      case 109:
+         return jjMoveStringLiteralDfa1_0(0x3000000000000L, 0x100L);
+      case 78:
+      case 110:
+         return jjMoveStringLiteralDfa1_0(0x2800800000L, 0x0L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa1_0(0x100442000000L, 0x0L);
+      case 80:
+      case 112:
+         return jjMoveStringLiteralDfa1_0(0x180000000000000L, 0x600L);
+      case 82:
+      case 114:
+         return jjMoveStringLiteralDfa1_0(0x200000004000000L, 0x3800L);
+      case 83:
+      case 115:
+         return jjMoveStringLiteralDfa1_0(0x4000000040000L, 0x404000L);
+      case 84:
+      case 116:
+         return jjMoveStringLiteralDfa1_0(0x100000L, 0x808000L);
+      case 85:
+      case 117:
+         return jjMoveStringLiteralDfa1_0(0x80000000L, 0x0L);
+      case 87:
+      case 119:
+         return jjMoveStringLiteralDfa1_0(0x100000000L, 0x0L);
+      case 124:
+         return jjMoveStringLiteralDfa1_0(0x80L, 0x0L);
+      default :
+         return jjMoveNfa_0(0, 0);
+   }
+}
+private int jjMoveStringLiteralDfa1_0(long active0, long active1)
+{
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(0, active0, active1);
+      return 1;
+   }
+   switch(curChar)
+   {
+      case 61:
+         if ((active0 & 0x8000L) != 0L)
+            return jjStopAtPos(1, 15);
+         else if ((active0 & 0x20000L) != 0L)
+            return jjStopAtPos(1, 17);
+         break;
+      case 65:
+      case 97:
+         return jjMoveStringLiteralDfa2_0(active0, 0x1080000800000L, active1, 0x801800L);
+      case 66:
+      case 98:
+         return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x2L);
+      case 67:
+      case 99:
+         return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x10000L);
+      case 69:
+      case 101:
+         return jjMoveStringLiteralDfa2_0(active0, 0x220404008040000L, active1, 0xcL);
+      case 72:
+      case 104:
+         return jjMoveStringLiteralDfa2_0(active0, 0x100000000L, active1, 0L);
+      case 73:
+      case 105:
+         if ((active1 & 0x200L) != 0L)
+            return jjStartNfaWithStates_0(1, 73, 37);
+         return jjMoveStringLiteralDfa2_0(active0, 0x42008004000000L, active1, 0x400001L);
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x20L);
+      case 78:
+      case 110:
+         if ((active0 & 0x40000000L) != 0L)
+            return jjStartNfaWithStates_0(1, 30, 37);
+         else if ((active0 & 0x10000000000L) != 0L)
+         {
+            jjmatchedKind = 40;
+            jjmatchedPos = 1;
+         }
+         return jjMoveStringLiteralDfa2_0(active0, 0x800000201000000L, active1, 0L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa2_0(active0, 0xe598000820100000L, active1, 0x3025c0L);
+      case 81:
+      case 113:
+         return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x4000L);
+      case 82:
+      case 114:
+         if ((active0 & 0x400000000L) != 0L)
+         {
+            jjmatchedKind = 34;
+            jjmatchedPos = 1;
+         }
+         return jjMoveStringLiteralDfa2_0(active0, 0x1000140000200000L, active1, 0x8000L);
+      case 83:
+      case 115:
+         if ((active0 & 0x400000L) != 0L)
+         {
+            jjmatchedKind = 22;
+            jjmatchedPos = 1;
+         }
+         else if ((active0 & 0x1000000000L) != 0L)
+            return jjStartNfaWithStates_0(1, 36, 37);
+         return jjMoveStringLiteralDfa2_0(active0, 0x200080000000L, active1, 0x20000L);
+      case 84:
+      case 116:
+         return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0xc0000L);
+      case 85:
+      case 117:
+         return jjMoveStringLiteralDfa2_0(active0, 0x4002012000000L, active1, 0L);
+      case 86:
+      case 118:
+         return jjMoveStringLiteralDfa2_0(active0, 0x800000000000L, active1, 0L);
+      case 88:
+      case 120:
+         return jjMoveStringLiteralDfa2_0(active0, 0x20000000000L, active1, 0x10L);
+      case 124:
+         if ((active0 & 0x80L) != 0L)
+            return jjStopAtPos(1, 7);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(0, active0, active1);
+}
+private int jjMoveStringLiteralDfa2_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(0, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(1, active0, active1);
+      return 2;
+   }
+   switch(curChar)
+   {
+      case 65:
+      case 97:
+         return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0xc0000L);
+      case 67:
+      case 99:
+         if ((active0 & 0x200000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 45, 37);
+         break;
+      case 68:
+      case 100:
+         if ((active0 & 0x200000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 33, 37);
+         else if ((active1 & 0x100L) != 0L)
+            return jjStartNfaWithStates_0(2, 72, 37);
+         return jjMoveStringLiteralDfa3_0(active0, 0x100000000000L, active1, 0x800L);
+      case 69:
+      case 101:
+         return jjMoveStringLiteralDfa3_0(active0, 0x1000000100000000L, active1, 0L);
+      case 70:
+      case 102:
+         return jjMoveStringLiteralDfa3_0(active0, 0x8000000L, active1, 0L);
+      case 71:
+      case 103:
+         if ((active0 & 0x800000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 47, 37);
+         else if ((active1 & 0x40L) != 0L)
+         {
+            jjmatchedKind = 70;
+            jjmatchedPos = 2;
+         }
+         return jjMoveStringLiteralDfa3_0(active0, 0x200000004000000L, active1, 0x88L);
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa3_0(active0, 0x800200a0000000L, active1, 0x20004L);
+      case 75:
+      case 107:
+         return jjMoveStringLiteralDfa3_0(active0, 0x8000000000L, active1, 0L);
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa3_0(active0, 0x100002010040000L, active1, 0L);
+      case 77:
+      case 109:
+         if ((active0 & 0x4000000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 50, 37);
+         break;
+      case 78:
+      case 110:
+         if ((active0 & 0x2000000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 49, 37);
+         else if ((active1 & 0x400000L) != 0L)
+            return jjStartNfaWithStates_0(2, 86, 37);
+         else if ((active1 & 0x800000L) != 0L)
+            return jjStartNfaWithStates_0(2, 87, 37);
+         return jjMoveStringLiteralDfa3_0(active0, 0x420000001000000L, active1, 0x1000L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa3_0(active0, 0xe000040000200000L, active1, 0x10020L);
+      case 80:
+      case 112:
+         if ((active0 & 0x100000L) != 0L)
+            return jjStartNfaWithStates_0(2, 20, 37);
+         else if ((active1 & 0x10L) != 0L)
+            return jjStartNfaWithStates_0(2, 68, 37);
+         break;
+      case 82:
+      case 114:
+         return jjMoveStringLiteralDfa3_0(active0, 0x40000000000000L, active1, 0x4000L);
+      case 83:
+      case 115:
+         if ((active1 & 0x2L) != 0L)
+            return jjStartNfaWithStates_0(2, 65, 37);
+         else if ((active1 & 0x100000L) != 0L)
+            return jjStartNfaWithStates_0(2, 84, 37);
+         return jjMoveStringLiteralDfa3_0(active0, 0x400000000000L, active1, 0x1L);
+      case 84:
+      case 116:
+         if ((active0 & 0x800000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 35, 37);
+         else if ((active1 & 0x200000L) != 0L)
+            return jjStartNfaWithStates_0(2, 85, 37);
+         return jjMoveStringLiteralDfa3_0(active0, 0x800004002800000L, active1, 0L);
+      case 85:
+      case 117:
+         return jjMoveStringLiteralDfa3_0(active0, 0x8000000000000L, active1, 0xa000L);
+      case 86:
+      case 118:
+         return jjMoveStringLiteralDfa3_0(active0, 0x80000000000L, active1, 0L);
+      case 87:
+      case 119:
+         return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0x400L);
+      case 88:
+      case 120:
+         if ((active0 & 0x1000000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 48, 37);
+         else if ((active0 & 0x10000000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 52, 37);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(1, active0, active1);
+}
+private int jjMoveStringLiteralDfa3_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(1, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(2, active0, active1);
+      return 3;
+   }
+   switch(curChar)
+   {
+      case 49:
+         return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x80L);
+      case 65:
+      case 97:
+         if ((active0 & 0x1000000000000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 60, 37);
+         break;
+      case 67:
+      case 99:
+         if ((active0 & 0x400000000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 46, 37);
+         return jjMoveStringLiteralDfa4_0(active0, 0x40000000000000L, active1, 0L);
+      case 68:
+      case 100:
+         if ((active1 & 0x1000L) != 0L)
+            return jjStartNfaWithStates_0(3, 76, 37);
+         break;
+      case 69:
+      case 101:
+         if ((active0 & 0x8000000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 39, 37);
+         return jjMoveStringLiteralDfa4_0(active0, 0x800100003040000L, active1, 0x400L);
+      case 72:
+      case 104:
+         return jjMoveStringLiteralDfa4_0(active0, 0x4000000L, active1, 0L);
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa4_0(active0, 0x200080000000000L, active1, 0x800L);
+      case 76:
+      case 108:
+         if ((active0 & 0x10000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 28, 37);
+         else if ((active0 & 0x2000000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 37, 37);
+         return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x4L);
+      case 77:
+      case 109:
+         if ((active0 & 0x200000L) != 0L)
+            return jjStartNfaWithStates_0(3, 21, 37);
+         break;
+      case 78:
+      case 110:
+         if ((active0 & 0x20000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 29, 37);
+         else if ((active1 & 0x20000L) != 0L)
+            return jjStartNfaWithStates_0(3, 81, 37);
+         else if ((active1 & 0x40000L) != 0L)
+         {
+            jjmatchedKind = 82;
+            jjmatchedPos = 3;
+         }
+         return jjMoveStringLiteralDfa4_0(active0, 0x88000080000000L, active1, 0x8a000L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x20L);
+      case 82:
+      case 114:
+         return jjMoveStringLiteralDfa4_0(active0, 0xe000000100000000L, active1, 0x8L);
+      case 83:
+      case 115:
+         if ((active1 & 0x10000L) != 0L)
+            return jjStartNfaWithStates_0(3, 80, 37);
+         return jjMoveStringLiteralDfa4_0(active0, 0x20000000000L, active1, 0L);
+      case 84:
+      case 116:
+         if ((active0 & 0x8000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 27, 37);
+         else if ((active1 & 0x4000L) != 0L)
+            return jjStartNfaWithStates_0(3, 78, 37);
+         return jjMoveStringLiteralDfa4_0(active0, 0x420000000000000L, active1, 0x1L);
+      case 85:
+      case 117:
+         return jjMoveStringLiteralDfa4_0(active0, 0x40000800000L, active1, 0L);
+      case 87:
+      case 119:
+         return jjMoveStringLiteralDfa4_0(active0, 0x4000000000L, active1, 0L);
+      case 89:
+      case 121:
+         return jjMoveStringLiteralDfa4_0(active0, 0x100000000000000L, active1, 0L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(2, active0, active1);
+}
+private int jjMoveStringLiteralDfa4_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(2, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(3, active0, active1);
+      return 4;
+   }
+   switch(curChar)
+   {
+      case 48:
+         if ((active1 & 0x80L) != 0L)
+            return jjStartNfaWithStates_0(4, 71, 21);
+         break;
+      case 50:
+         if ((active1 & 0x80000L) != 0L)
+            return jjStartNfaWithStates_0(4, 83, 21);
+         break;
+      case 65:
+      case 97:
+         return jjMoveStringLiteralDfa5_0(active0, 0x400000000000000L, active1, 0x801L);
+      case 67:
+      case 99:
+         return jjMoveStringLiteralDfa5_0(active0, 0x40000L, active1, 0x8000L);
+      case 68:
+      case 100:
+         if ((active1 & 0x2000L) != 0L)
+            return jjStartNfaWithStates_0(4, 77, 37);
+         return jjMoveStringLiteralDfa5_0(active0, 0xe000000000000000L, active1, 0L);
+      case 69:
+      case 101:
+         if ((active0 & 0x100000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 32, 37);
+         return jjMoveStringLiteralDfa5_0(active0, 0x4000000000L, active1, 0x8L);
+      case 71:
+      case 103:
+         if ((active0 & 0x80000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 31, 37);
+         return jjMoveStringLiteralDfa5_0(active0, 0x100000000000000L, active1, 0L);
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x4L);
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa5_0(active0, 0x40000000000000L, active1, 0L);
+      case 78:
+      case 110:
+         return jjMoveStringLiteralDfa5_0(active0, 0x80000000000L, active1, 0L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa5_0(active0, 0x200000000000000L, active1, 0L);
+      case 80:
+      case 112:
+         return jjMoveStringLiteralDfa5_0(active0, 0x40000000000L, active1, 0L);
+      case 82:
+      case 114:
+         if ((active0 & 0x1000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 24, 37);
+         else if ((active0 & 0x2000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 25, 37);
+         else if ((active1 & 0x20L) != 0L)
+            return jjStartNfaWithStates_0(4, 69, 37);
+         else if ((active1 & 0x400L) != 0L)
+            return jjStartNfaWithStates_0(4, 74, 37);
+         return jjMoveStringLiteralDfa5_0(active0, 0x820100000800000L, active1, 0L);
+      case 84:
+      case 116:
+         if ((active0 & 0x4000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 26, 37);
+         else if ((active0 & 0x8000000000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 51, 37);
+         else if ((active0 & 0x80000000000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 55, 37);
+         return jjMoveStringLiteralDfa5_0(active0, 0x20000000000L, active1, 0L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(3, active0, active1);
+}
+private int jjMoveStringLiteralDfa5_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(3, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(4, active0, active1);
+      return 5;
+   }
+   switch(curChar)
+   {
+      case 32:
+         return jjMoveStringLiteralDfa6_0(active0, 0x140000000000L, active1, 0L);
+      case 49:
+         if ((active0 & 0x2000000000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 61, 21);
+         break;
+      case 50:
+         if ((active0 & 0x4000000000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 62, 21);
+         break;
+      case 65:
+      case 97:
+         return jjMoveStringLiteralDfa6_0(active0, 0x800000L, active1, 0x8000L);
+      case 69:
+      case 101:
+         if ((active0 & 0x40000000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 54, 37);
+         return jjMoveStringLiteralDfa6_0(active0, 0x4000000000L, active1, 0x8L);
+      case 71:
+      case 103:
+         if ((active0 & 0x80000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 43, 37);
+         break;
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa6_0(active0, 0x400000000000000L, active1, 0L);
+      case 78:
+      case 110:
+         if ((active0 & 0x200000000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 57, 37);
+         return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x805L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa6_0(active0, 0x120000000000000L, active1, 0L);
+      case 83:
+      case 115:
+         if ((active0 & 0x20000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 41, 37);
+         return jjMoveStringLiteralDfa6_0(active0, 0x8800000000000000L, active1, 0L);
+      case 84:
+      case 116:
+         if ((active0 & 0x40000L) != 0L)
+            return jjStartNfaWithStates_0(5, 18, 37);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(4, active0, active1);
+}
+private int jjMoveStringLiteralDfa6_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(4, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(5, active0, active1);
+      return 6;
+   }
+   switch(curChar)
+   {
+      case 66:
+      case 98:
+         return jjMoveStringLiteralDfa7_0(active0, 0x140000000000L, active1, 0L);
+      case 67:
+      case 99:
+         return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x1L);
+      case 69:
+      case 101:
+         return jjMoveStringLiteralDfa7_0(active0, 0x800000000000000L, active1, 0L);
+      case 71:
+      case 103:
+         if ((active1 & 0x4L) != 0L)
+            return jjStartNfaWithStates_0(6, 66, 37);
+         break;
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa7_0(active0, 0x20000000000000L, active1, 0L);
+      case 76:
+      case 108:
+         if ((active0 & 0x800000L) != 0L)
+            return jjStartNfaWithStates_0(6, 23, 37);
+         break;
+      case 78:
+      case 110:
+         if ((active0 & 0x4000000000L) != 0L)
+            return jjStartNfaWithStates_0(6, 38, 37);
+         else if ((active0 & 0x100000000000000L) != 0L)
+            return jjStartNfaWithStates_0(6, 56, 37);
+         return jjMoveStringLiteralDfa7_0(active0, 0x400000000000000L, active1, 0L);
+      case 83:
+      case 115:
+         if ((active1 & 0x8L) != 0L)
+            return jjStartNfaWithStates_0(6, 67, 37);
+         else if ((active1 & 0x800L) != 0L)
+            return jjStartNfaWithStates_0(6, 75, 37);
+         break;
+      case 84:
+      case 116:
+         return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x8000L);
+      case 89:
+      case 121:
+         return jjMoveStringLiteralDfa7_0(active0, 0x8000000000000000L, active1, 0L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(5, active0, active1);
+}
+private int jjMoveStringLiteralDfa7_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(5, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(6, active0, active1);
+      return 7;
+   }
+   switch(curChar)
+   {
+      case 67:
+      case 99:
+         return jjMoveStringLiteralDfa8_0(active0, 0x800000000000000L, active1, 0L);
+      case 68:
+      case 100:
+         if ((active0 & 0x20000000000000L) != 0L)
+            return jjStartNfaWithStates_0(7, 53, 37);
+         break;
+      case 69:
+      case 101:
+         if ((active1 & 0x1L) != 0L)
+            return jjStartNfaWithStates_0(7, 64, 37);
+         else if ((active1 & 0x8000L) != 0L)
+            return jjStartNfaWithStates_0(7, 79, 37);
+         break;
+      case 83:
+      case 115:
+         if ((active0 & 0x400000000000000L) != 0L)
+            return jjStartNfaWithStates_0(7, 58, 37);
+         else if ((active0 & 0x8000000000000000L) != 0L)
+            return jjStartNfaWithStates_0(7, 63, 37);
+         break;
+      case 89:
+      case 121:
+         if ((active0 & 0x40000000000L) != 0L)
+            return jjStopAtPos(7, 42);
+         else if ((active0 & 0x100000000000L) != 0L)
+            return jjStopAtPos(7, 44);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(6, active0, active1);
+}
+private int jjMoveStringLiteralDfa8_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(6, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(7, active0, 0L);
+      return 8;
+   }
+   switch(curChar)
+   {
+      case 84:
+      case 116:
+         return jjMoveStringLiteralDfa9_0(active0, 0x800000000000000L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(7, active0, 0L);
+}
+private int jjMoveStringLiteralDfa9_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(7, old0, 0L);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(8, active0, 0L);
+      return 9;
+   }
+   switch(curChar)
+   {
+      case 83:
+      case 115:
+         if ((active0 & 0x800000000000000L) != 0L)
+            return jjStartNfaWithStates_0(9, 59, 37);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(8, active0, 0L);
+}
+private int jjStartNfaWithStates_0(int pos, int kind, int state)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) { return pos + 1; }
+   return jjMoveNfa_0(state, pos + 1);
+}
+private int jjMoveNfa_0(int startState, int curPos)
+{
+   int startsAt = 0;
+   jjnewStateCnt = 37;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 12:
+               case 21:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 38:
+                  if ((0x3ff000000000000L & l) != 0L)
+                  {
+                     if (kind > 100)
+                        kind = 100;
+                     jjCheckNAdd(27);
+                  }
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(23, 24);
+                  break;
+               case 37:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 0:
+                  if ((0x3ff000000000000L & l) != 0L)
+                  {
+                     if (kind > 101)
+                        kind = 101;
+                     jjCheckNAddStates(0, 6);
+                  }
+                  else if ((0x100002600L & l) != 0L)
+                  {
+                     if (kind > 1)
+                        kind = 1;
+                  }
+                  else if (curChar == 46)
+                     jjCheckNAddTwoStates(23, 27);
+                  else if (curChar == 45)
+                     jjCheckNAdd(19);
+                  else if (curChar == 33)
+                     jjstateSet[jjnewStateCnt++] = 5;
+                  else if (curChar == 60)
+                     jjstateSet[jjnewStateCnt++] = 3;
+                  if (curChar == 13)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 11:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 16:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 13:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 10:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 1:
+                  if (curChar == 10 && kind > 1)
+                     kind = 1;
+                  break;
+               case 2:
+                  if (curChar == 13)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 3:
+                  if (curChar == 62)
+                     kind = 13;
+                  break;
+               case 4:
+                  if (curChar == 60)
+                     jjstateSet[jjnewStateCnt++] = 3;
+                  break;
+               case 5:
+                  if (curChar == 61)
+                     kind = 13;
+                  break;
+               case 6:
+                  if (curChar == 33)
+                     jjstateSet[jjnewStateCnt++] = 5;
+                  break;
+               case 18:
+                  if (curChar == 45)
+                     jjCheckNAdd(19);
+                  break;
+               case 19:
+                  if (curChar != 45)
+                     break;
+                  if (kind > 88)
+                     kind = 88;
+                  jjCheckNAdd(19);
+                  break;
+               case 22:
+                  if (curChar == 46)
+                     jjCheckNAddTwoStates(23, 27);
+                  break;
+               case 23:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(23, 24);
+                  break;
+               case 25:
+                  if ((0x280000000000L & l) != 0L)
+                     jjCheckNAdd(26);
+                  break;
+               case 26:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 99)
+                     kind = 99;
+                  jjCheckNAdd(26);
+                  break;
+               case 27:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 100)
+                     kind = 100;
+                  jjCheckNAdd(27);
+                  break;
+               case 28:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 101)
+                     kind = 101;
+                  jjCheckNAddStates(0, 6);
+                  break;
+               case 29:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(29, 24);
+                  break;
+               case 30:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(30, 31);
+                  break;
+               case 31:
+                  if (curChar == 46)
+                     jjCheckNAddTwoStates(32, 24);
+                  break;
+               case 32:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(32, 24);
+                  break;
+               case 33:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(33, 34);
+                  break;
+               case 34:
+                  if (curChar != 46)
+                     break;
+                  if (kind > 100)
+                     kind = 100;
+                  jjCheckNAdd(35);
+                  break;
+               case 35:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 100)
+                     kind = 100;
+                  jjCheckNAdd(35);
+                  break;
+               case 36:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 101)
+                     kind = 101;
+                  jjCheckNAdd(36);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 12:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x8000000080000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 11;
+                  break;
+               case 37:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  break;
+               case 0:
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x200000002L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 16;
+                  else if ((0x1000000010L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 13;
+                  break;
+               case 11:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x10000000100000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 10;
+                  break;
+               case 16:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x100000001000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 15;
+                  break;
+               case 13:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x20000000200L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 12;
+                  break;
+               case 10:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x20000000200L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 9;
+                  break;
+               case 7:
+                  if ((0x10000000100000L & l) != 0L && kind > 19)
+                     kind = 19;
+                  break;
+               case 8:
+                  if ((0x800000008L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 7;
+                  break;
+               case 9:
+                  if ((0x400000004000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 8;
+                  break;
+               case 14:
+                  if ((0x1000000010L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 13;
+                  break;
+               case 15:
+                  if ((0x100000001000L & l) != 0L && kind > 19)
+                     kind = 19;
+                  break;
+               case 17:
+                  if ((0x200000002L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 16;
+                  break;
+               case 20:
+                  if ((0x7fffffe07fffffeL & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAddTwoStates(20, 21);
+                  break;
+               case 21:
+                  if ((0x7fffffe87fffffeL & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 24:
+                  if ((0x2000000020L & l) != 0L)
+                     jjAddStates(7, 8);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 37 - (jjnewStateCnt = startsAt)))
+         return curPos;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { return curPos; }
+   }
+}
+private final int jjStopStringLiteralDfa_2(int pos, long active0, long active1)
+{
+   switch (pos)
+   {
+      default :
+         return -1;
+   }
+}
+private final int jjStartNfa_2(int pos, long active0, long active1)
+{
+   return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0, active1), pos + 1);
+}
+private int jjMoveStringLiteralDfa0_2()
+{
+   switch(curChar)
+   {
+      case 39:
+         return jjStartNfaWithStates_2(0, 93, 1);
+      default :
+         return jjMoveNfa_2(0, 0);
+   }
+}
+private int jjStartNfaWithStates_2(int pos, int kind, int state)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) { return pos + 1; }
+   return jjMoveNfa_2(state, pos + 1);
+}
+private int jjMoveNfa_2(int startState, int curPos)
+{
+   int startsAt = 0;
+   jjnewStateCnt = 3;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((0xffffff7fffffffffL & l) != 0L)
+                  {
+                     if (kind > 92)
+                        kind = 92;
+                  }
+                  else if (curChar == 39)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 1:
+                  if (curChar == 39 && kind > 92)
+                     kind = 92;
+                  break;
+               case 2:
+                  if (curChar == 39)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  kind = 92;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((jjbitVec0[i2] & l2) != 0L && kind > 92)
+                     kind = 92;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+         return curPos;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { return curPos; }
+   }
+}
+private int jjMoveStringLiteralDfa0_1()
+{
+   return jjMoveNfa_1(0, 0);
+}
+private int jjMoveNfa_1(int startState, int curPos)
+{
+   int startsAt = 0;
+   jjnewStateCnt = 3;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((0x2400L & l) != 0L)
+                  {
+                     if (kind > 89)
+                        kind = 89;
+                  }
+                  if (curChar == 13)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 1:
+                  if (curChar == 10 && kind > 89)
+                     kind = 89;
+                  break;
+               case 2:
+                  if (curChar == 13)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+         return curPos;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { return curPos; }
+   }
+}
+static final int[] jjnextStates = {
+   29, 30, 31, 24, 33, 34, 36, 25, 26, 
+};
 
-	/** Constructor. */
-	public ADQLParserTokenManager(SimpleCharStream stream, int lexState){
-		this(stream);
-		SwitchTo(lexState);
-	}
+/** Token literal values. */
+public static final String[] jjstrLiteralImages = {
+"", null, "\50", "\51", "\56", "\54", "\73", "\174\174", "\53", "\55", "\52", 
+"\57", "\75", null, "\74", "\74\75", "\76", "\76\75", null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, };
 
-	/** Reinitialise parser. */
-	public void ReInit(SimpleCharStream stream){
-		jjmatchedPos = jjnewStateCnt = 0;
-		curLexState = defaultLexState;
-		input_stream = stream;
-		ReInitRounds();
-	}
+/** Lexer state names. */
+public static final String[] lexStateNames = {
+   "DEFAULT",
+   "WithinComment",
+   "WithinString",
+   "WithinDelimitedId",
+};
 
-	private void ReInitRounds(){
-		int i;
-		jjround = 0x80000001;
-		for(i = 37; i-- > 0;)
-			jjrounds[i] = 0x80000000;
-	}
+/** Lex State array. */
+public static final int[] jjnewLexState = {
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 0, -1, 2, -1, 0, 3, -1, 0, -1, -1, -1, 
+   -1, -1, -1, 
+};
+static final long[] jjtoToken = {
+   0xfffffffffffffffdL, 0x3b20ffffffL, 
+};
+static final long[] jjtoSkip = {
+   0x2L, 0x2000000L, 
+};
+static final long[] jjtoMore = {
+   0x0L, 0xdd000000L, 
+};
+protected SimpleCharStream input_stream;
+private final int[] jjrounds = new int[37];
+private final int[] jjstateSet = new int[74];
+protected char curChar;
+/** Constructor. */
+public ADQLParserTokenManager(SimpleCharStream stream){
+   if (SimpleCharStream.staticFlag)
+      throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+   input_stream = stream;
+}
 
-	/** Reinitialise parser. */
-	public void ReInit(SimpleCharStream stream, int lexState){
-		ReInit(stream);
-		SwitchTo(lexState);
-	}
+/** Constructor. */
+public ADQLParserTokenManager(SimpleCharStream stream, int lexState){
+   this(stream);
+   SwitchTo(lexState);
+}
 
-	/** Switch to specified lex state. */
-	public void SwitchTo(int lexState){
-		if (lexState >= 4 || lexState < 0)
-			throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
-		else
-			curLexState = lexState;
-	}
+/** Reinitialise parser. */
+public void ReInit(SimpleCharStream stream)
+{
+   jjmatchedPos = jjnewStateCnt = 0;
+   curLexState = defaultLexState;
+   input_stream = stream;
+   ReInitRounds();
+}
+private void ReInitRounds()
+{
+   int i;
+   jjround = 0x80000001;
+   for (i = 37; i-- > 0;)
+      jjrounds[i] = 0x80000000;
+}
 
-	protected Token jjFillToken(){
-		final Token t;
-		final String curTokenImage;
-		final int beginLine;
-		final int endLine;
-		final int beginColumn;
-		final int endColumn;
-		String im = jjstrLiteralImages[jjmatchedKind];
-		curTokenImage = (im == null) ? input_stream.GetImage() : im;
-		beginLine = input_stream.getBeginLine();
-		beginColumn = input_stream.getBeginColumn();
-		endLine = input_stream.getEndLine();
-		endColumn = input_stream.getEndColumn();
-		t = Token.newToken(jjmatchedKind, curTokenImage);
+/** Reinitialise parser. */
+public void ReInit(SimpleCharStream stream, int lexState)
+{
+   ReInit(stream);
+   SwitchTo(lexState);
+}
 
-		t.beginLine = beginLine;
-		t.endLine = endLine;
-		t.beginColumn = beginColumn;
-		t.endColumn = endColumn;
+/** Switch to specified lex state. */
+public void SwitchTo(int lexState)
+{
+   if (lexState >= 4 || lexState < 0)
+      throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+   else
+      curLexState = lexState;
+}
 
-		return t;
-	}
+protected Token jjFillToken()
+{
+   final Token t;
+   final String curTokenImage;
+   final int beginLine;
+   final int endLine;
+   final int beginColumn;
+   final int endColumn;
+   String im = jjstrLiteralImages[jjmatchedKind];
+   curTokenImage = (im == null) ? input_stream.GetImage() : im;
+   beginLine = input_stream.getBeginLine();
+   beginColumn = input_stream.getBeginColumn();
+   endLine = input_stream.getEndLine();
+   endColumn = input_stream.getEndColumn();
+   t = Token.newToken(jjmatchedKind, curTokenImage);
 
-	int curLexState = 0;
-	int defaultLexState = 0;
-	int jjnewStateCnt;
-	int jjround;
-	int jjmatchedPos;
-	int jjmatchedKind;
+   t.beginLine = beginLine;
+   t.endLine = endLine;
+   t.beginColumn = beginColumn;
+   t.endColumn = endColumn;
 
-	/** Get the next Token. */
-	public Token getNextToken(){
-		Token matchedToken;
-		int curPos = 0;
+   return t;
+}
 
-		EOFLoop: for(;;){
-			try{
-				curChar = input_stream.BeginToken();
-			}catch(java.io.IOException e){
-				jjmatchedKind = 0;
-				matchedToken = jjFillToken();
-				return matchedToken;
-			}
+int curLexState = 0;
+int defaultLexState = 0;
+int jjnewStateCnt;
+int jjround;
+int jjmatchedPos;
+int jjmatchedKind;
 
-			for(;;){
-				switch(curLexState){
-					case 0:
-						jjmatchedKind = 0x7fffffff;
-						jjmatchedPos = 0;
-						curPos = jjMoveStringLiteralDfa0_0();
-						break;
-					case 1:
-						jjmatchedKind = 0x7fffffff;
-						jjmatchedPos = 0;
-						curPos = jjMoveStringLiteralDfa0_1();
-						if (jjmatchedPos == 0 && jjmatchedKind > 90){
-							jjmatchedKind = 90;
-						}
-						break;
-					case 2:
-						jjmatchedKind = 0x7fffffff;
-						jjmatchedPos = 0;
-						curPos = jjMoveStringLiteralDfa0_2();
-						break;
-					case 3:
-						jjmatchedKind = 0x7fffffff;
-						jjmatchedPos = 0;
-						curPos = jjMoveStringLiteralDfa0_3();
-						break;
-				}
-				if (jjmatchedKind != 0x7fffffff){
-					if (jjmatchedPos + 1 < curPos)
-						input_stream.backup(curPos - jjmatchedPos - 1);
-					if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L){
-						matchedToken = jjFillToken();
-						if (jjnewLexState[jjmatchedKind] != -1)
-							curLexState = jjnewLexState[jjmatchedKind];
-						return matchedToken;
-					}else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L){
-						if (jjnewLexState[jjmatchedKind] != -1)
-							curLexState = jjnewLexState[jjmatchedKind];
-						continue EOFLoop;
-					}
-					if (jjnewLexState[jjmatchedKind] != -1)
-						curLexState = jjnewLexState[jjmatchedKind];
-					curPos = 0;
-					jjmatchedKind = 0x7fffffff;
-					try{
-						curChar = input_stream.readChar();
-						continue;
-					}catch(java.io.IOException e1){}
-				}
-				int error_line = input_stream.getEndLine();
-				int error_column = input_stream.getEndColumn();
-				String error_after = null;
-				boolean EOFSeen = false;
-				try{
-					input_stream.readChar();
-					input_stream.backup(1);
-				}catch(java.io.IOException e1){
-					EOFSeen = true;
-					error_after = curPos <= 1 ? "" : input_stream.GetImage();
-					if (curChar == '\n' || curChar == '\r'){
-						error_line++;
-						error_column = 0;
-					}else
-						error_column++;
-				}
-				if (!EOFSeen){
-					input_stream.backup(1);
-					error_after = curPos <= 1 ? "" : input_stream.GetImage();
-				}
-				throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
-			}
-		}
-	}
+/** Get the next Token. */
+public Token getNextToken() 
+{
+  Token matchedToken;
+  int curPos = 0;
 
-	private void jjCheckNAdd(int state){
-		if (jjrounds[state] != jjround){
-			jjstateSet[jjnewStateCnt++] = state;
-			jjrounds[state] = jjround;
-		}
-	}
+  EOFLoop :
+  for (;;)
+  {
+   try
+   {
+      curChar = input_stream.BeginToken();
+   }
+   catch(java.io.IOException e)
+   {
+      jjmatchedKind = 0;
+      matchedToken = jjFillToken();
+      return matchedToken;
+   }
 
-	private void jjAddStates(int start, int end){
-		do{
-			jjstateSet[jjnewStateCnt++] = jjnextStates[start];
-		}while(start++ != end);
-	}
+   for (;;)
+   {
+     switch(curLexState)
+     {
+       case 0:
+         jjmatchedKind = 0x7fffffff;
+         jjmatchedPos = 0;
+         curPos = jjMoveStringLiteralDfa0_0();
+         break;
+       case 1:
+         jjmatchedKind = 0x7fffffff;
+         jjmatchedPos = 0;
+         curPos = jjMoveStringLiteralDfa0_1();
+         if (jjmatchedPos == 0 && jjmatchedKind > 90)
+         {
+            jjmatchedKind = 90;
+         }
+         break;
+       case 2:
+         jjmatchedKind = 0x7fffffff;
+         jjmatchedPos = 0;
+         curPos = jjMoveStringLiteralDfa0_2();
+         break;
+       case 3:
+         jjmatchedKind = 0x7fffffff;
+         jjmatchedPos = 0;
+         curPos = jjMoveStringLiteralDfa0_3();
+         break;
+     }
+     if (jjmatchedKind != 0x7fffffff)
+     {
+        if (jjmatchedPos + 1 < curPos)
+           input_stream.backup(curPos - jjmatchedPos - 1);
+        if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+        {
+           matchedToken = jjFillToken();
+       if (jjnewLexState[jjmatchedKind] != -1)
+         curLexState = jjnewLexState[jjmatchedKind];
+           return matchedToken;
+        }
+        else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+        {
+         if (jjnewLexState[jjmatchedKind] != -1)
+           curLexState = jjnewLexState[jjmatchedKind];
+           continue EOFLoop;
+        }
+      if (jjnewLexState[jjmatchedKind] != -1)
+        curLexState = jjnewLexState[jjmatchedKind];
+        curPos = 0;
+        jjmatchedKind = 0x7fffffff;
+        try {
+           curChar = input_stream.readChar();
+           continue;
+        }
+        catch (java.io.IOException e1) { }
+     }
+     int error_line = input_stream.getEndLine();
+     int error_column = input_stream.getEndColumn();
+     String error_after = null;
+     boolean EOFSeen = false;
+     try { input_stream.readChar(); input_stream.backup(1); }
+     catch (java.io.IOException e1) {
+        EOFSeen = true;
+        error_after = curPos <= 1 ? "" : input_stream.GetImage();
+        if (curChar == '\n' || curChar == '\r') {
+           error_line++;
+           error_column = 0;
+        }
+        else
+           error_column++;
+     }
+     if (!EOFSeen) {
+        input_stream.backup(1);
+        error_after = curPos <= 1 ? "" : input_stream.GetImage();
+     }
+     throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+   }
+  }
+}
 
-	private void jjCheckNAddTwoStates(int state1, int state2){
-		jjCheckNAdd(state1);
-		jjCheckNAdd(state2);
-	}
+private void jjCheckNAdd(int state)
+{
+   if (jjrounds[state] != jjround)
+   {
+      jjstateSet[jjnewStateCnt++] = state;
+      jjrounds[state] = jjround;
+   }
+}
+private void jjAddStates(int start, int end)
+{
+   do {
+      jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+   } while (start++ != end);
+}
+private void jjCheckNAddTwoStates(int state1, int state2)
+{
+   jjCheckNAdd(state1);
+   jjCheckNAdd(state2);
+}
 
-	private void jjCheckNAddStates(int start, int end){
-		do{
-			jjCheckNAdd(jjnextStates[start]);
-		}while(start++ != end);
-	}
+private void jjCheckNAddStates(int start, int end)
+{
+   do {
+      jjCheckNAdd(jjnextStates[start]);
+   } while (start++ != end);
+}
 
 }
diff --git a/src/adql/parser/ADQLQueryFactory.java b/src/adql/parser/ADQLQueryFactory.java
index daef3dc567acbd9ee71e4b6b6729220e0349fb4e..4543372ddc2903a5e4d5ccf4bbb7fc7ce4a83a5f 100644
--- a/src/adql/parser/ADQLQueryFactory.java
+++ b/src/adql/parser/ADQLQueryFactory.java
@@ -16,12 +16,13 @@ package adql.parser;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.Collection;
-import java.util.Vector;
 
+import adql.db.FunctionDef;
 import adql.parser.IdentifierItems.IdentifierItem;
 import adql.query.ADQLOrder;
 import adql.query.ADQLQuery;
@@ -30,26 +31,24 @@ import adql.query.ColumnReference;
 import adql.query.IdentifierField;
 import adql.query.SelectItem;
 import adql.query.TextPosition;
+import adql.query.constraint.ADQLConstraint;
 import adql.query.constraint.Between;
 import adql.query.constraint.Comparison;
-import adql.query.constraint.ADQLConstraint;
-import adql.query.constraint.ConstraintsGroup;
 import adql.query.constraint.ComparisonOperator;
+import adql.query.constraint.ConstraintsGroup;
 import adql.query.constraint.Exists;
 import adql.query.constraint.In;
 import adql.query.constraint.IsNull;
 import adql.query.constraint.NotConstraint;
-
 import adql.query.from.ADQLJoin;
 import adql.query.from.ADQLTable;
-import adql.query.from.FromContent;
 import adql.query.from.CrossJoin;
+import adql.query.from.FromContent;
 import adql.query.from.InnerJoin;
 import adql.query.from.OuterJoin;
 import adql.query.from.OuterJoin.OuterType;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 import adql.query.operand.Concatenation;
 import adql.query.operand.NegativeOperand;
 import adql.query.operand.NumericConstant;
@@ -57,14 +56,12 @@ import adql.query.operand.Operation;
 import adql.query.operand.OperationType;
 import adql.query.operand.StringConstant;
 import adql.query.operand.WrappedOperand;
-
 import adql.query.operand.function.DefaultUDF;
 import adql.query.operand.function.MathFunction;
 import adql.query.operand.function.MathFunctionType;
 import adql.query.operand.function.SQLFunction;
 import adql.query.operand.function.SQLFunctionType;
 import adql.query.operand.function.UserDefinedFunction;
-import adql.query.operand.function.geometry.GeometryFunction.GeometryValue;
 import adql.query.operand.function.geometry.AreaFunction;
 import adql.query.operand.function.geometry.BoxFunction;
 import adql.query.operand.function.geometry.CentroidFunction;
@@ -74,6 +71,7 @@ import adql.query.operand.function.geometry.DistanceFunction;
 import adql.query.operand.function.geometry.ExtractCoord;
 import adql.query.operand.function.geometry.ExtractCoordSys;
 import adql.query.operand.function.geometry.GeometryFunction;
+import adql.query.operand.function.geometry.GeometryFunction.GeometryValue;
 import adql.query.operand.function.geometry.IntersectsFunction;
 import adql.query.operand.function.geometry.PointFunction;
 import adql.query.operand.function.geometry.PolygonFunction;
@@ -84,27 +82,30 @@ import adql.query.operand.function.geometry.RegionFunction;
  * 
  * <p>To customize the object representation you merely have to extends the appropriate functions of this class.</p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 08/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  * 
  * @see ADQLParser
  */
 public class ADQLQueryFactory {
 
-	protected boolean allowUnknownFunctions = false;
-
+	/**
+	 * Type of table JOIN.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (CDS)
+	 * @version 1.0 (08/2011)
+	 */
 	public static enum JoinType{
 		CROSS, INNER, OUTER_LEFT, OUTER_RIGHT, OUTER_FULL;
 	}
 
+	/**
+	 * Create a query factory.
+	 */
 	public ADQLQueryFactory(){
 		;
 	}
 
-	public ADQLQueryFactory(boolean allowUnknownFunctions){
-		this.allowUnknownFunctions = allowUnknownFunctions;
-	}
-
 	public ADQLQuery createQuery() throws Exception{
 		return new ADQLQuery();
 	}
@@ -273,21 +274,29 @@ public class ADQLQueryFactory {
 
 	/**
 	 * <p>Creates the user defined functions called as the given name and with the given parameters.</p>
-	 * <p><b>IMPORTANT: This function must be overridden if some user defined functions are available.</b></p>
+	 * 
+	 * <p>
+	 * 	By default, this function returns a {@link DefaultUDF} instance. It is generic enough to cover every kind of functions.
+	 * 	But you can of course override this function in order to return your own instance of {@link UserDefinedFunction}.
+	 * 	In this case, you may not forget to call the super function (super.createUserDefinedFunction(name, params)) so that
+	 * 	all other unknown functions are still returned as {@link DefaultUDF} instances.
+	 * </p>
+	 * 
+	 * <p><i><b>IMPORTANT:</b>
+	 * 	The tests done to check whether a user defined function is allowed/managed in this implementation, is done later by the parser.
+	 * 	Only declared UDF will pass the test of the parser. For that, you should give it a list of allowed UDFs (each UDF will be then
+	 * 	represented by a {@link FunctionDef} object). 
+	 * </i></p>
 	 * 
 	 * @param name			Name of the user defined function to create.
 	 * @param params		Parameters of the user defined function to create.
 	 * 
-	 * @return				The corresponding user defined function.
+	 * @return				The corresponding user defined function (by default an instance of {@link DefaultUDF}).
 	 * 
-	 * @throws Exception	An {@link UnsupportedOperationException} by default, otherwise any other type of error may be
-	 * 						thrown if there is a problem while creating the function.
+	 * @throws Exception	If there is a problem while creating the function.
 	 */
 	public UserDefinedFunction createUserDefinedFunction(String name, ADQLOperand[] params) throws Exception{
-		if (allowUnknownFunctions)
-			return new DefaultUDF(name, params);
-		else
-			throw new UnsupportedOperationException("No ADQL function called \"" + name + "\" !");
+		return new DefaultUDF(name, params);
 	}
 
 	public DistanceFunction createDistance(PointFunction point1, PointFunction point2) throws Exception{
@@ -322,7 +331,7 @@ public class ADQLQueryFactory {
 		return new RegionFunction(param);
 	}
 
-	public PolygonFunction createPolygon(ADQLOperand coordSys, Vector<ADQLOperand> coords) throws Exception{
+	public PolygonFunction createPolygon(ADQLOperand coordSys, Collection<? extends ADQLOperand> coords) throws Exception{
 		return new PolygonFunction(coordSys, coords);
 	}
 
diff --git a/src/adql/parser/adqlGrammar.jj b/src/adql/parser/adqlGrammar.jj
index bfac21d80477977153f30f41f3df1e0a7554b4fd..52e6ded3300d915ca5a701448aefd13215247d81 100644
--- a/src/adql/parser/adqlGrammar.jj
+++ b/src/adql/parser/adqlGrammar.jj
@@ -14,7 +14,7 @@
  * 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 2012-2014 - UDS/Centre de DonnM-CM-)es astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institute (ARI)
  */
 
@@ -26,7 +26,7 @@
 *  If the syntax is not conform to the ADQL definition an error message is printed else it will be the message "Correct syntax".
 *
 *  Author:  Gr&eacute;gory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de
-*  Version: 1.2 (03/2014)
+*  Version: 1.3 (10/2014)
 */
 
 							/* ########### */
@@ -35,7 +35,7 @@
 options {
 	STATIC = false;
 	IGNORE_CASE = true;
-	DEBUG_PARSER = true;
+	DEBUG_PARSER = false;
 }
 
 							/* ########## */
@@ -89,7 +89,7 @@ import adql.translator.TranslationException;
 * @see ADQLQueryFactory
 *
 * @author Gr&eacute;gory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de
-* @version 1.2 (03/2014)
+* @version 1.3 (10/2014)
 */
 public class ADQLParser {
 	
@@ -108,9 +108,6 @@ public class ADQLParser {
 	/** The first token of a table/column name. This token is extracted by {@link #Identifier()}. */
 	private Token currentIdentifierToken = null;
 	
-	/** List of all allowed coordinate systems. */
-	private ArrayList<String> allowedCoordSys = new ArrayList<String>();
-	
 	/**
 	* Builds an ADQL parser without a query to parse.
 	*/
@@ -348,24 +345,6 @@ public class ADQLParser {
 		return Query();
 	}
 	
-	public final void addCoordinateSystem(final String coordSys){
-		allowedCoordSys.add(coordSys);
-	}
-	
-	public final void setCoordinateSystems(final Collection<String> coordSys){
-		allowedCoordSys.clear();
-		if (coordSys != null)
-			allowedCoordSys.addAll(coordSys);
-	}
-	
-	public final boolean isAllowedCoordSys(final String coordSys) {
-		for(String cs : allowedCoordSys){
-			if (cs.equalsIgnoreCase(coordSys))
-				return true;
-		}
-		return false;
-	}
-	
 	public final void setDebug(boolean debug){
 		if (debug) enable_tracing();
 		else       disable_tracing();
@@ -983,8 +962,8 @@ ADQLJoin JoinSpecification(FromContent leftTable): { boolean natural = false; Jo
 /* STRING */
 /* ****** */
 String String(): {Token t; String str="";} {
-	(t=<STRING_LITERAL> {str += t.image;})+
-	{return (str!=null)?str.substring(1,str.length()-1):str;}
+	(t=<STRING_LITERAL> {str += t.image.substring(1,t.image.length()-1).replaceAll("''", "'");})+
+	{return str;}
 }
 
 /* ************* */
@@ -1011,30 +990,48 @@ String SignedInteger(): {Token sign=null, number;} {
 /* *********** */
 /* EXPRESSIONS */
 /* *********** */
-ADQLOperand ValueExpressionPrimary(): {String expr; ADQLColumn column; ADQLOperand op;} {
+ADQLOperand NumericValueExpressionPrimary(): {String expr; ADQLColumn column; ADQLOperand op;} {
 	try{
 		(// unsigned_value_specification
 		  expr=UnsignedNumeric() {return queryFactory.createNumericConstant(expr);}
-		// string
-		| expr=String() {return queryFactory.createStringConstant(expr);}
 		// column_reference
-		| column=Column() {return column;}
+		| column=Column() {column.setExpectedType('N'); return column;}
 		// set_function_specification
 		| op=SqlFunction() {return op;}
 		// LEFT_PAR value_expression RIGHT_PAR
-		| (<LEFT_PAR> op=ValueExpression() <RIGHT_PAR>) {return queryFactory.createWrappedOperand(op);})
+		| (<LEFT_PAR> op=NumericExpression() <RIGHT_PAR>) {return queryFactory.createWrappedOperand(op);})
+	}catch(Exception ex){
+		throw generateParseException(ex);
+	}
+}
+
+ADQLOperand StringValueExpressionPrimary(): {String expr; ADQLColumn column; ADQLOperand op;} {
+	try{
+		(// string
+		  expr=String() {return queryFactory.createStringConstant(expr);}
+		// column_reference
+		| column=Column() {column.setExpectedType('S'); return column;}
+		// LEFT_PAR value_expression RIGHT_PAR
+		| (<LEFT_PAR> (op=StringExpression()) <RIGHT_PAR>) {return queryFactory.createWrappedOperand(op);})
 	}catch(Exception ex){
 		throw generateParseException(ex);
 	}
 }
 
 ADQLOperand ValueExpression(): {ADQLOperand valueExpr = null; } {
-	(valueExpr=GeometryValueFunction()
-	| LOOKAHEAD(<PLUS> | <MINUS>) valueExpr=NumericExpression()
-	| LOOKAHEAD(<COORDSYS>) valueExpr=StringExpression()
-	| LOOKAHEAD(StringFactor() <CONCAT>) valueExpr=StringExpression()
-	| valueExpr=NumericExpression())
-	{return valueExpr;}
+	try{
+		(LOOKAHEAD((<PLUS>|<MINUS>) | (Factor() (<PLUS>|<MINUS>|<ASTERISK>|<DIVIDE>))) valueExpr=NumericExpression()
+		| LOOKAHEAD(<COORDSYS> | (StringFactor() <CONCAT>)) valueExpr=StringExpression()
+		| LOOKAHEAD(<LEFT_PAR>) <LEFT_PAR> valueExpr=ValueExpression() <RIGHT_PAR> { valueExpr = queryFactory.createWrappedOperand(valueExpr); }
+		| LOOKAHEAD(<REGULAR_IDENTIFIER> <LEFT_PAR>) valueExpr=UserDefinedFunction()
+		| valueExpr=GeometryValueFunction()
+		| LOOKAHEAD(Column()) valueExpr=Column()
+		| LOOKAHEAD(String()) valueExpr=StringFactor()
+		| valueExpr=Factor())
+		{return valueExpr;}
+	}catch(Exception ex){
+		throw generateParseException(ex);
+	}
 }
 
 ADQLOperand NumericExpression(): {Token sign=null; ADQLOperand leftOp, rightOp=null;} {
@@ -1070,7 +1067,7 @@ ADQLOperand NumericTerm(): {Token sign=null; ADQLOperand leftOp, rightOp=null;}
 ADQLOperand Factor(): {boolean negative = false;; ADQLOperand op;} {
 	(
 		(<PLUS> | (<MINUS> {negative = true;}))?
-		(LOOKAHEAD(2) op=NumericFunction() | op=ValueExpressionPrimary())
+		(LOOKAHEAD(2) op=NumericFunction() | op=NumericValueExpressionPrimary())
 	)
 	
 	{
@@ -1109,17 +1106,17 @@ ADQLOperand StringExpression(): {ADQLOperand leftOp; ADQLOperand rightOp = null;
 
 ADQLOperand StringFactor(): {ADQLOperand op;} {
 	(op=ExtractCoordSys()
-	| LOOKAHEAD(2) op=UserDefinedFunction()
-	| op=ValueExpressionPrimary())
+	| LOOKAHEAD(2) op=UserDefinedFunction() { ((UserDefinedFunction)op).setExpectedType('S'); }
+	| op=StringValueExpressionPrimary())
 	{return op;}
 }
 
 GeometryValue<GeometryFunction> GeometryExpression(): {ADQLColumn col = null; GeometryFunction gf = null;} {
 	(col=Column() | gf=GeometryValueFunction())
 	{
-		if (col != null)
+		if (col != null){
		  	col.setExpectedType('G');
 			return new GeometryValue<GeometryFunction>(col);
-		else
+		}else
 			return new GeometryValue<GeometryFunction>(gf);
 	}
 }
@@ -1282,24 +1279,26 @@ GeometryFunction GeometryFunction(): {Token t=null; GeometryValue<GeometryFuncti
 			})
 		// non_predicate_geometry_function
 		|	(<AREA> <LEFT_PAR> gvf1=GeometryExpression() <RIGHT_PAR>) {gf = queryFactory.createArea(gvf1);}
-		|	(<COORD1> <LEFT_PAR> (p1=Point() {gf = queryFactory.createCoord1(p1);} | col1=Column() {gf = queryFactory.createCoord1(col1);}) <RIGHT_PAR>)
-		|	(<COORD2> <LEFT_PAR> (p1=Point() {gf = queryFactory.createCoord2(p1);} | col1=Column() {gf = queryFactory.createCoord2(col1);}) <RIGHT_PAR>)
+		|	(<COORD1> <LEFT_PAR> (p1=Point() {gf = queryFactory.createCoord1(p1);} | col1=Column() {col1.setExpectedType('G'); gf = queryFactory.createCoord1(col1);}) <RIGHT_PAR>)
+		|	(<COORD2> <LEFT_PAR> (p1=Point() {gf = queryFactory.createCoord2(p1);} | col1=Column() {col1.setExpectedType('G'); gf = queryFactory.createCoord2(col1);}) <RIGHT_PAR>)
 		|	(<DISTANCE>
 				<LEFT_PAR>
 				(p1=Point()|col1=Column()) 
 				{
 					if (p1 != null)
 						gvp1 = new GeometryValue<PointFunction>(p1);
-					else
+					else{
						col1.setExpectedType('G');
 						gvp1 = new GeometryValue<PointFunction>(col1);
+					}
 				}
 				<COMMA>
 				(p2=Point()|col2=Column())
 				{
 					if (p2 != null)
 						gvp2 = new GeometryValue<PointFunction>(p2);
-					else
+					else{
						col2.setExpectedType('G');
 						gvp2 = new GeometryValue<PointFunction>(col2);
+					}
 				} 
 				<RIGHT_PAR>
 				{gf = queryFactory.createDistance(gvp1, gvp2);}
@@ -1312,19 +1311,9 @@ GeometryFunction GeometryFunction(): {Token t=null; GeometryValue<GeometryFuncti
 	{ return gf; }
 }
 
-ADQLOperand CoordinateSystem(): { Token oldToken = token; ADQLOperand coordSys=null;}{
+ADQLOperand CoordinateSystem(): { ADQLOperand coordSys=null;}{
 	coordSys=StringExpression()
-	{
-		if (allowedCoordSys.size() > 0){
-			TextPosition position = new TextPosition(oldToken.next, token);
-			if (coordSys == null)
-				throw new ParseException("A coordinate system must always be provided !", position);
-			if (coordSys instanceof StringConstant && !isAllowedCoordSys(((StringConstant)coordSys).getValue()))
-				throw new ParseException("\""+coordSys.toADQL()+"\" is not an allowed coordinate systems !", position);
-		}
-			
-		return coordSys;
-	}
+	{ return coordSys; }
 }
 
 GeometryFunction GeometryValueFunction(): {ADQLOperand coordSys; ADQLOperand width, height; ADQLOperand[] coords, tmp; Vector<ADQLOperand> vCoords; ADQLOperand op=null; GeometryValue<GeometryFunction> gvf = null; GeometryFunction gf = null;} {
@@ -1396,7 +1385,7 @@ ADQLFunction NumericFunction(): {ADQLFunction fct;} {
 	(fct=MathFunction()
 	| fct=TrigFunction()
 	| fct=GeometryFunction()
-	| fct=UserDefinedFunction())
+	| fct=UserDefinedFunction() { ((UserDefinedFunction)fct).setExpectedType('N'); })
 	{return fct;}
 }
 
@@ -1449,17 +1438,22 @@ MathFunction TrigFunction(): {Token fct=null; ADQLOperand param1=null, param2=nu
 	}
 }
 
-/* /!\ WARNING: The function name may be prefixed by "udf_" but there is no way to check it here ! */
 UserDefinedFunction UserDefinedFunction(): {Token fct; Vector<ADQLOperand> params = new Vector<ADQLOperand>(); ADQLOperand op;} {
 	fct=<REGULAR_IDENTIFIER> <LEFT_PAR> (op=ValueExpression() {params.add(op);} (<COMMA> op=ValueExpression() {params.add(op);})*)? <RIGHT_PAR>
 	{
 		//System.out.println("INFO [ADQLParser]: \""+fct.image+"\" (from line "+fct.beginLine+" and column "+fct.beginColumn+" to line "+token.endLine+" and column "+(token.endColumn+1)+") is considered as an user defined function !");
 		try{
+			//  Build the parameters list:
 			ADQLOperand[] parameters = new ADQLOperand[params.size()];
 			for(int i=0; i<params.size(); i++)
 				parameters[i] = params.get(i);
+
+			// Create the UDF function:
 			return queryFactory.createUserDefinedFunction(fct.image, parameters);
 		}catch(UnsupportedOperationException uoe){
+		  	/* This catch clause is just for backward compatibility:
+		  	 * if the createUserDefinedFunction(...) is overridden and
+		  	 * the function can not be identified a such exception may be thrown). */
 			throw new ParseException(uoe.getMessage(), new TextPosition(fct, token));
 		}catch(Exception ex){
 			throw generateParseException(ex);
diff --git a/src/adql/query/ADQLList.java b/src/adql/query/ADQLList.java
index 25d5933bdc4692b6e6297090ab6c902315fead9c..68c93a19bedc6e973a513dc89aa0a00103bbb5d0 100644
--- a/src/adql/query/ADQLList.java
+++ b/src/adql/query/ADQLList.java
@@ -16,7 +16,8 @@ package adql.query;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.Iterator;
@@ -27,8 +28,8 @@ import java.util.Vector;
  * 
  * <p>Since it is a list, it is possible to add, remove, modify and iterate on a such object.</p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 2.2 (10/2014)
  * 
  * @see ClauseADQL
  * @see ClauseConstraints
@@ -164,23 +165,30 @@ public abstract class ADQLList< T extends ADQLObject > implements ADQLObject, It
 		return list.isEmpty();
 	}
 
+	@Override
 	public String getName(){
 		return name;
 	}
 
+	@Override
 	public String toADQL(){
-		String adql = (getName() == null) ? "" : (getName() + " ");
+		StringBuffer adql = new StringBuffer((getName() == null) ? "" : (getName() + " "));
 
-		for(int i = 0; i < size(); i++)
-			adql += ((i == 0) ? "" : (" " + getSeparator(i) + " ")) + get(i).toADQL();
+		for(int i = 0; i < size(); i++){
+			if (i > 0)
+				adql.append(" " + getSeparator(i) + " ");
+			adql.append(get(i).toADQL());
+		}
 
-		return adql;
+		return adql.toString();
 	}
 
+	@Override
 	public Iterator<T> iterator(){
 		return list.iterator();
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new ADQLListIterator(this);
 	}
@@ -201,6 +209,7 @@ public abstract class ADQLList< T extends ADQLObject > implements ADQLObject, It
 	 */
 	public abstract String getSeparator(int index) throws ArrayIndexOutOfBoundsException;
 
+	@Override
 	public abstract ADQLObject getCopy() throws Exception;
 
 	/**
@@ -219,15 +228,18 @@ public abstract class ADQLList< T extends ADQLObject > implements ADQLObject, It
 			list = (ADQLList<ADQLObject>)lst;
 		}
 
+		@Override
 		public boolean hasNext(){
 			return index + 1 < list.size();
 		}
 
+		@Override
 		public ADQLObject next(){
 			removed = false;
 			return list.get(++index);
 		}
 
+		@Override
 		public void replace(ADQLObject replacer) throws UnsupportedOperationException, IllegalStateException{
 			if (index <= -1)
 				throw new IllegalStateException("replace(ADQLObject) impossible: next() has not yet been called !");
@@ -241,6 +253,7 @@ public abstract class ADQLList< T extends ADQLObject > implements ADQLObject, It
 				list.set(index, replacer);
 		}
 
+		@Override
 		public void remove(){
 			if (index <= -1)
 				throw new IllegalStateException("remove() impossible: next() has not yet been called !");
diff --git a/src/adql/query/ADQLQuery.java b/src/adql/query/ADQLQuery.java
index 75f3a647200aa376f34feee2fb92e852291c8d72..95725e4859a72b761739af9a21aa114fb7e8a354 100644
--- a/src/adql/query/ADQLQuery.java
+++ b/src/adql/query/ADQLQuery.java
@@ -17,7 +17,7 @@ package adql.query;
  * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
diff --git a/src/adql/query/from/ADQLJoin.java b/src/adql/query/from/ADQLJoin.java
index c7199334473bda2086332fe453cacab2026a12b4..bbc7482c288bace985ffc94cf0e425ad6cbae745 100644
--- a/src/adql/query/from/ADQLJoin.java
+++ b/src/adql/query/from/ADQLJoin.java
@@ -17,7 +17,7 @@ package adql.query.from;
  * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
diff --git a/src/adql/query/from/FromContent.java b/src/adql/query/from/FromContent.java
index 64da1e5220d2f434c807f7f1e26b2a3f43b9b62a..1660ebff4ba389588bcbcef2d3b1c2430ca3fb41 100644
--- a/src/adql/query/from/FromContent.java
+++ b/src/adql/query/from/FromContent.java
@@ -17,7 +17,7 @@ package adql.query.from;
  * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
diff --git a/src/adql/query/operand/ADQLColumn.java b/src/adql/query/operand/ADQLColumn.java
index d148bd70b061976d74f32df28891bed940bd9e11..57b30b5353b9871f16ebd7083912eddbf0a9112a 100644
--- a/src/adql/query/operand/ADQLColumn.java
+++ b/src/adql/query/operand/ADQLColumn.java
@@ -16,11 +16,11 @@ package adql.query.operand;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.db.DBColumn;
-
 import adql.query.ADQLIterator;
 import adql.query.ADQLObject;
 import adql.query.IdentifierField;
@@ -31,10 +31,10 @@ import adql.query.from.ADQLTable;
 /**
  * Represents the complete (literal) reference to a column ({schema(s)}.{table}.{column}).
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 07/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
-public class ADQLColumn implements ADQLOperand {
+public class ADQLColumn implements ADQLOperand, UnknownType {
 
 	/** Position in the original ADQL query string. */
 	private TextPosition position = null;
@@ -60,6 +60,10 @@ public class ADQLColumn implements ADQLOperand {
 	/** The {@link ADQLTable} which is supposed to contain this column. By default, this field is automatically filled by {@link adql.db.DBChecker}. */
 	private ADQLTable adqlTable = null;
 
+	/** Type expected by the parser.
+	 * @since 1.3 */
+	private char expectedType = '?';
+
 	/* ************ */
 	/* CONSTRUCTORS */
 	/* ************ */
@@ -457,26 +461,47 @@ public class ADQLColumn implements ADQLOperand {
 	/* ***************** */
 	/* INHERITED METHODS */
 	/* ***************** */
+	@Override
+	public char getExpectedType(){
+		return expectedType;
+	}
+
+	@Override
+	public void setExpectedType(final char c){
+		expectedType = c;
+	}
+
+	@Override
 	public boolean isNumeric(){
-		return true;
+		return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isNumeric());
 	}
 
+	@Override
 	public boolean isString(){
-		return true;
+		return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isString());
+	}
+
+	@Override
+	public boolean isGeometry(){
+		return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isGeometry());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new ADQLColumn(this);
 	}
 
+	@Override
 	public String getName(){
 		return getColumnName();
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new NullADQLIterator();
 	}
 
+	@Override
 	public String toADQL(){
 		return getFullColumnName();
 	}
diff --git a/src/adql/query/operand/ADQLOperand.java b/src/adql/query/operand/ADQLOperand.java
index 61dc647cfc22f38eebf4f731deb0d0469b97a221..413546b4f505b01dea9666c93c346fdcf6118336 100644
--- a/src/adql/query/operand/ADQLOperand.java
+++ b/src/adql/query/operand/ADQLOperand.java
@@ -16,22 +16,42 @@ package adql.query.operand;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
 
 /**
  * <p>Any ADQL operand (an operation, a constant, a column name, a function, ...) must implement this interface
- * and indicates whether it corresponds to a numeric or a string value.</p>
+ * and indicates whether it corresponds to a numeric, a string or a geometrical region value.</p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 11/2010
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public interface ADQLOperand extends ADQLObject {
 
+	/**
+	 * Tell whether this operand is numeric or not.
+	 * 
+	 * @return	<i>true</i> if this operand is numeric, <i>false</i> otherwise.
+	 */
 	public boolean isNumeric();
 
+	/**
+	 * Tell whether this operand is a string or not.
+	 * 
+	 * @return	<i>true</i> if this operand is a string, <i>false</i> otherwise.
+	 */
 	public boolean isString();
 
+	/**
+	 * Tell whether this operand is a geometrical region or not.
+	 * 
+	 * @return	<i>true</i> if this operand is a geometry, <i>false</i> otherwise.
+	 * 
+	 * @since 1.3
+	 */
+	public boolean isGeometry();
+
 }
diff --git a/src/adql/query/operand/Concatenation.java b/src/adql/query/operand/Concatenation.java
index a09b7905adc6e885cb0af492d19ef8a05e990782..24536c72cfafd754bc359a462ca7afe757b62dfc 100644
--- a/src/adql/query/operand/Concatenation.java
+++ b/src/adql/query/operand/Concatenation.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLList;
@@ -25,8 +26,8 @@ import adql.query.ADQLObject;
 /**
  * Represents a concatenation in ADQL (ex: <i>"_s_ra" || ':' || "_s_dec"</i>).
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 11/2010
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public final class Concatenation extends ADQLList<ADQLOperand> implements ADQLOperand {
 
@@ -65,12 +66,19 @@ public final class Concatenation extends ADQLList<ADQLOperand> implements ADQLOp
 		return "||";
 	}
 
+	@Override
 	public final boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public final boolean isString(){
 		return true;
 	}
 
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
 }
\ No newline at end of file
diff --git a/src/adql/query/operand/NegativeOperand.java b/src/adql/query/operand/NegativeOperand.java
index 82fad6bb3691b3dbaa474902526b68a8c3fdbc4c..b6e4dd7c022473e531b296c21c39ad7cd1eb3998 100644
--- a/src/adql/query/operand/NegativeOperand.java
+++ b/src/adql/query/operand/NegativeOperand.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.NoSuchElementException;
@@ -27,8 +28,8 @@ import adql.query.ADQLObject;
 /**
  * Lets putting a minus sign in front of any numeric operand.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public final class NegativeOperand implements ADQLOperand {
 
@@ -66,6 +67,7 @@ public final class NegativeOperand implements ADQLOperand {
 	/** Always returns <i>true</i>.
 	 * @see adql.query.operand.ADQLOperand#isNumeric()
 	 */
+	@Override
 	public final boolean isNumeric(){
 		return true;
 	}
@@ -73,24 +75,37 @@ public final class NegativeOperand implements ADQLOperand {
 	/** Always returns <i>false</i>.
 	 * @see adql.query.operand.ADQLOperand#isString()
 	 */
+	@Override
 	public final boolean isString(){
 		return false;
 	}
 
+	/** Always returns <i>false</i>.
+	 * @see adql.query.operand.ADQLOperand#isGeometry()
+	 */
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		NegativeOperand copy = new NegativeOperand((ADQLOperand)operand.getCopy());
 		return copy;
 	}
 
+	@Override
 	public String getName(){
 		return "-" + operand.getName();
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new ADQLIterator(){
 
 			private boolean operandGot = (operand == null);
 
+			@Override
 			public ADQLObject next(){
 				if (operandGot)
 					throw new NoSuchElementException();
@@ -98,10 +113,12 @@ public final class NegativeOperand implements ADQLOperand {
 				return operand;
 			}
 
+			@Override
 			public boolean hasNext(){
 				return !operandGot;
 			}
 
+			@Override
 			public void replace(ADQLObject replacer) throws UnsupportedOperationException, IllegalStateException{
 				if (!operandGot)
 					throw new IllegalStateException("replace(ADQLObject) impossible: next() has not yet been called !");
@@ -114,6 +131,7 @@ public final class NegativeOperand implements ADQLOperand {
 					throw new UnsupportedOperationException("Impossible to replace the operand \"" + operand.toADQL() + "\" by \"" + replacer.toADQL() + "\" in the NegativeOperand \"" + toADQL() + "\" because the replacer is not an ADQLOperand or is not numeric !");
 			}
 
+			@Override
 			public void remove(){
 				if (!operandGot)
 					throw new IllegalStateException("remove() impossible: next() has not yet been called !");
@@ -123,6 +141,7 @@ public final class NegativeOperand implements ADQLOperand {
 		};
 	}
 
+	@Override
 	public String toADQL(){
 		return "-" + operand.toADQL();
 	}
diff --git a/src/adql/query/operand/NumericConstant.java b/src/adql/query/operand/NumericConstant.java
index 2baa6d260079d0f290cab19b9f1983f1a1d3881f..8baa697effca3d8f284383d834eb5095d103c2af 100644
--- a/src/adql/query/operand/NumericConstant.java
+++ b/src/adql/query/operand/NumericConstant.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLIterator;
@@ -26,8 +27,8 @@ import adql.query.NullADQLIterator;
 /**
  * A numeric (integer, double, ...) constant.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public final class NumericConstant implements ADQLOperand {
 
@@ -149,6 +150,7 @@ public final class NumericConstant implements ADQLOperand {
 	/** Always returns <i>true</i>.
 	 * @see adql.query.operand.ADQLOperand#isNumeric()
 	 */
+	@Override
 	public final boolean isNumeric(){
 		return true;
 	}
@@ -156,22 +158,35 @@ public final class NumericConstant implements ADQLOperand {
 	/** Always returns <i>false</i>.
 	 * @see adql.query.operand.ADQLOperand#isString()
 	 */
+	@Override
 	public final boolean isString(){
 		return false;
 	}
 
+	/** Always returns <i>false</i>.
+	 * @see adql.query.operand.ADQLOperand#isGeometry()
+	 */
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
+	@Override
 	public ADQLObject getCopy(){
 		return new NumericConstant(this);
 	}
 
+	@Override
 	public String getName(){
 		return value;
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new NullADQLIterator();
 	}
 
+	@Override
 	public String toADQL(){
 		return value;
 	}
diff --git a/src/adql/query/operand/Operation.java b/src/adql/query/operand/Operation.java
index b5398ffeabc4864e09e40bf369402850cefd1c6e..d890770377557406eff06a686b0f11031322f1e9 100644
--- a/src/adql/query/operand/Operation.java
+++ b/src/adql/query/operand/Operation.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.NoSuchElementException;
@@ -27,8 +28,8 @@ import adql.query.ADQLObject;
 /**
  * It represents a simple numeric operation (sum, difference, multiplication and division).
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  * 
  * @see OperationType
  */
@@ -160,6 +161,7 @@ public class Operation implements ADQLOperand {
 	/** Always returns <i>true</i>.
 	 * @see adql.query.operand.ADQLOperand#isNumeric()
 	 */
+	@Override
 	public final boolean isNumeric(){
 		return true;
 	}
@@ -167,24 +169,37 @@ public class Operation implements ADQLOperand {
 	/** Always returns <i>false</i>.
 	 * @see adql.query.operand.ADQLOperand#isString()
 	 */
+	@Override
 	public final boolean isString(){
 		return false;
 	}
 
+	/** Always returns <i>false</i>.
+	 * @see adql.query.operand.ADQLOperand#isGeometry()
+	 */
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new Operation(this);
 	}
 
+	@Override
 	public String getName(){
 		return operation.toString();
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new ADQLIterator(){
 
 			private int index = -1;
 			private ADQLOperand operand = null;
 
+			@Override
 			public ADQLObject next(){
 				index++;
 
@@ -197,10 +212,12 @@ public class Operation implements ADQLOperand {
 				return operand;
 			}
 
+			@Override
 			public boolean hasNext(){
 				return index + 1 < 2;
 			}
 
+			@Override
 			public void replace(ADQLObject replacer) throws UnsupportedOperationException, IllegalStateException{
 				if (index <= -1)
 					throw new IllegalStateException("replace(ADQLObject) impossible: next() has not yet been called !");
@@ -218,6 +235,7 @@ public class Operation implements ADQLOperand {
 				}
 			}
 
+			@Override
 			public void remove(){
 				if (index <= -1)
 					throw new IllegalStateException("remove() impossible: next() has not yet been called !");
@@ -227,6 +245,7 @@ public class Operation implements ADQLOperand {
 		};
 	}
 
+	@Override
 	public String toADQL(){
 		return leftOperand.toADQL() + operation.toADQL() + rightOperand.toADQL();
 	}
diff --git a/src/adql/query/operand/StringConstant.java b/src/adql/query/operand/StringConstant.java
index 2e2b0d4ff1e5cc5e942f9c16120909ff5d687a0c..6c115e74b2287f87e4c2d021b69d9cbd3f8f2408 100644
--- a/src/adql/query/operand/StringConstant.java
+++ b/src/adql/query/operand/StringConstant.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLIterator;
@@ -26,8 +27,8 @@ import adql.query.NullADQLIterator;
 /**
  * A string constant.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public final class StringConstant implements ADQLOperand {
 
@@ -49,28 +50,39 @@ public final class StringConstant implements ADQLOperand {
 		this.value = value;
 	}
 
+	@Override
 	public final boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public final boolean isString(){
 		return true;
 	}
 
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
+	@Override
 	public ADQLObject getCopy(){
 		return new StringConstant(this);
 	}
 
+	@Override
 	public String getName(){
-		return "'" + value + "'";
+		return toADQL();
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new NullADQLIterator();
 	}
 
+	@Override
 	public String toADQL(){
-		return "'" + value + "'";
+		return "'" + value.replaceAll("'", "''") + "'";
 	}
 
 }
diff --git a/src/adql/query/operand/UnknownType.java b/src/adql/query/operand/UnknownType.java
new file mode 100644
index 0000000000000000000000000000000000000000..bae9d9bb728c2aa5947dced16dd86d73fb446f86
--- /dev/null
+++ b/src/adql/query/operand/UnknownType.java
@@ -0,0 +1,52 @@
+package adql.query.operand;
+
+/*
+ * This file is part of ADQLLibrary.
+ * 
+ * ADQLLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * ADQLLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * 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 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import adql.query.operand.function.UserDefinedFunction;
+
+/**
+ * <p>Operand whose the type can not be known at the parsing time.
+ * A post-parsing step with column metadata is needed to resolved their types.</p>
+ * 
+ * <p><i>Note:
+ * 	For the moment, only two operands are concerned: columns ({@link ADQLColumn}) and user defined functions ({@link UserDefinedFunction}).
+ * </i></p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 1.3 (10/2014)
+ * @since 1.3
+ */
+public interface UnknownType extends ADQLOperand {
+
+	/**
+	 * Get the type expected by the syntactic parser according to the context.
+	 * 
+	 * @return	Expected type: 'n' or 'N' for numeric, 's' or 'S' for string, 'g' or 'G' for geometry.
+	 */
+	public char getExpectedType();
+
+	/**
+	 * Set the type expected for this operand.
+	 * 
+	 * @param c	Expected type: 'n' or 'N' for numeric, 's' or 'S' for string, 'g' or 'G' for geometry.
+	 */
+	public void setExpectedType(final char c);
+
+}
diff --git a/src/adql/query/operand/WrappedOperand.java b/src/adql/query/operand/WrappedOperand.java
index 2ffae9bfd13bb85ed39029eef00bea7b5f9760dc..98fde1d171ef34f4a8d63998003a93755e6086f7 100644
--- a/src/adql/query/operand/WrappedOperand.java
+++ b/src/adql/query/operand/WrappedOperand.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.NoSuchElementException;
@@ -27,8 +28,8 @@ import adql.query.ADQLObject;
 /**
  * Lets wrapping an operand by parenthesis.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class WrappedOperand implements ADQLOperand {
 
@@ -57,27 +58,38 @@ public class WrappedOperand implements ADQLOperand {
 		return operand;
 	}
 
+	@Override
 	public final boolean isNumeric(){
 		return operand.isNumeric();
 	}
 
+	@Override
 	public final boolean isString(){
 		return operand.isString();
 	}
 
+	@Override
+	public final boolean isGeometry(){
+		return operand.isGeometry();
+	}
+
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new WrappedOperand((ADQLOperand)operand.getCopy());
 	}
 
+	@Override
 	public String getName(){
 		return "(" + operand.getName() + ")";
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new ADQLIterator(){
 
 			private boolean operandGot = (operand == null);
 
+			@Override
 			public ADQLObject next(){
 				if (operandGot)
 					throw new NoSuchElementException();
@@ -85,10 +97,12 @@ public class WrappedOperand implements ADQLOperand {
 				return operand;
 			}
 
+			@Override
 			public boolean hasNext(){
 				return !operandGot;
 			}
 
+			@Override
 			public void replace(ADQLObject replacer) throws UnsupportedOperationException, IllegalStateException{
 				if (!operandGot)
 					throw new IllegalStateException("replace(ADQLObject) impossible: next() has not yet been called !");
@@ -101,6 +115,7 @@ public class WrappedOperand implements ADQLOperand {
 					throw new UnsupportedOperationException("Impossible to replace an ADQLOperand (\"" + operand + "\") by a " + replacer.getClass().getName() + " (\"" + replacer.toADQL() + "\") !");
 			}
 
+			@Override
 			public void remove(){
 				if (!operandGot)
 					throw new IllegalStateException("remove() impossible: next() has not yet been called !");
@@ -110,6 +125,7 @@ public class WrappedOperand implements ADQLOperand {
 		};
 	}
 
+	@Override
 	public String toADQL(){
 		return "(" + operand.toADQL() + ")";
 	}
diff --git a/src/adql/query/operand/function/DefaultUDF.java b/src/adql/query/operand/function/DefaultUDF.java
index c08436508295c7a080a3151f2b74494607f9f382..dd0c615843e0282855415d4c9b50c7fd4f9dda74 100644
--- a/src/adql/query/operand/function/DefaultUDF.java
+++ b/src/adql/query/operand/function/DefaultUDF.java
@@ -16,9 +16,11 @@ package adql.query.operand.function;
  * 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 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS), Astronomisches Rechen Institut (ARI)
+ * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
+import adql.db.FunctionDef;
 import adql.query.ADQLList;
 import adql.query.ADQLObject;
 import adql.query.ClauseADQL;
@@ -28,13 +30,18 @@ import adql.query.operand.ADQLOperand;
  * It represents any function which is not managed by ADQL.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 1.2 (04/2014)
+ * @version 1.3 (10/2014)
  */
 public final class DefaultUDF extends UserDefinedFunction {
 
-	/** Its parameters. */
+	/** Define/Describe this user defined function.
+	 * This object gives the return type and the number and type of all parameters. */
+	protected FunctionDef definition = null;
+
+	/** Its parsed parameters. */
 	protected final ADQLList<ADQLOperand> parameters;
 
+	/** Parsed name of this UDF. */
 	protected final String functionName;
 
 	/**
@@ -62,19 +69,56 @@ public final class DefaultUDF extends UserDefinedFunction {
 		parameters = (ADQLList<ADQLOperand>)(toCopy.parameters.getCopy());
 	}
 
+	/**
+	 * Get the signature/definition/description of this user defined function.
+	 * The returned object provides information on the return type and the number and type of parameters. 
+	 * 
+	 * @return	Definition of this function. (MAY be NULL)
+	 */
+	public final FunctionDef getDefinition(){
+		return definition;
+	}
+
+	/**
+	 * <p>Let set the signature/definition/description of this user defined function.</p>
+	 * 
+	 * <p><i><b>IMPORTANT:</b>
+	 * 	No particular checks are done here except on the function name which MUST
+	 * 	be the same (case insensitive) as the name of the given definition.
+	 * 	Advanced checks must have been done before calling this setter.
+	 * </i></p>
+	 * 
+	 * @param def	The definition applying to this parsed UDF, or NULL if none has been found.
+	 * 
+	 * @throws IllegalArgumentException	If the name in the given definition does not match the name of this parsed function.
+	 */
+	public final void setDefinition(final FunctionDef def) throws IllegalArgumentException{
+		if (def != null && (def.name == null || !functionName.equalsIgnoreCase(def.name)))
+			throw new IllegalArgumentException("The parsed function name (" + functionName + ") does not match to the name of the given UDF definition (" + def.name + ").");
+
+		this.definition = def;
+	}
+
 	@Override
 	public final boolean isNumeric(){
-		return true;
+		return (definition == null || definition.isNumeric());
 	}
 
 	@Override
 	public final boolean isString(){
-		return true;
+		return (definition == null || definition.isString());
+	}
+
+	@Override
+	public final boolean isGeometry(){
+		return (definition == null || definition.isGeometry());
 	}
 
 	@Override
 	public ADQLObject getCopy() throws Exception{
-		return new DefaultUDF(this);
+		DefaultUDF copy = new DefaultUDF(this);
+		copy.setDefinition(definition);
+		return copy;
 	}
 
 	@Override
diff --git a/src/adql/query/operand/function/MathFunction.java b/src/adql/query/operand/function/MathFunction.java
index 15acf0a2f4a9689c483e22814477f150cc2d5631..c29d64a8c6f333fc241c5e10e24dc46cbccff4fd 100644
--- a/src/adql/query/operand/function/MathFunction.java
+++ b/src/adql/query/operand/function/MathFunction.java
@@ -27,7 +27,7 @@ import adql.query.operand.ADQLOperand;
  * It represents any basic mathematical function.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 1.2 (03/2014)
+ * @version 1.3 (10/2014)
  * 
  * @see MathFunctionType
  */
@@ -149,6 +149,11 @@ public class MathFunction extends ADQLFunction {
 		return false;
 	}
 
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		switch(getNbParameters()){
diff --git a/src/adql/query/operand/function/SQLFunction.java b/src/adql/query/operand/function/SQLFunction.java
index 3442aea581efa2e092d1eb65a78bdab9f62db74c..6603fd90b6c0083feb4468cde5b7e0a1bff5270f 100644
--- a/src/adql/query/operand/function/SQLFunction.java
+++ b/src/adql/query/operand/function/SQLFunction.java
@@ -16,18 +16,18 @@ package adql.query.operand.function;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
 import adql.query.operand.ADQLOperand;
 
 /**
  * It represents any SQL function (COUNT, MAX, MIN, AVG, SUM, etc...).
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  * 
  * @see SQLFunctionType
  */
@@ -116,22 +116,31 @@ public class SQLFunction extends ADQLFunction {
 		return type;
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new SQLFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return type.name();
 	}
 
+	@Override
 	public final boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public final boolean isString(){
 		return false;
 	}
 
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		if (param != null)
diff --git a/src/adql/query/operand/function/UserDefinedFunction.java b/src/adql/query/operand/function/UserDefinedFunction.java
index 39d87784e8a0c71fa26e6774277054eee98bb4f5..78026e72d5429d4b6d76f07b42c3226ca241bbeb 100644
--- a/src/adql/query/operand/function/UserDefinedFunction.java
+++ b/src/adql/query/operand/function/UserDefinedFunction.java
@@ -1,5 +1,7 @@
 package adql.query.operand.function;
 
+import adql.query.operand.UnknownType;
+
 /*
  * This file is part of ADQLLibrary.
  * 
@@ -16,17 +18,32 @@ package adql.query.operand.function;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 /**
  * Function defined by the user (i.e. PSQL functions).
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 01/2012
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  * 
  * @see DefaultUDF
  */
-public abstract class UserDefinedFunction extends ADQLFunction {
+public abstract class UserDefinedFunction extends ADQLFunction implements UnknownType {
+
+	/** Type expected by the parser.
+	 * @since 1.3 */
+	private char expectedType = '?';
+
+	@Override
+	public char getExpectedType(){
+		return expectedType;
+	}
+
+	@Override
+	public void setExpectedType(final char c){
+		expectedType = c;
+	}
 
 }
diff --git a/src/adql/query/operand/function/geometry/AreaFunction.java b/src/adql/query/operand/function/geometry/AreaFunction.java
index 42906c9811341c3c2929f26b3befe67f801fddef..6b163aca0a6775956801405384fb447e8b66de91 100644
--- a/src/adql/query/operand/function/geometry/AreaFunction.java
+++ b/src/adql/query/operand/function/geometry/AreaFunction.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the AREA function of ADQL.</p>
@@ -33,8 +33,8 @@ import adql.query.operand.ADQLColumn;
  * 
  * <p>Inappropriate geometries for this construct (e.g. POINT) SHOULD either return zero or throw an error message. <b>This choice must be done in an extended class of {@link AreaFunction}</b>.</p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class AreaFunction extends GeometryFunction {
 
@@ -47,7 +47,6 @@ public class AreaFunction extends GeometryFunction {
 	 * @param param					Parameter of AREA.
 	 * @throws NullPointerException	If the given operand is <i>null</i> or if it's not a {@link GeometryFunction}.
 	 */
-	@SuppressWarnings("unchecked")
 	public AreaFunction(GeometryValue<GeometryFunction> param) throws NullPointerException{
 		super();
 		if (param == null)
@@ -88,22 +87,31 @@ public class AreaFunction extends GeometryFunction {
 		this.parameter = parameter;
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new AreaFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "AREA";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		return new ADQLOperand[]{parameter.getValue()};
diff --git a/src/adql/query/operand/function/geometry/BoxFunction.java b/src/adql/query/operand/function/geometry/BoxFunction.java
index e9071330227d2d550e05cebba42a20ae8faca7fa..7e064a4f32d88b372036b606ea07443bcb02529f 100644
--- a/src/adql/query/operand/function/geometry/BoxFunction.java
+++ b/src/adql/query/operand/function/geometry/BoxFunction.java
@@ -16,11 +16,11 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
 import adql.query.operand.ADQLOperand;
 
 /**
@@ -38,9 +38,8 @@ import adql.query.operand.ADQLOperand;
  * In this second example the coordinates of the center position are extracted from a coordinate's column reference.
  * </i></p>
  * 
- * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class BoxFunction extends GeometryFunction {
 
@@ -93,19 +92,28 @@ public class BoxFunction extends GeometryFunction {
 		height = (ADQLOperand)(toCopy.height.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new BoxFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "BOX";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public boolean isString(){
+		return false;
+	}
+
+	@Override
+	public boolean isGeometry(){
 		return true;
 	}
 
diff --git a/src/adql/query/operand/function/geometry/CentroidFunction.java b/src/adql/query/operand/function/geometry/CentroidFunction.java
index 88f80e3fe0a3d97d516f0b73b79ec35c71fcc0af..3b3b0147982cbd9a1f50a2d8a6282e4e238484cb 100644
--- a/src/adql/query/operand/function/geometry/CentroidFunction.java
+++ b/src/adql/query/operand/function/geometry/CentroidFunction.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the CENTROID function of the ADQL language.</p>
@@ -35,8 +35,8 @@ import adql.query.operand.ADQLColumn;
  * in a position of (25.4,-20.0) degrees and defined according to the ICRS coordinate system with GEOCENTER reference position.
  * </i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class CentroidFunction extends GeometryFunction {
 
@@ -68,22 +68,31 @@ public class CentroidFunction extends GeometryFunction {
 		parameter = (GeometryValue<GeometryFunction>)(toCopy.parameter.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new CentroidFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "CENTROID";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		return new ADQLOperand[]{parameter.getValue()};
diff --git a/src/adql/query/operand/function/geometry/CircleFunction.java b/src/adql/query/operand/function/geometry/CircleFunction.java
index aff59d89e1832212617fff22d4a6a8f2b27ad34a..f979d3850bf3afe53526fe67e68cf47a85a6e3fa 100644
--- a/src/adql/query/operand/function/geometry/CircleFunction.java
+++ b/src/adql/query/operand/function/geometry/CircleFunction.java
@@ -16,11 +16,11 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
 import adql.query.operand.ADQLOperand;
 
 /**
@@ -35,8 +35,8 @@ import adql.query.operand.ADQLOperand;
  * according to the ICRS coordinate system with GEOCENTER reference position.
  * </i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class CircleFunction extends GeometryFunction {
 
@@ -83,19 +83,28 @@ public class CircleFunction extends GeometryFunction {
 		radius = (ADQLOperand)(toCopy.radius.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new CircleFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "CIRCLE";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public boolean isString(){
+		return false;
+	}
+
+	@Override
+	public boolean isGeometry(){
 		return true;
 	}
 
diff --git a/src/adql/query/operand/function/geometry/ContainsFunction.java b/src/adql/query/operand/function/geometry/ContainsFunction.java
index f97a9c7614240b3703aa1c75c97c9bcef54bbc2d..4cb4aad040c85f5a8bc225b1c3b5fc03ac60b4d0 100644
--- a/src/adql/query/operand/function/geometry/ContainsFunction.java
+++ b/src/adql/query/operand/function/geometry/ContainsFunction.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the CONTAINS function of the ADQL language.</p>
@@ -41,8 +41,8 @@ import adql.query.operand.ADQLColumn;
  * If it can not do so, it SHOULD throw an error message, to be defined by the service making use of ADQL.</li></ul>
  * </b></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class ContainsFunction extends GeometryFunction {
 
@@ -81,22 +81,31 @@ public class ContainsFunction extends GeometryFunction {
 		rightParam = (GeometryValue<GeometryFunction>)(toCopy.rightParam.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new ContainsFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "CONTAINS";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	/**
 	 * @return The leftParam.
 	 */
diff --git a/src/adql/query/operand/function/geometry/DistanceFunction.java b/src/adql/query/operand/function/geometry/DistanceFunction.java
index d9429675837a943e30a23ad807ef5611ba98964c..ead9b4b465c21e70cad5d6a1623b57c7efac19bc 100644
--- a/src/adql/query/operand/function/geometry/DistanceFunction.java
+++ b/src/adql/query/operand/function/geometry/DistanceFunction.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the DISTANCE function of the ADQL language.</p>
@@ -34,8 +34,8 @@ import adql.query.operand.ADQLColumn;
  * In this example the function computes the distance between two points of coordinates (25, -19.5) and (25.4, -20) both expressed according to the ICRS
  * coordinate system with GEOCENTER reference position.</i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class DistanceFunction extends GeometryFunction {
 
@@ -79,22 +79,31 @@ public class DistanceFunction extends GeometryFunction {
 		throw new UnsupportedOperationException("A DISTANCE function is not associated to a coordinate system !");
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new DistanceFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "DISTANCE";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	/**
 	 * Gets the first point.
 	 * 
diff --git a/src/adql/query/operand/function/geometry/ExtractCoord.java b/src/adql/query/operand/function/geometry/ExtractCoord.java
index 6f016c9c0ca356d495587262df438eb758ba4116..2fa17cd953ceb46f0da616199f7c9e1283156489 100644
--- a/src/adql/query/operand/function/geometry/ExtractCoord.java
+++ b/src/adql/query/operand/function/geometry/ExtractCoord.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the COORD1 and the COORD2 functions of the ADQL language.</p>
@@ -35,8 +35,8 @@ import adql.query.operand.ADQLColumn;
  * system with GEOCENTER reference position.
  * </i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class ExtractCoord extends GeometryFunction {
 
@@ -78,22 +78,31 @@ public class ExtractCoord extends GeometryFunction {
 		point = (GeometryValue<PointFunction>)(toCopy.point.getCopy());
 	}
 
+	@Override
 	public String getName(){
 		return "COORD" + indCoord;
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new ExtractCoord(this);
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		return new ADQLOperand[]{point.getValue()};
diff --git a/src/adql/query/operand/function/geometry/ExtractCoordSys.java b/src/adql/query/operand/function/geometry/ExtractCoordSys.java
index 5337f494b43151115804d82f0139fa3c1bd4083c..dcd8e542a75d8a11d7961788fd3ed64a7996e05b 100644
--- a/src/adql/query/operand/function/geometry/ExtractCoordSys.java
+++ b/src/adql/query/operand/function/geometry/ExtractCoordSys.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the COORDSYS function the ADQL language.</p>
@@ -35,8 +35,8 @@ import adql.query.operand.ADQLColumn;
  * system with GEOCENTER reference position.
  * </i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class ExtractCoordSys extends GeometryFunction {
 
@@ -65,22 +65,31 @@ public class ExtractCoordSys extends GeometryFunction {
 		geomExpr = (GeometryValue<GeometryFunction>)(toCopy.geomExpr.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new ExtractCoordSys(this);
 	}
 
+	@Override
 	public String getName(){
 		return "COORDSYS";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public boolean isString(){
 		return true;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		return new ADQLOperand[]{geomExpr.getValue()};
diff --git a/src/adql/query/operand/function/geometry/GeometryFunction.java b/src/adql/query/operand/function/geometry/GeometryFunction.java
index 4270eb183e469f6c6f144d7202a57766b9e64b19..e6e10ecf2513847a9ca922fd53e78e621f8ff670 100644
--- a/src/adql/query/operand/function/geometry/GeometryFunction.java
+++ b/src/adql/query/operand/function/geometry/GeometryFunction.java
@@ -16,20 +16,23 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
+import adql.parser.ParseException;
 import adql.query.ADQLIterator;
 import adql.query.ADQLObject;
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.StringConstant;
 import adql.query.operand.function.ADQLFunction;
 
 /**
  * <p>It represents any geometric function of ADQL.</p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public abstract class GeometryFunction extends ADQLFunction {
 
@@ -80,13 +83,13 @@ public abstract class GeometryFunction extends ADQLFunction {
 	 * @param coordSys							Its new coordinate system.
 	 * @throws UnsupportedOperationException	If this function is not associated with a coordinate system.
 	 * @throws NullPointerException				If the given operand is <i>null</i>.
-	 * @throws Exception						If the given operand is not a string.
+	 * @throws ParseException					If the given operand is not a string.
 	 */
-	public void setCoordinateSystem(ADQLOperand coordSys) throws UnsupportedOperationException, NullPointerException, Exception{
+	public void setCoordinateSystem(ADQLOperand coordSys) throws UnsupportedOperationException, NullPointerException, ParseException{
 		if (coordSys == null)
-			throw new NullPointerException("");
+			this.coordSys = new StringConstant("");
 		else if (!coordSys.isString())
-			throw new Exception("A coordinate system must be a string literal: \"" + coordSys.toADQL() + "\" is not a string operand !");
+			throw new ParseException("A coordinate system must be a string literal: \"" + coordSys.toADQL() + "\" is not a string operand!");
 		else
 			this.coordSys = coordSys;
 	}
@@ -95,8 +98,8 @@ public abstract class GeometryFunction extends ADQLFunction {
 	 * This class represents a parameter of a geometry function
 	 * which, in general, is either a GeometryFunction or a Column.
 	 * 
-	 * @author Gr&eacute;gory Mantelet (CDS)
-	 * @version 06/2011
+	 * @author Gr&eacute;gory Mantelet (CDS;ARI)
+	 * @version 1.3 (10/2014)
 	 */
 	public static final class GeometryValue< F extends GeometryFunction > implements ADQLOperand {
 		private ADQLColumn column;
@@ -142,26 +145,37 @@ public abstract class GeometryFunction extends ADQLFunction {
 			return column != null;
 		}
 
+		@Override
 		public boolean isNumeric(){
 			return getValue().isNumeric();
 		}
 
+		@Override
 		public boolean isString(){
 			return getValue().isString();
 		}
 
+		@Override
+		public boolean isGeometry(){
+			return getValue().isGeometry();
+		}
+
+		@Override
 		public ADQLObject getCopy() throws Exception{
 			return new GeometryValue<F>(this);
 		}
 
+		@Override
 		public String getName(){
 			return getValue().getName();
 		}
 
+		@Override
 		public ADQLIterator adqlIterator(){
 			return getValue().adqlIterator();
 		}
 
+		@Override
 		public String toADQL(){
 			return getValue().toADQL();
 		}
diff --git a/src/adql/query/operand/function/geometry/IntersectsFunction.java b/src/adql/query/operand/function/geometry/IntersectsFunction.java
index 0a37597eadac5b751740c327abb68402f33be157..29bc881fdf61bd455068e3afe694dca137a7a2c4 100644
--- a/src/adql/query/operand/function/geometry/IntersectsFunction.java
+++ b/src/adql/query/operand/function/geometry/IntersectsFunction.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the INTERSECTS function of the ADQL language.</p>
@@ -42,8 +42,8 @@ import adql.query.operand.ADQLColumn;
  * If it can not do so, it SHOULD throw an error message, to be defined by the service making use of ADQL.</li></ul>
  * </b></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class IntersectsFunction extends GeometryFunction {
 
@@ -82,22 +82,31 @@ public class IntersectsFunction extends GeometryFunction {
 		rightParam = (GeometryValue<GeometryFunction>)(toCopy.rightParam.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new IntersectsFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "INTERSECTS";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	/**
 	 * @return The leftParam.
 	 */
diff --git a/src/adql/query/operand/function/geometry/PointFunction.java b/src/adql/query/operand/function/geometry/PointFunction.java
index 626e34f22a0cbd7ed93ec71ea5f228cc87643f6a..1b736ffabe897b65ee20200f0a9db0602d0f4f00 100644
--- a/src/adql/query/operand/function/geometry/PointFunction.java
+++ b/src/adql/query/operand/function/geometry/PointFunction.java
@@ -16,11 +16,11 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
 import adql.query.operand.ADQLOperand;
 
 /**
@@ -34,8 +34,8 @@ import adql.query.operand.ADQLOperand;
  * In this example the function expresses a point with right ascension of 25 degrees and declination of  -19.5 degrees according
  * to the ICRS coordinate system with GEOCENTER reference position.</i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class PointFunction extends GeometryFunction {
 
@@ -59,7 +59,7 @@ public class PointFunction extends GeometryFunction {
 		super(coordinateSystem);
 
 		if (firstCoord == null || secondCoord == null)
-			throw new NullPointerException("The POINT function must have non-null coordinates !");
+			throw new NullPointerException("The POINT function must have non-null coordinates!");
 
 		coord1 = firstCoord;
 		coord2 = secondCoord;
@@ -127,19 +127,28 @@ public class PointFunction extends GeometryFunction {
 			this.coord2 = coord2;
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new PointFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "POINT";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public boolean isString(){
+		return false;
+	}
+
+	@Override
+	public boolean isGeometry(){
 		return true;
 	}
 
diff --git a/src/adql/query/operand/function/geometry/PolygonFunction.java b/src/adql/query/operand/function/geometry/PolygonFunction.java
index 07e47ba629e47d979dae71d4db2c36f33819f866..33dd2a629c49bc989025a4fba21fe7d8ab343a9c 100644
--- a/src/adql/query/operand/function/geometry/PolygonFunction.java
+++ b/src/adql/query/operand/function/geometry/PolygonFunction.java
@@ -16,13 +16,14 @@ package adql.query.operand.function.geometry;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
+import java.util.Collection;
 import java.util.Vector;
 
 import adql.query.ADQLObject;
-
 import adql.query.operand.ADQLOperand;
 
 /**
@@ -40,8 +41,8 @@ import adql.query.operand.ADQLOperand;
  * In this example the function expresses a triangle, whose vertices are (10.0, -10.5), (20.0, 20.5) and (30.0, 30.5) in degrees
  * according to the STC coordinate system with GEOCENTER reference position.</i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class PolygonFunction extends GeometryFunction {
 
@@ -79,7 +80,7 @@ public class PolygonFunction extends GeometryFunction {
 	 * @throws NullPointerException				If one of the parameters is <i>null</i>.
 	 * @throws Exception						If there is another error.
 	 */
-	public PolygonFunction(ADQLOperand coordSystem, Vector<ADQLOperand> coords) throws UnsupportedOperationException, NullPointerException, Exception{
+	public PolygonFunction(ADQLOperand coordSystem, Collection<? extends ADQLOperand> coords) throws UnsupportedOperationException, NullPointerException, Exception{
 		super(coordSystem);
 		if (coords == null || coords.size() < 6)
 			throw new NullPointerException("A POLYGON function must have at least 3 2-D coordinates !");
@@ -102,19 +103,28 @@ public class PolygonFunction extends GeometryFunction {
 			coordinates.add((ADQLOperand)(item.getCopy()));
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new PolygonFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "POLYGON";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public boolean isString(){
+		return false;
+	}
+
+	@Override
+	public boolean isGeometry(){
 		return true;
 	}
 
diff --git a/src/adql/query/operand/function/geometry/RegionFunction.java b/src/adql/query/operand/function/geometry/RegionFunction.java
index dea04a27d30356320f9e011fa7e9288d7c5878d1..998d4a56a7abb22bc75cf9810334e6db7daa4a5b 100644
--- a/src/adql/query/operand/function/geometry/RegionFunction.java
+++ b/src/adql/query/operand/function/geometry/RegionFunction.java
@@ -38,7 +38,7 @@ import adql.query.operand.ADQLOperand;
  * Inappropriate geometries for this construct SHOULD throw an error message, to be defined by the service making use of ADQL.</b></p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 1.2 (02/2014)
+ * @version 1.3 (10/2014)
  */
 public class RegionFunction extends GeometryFunction {
 
@@ -88,6 +88,11 @@ public class RegionFunction extends GeometryFunction {
 
 	@Override
 	public boolean isString(){
+		return false;
+	}
+
+	@Override
+	public boolean isGeometry(){
 		return true;
 	}
 
diff --git a/src/tap/ADQLExecutor.java b/src/tap/ADQLExecutor.java
index fc9c6c4481eb529331b769d177b1733b8a5556be..43b18fbfd1f271d06c5abcfc40643602a1bc2643 100644
--- a/src/tap/ADQLExecutor.java
+++ b/src/tap/ADQLExecutor.java
@@ -496,8 +496,6 @@ public class ADQLExecutor {
 			parser = new ADQLParser(queryChecker);
 		else
 			parser = new ADQLParser(queryChecker, queryFactory);
-		parser.setCoordinateSystems(service.getCoordinateSystems());
-		parser.setDebug(false);
 		ADQLQuery query = parser.parseQuery(tapParams.getQuery());
 
 		// Set or check the row limit:
diff --git a/src/tap/AbstractTAPFactory.java b/src/tap/AbstractTAPFactory.java
index 642aa10b6cf3152f49c2ad1aeea8909fd55a23f2..36984fac6ae7b123c3899bea600228f91148db59 100644
--- a/src/tap/AbstractTAPFactory.java
+++ b/src/tap/AbstractTAPFactory.java
@@ -44,6 +44,7 @@ import uws.service.backup.UWSBackupManager;
 import uws.service.error.ServiceErrorWriter;
 import adql.db.DBChecker;
 import adql.parser.ADQLQueryFactory;
+import adql.parser.ParseException;
 import adql.parser.QueryChecker;
 import adql.query.ADQLQuery;
 
@@ -52,7 +53,7 @@ import adql.query.ADQLQuery;
  * Only the functions related with the database connection stay abstract.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (09/2014)
+ * @version 2.0 (10/2014)
  */
 public abstract class AbstractTAPFactory extends TAPFactory {
 
@@ -130,7 +131,7 @@ public abstract class AbstractTAPFactory extends TAPFactory {
 	 * </p>
 	 * 
 	 * <p><i>Note:
-	 * 	This function can not be overrided, but {@link #createQueryChecker(Collection)} can be.
+	 * 	This function can not be overridded, but {@link #createQueryChecker(Collection)} can be.
 	 * </i></p>
 	 */
 	@Override
@@ -172,7 +173,11 @@ public abstract class AbstractTAPFactory extends TAPFactory {
 	 * @throws TAPException	If any error occurs while creating the query checker.
 	 */
 	protected QueryChecker createQueryChecker(final Collection<TAPTable> tables) throws TAPException{
-		return new DBChecker(tables);
+		try{
+			return new DBChecker(tables, service.getUDFs(), service.getGeometries(), service.getCoordinateSystems());
+		}catch(ParseException e){
+			throw new TAPException("Unable to build a DBChecker instance! " + e.getMessage(), e, UWSException.INTERNAL_SERVER_ERROR);
+		}
 	}
 
 	/* ****** */
diff --git a/src/tap/ServiceConnection.java b/src/tap/ServiceConnection.java
index 360fcece32850ec82e8c2e2abcac7b157e47de46..f849d8283bee98b6ec763afb837c75049ba882b5 100644
--- a/src/tap/ServiceConnection.java
+++ b/src/tap/ServiceConnection.java
@@ -23,64 +23,420 @@ package tap;
 import java.util.Collection;
 import java.util.Iterator;
 
+import tap.file.LocalTAPFileManager;
 import tap.file.TAPFileManager;
 import tap.formatter.OutputFormat;
+import tap.log.DefaultTAPLog;
 import tap.log.TAPLog;
 import tap.metadata.TAPMetadata;
 import uws.service.UserIdentifier;
+import adql.db.FunctionDef;
 
 /**
- * TODO JAVADOC OF THE WHOLE CLASS!
+ * <p>Description and parameters list of a TAP service.</p>
+ * 
+ * <p>
+ * 	Through this object, it is possible to configure the different limits and formats,
+ * 	but also to list all available tables and columns, to declare geometry features as all allowed user defined functions
+ * 	and to say where log and other kinds of files must be stored.
+ * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (09/2014)
+ * @version 2.0 (10/2014)
  */
 public interface ServiceConnection {
 
+	/**
+	 * List of possible limit units.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (CDS;ARI)
+	 * @version 2.0 (10/2014)
+	 */
 	public static enum LimitUnit{
-		rows("row"),
-		bytes("byte");
-		
+		rows("row"), bytes("byte");
+
 		private final String str;
+
 		private LimitUnit(final String str){
 			this.str = str;
 		}
+
+		@Override
 		public String toString(){
 			return str;
 		}
 	}
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Name of the service provider ; it can be an organization as an individual person.</p>
+	 * 
+	 * <p>There is no restriction on the syntax or on the label to use ; this information is totally free</p>
+	 * 
+	 * <p>It will be used as additional information (INFO tag) in any VOTable and HTML output.</p>
+	 * 
+	 * @return	The TAP service provider or NULL to leave this field blank.
+	 */
 	public String getProviderName();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Description of the service provider.</p>
+	 * 
+	 * <p>It will be used as additional information (INFO tag) in any VOTable output.</p>
+	 * 
+	 * @return	The TAP service description or NULL to leave this field blank.
+	 */
 	public String getProviderDescription();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>This function controls the state of the whole TAP service.</p>
+	 * 
+	 * <p>
+	 * 	A message explaining the current state of the TAP service could be provided thanks to {@link #getAvailability()}.
+	 * </p>
+	 * 
+	 * @return	<i>true</i> to enable all TAP resources, <i>false</i> to disable all of them (except /availability).
+	 */
 	public boolean isAvailable();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get an explanation about the current TAP service state (working or not).
+	 * This message aims to provide more details to the users about the availability of this service,
+	 * or more particularly about its unavailability.</p>
+	 * 
+	 * @return	Explanation about the TAP service state.
+	 */
 	public String getAvailability();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the limit of the retention period.</p>
+	 * 
+	 * <p>
+	 * 	It is the maximum period while an asynchronous job can leave in the jobs list
+	 * 	and so can stay on the server.
+	 * </p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the retention period is not limited and jobs will
+	 * 	    theoretically stay infinitely on the server.</li>
+	 * 	<li><b>If not NULL</b>, the 2 values must correspond to the default retention period
+	 * 	    and the maximum retention period.</li>
+	 * 	<li><b>The default value</b> is used to set the retention period when a job is created with no user defined retention period.</li>
+	 * 	<li><b>The maximum value</b> is used to limit the retention period when specified by the user while creating a job.</li>
+	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
+	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
+	 * </ul>
+	 * 
+	 * @return NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
+	 */
 	public int[] getRetentionPeriod();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the limit of the job execution duration.</p>
+	 * 
+	 * <p>
+	 * 	It is the duration of a running job (including the query execution).
+	 * 	This duration is used for synchronous AND asynchronous jobs.
+	 * </p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the execution duration is not limited and jobs could
+	 * 	    theoretically run infinitely.</li>
+	 * 	<li><b>If not NULL</b>, the 2 values must correspond to the default execution duration
+	 * 	    and the maximum execution duration.</li>
+	 * 	<li><b>The default value</b> is used to set the execution duration when a job is created with no user defined execution duration.</li>
+	 * 	<li><b>The maximum value</b> is used to limit the execution duration when specified by the user while creating a job.</li>
+	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
+	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
+	 * </ul>
+	 * 
+	 * @return	NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
+	 */
 	public int[] getExecutionDuration();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the limit of the job execution result.</p>
+	 * 
+	 * <p>
+	 * 	This value will limit the size of the query results, either in rows or in bytes.
+	 * 	The type of limit is defined by the function {@link #getOutputLimitType()}.
+	 * </p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the output limit is not limited and jobs could theoretically
+	 * 	    return very big files.</li>
+	 * 	<li><b>If not NULL</b>, the 2 values must correspond to the default output limit
+	 * 	    and the maximum output limit.</li>
+	 * 	<li><b>The default value</b> is used to set the output limit when a job is created with no user defined output limit.</li>
+	 * 	<li><b>The maximum value</b> is used to limit the output limit when specified by the user while creating a job.</li>
+	 * 	<li><b>The structure of the object</b> returned by this function MUST be the same as the object returned by {@link #getOutputLimitType()}.
+	 * 	    Particularly, the type given by the N-th item of {@link #getOutputLimitType()} must correspond to the N-th limit returned by this function.</li>
+	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
+	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
+	 * </ul>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	To save performances, it is strongly recommended to use ROWS limit rather than in bytes. Indeed, the rows limit can be taken
+	 * 	into account at the effective execution of the query (so before getting the result), on the contrary of the bytes limit which
+	 * 	will be applied on the query result.
+	 * </i></p>
+	 * 
+	 * @return	NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
+	 * 
+	 * @see #getOutputLimitType()
+	 */
 	public int[] getOutputLimit();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the type of each output limit set by this service connection (and accessible with {@link #getOutputLimit()}).</p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the output limit will be considered as expressed in ROWS.</li>
+	 * 	<li><b>The structure of the object</b> returned by this function MUST be the same as the object returned by {@link #getOutputLimit()}.
+	 * 	    Particularly, the type given by the N-th item of this function must correspond to the N-th limit returned by {@link #getOutputLimit()}.</li>
+	 * </ul>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	To save performances, it is strongly recommended to use ROWS limit rather than in bytes. Indeed, the rows limit can be taken
+	 * 	into account at the effective execution of the query (so before getting the result), on the contrary of the bytes limit which
+	 * 	will be applied on the query result.
+	 * </i></p>
+	 * 
+	 * @return	NULL if limits should be expressed in ROWS, or a two-items array ([0]: type of getOutputLimit()[0], [1]: type of getOutputLimit()[1]). 
+	 * 
+	 * @see #getOutputLimit()
+	 */
 	public LimitUnit[] getOutputLimitType();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the object to use in order to identify users at the origin of requests.</p>
+	 * 
+	 * @return	NULL if no user identification should be done, a {@link UserIdentifier} instance otherwise.
+	 */
 	public UserIdentifier getUserIdentifier();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>This function let enable or disable the upload capability of this TAP service.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	If the upload is disabled, the request is aborted and an HTTP-400 error is thrown each time some tables are uploaded.
+	 * </i></p>
+	 * 
+	 * @return	<i>true</i> to enable the upload capability, <i>false</i> to disable it.
+	 */
 	public boolean uploadEnabled();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the maximum size of EACH uploaded table.</p>
+	 * 
+	 * <p>
+	 * 	This value is expressed either in rows or in bytes.
+	 * 	The unit limit is defined by the function {@link #getUploadLimitType()}.
+	 * </p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the upload limit is not limited and uploads could be
+	 * 	    theoretically unlimited.</li>
+	 * 	<li><b>If not NULL</b>, the 2 values must correspond to the default upload limit
+	 * 	    and the maximum upload limit.</li>
+	 * 	<li><b>The default value</b> is used inform the user about the server wishes.</li>
+	 * 	<li><b>The maximum value</b> is used to really limit the upload limit.</li>
+	 * 	<li><b>The structure of the object</b> returned by this function MUST be the same as the object returned by {@link #getUploadLimitType()}.
+	 * 	    Particularly, the type given by the N-th item of {@link #getUploadLimitType()} must correspond to the N-th limit returned by this function.</li>
+	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
+	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
+	 * </ul>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	To save performances, it is recommended to use BYTES limit rather than in rows. Indeed, the bytes limit can be taken
+	 * 	into account at directly when reading the bytes of the request, on the contrary of the rows limit which
+	 * 	requires to parse the uploaded tables.
+	 * </i></p>
+	 * 
+	 * @return	NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
+	 * 
+	 * @see #getUploadLimitType()
+	 */
 	public int[] getUploadLimit();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the type of each upload limit set by this service connection (and accessible with {@link #getUploadLimit()}).</p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the upload limit will be considered as expressed in ROWS.</li>
+	 * 	<li><b>The structure of the object</b> returned by this function MUST be the same as the object returned by {@link #getUploadLimit()}.
+	 * 	    Particularly, the type given by the N-th item of this function must correspond to the N-th limit returned by {@link #getUploadLimit()}.</li>
+	 * </ul>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	To save performances, it is recommended to use BYTES limit rather than in rows. Indeed, the bytes limit can be taken
+	 * 	into account at directly when reading the bytes of the request, on the contrary of the rows limit which
+	 * 	requires to parse the uploaded tables.
+	 * </i></p>
+	 * 
+	 * @return	NULL if limits should be expressed in ROWS, or a two-items array ([0]: type of getUploadLimit()[0], [1]: type of getUploadLimit()[1]). 
+	 * 
+	 * @see #getUploadLimit()
+	 */
 	public LimitUnit[] getUploadLimitType();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the maximum size of the whole set of all tables uploaded in one request.
+	 * This size is expressed in bytes.</p>
+	 * 
+	 * <p><b>IMPORTANT 1:
+	 * 	This value is always used when the upload capability is enabled.
+	 * </b></p>
+	 * 
+	 * <p><b>IMPORTANT 2:
+	 * 	The value returned by this function MUST always be positive.
+	 * 	A zero or negative value will throw an exception later while
+	 * 	reading parameters in a request with some uploaded tables.
+	 * </b></p>
+	 * 
+	 * @return	A positive (&gt;0) value corresponding to the maximum number of bytes of all uploaded tables sent in one request.
+	 */
 	public int getMaxUploadSize();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the list of all available tables and columns.</p>
+	 * 
+	 * <p>
+	 * 	This object is really important since it lets the library check ADQL queries properly and set the good type
+	 * 	and formatting in the query results.
+	 * </p>
+	 *  
+	 * @return	A TAPMetadata object. <b>NULL is not allowed and will throw a grave error at the service initialization.</b>
+	 */
 	public TAPMetadata getTAPMetadata();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the list of all allowed coordinate systems.</p>
+	 * 
+	 * <u><b>Special values</b></u>
+	 * 
+	 * <p>Two special values can be returned by this function:</p>
+	 * <ul>
+	 * 	<li><b>NULL</b> which means that all coordinate systems are allowed,</li>
+	 * 	<li><b>the empty list</b> which means that no coordinate system - except
+	 * 	    the default one (which can be reduced to an empty string) - is allowed.</li>
+	 * </ul>
+	 * 
+	 * <u><b>List item syntax</b></u>
+	 * 
+	 * <p>
+	 * 	Each item of this list is a <b>pattern</b> and not a simple coordinate system.
+	 * 	Thus each item MUST respect the following syntax:
+	 * </p>
+	 * <pre>{framePattern} {refposPattern} {flavorPattern}</pre>
+	 * <p>
+	 * 	Contrary to a coordinate system expression, all these 3 information are required.
+	 * 	Each may take 3 kinds of value:
+	 * </p>
+	 * <ul>
+	 * 	<li>a single value (i.e. "ICRS"),</li>
+	 * 	<li>a list of values with the syntax <code>({value1}|{value2}|...)</code> (i.e. "(ICRS|FK4)"),</li>
+	 * 	<li>a "*" which means that all values are possible.
+	 * </ul>
+	 * <p>
+	 * 	For instance: <code>(ICRS|FK4) HELIOCENTER *</code> is a good syntax,
+	 * 	but not <code>ICRS</code> or <code>ICRS HELIOCENTER</code>.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	Even if not explicitly part of the possible values, the default value of each part (i.e. UNKNOWNFRAME for frame) is always taken into account by the library.
+	 * 	Particularly, the empty string will always be allowed even if not explicitly listed in the list returned by this function.
+	 * </i></p>
+	 * 
+	 * @return	NULL to allow ALL coordinate systems, an empty list to allow NO coordinate system,
+	 *        	or a list of coordinate system patterns otherwise.
+	 * 
+	 * @since 2.0
+	 */
 	public Collection<String> getCoordinateSystems();
 
 	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the list of all allowed geometrical functions.</p>
+	 * 
+	 * <u><b>Special values</b></u>
+	 * 
+	 * <p>Two special values can be returned by this function:</p>
+	 * <ul>
+	 * 	<li><b>NULL</b> which means that all geometrical functions are allowed,</li>
+	 * 	<li><b>the empty list</b> which means that no geometrical functions is allowed.</li>
+	 * </ul>
+	 * 
+	 * <u><b>List item syntax</b></u>
+	 * 
+	 * <p>
+	 * 	Each item of the returned list MUST be a function name (i.e. "CONTAINS", "POINT").
+	 * 	It can also be a type of STC region to forbid (i.e. "POSITION", "UNION").
+	 * </p>
+	 * 
+	 * <p>The given names are not case sensitive.</p>
+	 * 
+	 * @return	NULL to allow ALL geometrical functions, an empty list to allow NO geometrical function,
+	 *        	or a list of geometrical function names otherwise.
+	 * 
+	 * @since 2.0
+	 */
+	public Collection<String> getGeometries();
+
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the list of all allowed User Defined Functions (UDFs).</p>
+	 * 
+	 * <u><b>Special values</b></u>
+	 * 
+	 * <p>Two special values can be returned by this function:</p>
+	 * <ul>
+	 * 	<li><b>NULL</b> which means that all unknown functions (which should be UDFs) are allowed,</li>
+	 * 	<li><b>the empty list</b> which means that no unknown functions (which should be UDFs) is allowed.</li>
+	 * </ul>
+	 * 
+	 * <u><b>List item syntax</b></u>
+	 * 
+	 * <p>
+	 * 	Each item of the returned list MUST be an instance of {@link FunctionDef}.
+	 * </p>
+	 * 
+	 * @return	NULL to allow ALL unknown functions, an empty list to allow NO unknown function,
+	 *        	or a list of user defined functions otherwise.
+	 * 
+	 * @since 2.0
+	 */
+	public Collection<FunctionDef> getUDFs();
+
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * 
 	 * <p>Get the maximum number of asynchronous jobs that can run in the same time.</p>
 	 * 
 	 * <p>A null or negative value means <b>no limit</b> on the number of running asynchronous jobs.</p> 
@@ -91,14 +447,88 @@ public interface ServiceConnection {
 	 */
 	public int getNbMaxAsyncJobs();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the logger to use in the whole service when any error, warning or info happens.</p>
+	 * 
+	 * <p><b>IMPORTANT:
+	 * 	If NULL is returned by this function, grave errors will occur while executing a query or managing an error.
+	 * 	It is strongly recommended to provide a logger, even a basic implementation.
+	 * </b></p>
+	 * 
+	 * <p><i>Piece of advice:
+	 * 	A default implementation like {@link DefaultTAPLog} would be most of time largely enough.
+	 * </i></p>
+	 * 
+	 * @return	An instance of {@link TAPLog}.
+	 */
 	public TAPLog getLogger();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the object able to build other objects essentials to configure the TAP service or to run every queries.</p>
+	 * 
+	 * <p><b>IMPORTANT:
+	 * 	If NULL is returned by this function, grave errors will occur while initializing the service.
+	 * </b></p>
+	 * 
+	 * <p><i>Piece of advice:
+	 * 	The {@link TAPFactory} is an interface which contains a lot of functions to implement.
+	 * 	It is rather recommended to extend {@link AbstractTAPFactory}: just 3 functions ({@link AbstractTAPFactory#countFreeConnections()},
+	 * 	{@link AbstractTAPFactory#freeConnection()}, {@link AbstractTAPFactory#getConnection(String)}) will have to be implemented. 
+	 * </i></p>
+	 * 
+	 * @return	An instance of {@link TAPFactory}.
+	 * 
+	 * @see AbstractTAPFactory
+	 */
 	public TAPFactory getFactory();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the object in charge of the files management.
+	 * This object manages log, error, result and backup files of the whole service.</p>
+	 * 
+	 * <p><b>IMPORTANT:
+	 * 	If NULL is returned by this function, grave errors will occur while initializing the service.
+	 * </b></p>
+	 * 
+	 * <p><i>Piece of advice:
+	 * 	The library provides a default implementation of the interface {@link TAPFileManager}:
+	 * 	{@link LocalTAPFileManager}, which stores all files on the local file-system.
+	 * </i></p>
+	 * 
+	 * @return	An instance of {@link TAPFileManager}.
+	 */
 	public TAPFileManager getFileManager();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the list of all available output formats.</p>
+	 * 
+	 * <p><b>IMPORTANT:</b></p>
+	 * <ul>
+	 * 	<li>All formats of this list MUST have a different MIME type.</li>
+	 * 	<li>At least one item must correspond to the MIME type "votable".</li>
+	 * 	<li>If NULL is returned by this function, grave errors will occur while writing the capabilities of this service.</li>
+	 * </li>
+	 * 
+	 * @return	An iterator on the list of all available output formats.
+	 */
 	public Iterator<OutputFormat> getOutputFormats();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the output format having the given MIME type (or short MIME type ~ alias).</p>
+	 * 
+	 * <p><b>IMPORTANT:
+	 * 	This function MUST always return an {@link OutputFormat} instance when the MIME type "votable" is given in parameter.
+	 * </b></p>
+	 * 
+	 * @param mimeOrAlias	MIME type or short MIME type of the format to get.
+	 * 
+	 * @return	The corresponding {@link OutputFormat} or NULL if not found.
+	 */
 	public OutputFormat getOutputFormat(final String mimeOrAlias);
 
 }
diff --git a/src/tap/data/LimitedTableIterator.java b/src/tap/data/LimitedTableIterator.java
index 570351137980821c99384fb5fc829b439b8a9f29..97f82e6661285782af2ed190f65e3b07c51f4912 100644
--- a/src/tap/data/LimitedTableIterator.java
+++ b/src/tap/data/LimitedTableIterator.java
@@ -27,8 +27,8 @@ import java.util.NoSuchElementException;
 
 import tap.ServiceConnection.LimitUnit;
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
 import tap.upload.LimitedSizeInputStream;
+import adql.db.DBType;
 
 import com.oreilly.servlet.multipart.ExceededSizeException;
 
@@ -219,7 +219,7 @@ public class LimitedTableIterator implements TableIterator {
 	}
 
 	@Override
-	public TAPType getColType() throws IllegalStateException, DataReadException{
+	public DBType getColType() throws IllegalStateException, DataReadException{
 		testOverflow();
 		return innerIt.getColType();
 	}
diff --git a/src/tap/data/ResultSetTableIterator.java b/src/tap/data/ResultSetTableIterator.java
index 6f9f6d740fe44be98d7ece03236a7e3a8bc48b2a..a580c87fbfec937fb3008eb44504d10bc2a0d1d3 100644
--- a/src/tap/data/ResultSetTableIterator.java
+++ b/src/tap/data/ResultSetTableIterator.java
@@ -26,10 +26,10 @@ import java.sql.Timestamp;
 import java.util.NoSuchElementException;
 
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import uws.ISO8601Format;
 import adql.db.DBColumn;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 
 /**
  * <p>{@link TableIterator} which lets iterate over a SQL {@link ResultSet}.</p>
@@ -198,11 +198,11 @@ public class ResultSetTableIterator implements TableIterator {
 					try{
 						colMeta[i - 1] = (TAPColumn)resultMeta[i - 1];
 					}catch(ClassCastException cce){
-						TAPType datatype = convertType(metadata.getColumnTypeName(i), dbms);
+						DBType datatype = convertType(metadata.getColumnTypeName(i), dbms);
 						colMeta[i - 1] = new TAPColumn(resultMeta[i - 1].getADQLName(), datatype);
 					}
 				}else{
-					TAPType datatype = convertType(metadata.getColumnTypeName(i), dbms);
+					DBType datatype = convertType(metadata.getColumnTypeName(i), dbms);
 					colMeta[i - 1] = new TAPColumn(metadata.getColumnLabel(i), datatype);
 				}
 			}
@@ -284,7 +284,7 @@ public class ResultSetTableIterator implements TableIterator {
 	}
 
 	@Override
-	public TAPType getColType() throws IllegalStateException, DataReadException{
+	public DBType getColType() throws IllegalStateException, DataReadException{
 		// Basically check the read state (for rows iteration):
 		checkReadState();
 
@@ -299,7 +299,7 @@ public class ResultSetTableIterator implements TableIterator {
 	}
 
 	/**
-	 * <p>Convert the given DBMS type into the better matching {@link TAPType} instance.
+	 * <p>Convert the given DBMS type into the better matching {@link DBType} instance.
 	 * This function is used to guess the TAP type of a column when it is not provided in the constructor.
 	 * It aims not to be exhaustive, but just to provide a type when the given TAP metadata are incomplete.</p>
 	 * 
@@ -328,12 +328,12 @@ public class ResultSetTableIterator implements TableIterator {
 	 * @param dbmsType	DBMS column datatype name.
 	 * @param dbms		Lower-case string which indicates which DBMS the ResultSet is coming from. <i>note: MAY be NULL.</i>
 	 * 
-	 * @return	The best suited {@link TAPType} object.
+	 * @return	The best suited {@link DBType} object.
 	 */
-	protected TAPType convertType(String dbmsType, final String dbms){
+	protected DBType convertType(String dbmsType, final String dbms){
 		// If no type is provided return VARCHAR:
 		if (dbmsType == null || dbmsType.trim().length() == 0)
-			return new TAPType(TAPDatatype.VARCHAR, TAPType.NO_LENGTH);
+			return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
 
 		// Extract the type prefix and lower-case it:
 		dbmsType = dbmsType.toLowerCase();
@@ -345,61 +345,61 @@ public class ResultSetTableIterator implements TableIterator {
 		if (dbms != null && dbms.equals("sqlite")){
 			// INTEGER -> SMALLINT, INTEGER, BIGINT
 			if (dbmsTypePrefix.equals("integer"))
-				return new TAPType(TAPDatatype.BIGINT);
+				return new DBType(DBDatatype.BIGINT);
 			// REAL -> REAL, DOUBLE
 			else if (dbmsTypePrefix.equals("real"))
-				return new TAPType(TAPDatatype.DOUBLE);
+				return new DBType(DBDatatype.DOUBLE);
 			// TEXT -> CHAR, VARCHAR, CLOB, TIMESTAMP
 			else if (dbmsTypePrefix.equals("text"))
-				return new TAPType(TAPDatatype.VARCHAR);
+				return new DBType(DBDatatype.VARCHAR);
 			// BLOB -> BINARY, VARBINARY, BLOB
 			else if (dbmsTypePrefix.equals("blob"))
-				return new TAPType(TAPDatatype.BLOB);
+				return new DBType(DBDatatype.BLOB);
 			// Default:
 			else
-				return new TAPType(TAPDatatype.VARCHAR, TAPType.NO_LENGTH);
+				return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
 		}
 		// CASE: OTHER DBMS
 		else{
 			// SMALLINT
 			if (dbmsTypePrefix.equals("smallint") || dbmsTypePrefix.equals("int2"))
-				return new TAPType(TAPDatatype.SMALLINT);
+				return new DBType(DBDatatype.SMALLINT);
 			// INTEGER
 			else if (dbmsTypePrefix.equals("integer") || dbmsTypePrefix.equals("int") || dbmsTypePrefix.equals("int4"))
-				return new TAPType(TAPDatatype.INTEGER);
+				return new DBType(DBDatatype.INTEGER);
 			// BIGINT
-			else if (dbmsTypePrefix.equals("bigint") || dbmsTypePrefix.equals("int8") || dbmsTypePrefix.equals("int4") || dbmsTypePrefix.equals("number"))
-				return new TAPType(TAPDatatype.BIGINT);
+			else if (dbmsTypePrefix.equals("bigint") || dbmsTypePrefix.equals("int8") || dbmsTypePrefix.equals("number"))
+				return new DBType(DBDatatype.BIGINT);
 			// REAL
 			else if (dbmsTypePrefix.equals("float4") || (dbmsTypePrefix.equals("float") && firstParam <= 63))
-				return new TAPType(TAPDatatype.REAL);
+				return new DBType(DBDatatype.REAL);
 			// DOUBLE
 			else if (dbmsTypePrefix.equals("double") || dbmsTypePrefix.equals("double precision") || dbmsTypePrefix.equals("float8") || (dbmsTypePrefix.equals("float") && firstParam > 63))
-				return new TAPType(TAPDatatype.DOUBLE);
+				return new DBType(DBDatatype.DOUBLE);
 			// BINARY
 			else if (dbmsTypePrefix.equals("binary") || dbmsTypePrefix.equals("raw") || ((dbmsTypePrefix.equals("char") || dbmsTypePrefix.equals("character")) && dbmsType.endsWith(" for bit data")))
-				return new TAPType(TAPDatatype.BINARY, firstParam);
+				return new DBType(DBDatatype.BINARY, firstParam);
 			// VARBINARY
 			else if (dbmsTypePrefix.equals("varbinary") || dbmsTypePrefix.equals("long raw") || ((dbmsTypePrefix.equals("varchar") || dbmsTypePrefix.equals("character varying")) && dbmsType.endsWith(" for bit data")))
-				return new TAPType(TAPDatatype.VARBINARY, firstParam);
+				return new DBType(DBDatatype.VARBINARY, firstParam);
 			// CHAR
 			else if (dbmsTypePrefix.equals("char") || dbmsTypePrefix.equals("character"))
-				return new TAPType(TAPDatatype.CHAR, firstParam);
+				return new DBType(DBDatatype.CHAR, firstParam);
 			// VARCHAR
 			else if (dbmsTypePrefix.equals("varchar") || dbmsTypePrefix.equals("varchar2") || dbmsTypePrefix.equals("character varying"))
-				return new TAPType(TAPDatatype.VARBINARY, firstParam);
+				return new DBType(DBDatatype.VARBINARY, firstParam);
 			// BLOB
 			else if (dbmsTypePrefix.equals("bytea") || dbmsTypePrefix.equals("blob") || dbmsTypePrefix.equals("binary large object"))
-				return new TAPType(TAPDatatype.BLOB);
+				return new DBType(DBDatatype.BLOB);
 			// CLOB
 			else if (dbmsTypePrefix.equals("text") || dbmsTypePrefix.equals("clob") || dbmsTypePrefix.equals("character large object"))
-				return new TAPType(TAPDatatype.CLOB);
+				return new DBType(DBDatatype.CLOB);
 			// TIMESTAMP
 			else if (dbmsTypePrefix.equals("timestamp"))
-				return new TAPType(TAPDatatype.TIMESTAMP);
+				return new DBType(DBDatatype.TIMESTAMP);
 			// Default:
 			else
-				return new TAPType(TAPDatatype.VARCHAR, TAPType.NO_LENGTH);
+				return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
 		}
 	}
 
@@ -409,22 +409,22 @@ public class ResultSetTableIterator implements TableIterator {
 	 * <p>
 	 * 	If the given type string does not contain any parameter
 	 * 	OR if the first parameter can not be casted into an integer,
-	 * 	{@link TAPType#NO_LENGTH} will be returned.
+	 * 	{@link DBType#NO_LENGTH} will be returned.
 	 * </p>
 	 * 
 	 * @param dbmsType		DBMS type string (containing the datatype and the 'length' parameter).
 	 * @param paramIndex	Index of the open bracket.
 	 * 
-	 * @return	The 'length' parameter value if found, {@link TAPType#NO_LENGTH} otherwise.
+	 * @return	The 'length' parameter value if found, {@link DBType#NO_LENGTH} otherwise.
 	 */
 	protected final int getLengthParam(final String dbmsType, final int paramIndex){
 		// If no parameter has been previously detected, no length parameter:
 		if (paramIndex <= 0)
-			return TAPType.NO_LENGTH;
+			return DBType.NO_LENGTH;
 
 		// If there is one and that at least ONE parameter is provided....
 		else{
-			int lengthParam = TAPType.NO_LENGTH;
+			int lengthParam = DBType.NO_LENGTH;
 			String paramsStr = dbmsType.substring(paramIndex + 1);
 
 			// ...extract the 'length' parameter:
diff --git a/src/tap/data/TableIterator.java b/src/tap/data/TableIterator.java
index 0aed73cc51e660db38c53a58a752a471df8fa766..17d8445d1e3e7a681685e0bd73b6254d0ba63cb4 100644
--- a/src/tap/data/TableIterator.java
+++ b/src/tap/data/TableIterator.java
@@ -21,8 +21,8 @@ package tap.data;
 
 import java.util.NoSuchElementException;
 
+import adql.db.DBType;
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
 
 /**
  * <p>Let's iterate on each row and then on each column over a table dataset.</p>
@@ -126,7 +126,7 @@ public interface TableIterator {
 	 * @throws IllegalStateException	If {@link #nextCol()} has not yet been called.
 	 * @throws DataReadException		If an error occurs while reading the table dataset.
 	 */
-	public TAPType getColType() throws IllegalStateException, DataReadException;
+	public DBType getColType() throws IllegalStateException, DataReadException;
 
 	/**
 	 * Close the stream or input over which this class iterates.
diff --git a/src/tap/data/VOTableIterator.java b/src/tap/data/VOTableIterator.java
index 861ac52b8dc9e6d9bfcb5e59d117db9508a80501..c79dd05eeb33ffd48169d84174f420e5aa476b42 100644
--- a/src/tap/data/VOTableIterator.java
+++ b/src/tap/data/VOTableIterator.java
@@ -23,9 +23,9 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.NoSuchElementException;
 
+import adql.db.DBType;
 import tap.TAPException;
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
 import tap.metadata.VotType;
 import tap.metadata.VotType.VotDatatype;
 import uk.ac.starlink.table.ColumnInfo;
@@ -131,7 +131,7 @@ public class VOTableIterator implements TableIterator {
 			String xtype = getAuxDatumValue(colInfo, "xtype");
 
 			// Resolve the field type:
-			TAPType type = resolveVotType(datatype, arraysize, xtype).toTAPType();
+			DBType type = resolveVotType(datatype, arraysize, xtype).toTAPType();
 
 			// build the TAPColumn object:
 			TAPColumn col = new TAPColumn(colInfo.getName(), type, colInfo.getDescription(), colInfo.getUnitString(), colInfo.getUCD(), colInfo.getUtype());
@@ -255,7 +255,7 @@ public class VOTableIterator implements TableIterator {
 	}
 
 	@Override
-	public TAPType getColType() throws IllegalStateException, DataReadException{
+	public DBType getColType() throws IllegalStateException, DataReadException{
 		// Basically check the read state (for rows iteration):
 		checkReadState();
 
diff --git a/src/tap/db/JDBCConnection.java b/src/tap/db/JDBCConnection.java
index fc1331fcbcf320b0f280e86d7eef9bc5ce4192d5..1e1a836f92d422fbf8ed4f89247d203993947a56 100644
--- a/src/tap/db/JDBCConnection.java
+++ b/src/tap/db/JDBCConnection.java
@@ -17,7 +17,7 @@ package tap.db;
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.sql.Connection;
@@ -47,10 +47,10 @@ import tap.metadata.TAPMetadata.STDTable;
 import tap.metadata.TAPSchema;
 import tap.metadata.TAPTable;
 import tap.metadata.TAPTable.TableType;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import uws.ISO8601Format;
 import uws.service.log.UWSLog.LogLevel;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 import adql.query.ADQLQuery;
 import adql.query.IdentifierField;
 import adql.translator.ADQLTranslator;
@@ -97,7 +97,7 @@ import adql.translator.TranslationException;
  * 
  * <h3>Datatypes</h3>
  * 
- * <p>Column types are converted from DBMS to TAP types with {@link #getTAPType(String)} and from TAP to DBMS types with {@link #getDBMSDatatype(TAPType)}.</p>
+ * <p>Column types are converted from DBMS to TAP types with {@link #getTAPType(String)} and from TAP to DBMS types with {@link #getDBMSDatatype(DBType)}.</p>
  * 
  * <p>
  * 	All typical DBMS datatypes are taken into account, <b>EXCEPT the geometrical types</b> (POINT and REGION). For these types, the only object having this
@@ -635,19 +635,19 @@ public class JDBCConnection implements DBConnection {
 				}
 
 				// resolve the column type (if any) ; by default, it will be "VARCHAR" if unknown or missing:
-				TAPDatatype tapDatatype = null;
+				DBDatatype tapDatatype = null;
 				// ...try to resolve the datatype in function of all datatypes declared by the TAP standard.
 				if (datatype != null){
 					try{
-						tapDatatype = TAPDatatype.valueOf(datatype.toUpperCase());
+						tapDatatype = DBDatatype.valueOf(datatype.toUpperCase());
 					}catch(IllegalArgumentException iae){}
 				}
 				// ...build the column type:
-				TAPType type;
+				DBType type;
 				if (tapDatatype == null)
-					type = new TAPType(TAPDatatype.VARCHAR);
+					type = new DBType(DBDatatype.VARCHAR);
 				else
-					type = new TAPType(tapDatatype, size);
+					type = new DBType(tapDatatype, size);
 
 				// create the new column:
 				TAPColumn newColumn = new TAPColumn(columnName, type, nullifyIfNeeded(description), nullifyIfNeeded(unit), nullifyIfNeeded(ucd), nullifyIfNeeded(utype));
@@ -1591,7 +1591,7 @@ public class JDBCConnection implements DBConnection {
 					Object val = data.nextCol();
 					/* If the value is supposed to be a Timestamp, parse it
 					 * and build an appropriate SQL object: */
-					if (val != null && cols[c - 1].getDatatype().type == TAPDatatype.TIMESTAMP){
+					if (val != null && cols[c - 1].getDatatype().type == DBDatatype.TIMESTAMP){
 						try{
 							val = new Timestamp(ISO8601Format.parse(val.toString()));
 						}catch(ParseException pe){
@@ -1728,9 +1728,9 @@ public class JDBCConnection implements DBConnection {
 	 * 
 	 * @return	The corresponding DB type, or NULL if the given type is not managed or is NULL.
 	 */
-	protected String getDBMSDatatype(TAPType datatype){
+	protected String getDBMSDatatype(DBType datatype){
 		if (datatype == null)
-			datatype = new TAPType(TAPDatatype.VARCHAR);
+			datatype = new DBType(DBDatatype.VARCHAR);
 
 		switch(datatype.type){
 
diff --git a/src/tap/error/DefaultTAPErrorWriter.java b/src/tap/error/DefaultTAPErrorWriter.java
index d347c673585433407fd56e73bf7e55cbb07f031b..eb194e35711bd4606a0272d959ae7b1ceed93505 100644
--- a/src/tap/error/DefaultTAPErrorWriter.java
+++ b/src/tap/error/DefaultTAPErrorWriter.java
@@ -114,7 +114,7 @@ public class DefaultTAPErrorWriter implements ServiceErrorWriter {
 		// Otherwise, log it and write a message to the user:
 		else
 			// write a message to the user:
-			writeError("INTERNAL SERVER ERROR! Sorry, this error is grave and unexpected. No explaination can be provided for the moment. Details about this error have been reported in the service log files ; you should try again your request later or notify the administrator(s) by yourself (with the following REQ_ID).", ErrorType.FATAL, UWSException.INTERNAL_SERVER_ERROR, response, request, reqID, user, action);
+			writeError("INTERNAL SERVER ERROR! Sorry, this error is grave and unexpected. No explanation can be provided for the moment. Details about this error have been reported in the service log files ; you should try again your request later or notify the administrator(s) by yourself (with the following REQ_ID).", ErrorType.FATAL, UWSException.INTERNAL_SERVER_ERROR, response, request, reqID, user, action);
 	}
 
 	@Override
diff --git a/src/tap/formatter/JSONFormat.java b/src/tap/formatter/JSONFormat.java
index 06f3e230231c8341f395d90f4dc29a6960269514..1596827b50b8459c191c808c29a9c51c0c2f1091 100644
--- a/src/tap/formatter/JSONFormat.java
+++ b/src/tap/formatter/JSONFormat.java
@@ -32,11 +32,11 @@ import tap.TAPException;
 import tap.TAPExecutionReport;
 import tap.data.TableIterator;
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.metadata.VotType;
 import uws.service.log.UWSLog.LogLevel;
 import adql.db.DBColumn;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 
 /**
  * Format any given query (table) result into JSON.
@@ -212,7 +212,7 @@ public class JSONFormat implements OutputFormat {
 			else
 				return (TAPColumn)typeFromResult.copy();
 		}else
-			return new TAPColumn((typeFromQuery != null) ? typeFromQuery.getADQLName() : "?", new TAPType(TAPDatatype.VARCHAR), "?");
+			return new TAPColumn((typeFromQuery != null) ? typeFromQuery.getADQLName() : "?", new DBType(DBDatatype.VARCHAR), "?");
 	}
 
 	/**
@@ -237,7 +237,7 @@ public class JSONFormat implements OutputFormat {
 			out.key("description").value(tapCol.getDescription());
 
 		// "datatype": "..."
-		VotType votType = tapCol.getDatatype().toVotType();
+		VotType votType = new VotType(tapCol.getDatatype());
 		out.key("datatype").value(votType.datatype);
 
 		// "arraysize": "..." (if any)
diff --git a/src/tap/formatter/VOTableFormat.java b/src/tap/formatter/VOTableFormat.java
index a1beb23fcb8edc20b4236bf255a5425961e35c86..7ce1f63abe5fc1bed42e3ffe77db86dad6000c97 100644
--- a/src/tap/formatter/VOTableFormat.java
+++ b/src/tap/formatter/VOTableFormat.java
@@ -35,8 +35,6 @@ import tap.data.DataReadException;
 import tap.data.TableIterator;
 import tap.error.DefaultTAPErrorWriter;
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.metadata.VotType;
 import tap.metadata.VotType.VotDatatype;
 import uk.ac.starlink.table.AbstractStarTable;
@@ -50,6 +48,8 @@ import uk.ac.starlink.votable.VOSerializer;
 import uk.ac.starlink.votable.VOTableVersion;
 import uws.service.log.UWSLog.LogLevel;
 import adql.db.DBColumn;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 
 /**
  * <p>Format any given query (table) result into VOTable.</p>
@@ -426,6 +426,12 @@ public class VOTableFormat implements OutputFormat {
 			out.newLine();
 		}
 
+		/* TODO Add somewhere in the table header the different Coordinate Systems used in this result!
+		 * 2 ways to do so:
+		 * 	1/ COOSYS (deprecated from VOTable 1.2, but soon un-deprecated)
+		 * 	2/ a GROUP item with the STC expression of the coordinate system. 
+		 */
+
 		out.flush();
 	}
 
@@ -495,7 +501,7 @@ public class VOTableFormat implements OutputFormat {
 			else
 				return (TAPColumn)typeFromResult.copy();
 		}else
-			return new TAPColumn((typeFromQuery != null) ? typeFromQuery.getADQLName() : "?", new TAPType(TAPDatatype.VARCHAR), "?");
+			return new TAPColumn((typeFromQuery != null) ? typeFromQuery.getADQLName() : "?", new DBType(DBDatatype.VARCHAR), "?");
 	}
 
 	/**
@@ -507,7 +513,7 @@ public class VOTableFormat implements OutputFormat {
 	 */
 	protected static final ColumnInfo getColumnInfo(final TAPColumn tapCol){
 		// Get the VOTable type:
-		VotType votType = tapCol.getDatatype().toVotType();
+		VotType votType = new VotType(tapCol.getDatatype());
 
 		// Build a ColumnInfo with the name, type and description:
 		ColumnInfo colInfo = new ColumnInfo(tapCol.getADQLName(), getDatatypeClass(votType.datatype, votType.arraysize), tapCol.getDescription());
diff --git a/src/tap/metadata/TAPColumn.java b/src/tap/metadata/TAPColumn.java
index 3e56db180277b7df80385d058868e706bc1f4cdc..13cb81a25ce9d59dc184c3a7c9737a2ea5cc58a1 100644
--- a/src/tap/metadata/TAPColumn.java
+++ b/src/tap/metadata/TAPColumn.java
@@ -25,9 +25,10 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Map;
 
-import tap.metadata.TAPType.TAPDatatype;
 import adql.db.DBColumn;
 import adql.db.DBTable;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 
 /**
  * <p>Represent a column as described by the IVOA standard in the TAP protocol definition.</p>
@@ -101,7 +102,7 @@ public class TAPColumn implements DBColumn {
 
 	/** Type of this column.
 	 * <i>Note: Standard TAP column field ; CAN'T be NULL.</i> */
-	private TAPType datatype = new TAPType(TAPDatatype.VARCHAR);
+	private DBType datatype = new DBType(DBDatatype.VARCHAR);
 
 	/** Flag indicating whether this column is one of those that should be returned by default.
 	 * <i>Note: Standard TAP column field ; FALSE by default.</i> */
@@ -170,16 +171,16 @@ public class TAPColumn implements DBColumn {
 	 * </i></p>
 	 * 
 	 * <p><i>Note:
-	 *	The datatype is set by calling the function {@link #setDatatype(TAPType)} which does not do
+	 *	The datatype is set by calling the function {@link #setDatatype(DBType)} which does not do
 	 *	anything if the given datatype is NULL.
 	 * </i></p>
 	 * 
 	 * @param columnName	Name that this column MUST have in ADQL queries. <i>CAN'T be NULL ; this name can never be changed after.</i>
 	 * @param type			Datatype of this column. <i>If NULL, VARCHAR will be the datatype of this column</i>
 	 * 
-	 * @see #setDatatype(TAPType)
+	 * @see #setDatatype(DBType)
 	 */
-	public TAPColumn(String columnName, TAPType type){
+	public TAPColumn(String columnName, DBType type){
 		this(columnName);
 		setDatatype(type);
 	}
@@ -201,7 +202,7 @@ public class TAPColumn implements DBColumn {
 	 * @param description	Description of the column's content. <i>May be NULL</i>
 	 */
 	public TAPColumn(String columnName, String description){
-		this(columnName, (TAPType)null, description);
+		this(columnName, (DBType)null, description);
 	}
 
 	/**
@@ -218,7 +219,7 @@ public class TAPColumn implements DBColumn {
 	 * </i></p>
 	 * 
 	 * <p><i>Note:
-	 *	The datatype is set by calling the function {@link #setDatatype(TAPType)} which does do
+	 *	The datatype is set by calling the function {@link #setDatatype(DBType)} which does do
 	 *	anything if the given datatype is NULL.
 	 * </i></p>
 	 * 
@@ -226,7 +227,7 @@ public class TAPColumn implements DBColumn {
 	 * @param type			Datatype of this column. <i>If NULL, VARCHAR will be the datatype of this column</i>
 	 * @param description	Description of the column's content. <i>May be NULL</i>
 	 */
-	public TAPColumn(String columnName, TAPType type, String description){
+	public TAPColumn(String columnName, DBType type, String description){
 		this(columnName, type);
 		this.description = description;
 	}
@@ -266,7 +267,7 @@ public class TAPColumn implements DBColumn {
 	 * </i></p>
 	 * 
 	 * <p><i>Note:
-	 *	The datatype is set by calling the function {@link #setDatatype(TAPType)} which does do
+	 *	The datatype is set by calling the function {@link #setDatatype(DBType)} which does do
 	 *	anything if the given datatype is NULL.
 	 * </i></p>
 	 * 
@@ -275,7 +276,7 @@ public class TAPColumn implements DBColumn {
 	 * @param description	Description of the column's content. <i>May be NULL</i>
 	 * @param unit			Unit of the column's values. <i>May be NULL</i>
 	 */
-	public TAPColumn(String columnName, TAPType type, String description, String unit){
+	public TAPColumn(String columnName, DBType type, String description, String unit){
 		this(columnName, type, description);
 		this.unit = unit;
 	}
@@ -294,7 +295,7 @@ public class TAPColumn implements DBColumn {
 	 * </i></p>
 	 * 
 	 * <p><i>Note:
-	 *	The datatype is set by calling the function {@link #setDatatype(TAPType)} which does do
+	 *	The datatype is set by calling the function {@link #setDatatype(DBType)} which does do
 	 *	anything if the given datatype is NULL.
 	 * </i></p>
 	 * 
@@ -322,7 +323,7 @@ public class TAPColumn implements DBColumn {
 	 * </i></p>
 	 * 
 	 * <p><i>Note:
-	 *	The datatype is set by calling the function {@link #setDatatype(TAPType)} which does do
+	 *	The datatype is set by calling the function {@link #setDatatype(DBType)} which does do
 	 *	anything if the given datatype is NULL.
 	 * </i></p>
 	 * 
@@ -333,7 +334,7 @@ public class TAPColumn implements DBColumn {
 	 * @param ucd			UCD describing the scientific content of this column.
 	 * @param utype			UType associating this column with a data-model.
 	 */
-	public TAPColumn(String columnName, TAPType type, String description, String unit, String ucd, String utype){
+	public TAPColumn(String columnName, DBType type, String description, String unit, String ucd, String utype){
 		this(columnName, type, description, unit);
 		this.ucd = ucd;
 		this.utype = utype;
@@ -479,7 +480,7 @@ public class TAPColumn implements DBColumn {
 	 * 
 	 * @return	Its datatype. <i>CAN'T be NULL</i>
 	 */
-	public final TAPType getDatatype(){
+	public final DBType getDatatype(){
 		return datatype;
 	}
 
@@ -492,7 +493,7 @@ public class TAPColumn implements DBColumn {
 	 * 
 	 * @param type	Its new datatype.
 	 */
-	public final void setDatatype(final TAPType type){
+	public final void setDatatype(final DBType type){
 		if (type != null)
 			datatype = type;
 	}
diff --git a/src/tap/metadata/TAPMetadata.java b/src/tap/metadata/TAPMetadata.java
index 15a57b044b4259a1e6f2cf5b06fa374a58f8aa4c..68e0b1c63b15ea90cf1fbec001147e61da00989f 100644
--- a/src/tap/metadata/TAPMetadata.java
+++ b/src/tap/metadata/TAPMetadata.java
@@ -34,11 +34,12 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import tap.metadata.TAPTable.TableType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.resource.Capabilities;
 import tap.resource.TAPResource;
 import tap.resource.VOSIResource;
 import adql.db.DBTable;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 
 /**
  * <p>Let listing all schemas, tables and columns available in a TAP service.
@@ -730,49 +731,49 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour
 
 			case SCHEMAS:
 				TAPTable schemas = new TAPTable(STDTable.SCHEMAS.toString(), TableType.table, "List of schemas published in this TAP service.", null);
-				schemas.addColumn("schema_name", new TAPType(TAPDatatype.VARCHAR), "schema name, possibly qualified", null, null, null, true, true, true);
-				schemas.addColumn("description", new TAPType(TAPDatatype.VARCHAR), "brief description of schema", null, null, null, false, false, true);
-				schemas.addColumn("utype", new TAPType(TAPDatatype.VARCHAR), "UTYPE if schema corresponds to a data model", null, null, null, false, false, true);
+				schemas.addColumn("schema_name", new DBType(DBDatatype.VARCHAR), "schema name, possibly qualified", null, null, null, true, true, true);
+				schemas.addColumn("description", new DBType(DBDatatype.VARCHAR), "brief description of schema", null, null, null, false, false, true);
+				schemas.addColumn("utype", new DBType(DBDatatype.VARCHAR), "UTYPE if schema corresponds to a data model", null, null, null, false, false, true);
 				return schemas;
 
 			case TABLES:
 				TAPTable tables = new TAPTable(STDTable.TABLES.toString(), TableType.table, "List of tables published in this TAP service.", null);
-				tables.addColumn("schema_name", new TAPType(TAPDatatype.VARCHAR), "the schema name from TAP_SCHEMA.schemas", null, null, null, true, true, true);
-				tables.addColumn("table_name", new TAPType(TAPDatatype.VARCHAR), "table name as it should be used in queries", null, null, null, true, true, true);
-				tables.addColumn("table_type", new TAPType(TAPDatatype.VARCHAR), "one of: table, view", null, null, null, false, false, true);
-				tables.addColumn("description", new TAPType(TAPDatatype.VARCHAR), "brief description of table", null, null, null, false, false, true);
-				tables.addColumn("utype", new TAPType(TAPDatatype.VARCHAR), "UTYPE if table corresponds to a data model", null, null, null, false, false, true);
+				tables.addColumn("schema_name", new DBType(DBDatatype.VARCHAR), "the schema name from TAP_SCHEMA.schemas", null, null, null, true, true, true);
+				tables.addColumn("table_name", new DBType(DBDatatype.VARCHAR), "table name as it should be used in queries", null, null, null, true, true, true);
+				tables.addColumn("table_type", new DBType(DBDatatype.VARCHAR), "one of: table, view", null, null, null, false, false, true);
+				tables.addColumn("description", new DBType(DBDatatype.VARCHAR), "brief description of table", null, null, null, false, false, true);
+				tables.addColumn("utype", new DBType(DBDatatype.VARCHAR), "UTYPE if table corresponds to a data model", null, null, null, false, false, true);
 				return tables;
 
 			case COLUMNS:
 				TAPTable columns = new TAPTable(STDTable.COLUMNS.toString(), TableType.table, "List of columns of all tables listed in TAP_SCHEMA.TABLES and published in this TAP service.", null);
-				columns.addColumn("table_name", new TAPType(TAPDatatype.VARCHAR), "table name from TAP_SCHEMA.tables", null, null, null, true, true, true);
-				columns.addColumn("column_name", new TAPType(TAPDatatype.VARCHAR), "column name", null, null, null, true, true, true);
-				columns.addColumn("description", new TAPType(TAPDatatype.VARCHAR), "brief description of column", null, null, null, false, false, true);
-				columns.addColumn("unit", new TAPType(TAPDatatype.VARCHAR), "unit in VO standard format", null, null, null, false, false, true);
-				columns.addColumn("ucd", new TAPType(TAPDatatype.VARCHAR), "UCD of column if any", null, null, null, false, false, true);
-				columns.addColumn("utype", new TAPType(TAPDatatype.VARCHAR), "UTYPE of column if any", null, null, null, false, false, true);
-				columns.addColumn("datatype", new TAPType(TAPDatatype.VARCHAR), "ADQL datatype as in section 2.5", null, null, null, false, false, true);
-				columns.addColumn("size", new TAPType(TAPDatatype.INTEGER), "length of variable length datatypes", null, null, null, false, false, true);
-				columns.addColumn("principal", new TAPType(TAPDatatype.INTEGER), "a principal column; 1 means true, 0 means false", null, null, null, false, false, true);
-				columns.addColumn("indexed", new TAPType(TAPDatatype.INTEGER), "an indexed column; 1 means true, 0 means false", null, null, null, false, false, true);
-				columns.addColumn("std", new TAPType(TAPDatatype.INTEGER), "a standard column; 1 means true, 0 means false", null, null, null, false, false, true);
+				columns.addColumn("table_name", new DBType(DBDatatype.VARCHAR), "table name from TAP_SCHEMA.tables", null, null, null, true, true, true);
+				columns.addColumn("column_name", new DBType(DBDatatype.VARCHAR), "column name", null, null, null, true, true, true);
+				columns.addColumn("description", new DBType(DBDatatype.VARCHAR), "brief description of column", null, null, null, false, false, true);
+				columns.addColumn("unit", new DBType(DBDatatype.VARCHAR), "unit in VO standard format", null, null, null, false, false, true);
+				columns.addColumn("ucd", new DBType(DBDatatype.VARCHAR), "UCD of column if any", null, null, null, false, false, true);
+				columns.addColumn("utype", new DBType(DBDatatype.VARCHAR), "UTYPE of column if any", null, null, null, false, false, true);
+				columns.addColumn("datatype", new DBType(DBDatatype.VARCHAR), "ADQL datatype as in section 2.5", null, null, null, false, false, true);
+				columns.addColumn("size", new DBType(DBDatatype.INTEGER), "length of variable length datatypes", null, null, null, false, false, true);
+				columns.addColumn("principal", new DBType(DBDatatype.INTEGER), "a principal column; 1 means true, 0 means false", null, null, null, false, false, true);
+				columns.addColumn("indexed", new DBType(DBDatatype.INTEGER), "an indexed column; 1 means true, 0 means false", null, null, null, false, false, true);
+				columns.addColumn("std", new DBType(DBDatatype.INTEGER), "a standard column; 1 means true, 0 means false", null, null, null, false, false, true);
 				return columns;
 
 			case KEYS:
 				TAPTable keys = new TAPTable(STDTable.KEYS.toString(), TableType.table, "List all foreign keys but provides just the tables linked by the foreign key. To know which columns of these tables are linked, see in TAP_SCHEMA.key_columns using the key_id.", null);
-				keys.addColumn("key_id", new TAPType(TAPDatatype.VARCHAR), "unique key identifier", null, null, null, true, true, true);
-				keys.addColumn("from_table", new TAPType(TAPDatatype.VARCHAR), "fully qualified table name", null, null, null, false, false, true);
-				keys.addColumn("target_table", new TAPType(TAPDatatype.VARCHAR), "fully qualified table name", null, null, null, false, false, true);
-				keys.addColumn("description", new TAPType(TAPDatatype.VARCHAR), "description of this key", null, null, null, false, false, true);
-				keys.addColumn("utype", new TAPType(TAPDatatype.VARCHAR), "utype of this key", null, null, null, false, false, true);
+				keys.addColumn("key_id", new DBType(DBDatatype.VARCHAR), "unique key identifier", null, null, null, true, true, true);
+				keys.addColumn("from_table", new DBType(DBDatatype.VARCHAR), "fully qualified table name", null, null, null, false, false, true);
+				keys.addColumn("target_table", new DBType(DBDatatype.VARCHAR), "fully qualified table name", null, null, null, false, false, true);
+				keys.addColumn("description", new DBType(DBDatatype.VARCHAR), "description of this key", null, null, null, false, false, true);
+				keys.addColumn("utype", new DBType(DBDatatype.VARCHAR), "utype of this key", null, null, null, false, false, true);
 				return keys;
 
 			case KEY_COLUMNS:
 				TAPTable key_columns = new TAPTable(STDTable.KEY_COLUMNS.toString(), TableType.table, "List all foreign keys but provides just the columns linked by the foreign key. To know the table of these columns, see in TAP_SCHEMA.keys using the key_id.", null);
-				key_columns.addColumn("key_id", new TAPType(TAPDatatype.VARCHAR), "unique key identifier", null, null, null, true, true, true);
-				key_columns.addColumn("from_column", new TAPType(TAPDatatype.VARCHAR), "key column name in the from_table", null, null, null, false, false, true);
-				key_columns.addColumn("target_column", new TAPType(TAPDatatype.VARCHAR), "key column name in the target_table", null, null, null, false, false, true);
+				key_columns.addColumn("key_id", new DBType(DBDatatype.VARCHAR), "unique key identifier", null, null, null, true, true, true);
+				key_columns.addColumn("from_column", new DBType(DBDatatype.VARCHAR), "key column name in the from_table", null, null, null, false, false, true);
+				key_columns.addColumn("target_column", new DBType(DBDatatype.VARCHAR), "key column name in the target_table", null, null, null, false, false, true);
 				return key_columns;
 
 			default:
diff --git a/src/tap/metadata/TAPTable.java b/src/tap/metadata/TAPTable.java
index 4685a41eb315ce175d9f23a6b5b907d768814976..9f001f323b24bc37c9eec525e3bcee1784ffe74f 100644
--- a/src/tap/metadata/TAPTable.java
+++ b/src/tap/metadata/TAPTable.java
@@ -30,6 +30,7 @@ import java.util.Map;
 import tap.TAPException;
 import adql.db.DBColumn;
 import adql.db.DBTable;
+import adql.db.DBType;
 
 /**
  * <p>Represent a table as described by the IVOA standard in the TAP protocol definition.</p>
@@ -433,10 +434,10 @@ public class TAPTable implements DBTable {
 	 * @return	The created and added {@link TAPColumn} object,
 	 *        	or NULL if the given name is NULL or an empty string.
 	 * 
-	 * @see TAPColumn#TAPColumn(String, TAPType, String, String, String, String)
+	 * @see TAPColumn#TAPColumn(String, DBType, String, String, String, String)
 	 * @see #addColumn(TAPColumn)
 	 */
-	public TAPColumn addColumn(String columnName, TAPType datatype, String description, String unit, String ucd, String utype){
+	public TAPColumn addColumn(String columnName, DBType datatype, String description, String unit, String ucd, String utype){
 		if (columnName == null || columnName.trim().length() <= 0)
 			return null;
 
@@ -466,13 +467,13 @@ public class TAPTable implements DBTable {
 	 * @return	The created and added {@link TAPColumn} object,
 	 *        	or NULL if the given name is NULL or an empty string.
 	 * 
-	 * @see TAPColumn#TAPColumn(String, TAPType, String, String, String, String)
+	 * @see TAPColumn#TAPColumn(String, DBType, String, String, String, String)
 	 * @see TAPColumn#setPrincipal(boolean)
 	 * @see TAPColumn#setIndexed(boolean)
 	 * @see TAPColumn#setStd(boolean)
 	 * @see #addColumn(TAPColumn)
 	 */
-	public TAPColumn addColumn(String columnName, TAPType datatype, String description, String unit, String ucd, String utype, boolean principal, boolean indexed, boolean std){
+	public TAPColumn addColumn(String columnName, DBType datatype, String description, String unit, String ucd, String utype, boolean principal, boolean indexed, boolean std){
 		if (columnName == null || columnName.trim().length() <= 0)
 			return null;
 
diff --git a/src/tap/metadata/VotType.java b/src/tap/metadata/VotType.java
index 203c01d0733c73c3797b883fa9b78fe7611c65f9..35f8babfd4e5863b573ef993d29c876975dbf742 100644
--- a/src/tap/metadata/VotType.java
+++ b/src/tap/metadata/VotType.java
@@ -17,11 +17,12 @@ package tap.metadata;
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 import tap.TAPException;
-import tap.metadata.TAPType.TAPDatatype;
 import uk.ac.starlink.votable.VOSerializer;
 
 /**
@@ -120,11 +121,11 @@ public final class VotType {
 	}
 
 	/**
-	 * Build a {@link VotType} object by converting the given {@link TAPType}.
+	 * Build a {@link VotType} object by converting the given {@link DBType}.
 	 * 
-	 * @param tapType	{@link TAPType} to convert.
+	 * @param tapType	{@link DBType} to convert.
 	 */
-	public VotType(final TAPType tapType){
+	public VotType(final DBType tapType){
 		switch(tapType.type){
 			case SMALLINT:
 				this.datatype = VotDatatype.SHORT;
@@ -250,11 +251,11 @@ public final class VotType {
 	/**
 	 * Convert this VOTable type definition into a TAPColumn type.
 	 * 
-	 * @return	The corresponding {@link TAPType}.
+	 * @return	The corresponding {@link DBType}.
 	 * 
 	 * @throws TAPException	If the conversion is impossible (particularly if the array-size refers to a multi-dimensional array ; only 1D arrays are allowed). 
 	 */
-	public TAPType toTAPType() throws TAPException{
+	public DBType toTAPType() throws TAPException{
 
 		/* Stop immediately if the arraysize refers to a multi-dimensional array:
 		 * (Note: 'x' is the dimension separator of the VOTable attribute 'arraysize') */
@@ -266,29 +267,29 @@ public final class VotType {
 		/* NUMERIC TYPES */
 			case SHORT:
 			case BOOLEAN:
-				return convertNumericType(TAPDatatype.SMALLINT);
+				return convertNumericType(DBDatatype.SMALLINT);
 
 			case INT:
-				return convertNumericType(TAPDatatype.INTEGER);
+				return convertNumericType(DBDatatype.INTEGER);
 
 			case LONG:
-				return convertNumericType(TAPDatatype.BIGINT);
+				return convertNumericType(DBDatatype.BIGINT);
 
 			case FLOAT:
-				return convertNumericType(TAPDatatype.REAL);
+				return convertNumericType(DBDatatype.REAL);
 
 			case DOUBLE:
-				return convertNumericType(TAPDatatype.DOUBLE);
+				return convertNumericType(DBDatatype.DOUBLE);
 
 				/* BINARY TYPES */
 			case UNSIGNED_BYTE:
 				// BLOB exception:
 				if (xtype != null && xtype.equalsIgnoreCase(XTYPE_BLOB))
-					return new TAPType(TAPDatatype.BLOB);
+					return new DBType(DBDatatype.BLOB);
 
 				// Or else, just (var)binary:
 				else
-					return convertVariableLengthType(TAPDatatype.VARBINARY, TAPDatatype.BINARY);
+					return convertVariableLengthType(DBDatatype.VARBINARY, DBDatatype.BINARY);
 
 				/* CHARACTER TYPES */
 			case CHAR:
@@ -296,47 +297,47 @@ public final class VotType {
 				/* Special type cases: */
 				if (xtype != null){
 					if (xtype.equalsIgnoreCase(VotType.XTYPE_CLOB))
-						return new TAPType(TAPDatatype.CLOB);
+						return new DBType(DBDatatype.CLOB);
 					else if (xtype.equalsIgnoreCase(VotType.XTYPE_TIMESTAMP))
-						return new TAPType(TAPDatatype.TIMESTAMP);
+						return new DBType(DBDatatype.TIMESTAMP);
 					else if (xtype.equalsIgnoreCase(VotType.XTYPE_POINT))
-						return new TAPType(TAPDatatype.POINT);
+						return new DBType(DBDatatype.POINT);
 					else if (xtype.equalsIgnoreCase(VotType.XTYPE_REGION))
-						return new TAPType(TAPDatatype.REGION);
+						return new DBType(DBDatatype.REGION);
 				}
 
 				// Or if not known or missing, just a (var)char:
-				return convertVariableLengthType(TAPDatatype.VARCHAR, TAPDatatype.CHAR);
+				return convertVariableLengthType(DBDatatype.VARCHAR, DBDatatype.CHAR);
 		}
 	}
 
 	/**
-	 * <p>Convert this numeric {@link VotType} object into a corresponding {@link TAPType} whose the datatype is provided in parameter.</p>
+	 * <p>Convert this numeric {@link VotType} object into a corresponding {@link DBType} whose the datatype is provided in parameter.</p>
 	 * 
 	 * <p>
 	 * 	Thus, just the arraysize must be managed here. If there is no arraysize or if equals to '1', the given datatype will be used.
-	 * 	Otherwise, it is ignored and a {@link TAPType} with VARBINARY is returned.
+	 * 	Otherwise, it is ignored and a {@link DBType} with VARBINARY is returned.
 	 * </p>
 	 * 
 	 * @param tapDatatype	TAP datatype corresponding to this {@link VotType} (only when arraysize != '*' and 'n').
 	 * 
-	 * @return	The corresponding {@link TAPType}.
+	 * @return	The corresponding {@link DBType}.
 	 */
-	protected TAPType convertNumericType(final TAPDatatype tapDatatype){
+	protected DBType convertNumericType(final DBDatatype tapDatatype){
 		// If no arraysize:
 		if (arraysize == null || arraysize.equals("1"))
-			return new TAPType(tapDatatype);
+			return new DBType(tapDatatype);
 
 		// If only one dimension:
 		else
-			return new TAPType(TAPDatatype.VARBINARY);
+			return new DBType(DBDatatype.VARBINARY);
 
 		/* Note: The test of multi-dimensional array should have been already done at the beginning of #toTAPType(). */
 	}
 
 	/**
 	 * <p>
-	 * 	Convert this variable length {@link VotType} (unsignedByte and char) object into a corresponding {@link TAPType}
+	 * 	Convert this variable length {@link VotType} (unsignedByte and char) object into a corresponding {@link DBType}
 	 * 	whose the variable length and fixed length versions are given in parameters.
 	 * </p>
 	 * 
@@ -350,23 +351,23 @@ public final class VotType {
 	 * @param varType		Variable length type (i.e. VARCHAR, VARBINARY).
 	 * @param fixedType		Fixed length type (i.e. CHAR, BINARY).
 	 * 
-	 * @return	The corresponding {@link TAPType}.
+	 * @return	The corresponding {@link DBType}.
 	 * 
 	 * @throws TAPException	If the arraysize is not valid (that's to say, different from the following syntaxes: NULL, '*', 'n' or 'n*' (where n is a positive and not-null integer)).
 	 */
-	protected TAPType convertVariableLengthType(final TAPDatatype varType, final TAPDatatype fixedType) throws TAPException{
+	protected DBType convertVariableLengthType(final DBDatatype varType, final DBDatatype fixedType) throws TAPException{
 		try{
 			// no arraysize or '*' => VARCHAR or VARBINARY
 			if (arraysize == null || arraysize.equals("*"))
-				return new TAPType(varType);
+				return new DBType(varType);
 
 			// 'n*' => VARCHAR(n) or VARBINARY(n)
 			else if (arraysize.charAt(arraysize.length() - 1) == '*')
-				return new TAPType(varType, Integer.parseInt(arraysize.substring(0, arraysize.length() - 1)));
+				return new DBType(varType, Integer.parseInt(arraysize.substring(0, arraysize.length() - 1)));
 
 			// 'n' => CHAR(n) or BINARY(n)
 			else
-				return new TAPType(fixedType, Integer.parseInt(arraysize));
+				return new DBType(fixedType, Integer.parseInt(arraysize));
 
 		}catch(NumberFormatException nfe){
 			throw new TAPException("failed conversion of a VOTable datatype: non-numeric arraysize (" + arraysize + ")!");
diff --git a/src/tap/parameters/TAPParameters.java b/src/tap/parameters/TAPParameters.java
index 461c7ee024abb59fca941b3de91cc070f194b54c..6eb4f0936bc064396a551acdf551928e97f9f248 100644
--- a/src/tap/parameters/TAPParameters.java
+++ b/src/tap/parameters/TAPParameters.java
@@ -81,7 +81,7 @@ public class TAPParameters extends UWSParameters {
 		// Multipart HTTP parameters:
 		if (isMultipartContent(request)){
 			if (!service.uploadEnabled())
-				throw new TAPException("Request error ! This TAP service has no Upload capability !", UWSException.BAD_REQUEST);
+				throw new TAPException("Request error! This TAP service has no Upload capability!", UWSException.BAD_REQUEST);
 
 			File uploadDir = service.getFileManager().getUploadDirectory();
 			try{
@@ -97,14 +97,14 @@ public class TAPParameters extends UWSParameters {
 					set(param, multipart.getParameter(param));
 				}
 			}catch(IOException ioe){
-				throw new TAPException("Error while reading the Multipart content !", ioe);
+				throw new TAPException("Error while reading the Multipart content!", ioe);
 			}catch(IllegalArgumentException iae){
 				String confError = iae.getMessage();
 				if (service.getMaxUploadSize() <= 0)
-					confError = "The maximum upload size (see ServiceConnection.getMaxUploadSize() must be positive !";
+					confError = "The maximum upload size (see ServiceConnection.getMaxUploadSize() must be positive!";
 				else if (uploadDir == null)
-					confError = "Missing upload directory (see TAPFileManager.getUploadDirectory()) !";
-				throw new TAPException("Incorrect Upload capability configuration ! " + confError, iae);
+					confError = "Missing upload directory (see TAPFileManager.getUploadDirectory())!";
+				throw new TAPException("Incorrect Upload capability configuration! " + confError, iae);
 			}
 
 		}// Classic HTTP parameters (GET or POST):
diff --git a/src/tap/resource/TAP.java b/src/tap/resource/TAP.java
index 00e8496d6547e24c377da95de8fd3fc81a848655..54724c86cb256439473e6e916468bbda58293d2a 100644
--- a/src/tap/resource/TAP.java
+++ b/src/tap/resource/TAP.java
@@ -50,6 +50,7 @@ import uws.service.UWSService;
 import uws.service.UWSUrl;
 import uws.service.error.ServiceErrorWriter;
 import uws.service.log.UWSLog.LogLevel;
+import adql.db.FunctionDef;
 
 /**
  * <p>Root/Home of the TAP service. It is also the resource (HOME) which gathers all the others of the same TAP service.</p>
@@ -57,7 +58,7 @@ import uws.service.log.UWSLog.LogLevel;
  * <p>At its creation it is creating and configuring the other resources in function of the given description of the TAP service.</p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (09/2014)
+ * @version 2.0 (10/2014)
  */
 public class TAP implements VOSIResource {
 
@@ -113,8 +114,7 @@ public class TAP implements VOSIResource {
 		resources.put(res.getName(), res);
 
 		TAPMetadata metadata = service.getTAPMetadata();
-		if (metadata != null)
-			resources.put(metadata.getName(), metadata);
+		resources.put(metadata.getName(), metadata);
 	}
 
 	/**
@@ -422,6 +422,35 @@ public class TAP implements VOSIResource {
 		xml.append("\t\t<name>ADQL</name>\n");
 		xml.append("\t\t<version ivo-id=\"ivo://ivoa.net/std/ADQL#v2.0\">2.0</version>\n");
 		xml.append("\t\t<description>ADQL 2.0</description>\n");
+
+		// Geometrical functions:
+		if (service.getGeometries() != null && service.getGeometries().size() > 0){
+			xml.append("\t\t<languageFeatures type=\"ivo://ivoa.net/std/TAPRegExt#features-adqlgeo\">");
+			for(String geom : service.getGeometries()){
+				if (geom != null){
+					xml.append("\t\t\t<feature>");
+					xml.append("\t\t\t\t<form>").append(VOSerializer.formatText(geom.toUpperCase())).append("</form>");
+					xml.append("\t\t\t</feature>");
+				}
+			}
+			xml.append("\t\t</languageFeatures>");
+		}
+
+		// User Defined Functions (UDFs):
+		if (service.getUDFs() != null && service.getUDFs().size() > 0){
+			xml.append("\t\t<languageFeatures type=\"ivo://ivoa.net/std/TAPRegExt#features-udf\">");
+			for(FunctionDef udf : service.getUDFs()){
+				if (udf != null){
+					xml.append("\t\t\t<feature>");
+					xml.append("\t\t\t\t<form>").append(VOSerializer.formatText(udf.toString())).append("</form>");
+					if (udf.description != null && udf.description.length() > 0)
+						xml.append("\t\t\t\t<description>").append(VOSerializer.formatText(udf.description)).append("</description>");
+					xml.append("\t\t\t</feature>");
+				}
+			}
+			xml.append("\t\t</languageFeatures>");
+		}
+
 		xml.append("\t</language>\n");
 
 		// Available output formats:
diff --git a/src/uws/service/error/DefaultUWSErrorWriter.java b/src/uws/service/error/DefaultUWSErrorWriter.java
index f9854a45268be0311ba30205dce08abc51e82a65..74366d32d4f489cfe085670c21559ca9e0a3a4ec 100644
--- a/src/uws/service/error/DefaultUWSErrorWriter.java
+++ b/src/uws/service/error/DefaultUWSErrorWriter.java
@@ -104,7 +104,7 @@ public class DefaultUWSErrorWriter implements ServiceErrorWriter {
 			// log the error as GRAVE/FATAL (because unexpected/unmanaged):
 			logger.logUWS(LogLevel.FATAL, null, null, "[REQUEST N°" + reqID + "] " + t.getMessage(), t);
 			// write a message to the user:
-			writeError("INTERNAL SERVER ERROR! Sorry, this error is unexpected and no explaination can be provided for the moment. Details about this error have been reported in the service log files ; you should try again your request later or notify the administrator(s) by yourself (with the following 'Request ID').", ErrorType.FATAL, UWSException.INTERNAL_SERVER_ERROR, response, request, reqID, user, action);
+			writeError("INTERNAL SERVER ERROR! Sorry, this error is unexpected and no explanation can be provided for the moment. Details about this error have been reported in the service log files ; you should try again your request later or notify the administrator(s) by yourself (with the following 'Request ID').", ErrorType.FATAL, UWSException.INTERNAL_SERVER_ERROR, response, request, reqID, user, action);
 		}
 	}
 
diff --git a/test/adql/SearchColumnListTest.java b/test/adql/SearchColumnListTest.java
index b2e5988b240207115cf923cbef2f1ee1d738315b..d3f614fb650fd9833f836fa84f1976de3238e07f 100644
--- a/test/adql/SearchColumnListTest.java
+++ b/test/adql/SearchColumnListTest.java
@@ -9,12 +9,12 @@ import tap.metadata.TAPColumn;
 import tap.metadata.TAPSchema;
 import tap.metadata.TAPTable;
 import tap.metadata.TAPTable.TableType;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import adql.db.DBColumn;
 import adql.db.DBCommonColumn;
 import adql.db.DBTable;
+import adql.db.DBType;
 import adql.db.SearchColumnList;
+import adql.db.DBType.DBDatatype;
 import adql.db.exception.UnresolvedJoin;
 import adql.parser.ParseException;
 import adql.query.IdentifierField;
@@ -32,16 +32,16 @@ public class SearchColumnListTest {
 		TAPTable tableD = new TAPTable("D", TableType.table, "NATURAL JOIN Test table", null);
 
 		// Describe its columns:
-		tableA.addColumn(new TAPColumn("id", new TAPType(TAPDatatype.VARCHAR), "Object ID"));
-		tableA.addColumn(new TAPColumn("txta", new TAPType(TAPDatatype.VARCHAR), "Text of table A"));
-		tableB.addColumn(new TAPColumn("id", new TAPType(TAPDatatype.VARCHAR), "Object ID"));
-		tableB.addColumn(new TAPColumn("txtb", new TAPType(TAPDatatype.VARCHAR), "Text of table B"));
-		tableC.addColumn(new TAPColumn("Id", new TAPType(TAPDatatype.VARCHAR), "Object ID"));
-		tableC.addColumn(new TAPColumn("txta", new TAPType(TAPDatatype.VARCHAR), "Text of table A"));
-		tableC.addColumn(new TAPColumn("txtc", new TAPType(TAPDatatype.VARCHAR), "Text of table C"));
-		tableD.addColumn(new TAPColumn("id", new TAPType(TAPDatatype.VARCHAR), "Object ID"));
-		tableD.addColumn(new TAPColumn("txta", new TAPType(TAPDatatype.VARCHAR), "Text of table A"));
-		tableD.addColumn(new TAPColumn("txtd", new TAPType(TAPDatatype.VARCHAR), "Text of table D"));
+		tableA.addColumn(new TAPColumn("id", new DBType(DBDatatype.VARCHAR), "Object ID"));
+		tableA.addColumn(new TAPColumn("txta", new DBType(DBDatatype.VARCHAR), "Text of table A"));
+		tableB.addColumn(new TAPColumn("id", new DBType(DBDatatype.VARCHAR), "Object ID"));
+		tableB.addColumn(new TAPColumn("txtb", new DBType(DBDatatype.VARCHAR), "Text of table B"));
+		tableC.addColumn(new TAPColumn("Id", new DBType(DBDatatype.VARCHAR), "Object ID"));
+		tableC.addColumn(new TAPColumn("txta", new DBType(DBDatatype.VARCHAR), "Text of table A"));
+		tableC.addColumn(new TAPColumn("txtc", new DBType(DBDatatype.VARCHAR), "Text of table C"));
+		tableD.addColumn(new TAPColumn("id", new DBType(DBDatatype.VARCHAR), "Object ID"));
+		tableD.addColumn(new TAPColumn("txta", new DBType(DBDatatype.VARCHAR), "Text of table A"));
+		tableD.addColumn(new TAPColumn("txtd", new DBType(DBDatatype.VARCHAR), "Text of table D"));
 
 		// List all available tables:
 		TAPSchema schema = new TAPSchema("public");
diff --git a/test/adql/db/TestDBChecker.java b/test/adql/db/TestDBChecker.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8e8b4b43dd1440f9371b6ae575837b560549084
--- /dev/null
+++ b/test/adql/db/TestDBChecker.java
@@ -0,0 +1,609 @@
+package adql.db;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef.FunctionParam;
+import adql.db.exception.UnresolvedIdentifiersException;
+import adql.parser.ADQLParser;
+import adql.parser.ParseException;
+import adql.query.ADQLQuery;
+import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.function.DefaultUDF;
+
+public class TestDBChecker {
+
+	private static List<DBTable> tables;
+
+	@BeforeClass
+	public static void setUpBeforeClass() throws Exception{
+		tables = new ArrayList<DBTable>();
+
+		DefaultDBTable fooTable = new DefaultDBTable("foo");
+		DBColumn col = new DefaultDBColumn("colS", new DBType(DBDatatype.VARCHAR), fooTable);
+		fooTable.addColumn(col);
+		col = new DefaultDBColumn("colI", new DBType(DBDatatype.INTEGER), fooTable);
+		fooTable.addColumn(col);
+		col = new DefaultDBColumn("colG", new DBType(DBDatatype.POINT), fooTable);
+		fooTable.addColumn(col);
+
+		tables.add(fooTable);
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() throws Exception{}
+
+	@Before
+	public void setUp() throws Exception{}
+
+	@After
+	public void tearDown() throws Exception{}
+
+	@Test
+	public void testNumericOrStringValueExpressionPrimary(){
+		ADQLParser parser = new ADQLParser();
+		try{
+			assertNotNull(parser.parseQuery("SELECT 'toto' FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ('toto') FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT (('toto')) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT 'toto' || 'blabla' FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ('toto' || 'blabla') FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT (('toto' || 'blabla')) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT (('toto') || (('blabla'))) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT 3 FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ((2+3)*5) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ABS(-123) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ABS(2*-1+5) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ABS(COUNT(*)) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT toto FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT toto * 3 FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT toto || 'blabla' FROM foo;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail();
+		}
+		try{
+			parser.parseQuery("SELECT ABS('toto') FROM foo;");
+			fail();
+		}catch(ParseException pe){}
+		try{
+			parser.parseQuery("SELECT ABS(('toto' || 'blabla')) FROM foo;");
+			fail();
+		}catch(ParseException pe){}
+		try{
+			parser.parseQuery("SELECT 'toto' || 1 FROM foo;");
+			fail();
+		}catch(ParseException pe){}
+		try{
+			parser.parseQuery("SELECT 1 || 'toto' FROM foo;");
+			fail();
+		}catch(ParseException pe){}
+		try{
+			parser.parseQuery("SELECT 'toto' * 3 FROM foo;");
+			fail();
+		}catch(ParseException pe){}
+	}
+
+	@Test
+	public void testUDFManagement(){
+		// UNKNOWN FUNCTIONS ARE NOT ALLOWED:
+		ADQLParser parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0)));
+
+		// Test with a simple ADQL query without unknown or user defined function:
+		try{
+			assertNotNull(parser.parseQuery("SELECT * FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("A simple and basic query should not be a problem for the parser!");
+		}
+
+		// Test with an ADQL query containing one not declared UDF:
+		try{
+			parser.parseQuery("SELECT toto() FROM foo;");
+			fail("This query contains a UDF while it's not allowed: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Unresolved function: \"toto()\"! No UDF has been defined or found with the signature: toto().", ex.getErrors().next().getMessage());
+		}
+
+		// DECLARE THE UDFs:
+		FunctionDef[] udfs = new FunctionDef[]{new FunctionDef("toto", new DBType(DBDatatype.VARCHAR)),new FunctionDef("tata", new DBType(DBDatatype.INTEGER))};
+		parser = new ADQLParser(new DBChecker(tables, Arrays.asList(udfs)));
+
+		// Test again:
+		try{
+			assertNotNull(parser.parseQuery("SELECT toto() FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT tata() FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("This query contains a DECLARED UDF: this test should have succeeded!");
+		}
+
+		// Test but with at least one parameter:
+		try{
+			parser.parseQuery("SELECT toto('blabla') FROM foo;");
+			fail("This query contains an unknown UDF signature (the fct toto is declared with no parameter): this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Unresolved function: \"toto('blabla')\"! No UDF has been defined or found with the signature: toto(STRING).", ex.getErrors().next().getMessage());
+		}
+	}
+
+	@Test
+	public void testGeometry(){
+		// DECLARE A SIMPLE PARSER where all geometries are allowed by default:
+		ADQLParser parser = new ADQLParser(new DBChecker(tables));
+
+		// Test with several geometries while all are allowed:
+		try{
+			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();
+			fail("This query contains several geometries, and all are theoretically allowed: this test should have succeeded!");
+		}
+
+		// Test with several geometries while only the allowed ones:
+		try{
+			parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0), Arrays.asList(new String[]{"CONTAINS","POINT","CIRCLE"}), null));
+			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();
+			fail("This query contains several geometries, and all are theoretically allowed: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT * FROM foo WHERE INTERSECTS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;");
+			fail("This query contains a not-allowed geometry function (INTERSECTS): this test should have failed!");
+		}catch(ParseException pe){
+			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());
+		}
+
+		// Test by adding REGION:
+		try{
+			parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0), Arrays.asList(new String[]{"CONTAINS","POINT","CIRCLE","REGION"}), null));
+			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!");
+		}
+		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!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("The geometrical function \"BOX\" is not available in this implementation!", ex.getErrors().next().getMessage());
+		}
+
+		// Test with several geometries while none geometry is allowed:
+		try{
+			parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0), new ArrayList<String>(0), null));
+			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){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			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());
+		}
+	}
+
+	@Test
+	public void testCoordSys(){
+		// DECLARE A SIMPLE PARSER where all coordinate systems are allowed by default:
+		ADQLParser parser = new ADQLParser(new DBChecker(tables));
+
+		// Test with several coordinate systems while all are allowed:
+		try{
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('icrs', 12.3, 45.6), CIRCLE('cartesian2', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('lsr', 12.3, 45.6), CIRCLE('galactic heliocenter', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('unknownframe', 12.3, 45.6), CIRCLE('galactic unknownrefpos spherical2', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(REGION('position icrs lsr 12.3 45.6'), REGION('circle fk5 1.2 2.3 5')) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT Region('not(position 1 2)') FROM foo;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail("This query contains several valid coordinate systems, and all are theoretically allowed: this test should have succeeded!");
+		}
+
+		// Concatenation as coordinate systems not checked:
+		try{
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('From ' || 'here', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail("This query contains a concatenation as coordinate systems (but only string constants are checked): this test should have succeeded!");
+		}
+
+		// Test with several coordinate systems while only some allowed:
+		try{
+			parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0), null, Arrays.asList(new String[]{"icrs * *","fk4 geocenter *","galactic * spherical2"})));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('icrs', 12.3, 45.6), CIRCLE('cartesian3', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT POINT('fk4', 12.3, 45.6) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('fk4 geocenter', 12.3, 45.6), CIRCLE('cartesian2', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('galactic', 12.3, 45.6), CIRCLE('galactic spherical2', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('galactic geocenter', 12.3, 45.6), CIRCLE('galactic lsr spherical2', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(REGION('position galactic lsr 12.3 45.6'), REGION('circle icrs 1.2 2.3 5')) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT Region('not(position 1 2)') FROM foo;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail("This query contains several valid coordinate systems, and all are theoretically allowed: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT POINT('fk5 geocenter', 12.3, 45.6) FROM foo;");
+			fail("This query contains a not-allowed coordinate system ('fk5' is not allowed): this test should have failed!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Coordinate system \"fk5 geocenter\" (= \"FK5 GEOCENTER SPHERICAL2\") not allowed in this implementation.", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT Region('not(position fk5 heliocenter 1 2)') FROM foo;");
+			fail("This query contains a not-allowed coordinate system ('fk5' is not allowed): this test should have failed!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Coordinate system \"FK5 HELIOCENTER\" (= \"FK5 HELIOCENTER SPHERICAL2\") not allowed in this implementation.", ex.getErrors().next().getMessage());
+		}
+
+		// Test with a coordinate system while none is allowed:
+		try{
+			parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0), null, new ArrayList<String>(0)));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(REGION('position 12.3 45.6'), REGION('circle 1.2 2.3 5')) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT Region('not(position 1 2)') FROM foo;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail("This query specifies none coordinate system: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('ICRS SPHERICAL2', 12.3, 45.6), CIRCLE('icrs', 1.2, 2.3, 5)) = 1;");
+			fail("This query specifies coordinate systems while they are all forbidden: this test should have failed!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(2, ex.getNbErrors());
+			Iterator<ParseException> itErrors = ex.getErrors();
+			assertEquals("Coordinate system \"ICRS SPHERICAL2\" (= \"ICRS UNKNOWNREFPOS SPHERICAL2\") not allowed in this implementation.", itErrors.next().getMessage());
+			assertEquals("Coordinate system \"icrs\" (= \"ICRS UNKNOWNREFPOS SPHERICAL2\") not allowed in this implementation.", itErrors.next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT Region('not(position fk4 1 2)') FROM foo;");
+			fail("This query specifies coordinate systems while they are all forbidden: this test should have failed!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Coordinate system \"FK4\" (= \"FK4 UNKNOWNREFPOS SPHERICAL2\") not allowed in this implementation.", ex.getErrors().next().getMessage());
+		}
+	}
+
+	@Test
+	public void testTypesChecking(){
+		// DECLARE A SIMPLE PARSER:
+		ADQLParser parser = new ADQLParser(new DBChecker(tables));
+
+		// Test the type of columns generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT colS, colI, colG FROM foo;");
+			ADQLOperand colS = query.getSelect().get(0).getOperand();
+			ADQLOperand colI = query.getSelect().get(1).getOperand();
+			ADQLOperand colG = query.getSelect().get(2).getOperand();
+			// test string column:
+			assertTrue(colS instanceof ADQLColumn);
+			assertTrue(colS.isString());
+			assertFalse(colS.isNumeric());
+			assertFalse(colS.isGeometry());
+			// test integer column:
+			assertTrue(colI instanceof ADQLColumn);
+			assertFalse(colI.isString());
+			assertTrue(colI.isNumeric());
+			assertFalse(colI.isGeometry());
+			// test geometry column:
+			assertTrue(colG instanceof ADQLColumn);
+			assertFalse(colG.isString());
+			assertFalse(colG.isNumeric());
+			assertTrue(colG.isGeometry());
+		}catch(ParseException e1){
+			if (e1 instanceof UnresolvedIdentifiersException)
+				((UnresolvedIdentifiersException)e1).getErrors().next().printStackTrace();
+			else
+				e1.printStackTrace();
+			fail("This query contains known columns: this test should have succeeded!");
+		}
+
+		// Test the expected type - NUMERIC - generated by the parser:
+		try{
+			assertNotNull(parser.parseQuery("SELECT colI * 3 FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("This query contains a product between 2 numerics: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT colS * 3 FROM foo;");
+			fail("This query contains a product between a string and an integer: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A numeric value was expected instead of \"colS\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT colG * 3 FROM foo;");
+			fail("This query contains a product between a geometry and an integer: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A numeric value was expected instead of \"colG\".", ex.getErrors().next().getMessage());
+		}
+
+		// Test the expected type - STRING - generated by the parser:
+		try{
+			assertNotNull(parser.parseQuery("SELECT colS || 'blabla' FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("This query contains a concatenation between 2 strings: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT colI || 'blabla' FROM foo;");
+			fail("This query contains a concatenation between an integer and a string: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"colI\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT colG || 'blabla' FROM foo;");
+			fail("This query contains a concatenation between a geometry and a string: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"colG\".", ex.getErrors().next().getMessage());
+		}
+
+		// Test the expected type - GEOMETRY - generated by the parser:
+		try{
+			assertNotNull(parser.parseQuery("SELECT CONTAINS(colG, CIRCLE('', 1, 2, 5)) FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("This query contains a geometrical predicate between 2 geometries: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT CONTAINS(colI, CIRCLE('', 1, 2, 5)) FROM foo;");
+			fail("This query contains a geometrical predicate between an integer and a geometry: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A geometry was expected instead of \"colI\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT CONTAINS(colS, CIRCLE('', 1, 2, 5)) FROM foo;");
+			fail("This query contains a geometrical predicate between a string and a geometry: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A geometry was expected instead of \"colS\".", ex.getErrors().next().getMessage());
+		}
+
+		// DECLARE SOME UDFs:
+		FunctionDef[] udfs = new FunctionDef[]{new FunctionDef("toto", new DBType(DBDatatype.VARCHAR)),new FunctionDef("tata", new DBType(DBDatatype.INTEGER)),new FunctionDef("titi", new DBType(DBDatatype.REGION))};
+		parser = new ADQLParser(new DBChecker(tables, Arrays.asList(udfs)));
+
+		// Test the return type of the function TOTO generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT toto() FROM foo;");
+			ADQLOperand fct = query.getSelect().get(0).getOperand();
+			assertTrue(fct instanceof DefaultUDF);
+			assertNotNull(((DefaultUDF)fct).getDefinition());
+			assertTrue(fct.isString());
+			assertFalse(fct.isNumeric());
+			assertFalse(fct.isGeometry());
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains a DECLARED UDF: this test should have succeeded!");
+		}
+
+		// Test the return type checking inside a whole query:
+		try{
+			assertNotNull(parser.parseQuery("SELECT toto() || 'Blabla ' AS \"SuperText\" FROM foo;"));
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains a DECLARED UDF concatenated to a String: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT toto()*3 AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as numeric...which is here not possible: this test should have failed!");
+		}catch(ParseException e1){
+			assertTrue(e1 instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e1;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A numeric value was expected instead of \"toto()\".", ex.getErrors().next().getMessage());
+		}
+
+		// Test the return type of the function TATA generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT tata() FROM foo;");
+			ADQLOperand fct = query.getSelect().get(0).getOperand();
+			assertTrue(fct instanceof DefaultUDF);
+			assertNotNull(((DefaultUDF)fct).getDefinition());
+			assertFalse(fct.isString());
+			assertTrue(fct.isNumeric());
+			assertFalse(fct.isGeometry());
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains a DECLARED UDF: this test should have succeeded!");
+		}
+
+		// Test the return type checking inside a whole query:
+		try{
+			assertNotNull(parser.parseQuery("SELECT tata()*3 AS \"aNumeric\" FROM foo;"));
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains a DECLARED UDF multiplicated by 3: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT 'Blabla ' || tata() AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as string...which is here not possible: this test should have failed!");
+		}catch(ParseException e1){
+			assertTrue(e1 instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e1;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"tata()\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT tata() || 'Blabla ' AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as string...which is here not possible: this test should have failed!");
+		}catch(ParseException e1){
+			assertTrue(e1 instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e1;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"tata()\".", ex.getErrors().next().getMessage());
+		}
+
+		// Test the return type of the function TITI generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT titi() FROM foo;");
+			ADQLOperand fct = query.getSelect().get(0).getOperand();
+			assertTrue(fct instanceof DefaultUDF);
+			assertNotNull(((DefaultUDF)fct).getDefinition());
+			assertFalse(fct.isString());
+			assertFalse(fct.isNumeric());
+			assertTrue(fct.isGeometry());
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains a DECLARED UDF: this test should have succeeded!");
+		}
+
+		// Test the return type checking inside a whole query:
+		try{
+			parser.parseQuery("SELECT CONTAINS(colG, titi()) ' AS \"Super\" FROM foo;");
+			fail("Geometrical UDFs are not allowed for the moment in the ADQL language: this test should have failed!");
+		}catch(ParseException e1){
+			assertTrue(e1 instanceof ParseException);
+			assertEquals(" Encountered \"(\". Was expecting one of: \")\" \".\" \".\" \")\" ", e1.getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT titi()*3 AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as numeric...which is here not possible: this test should have failed!");
+		}catch(ParseException e1){
+			assertTrue(e1 instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e1;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A numeric value was expected instead of \"titi()\".", ex.getErrors().next().getMessage());
+		}
+
+		// CLEAR ALL UDFs AND ALLOW UNKNOWN FUNCTION:
+		parser = new ADQLParser(new DBChecker(tables, null));
+
+		// Test again:
+		try{
+			assertNotNull(parser.parseQuery("SELECT toto() FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("The parser allow ANY unknown function: this test should have succeeded!");
+		}
+
+		// Test the return type of the function generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT toto() FROM foo;");
+			ADQLOperand fct = query.getSelect().get(0).getOperand();
+			assertTrue(fct instanceof DefaultUDF);
+			assertNull(((DefaultUDF)fct).getDefinition());
+			assertTrue(fct.isString());
+			assertTrue(fct.isNumeric());
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("The parser allow ANY unknown function: this test should have succeeded!");
+		}
+
+		// DECLARE THE UDF (while unknown functions are allowed):
+		parser = new ADQLParser(new DBChecker(tables, Arrays.asList(new FunctionDef[]{new FunctionDef("toto", new DBType(DBDatatype.VARCHAR))})));
+
+		// Test the return type of the function generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT toto() FROM foo;");
+			ADQLOperand fct = query.getSelect().get(0).getOperand();
+			assertTrue(fct instanceof DefaultUDF);
+			assertNotNull(((DefaultUDF)fct).getDefinition());
+			assertTrue(fct.isString());
+			assertFalse(fct.isNumeric());
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("The parser allow ANY unknown function: this test should have succeeded!");
+		}
+
+		// DECLARE UDFs WITH SAME NAMES BUT DIFFERENT TYPE OF ARGUMENT:
+		udfs = new FunctionDef[]{new FunctionDef("toto", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("attr", new DBType(DBDatatype.VARCHAR))}),new FunctionDef("toto", new DBType(DBDatatype.INTEGER), new FunctionParam[]{new FunctionParam("attr", new DBType(DBDatatype.INTEGER))}),new FunctionDef("toto", new DBType(DBDatatype.INTEGER), new FunctionParam[]{new FunctionParam("attr", new DBType(DBDatatype.POINT))})};
+		parser = new ADQLParser(new DBChecker(tables, Arrays.asList(udfs)));
+
+		// Test the return type in function of the parameter:
+		try{
+			assertNotNull(parser.parseQuery("SELECT toto('blabla') AS toto1, toto(123) AS toto2, toto(POINT('', 1, 2)) AS toto3  FROM foo;"));
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains two DECLARED UDFs used here: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT toto('blabla') * 123 AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as numeric...which is here not possible: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A numeric value was expected instead of \"toto('blabla')\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT toto(123) || 'blabla' AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as string...which is here not possible: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"toto(123)\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT toto(POINT('', 1, 2)) || 'blabla' AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as string...which is here not possible: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"toto(POINT('', 1, 2))\".", ex.getErrors().next().getMessage());
+		}
+	}
+
+}
diff --git a/test/adql/db/TestFunctionDef.java b/test/adql/db/TestFunctionDef.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3d738a07aa0185136ab5199b98c915ec51ae1ed
--- /dev/null
+++ b/test/adql/db/TestFunctionDef.java
@@ -0,0 +1,312 @@
+package adql.db;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef.FunctionParam;
+import adql.parser.ParseException;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.NumericConstant;
+import adql.query.operand.StringConstant;
+import adql.query.operand.function.ADQLFunction;
+import adql.query.operand.function.DefaultUDF;
+import adql.query.operand.function.geometry.PointFunction;
+
+public class TestFunctionDef {
+
+	@Test
+	public void testIsString(){
+		for(DBDatatype type : DBDatatype.values()){
+			switch(type){
+				case CHAR:
+				case VARCHAR:
+				case TIMESTAMP:
+				case CLOB:
+					assertTrue(new FunctionDef("foo", new DBType(type)).isString);
+					break;
+				default:
+					assertFalse(new FunctionDef("foo", new DBType(type)).isString);
+			}
+		}
+	}
+
+	@Test
+	public void testIsGeometry(){
+		for(DBDatatype type : DBDatatype.values()){
+			switch(type){
+				case POINT:
+				case REGION:
+					assertTrue(new FunctionDef("foo", new DBType(type)).isGeometry);
+					break;
+				default:
+					assertFalse(new FunctionDef("foo", new DBType(type)).isGeometry);
+			}
+		}
+	}
+
+	@Test
+	public void testIsNumeric(){
+		for(DBDatatype type : DBDatatype.values()){
+			switch(type){
+				case CHAR:
+				case VARCHAR:
+				case TIMESTAMP:
+				case POINT:
+				case REGION:
+				case CLOB:
+					assertFalse(new FunctionDef("foo", new DBType(type)).isNumeric);
+					break;
+				default:
+					assertTrue(new FunctionDef("foo", new DBType(type)).isNumeric);
+			}
+		}
+	}
+
+	@Test
+	public void testToString(){
+		assertEquals("fct1()", new FunctionDef("fct1").toString());
+		assertEquals("fct1() -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR)).toString());
+		assertEquals("fct1(foo DOUBLE) -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE))}).toString());
+		assertEquals("fct1(foo DOUBLE)", new FunctionDef("fct1", new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE))}).toString());
+		assertEquals("fct1(foo DOUBLE, pt POINT) -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE)),new FunctionParam("pt", new DBType(DBDatatype.POINT))}).toString());
+		assertEquals("fct1(foo DOUBLE, pt POINT)", new FunctionDef("fct1", null, new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE)),new FunctionParam("pt", new DBType(DBDatatype.POINT))}).toString());
+	}
+
+	@Test
+	public void testParse(){
+		final String WRONG_FULL_SYNTAX = "Wrong function definition syntax! Expected syntax: \"<regular_identifier>(<parameters>?) <return_type>?\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <return_type>=\" -> <type_name>\", <parameters>=\"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"foo()\", \"foo() -> VARCHAR\", \"foo(param INTEGER)\", \"foo(param1 INTEGER, param2 DOUBLE) -> DOUBLE\"";
+		final String WRONG_PARAM_SYNTAX = "Wrong parameters syntax! Expected syntax: \"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"()\", \"(param INTEGER)\", \"(param1 INTEGER, param2 DOUBLE)\"";
+
+		// NULL test:
+		try{
+			FunctionDef.parse(null);
+			fail("A NULL string is not valide!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof NullPointerException);
+			assertEquals("Missing string definition to build a FunctionDef!", ex.getMessage());
+		}
+
+		// EMPTY STRING test:
+		try{
+			FunctionDef.parse("");
+			fail("An empty string is not valide!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+
+		// CORRECT string definitions:
+		try{
+			assertEquals("foo()", FunctionDef.parse("foo()").toString());
+			assertEquals("foo() -> VARCHAR", FunctionDef.parse("foo() -> string").toString());
+			assertEquals("foo() -> VARCHAR", FunctionDef.parse("foo()->string").toString());
+			assertEquals("foo(toto VARCHAR) -> SMALLINT", FunctionDef.parse("foo(toto varchar) -> boolean").toString());
+			assertEquals("foo(param1 DOUBLE, param2 INTEGER) -> DOUBLE", FunctionDef.parse(" foo ( param1	numeric,	param2    int )	->	DOUBLE ").toString());
+			assertEquals("foo_ALTernative2first(p POINT, d TIMESTAMP) -> TIMESTAMP", FunctionDef.parse("foo_ALTernative2first	(p POINT,d date) -> time").toString());
+			assertEquals("blabla_123(toto INTEGER, bla SMALLINT, truc CLOB, bidule CHAR, smurph POINT, date TIMESTAMP) -> SMALLINT", FunctionDef.parse("blabla_123(toto int4, bla bool, truc text, bidule character, smurph point, date timestamp) -> BOOLEAN").toString());
+		}catch(Exception ex){
+			ex.printStackTrace(System.err);
+			fail("All this string definitions are correct.");
+		}
+
+		// TYPE PARAMETER test:
+		try{
+			for(DBDatatype t : DBDatatype.values()){
+				switch(t){
+					case CHAR:
+					case VARCHAR:
+					case BINARY:
+					case VARBINARY:
+						assertEquals("foo() -> " + t.toString() + "(10)", FunctionDef.parse("foo() -> " + t.toString() + "(10)").toString());
+						break;
+					default:
+						assertEquals("foo() -> " + t.toString(), FunctionDef.parse("foo() -> " + t.toString() + "(10)").toString());
+				}
+			}
+		}catch(Exception ex){
+			ex.printStackTrace(System.err);
+			fail("Wrong type parsing!");
+		}
+
+		// WRONG string definitions:
+		try{
+			FunctionDef.parse("123()");
+			fail("No number is allowed as first character of a function name!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("1foo()");
+			fail("No number is allowed as first character of a function name!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo,truc()");
+			fail("No other character than [a-zA-Z0-9_] is allowed after a first character [a-zA-Z] in a function name!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo");
+			fail("A function definition must contain at list parenthesis even if there is no parameter.");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo(param)");
+			fail("A parameter must always have a type!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals("Wrong syntax for the 1-th parameter: \"param\"! Expected syntax: \"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"()\", \"(param INTEGER)\", \"(param1 INTEGER, param2 DOUBLE)\"", ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo()->aType");
+			fail("Wrong (return) type!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals("Unknown return type: \"aType\"!", ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo()->aType(10)");
+			fail("Wrong (return) type!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals("Unknown return type: \"aType(10)\"!", ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo() -> ");
+			fail("The return type is missing!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo(,)");
+			fail("Missing parameter definition!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_PARAM_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo(param1 int,)");
+			fail("Missing parameter definition!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_PARAM_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo(param1 aType)");
+			fail("Wrong parameter type!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals("Unknown type for the parameter \"param1\": \"aType\"!", ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo(param1 aType(10))");
+			fail("Wrong parameter type!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals("Unknown type for the parameter \"param1\": \"aType(10)\"!", ex.getMessage());
+		}
+	}
+
+	@Test
+	public void testCompareToFunctionDef(){
+		// DEFINITION 1 :: fct1() -> VARCHAR
+		FunctionDef def1 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR));
+
+		// TEST :: Identity test (def1 with def1): [EQUAL]
+		assertEquals(0, def1.compareTo(def1));
+
+		// TEST :: With a function having a different name and also no parameter: [GREATER]
+		assertEquals(1, def1.compareTo(new FunctionDef("fct0", new DBType(DBDatatype.VARCHAR))));
+
+		// TEST :: With a function having the same name, but a different return type: [EQUAL}
+		assertEquals(0, def1.compareTo(new FunctionDef("fct1", new DBType(DBDatatype.INTEGER))));
+
+		// TEST :: With a function having the same name, but 2 parameters: [LESS (4 characters: ø against 1010)]
+		assertEquals(-6, def1.compareTo(new FunctionDef("fct1", new DBType(DBDatatype.INTEGER), new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.INTEGER)),new FunctionParam("foo", new DBType(DBDatatype.INTEGER))})));
+
+		// DEFINITION 1 :: fct1(foo1 CHAR(12), foo2 DOUBLE) -> VARCHAR
+		def1 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE))});
+
+		// TEST :: Identity test (def1 with def1): [EQUAL]
+		assertEquals(0, def1.compareTo(def1));
+
+		// DEFINITION 2 :: fct1(foo1 CHAR(12), foo2 VARCHAR) -> VARCHAR
+		FunctionDef def2 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.VARCHAR))});
+
+		// TEST :: Identity test (def2 with def2): [EQUAL]
+		assertEquals(0, def2.compareTo(def2));
+
+		// TEST :: Same name, but different type for the last parameter only: [GREATER (because Numeric = 10 > String = 01)]
+		assertEquals(1, def1.compareTo(def2));
+
+		// DEFINITION 2 :: fct2(foo1 CHAR(12), foo2 DOUBLE) -> VARCHAR
+		def2 = new FunctionDef("fct2", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE))});
+
+		// TEST :: Identity test (def2 with def2): [EQUAL]
+		assertEquals(0, def2.compareTo(def2));
+
+		// TEST :: Different name but same parameters: [LESS]
+		assertEquals(-1, def1.compareTo(def2));
+
+		// DEFINITION 2 :: fct1(foo1 CHAR(12), foo2 POINT) -> VARCHAR
+		def2 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.POINT))});
+
+		// TEST :: Identity test (def2 with def2): [EQUAL]
+		assertEquals(0, def2.compareTo(def2));
+
+		// TEST :: Same name, but different type for the last parameter only: [GREATER]
+		assertEquals(1, def1.compareTo(def2));
+	}
+
+	@Test
+	public void testCompareToADQLFunction(){
+		// DEFINITION :: fct1() -> VARCHAR
+		FunctionDef def = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR));
+
+		// TEST :: NULL:
+		try{
+			def.compareTo((ADQLFunction)null);
+			fail("Missing ADQL function for comparison with FunctionDef!");
+		}catch(Exception e){
+			assertTrue(e instanceof NullPointerException);
+			assertEquals("Missing ADQL function with which comparing this function definition!", e.getMessage());
+		}
+
+		// TEST :: "fct1()": [EQUAL]
+		assertEquals(0, def.compareTo(new DefaultUDF("fct1", null)));
+
+		// TEST :: "fct0()": [GREATER]
+		assertEquals(1, def.compareTo(new DefaultUDF("fct0", null)));
+
+		// TEST :: "fct1(12.3, 3.14)": [LESS (of 2 params)]
+		assertEquals(-2, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{new NumericConstant(12.3),new NumericConstant(3.14)})));
+
+		// DEFINITION :: fct1(foo1 CHAR(12), foo2 DOUBLE) -> VARCHAR
+		def = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE))});
+
+		// TEST :: "fct1('blabla', 'blabla2')": [GREATER (because the second param is numeric and Numeric = 10 > String = 01)]
+		assertEquals(1, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{new StringConstant("blabla"),new StringConstant("blabla2")})));
+
+		// TEST :: "fct1('blabla', POINT('COORDSYS', 1.2, 3.4))": [GREATER (same reason ; POINT is considered as a String)]
+		try{
+			assertEquals(1, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{new StringConstant("blabla"),new PointFunction(new StringConstant("COORDSYS"), new NumericConstant(1.2), new NumericConstant(3.4))})));
+		}catch(Exception e){
+			e.printStackTrace();
+			fail();
+		}
+	}
+
+}
diff --git a/test/adql/db/TestSTCS.java b/test/adql/db/TestSTCS.java
new file mode 100644
index 0000000000000000000000000000000000000000..8ff76460c076c73b611c4e8cf1e564b55aa6c91b
--- /dev/null
+++ b/test/adql/db/TestSTCS.java
@@ -0,0 +1,536 @@
+package adql.db;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.StringBufferInputStream;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import adql.db.STCS.CoordSys;
+import adql.db.STCS.Flavor;
+import adql.db.STCS.Frame;
+import adql.db.STCS.RefPos;
+import adql.db.STCS.Region;
+import adql.db.STCS.RegionType;
+import adql.parser.ADQLParser;
+import adql.parser.ParseException;
+import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.NegativeOperand;
+import adql.query.operand.NumericConstant;
+import adql.query.operand.Operation;
+import adql.query.operand.OperationType;
+import adql.query.operand.StringConstant;
+import adql.query.operand.function.geometry.BoxFunction;
+import adql.query.operand.function.geometry.CircleFunction;
+import adql.query.operand.function.geometry.ContainsFunction;
+import adql.query.operand.function.geometry.GeometryFunction;
+import adql.query.operand.function.geometry.GeometryFunction.GeometryValue;
+import adql.query.operand.function.geometry.PointFunction;
+import adql.query.operand.function.geometry.PolygonFunction;
+import adql.query.operand.function.geometry.RegionFunction;
+
+@SuppressWarnings("deprecation")
+public class TestSTCS {
+
+	@BeforeClass
+	public static void setUpBeforeClass() throws Exception{}
+
+	@AfterClass
+	public static void tearDownAfterClass() throws Exception{}
+
+	@Before
+	public void setUp() throws Exception{}
+
+	@After
+	public void tearDown() throws Exception{}
+
+	@Test
+	public void buildRegion(){
+		// Special values:
+		try{
+			new Region((GeometryFunction)null);
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof NullPointerException);
+			assertEquals("Missing geometry to convert into STCS.Region!", e.getMessage());
+		}
+
+		try{
+			new Region((Region)null);
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof NullPointerException);
+			assertEquals("Missing region to NOT select!", e.getMessage());
+		}
+
+		try{
+			new Region(new ContainsFunction(new GeometryValue<GeometryFunction>(new RegionFunction(new StringConstant("position 1 2"))), new GeometryValue<GeometryFunction>(new RegionFunction(new StringConstant("circle 0 1 4")))));
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof IllegalArgumentException);
+			assertEquals("Unknown region type! Only geometrical function PointFunction, CircleFunction, BoxFunction, PolygonFunction and RegionFunction are allowed.", e.getMessage());
+		}
+
+		// Allowed values (1 test for each type of region):
+		try{
+			Region r = new Region(new PointFunction(new StringConstant(""), new NumericConstant(1.2), new NegativeOperand(new NumericConstant(2.3))));
+			assertEquals(RegionType.POSITION, r.type);
+			assertEquals("", r.coordSys.toSTCS());
+			assertEquals(1, r.coordinates.length);
+			assertEquals(2, r.coordinates[0].length);
+			assertEquals(1.2, r.coordinates[0][0], 0);
+			assertEquals(-2.3, r.coordinates[0][1], 0);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertNull(r.regions);
+			assertEquals("POSITION 1.2 -2.3", r.toSTCS());
+
+			r = new Region(new CircleFunction(new StringConstant("ICRS"), new NumericConstant(1.2), new NegativeOperand(new NumericConstant(2.3)), new NumericConstant(5)));
+			assertEquals(RegionType.CIRCLE, r.type);
+			assertEquals("ICRS", r.coordSys.toSTCS());
+			assertEquals(1, r.coordinates.length);
+			assertEquals(2, r.coordinates[0].length);
+			assertEquals(1.2, r.coordinates[0][0], 0);
+			assertEquals(-2.3, r.coordinates[0][1], 0);
+			assertEquals(5, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertNull(r.regions);
+			assertEquals("CIRCLE ICRS 1.2 -2.3 5.0", r.toSTCS());
+
+			r = new Region(new BoxFunction(new StringConstant("ICRS heliocenter"), new NumericConstant(1.2), new NegativeOperand(new NumericConstant(2.3)), new NumericConstant(5), new NumericConstant(4.6)));
+			assertEquals(RegionType.BOX, r.type);
+			assertEquals("ICRS HELIOCENTER", r.coordSys.toSTCS());
+			assertEquals(1, r.coordinates.length);
+			assertEquals(2, r.coordinates[0].length);
+			assertEquals(1.2, r.coordinates[0][0], 0);
+			assertEquals(-2.3, r.coordinates[0][1], 0);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(5, r.width, 0);
+			assertEquals(4.6, r.height, 0);
+			assertNull(r.regions);
+			assertEquals("BOX ICRS HELIOCENTER 1.2 -2.3 5.0 4.6", r.toSTCS());
+
+			r = new Region(new PolygonFunction(new StringConstant("cartesian2"), new ADQLOperand[]{new NumericConstant(1.2),new NegativeOperand(new NumericConstant(2.3)),new NumericConstant(5),new NumericConstant(4.6),new NegativeOperand(new NumericConstant(.89)),new NumericConstant(1)}));
+			assertEquals(RegionType.POLYGON, r.type);
+			assertEquals("CARTESIAN2", r.coordSys.toSTCS());
+			assertEquals(3, r.coordinates.length);
+			assertEquals(2, r.coordinates[0].length);
+			assertEquals(1.2, r.coordinates[0][0], 0);
+			assertEquals(-2.3, r.coordinates[0][1], 0);
+			assertEquals(5, r.coordinates[1][0], 0);
+			assertEquals(4.6, r.coordinates[1][1], 0);
+			assertEquals(-0.89, r.coordinates[2][0], 0);
+			assertEquals(1, r.coordinates[2][1], 0);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertNull(r.regions);
+			assertEquals("POLYGON CARTESIAN2 1.2 -2.3 5.0 4.6 -0.89 1.0", r.toSTCS());
+
+			r = new Region(new RegionFunction(new StringConstant("position ICrs 1.2 -2.3")));
+			assertEquals(RegionType.POSITION, r.type);
+			assertEquals("ICRS", r.coordSys.toSTCS());
+			assertEquals(1, r.coordinates.length);
+			assertEquals(2, r.coordinates[0].length);
+			assertEquals(1.2, r.coordinates[0][0], 0);
+			assertEquals(-2.3, r.coordinates[0][1], 0);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertNull(r.regions);
+			assertEquals("POSITION ICRS 1.2 -2.3", r.toSTCS());
+
+			r = new Region(new RegionFunction(new StringConstant("Union ICRS (Polygon 1 4 2 4 2 5 1 5 Polygon 3 4 4 4 4 5 3 5)")));
+			assertEquals(RegionType.UNION, r.type);
+			assertEquals("ICRS", r.coordSys.toSTCS());
+			assertNull(r.coordinates);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertEquals(2, r.regions.length);
+			assertEquals("UNION ICRS (POLYGON 1.0 4.0 2.0 4.0 2.0 5.0 1.0 5.0 POLYGON 3.0 4.0 4.0 4.0 4.0 5.0 3.0 5.0)", r.toString());
+			// inner region 1
+			Region innerR = r.regions[0];
+			assertEquals(RegionType.POLYGON, innerR.type);
+			assertEquals("", innerR.coordSys.toSTCS());
+			assertEquals(4, innerR.coordinates.length);
+			assertEquals(2, innerR.coordinates[0].length);
+			assertEquals(1, innerR.coordinates[0][0], 0);
+			assertEquals(4, innerR.coordinates[0][1], 0);
+			assertEquals(2, innerR.coordinates[1][0], 0);
+			assertEquals(4, innerR.coordinates[1][1], 0);
+			assertEquals(2, innerR.coordinates[2][0], 0);
+			assertEquals(5, innerR.coordinates[2][1], 0);
+			assertEquals(1, innerR.coordinates[3][0], 0);
+			assertEquals(5, innerR.coordinates[3][1], 0);
+			assertEquals(Double.NaN, innerR.radius, 0);
+			assertEquals(Double.NaN, innerR.width, 0);
+			assertEquals(Double.NaN, innerR.height, 0);
+			assertNull(innerR.regions);
+			assertEquals("POLYGON 1.0 4.0 2.0 4.0 2.0 5.0 1.0 5.0", innerR.toSTCS());
+			// inner region 2
+			innerR = r.regions[1];
+			assertEquals(RegionType.POLYGON, innerR.type);
+			assertEquals("", innerR.coordSys.toSTCS());
+			assertEquals(4, innerR.coordinates.length);
+			assertEquals(2, innerR.coordinates[0].length);
+			assertEquals(3, innerR.coordinates[0][0], 0);
+			assertEquals(4, innerR.coordinates[0][1], 0);
+			assertEquals(4, innerR.coordinates[1][0], 0);
+			assertEquals(4, innerR.coordinates[1][1], 0);
+			assertEquals(4, innerR.coordinates[2][0], 0);
+			assertEquals(5, innerR.coordinates[2][1], 0);
+			assertEquals(3, innerR.coordinates[3][0], 0);
+			assertEquals(5, innerR.coordinates[3][1], 0);
+			assertEquals(Double.NaN, innerR.radius, 0);
+			assertEquals(Double.NaN, innerR.width, 0);
+			assertEquals(Double.NaN, innerR.height, 0);
+			assertNull(innerR.regions);
+			assertEquals("POLYGON 3.0 4.0 4.0 4.0 4.0 5.0 3.0 5.0", innerR.toSTCS());
+
+			r = new Region(new RegionFunction(new StringConstant("NOT(CIRCLE ICRS 1.2 -2.3 5)")));
+			assertEquals(RegionType.NOT, r.type);
+			assertNull(r.coordSys);
+			assertNull(r.coordinates);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertEquals(1, r.regions.length);
+			assertEquals("NOT(CIRCLE ICRS 1.2 -2.3 5.0)", r.toSTCS());
+			// inner region
+			innerR = r.regions[0];
+			assertEquals(RegionType.CIRCLE, innerR.type);
+			assertEquals("ICRS", innerR.coordSys.toSTCS());
+			assertEquals(1, innerR.coordinates.length);
+			assertEquals(2, innerR.coordinates[0].length);
+			assertEquals(1.2, innerR.coordinates[0][0], 0);
+			assertEquals(-2.3, innerR.coordinates[0][1], 0);
+			assertEquals(5, innerR.radius, 0);
+			assertEquals(Double.NaN, innerR.width, 0);
+			assertEquals(Double.NaN, innerR.height, 0);
+			assertNull(innerR.regions);
+			assertEquals("CIRCLE ICRS 1.2 -2.3 5.0", innerR.toSTCS());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail();
+		}
+
+		// Test with incorrect syntaxes:
+		try{
+			new Region(new PointFunction(new StringConstant(""), new StringConstant("1.2"), new NegativeOperand(new NumericConstant(2.3))));
+			fail("The first coordinate is a StringConstant rather than a NumericConstant!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Can not convert into STC-S a non numeric argument (including ADQLColumn and Operation)!", e.getMessage());
+		}
+		try{
+			new Region(new PointFunction(new NumericConstant(.65), new NumericConstant(1.2), new NegativeOperand(new NumericConstant(2.3))));
+			fail("The coordinate system is a NumericConstant rather than a StringConstant!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("A coordinate system must be a string literal: \"0.65\" is not a string operand!", e.getMessage());
+		}
+		try{
+			new Region(new PointFunction(new StringConstant(""), null, new NegativeOperand(new NumericConstant(2.3))));
+			fail("The first coordinate is missing!");
+		}catch(Exception e){
+			assertTrue(e instanceof NullPointerException);
+			assertEquals("The POINT function must have non-null coordinates!", e.getMessage());
+		}
+		try{
+			new Region(new RegionFunction(new StringConstant("")));
+			fail("Missing STC-S expression!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Missing STC-S expression to parse!", e.getMessage());
+		}
+		try{
+			new Region(new RegionFunction(new StringConstant("MyRegion HERE 1.2")));
+			fail("Totally incorrect region type!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Unknown STC region type: \"MYREGION\"!", e.getMessage());
+		}
+		try{
+			new Region(new RegionFunction((new ADQLParser(new StringBufferInputStream("'POSITION ' || coordinateSys || ' ' || ra || ' ' || dec"))).StringExpression()));
+			fail("String concatenation can not be managed!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Can not convert into STC-S a non string argument (including ADQLColumn and Concatenation)!", e.getMessage());
+		}
+		try{
+			new Region(new PointFunction(new ADQLColumn("coordSys"), new NumericConstant(1), new NumericConstant(2)));
+			fail("Columns can not be managed!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Can not convert into STC-S a non string argument (including ADQLColumn and Concatenation)!", e.getMessage());
+		}
+		try{
+			new Region(new PointFunction(new StringConstant("ICRS"), new Operation(new NumericConstant(2), OperationType.MULT, new NumericConstant(5)), new NumericConstant(2)));
+			fail("Operations can not be managed!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Can not convert into STC-S a non numeric argument (including ADQLColumn and Operation)!", e.getMessage());
+		}
+	}
+
+	@Test
+	public void parseCoordSys(){
+		// GOOD SYNTAXES:
+		try{
+			CoordSys p;
+
+			// Default coordinate system (should be then interpreted as local coordinate system):
+			for(String s : new String[]{null,"","  	"}){
+				p = STCS.parseCoordSys(s);
+				assertEquals(Frame.UNKNOWNFRAME, p.frame);
+				assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+				assertEquals(Flavor.SPHERICAL2, p.flavor);
+				assertTrue(p.isDefault());
+			}
+
+			// Just a frame:
+			p = STCS.parseCoordSys("ICRS");
+			assertEquals(Frame.ICRS, p.frame);
+			assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Just a reference position:
+			p = STCS.parseCoordSys("LSR");
+			assertEquals(Frame.UNKNOWNFRAME, p.frame);
+			assertEquals(RefPos.LSR, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Just a flavor:
+			p = STCS.parseCoordSys("CARTESIAN2");
+			assertEquals(Frame.UNKNOWNFRAME, p.frame);
+			assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+			assertEquals(Flavor.CARTESIAN2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Frame + RefPos:
+			p = STCS.parseCoordSys("ICRS LSR");
+			assertEquals(Frame.ICRS, p.frame);
+			assertEquals(RefPos.LSR, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Frame + Flavor:
+			p = STCS.parseCoordSys("ICRS SPHERICAL2");
+			assertEquals(Frame.ICRS, p.frame);
+			assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// RefPos + Flavor:
+			p = STCS.parseCoordSys("HELIOCENTER SPHERICAL2");
+			assertEquals(Frame.UNKNOWNFRAME, p.frame);
+			assertEquals(RefPos.HELIOCENTER, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Frame + RefPos + Flavor
+			p = STCS.parseCoordSys("ICRS GEOCENTER SPHERICAL2");
+			assertEquals(Frame.ICRS, p.frame);
+			assertEquals(RefPos.GEOCENTER, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Lets try in a different case:
+			p = STCS.parseCoordSys("icrs Geocenter SpheriCAL2");
+			assertEquals(Frame.ICRS, p.frame);
+			assertEquals(RefPos.GEOCENTER, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail();
+		}
+
+		// WRONG SYNTAXES:
+		try{
+			STCS.parseCoordSys("HOME");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Incorrect syntax: \"HOME\" was unexpected! Expected syntax: \"[(ECLIPTIC|FK4|FK5|GALACTIC|ICRS|UNKNOWNFRAME)] [(BARYCENTER|GEOCENTER|HELIOCENTER|LSR|TOPOCENTER|RELOCATABLE|UNKNOWNREFPOS)] [(CARTESIAN2|CARTESIAN3|SPHERICAL2)]\" ; an empty string is also allowed and will be interpreted as the coordinate system locally used.", e.getMessage());
+		}
+
+		// With wrong reference position:
+		try{
+			STCS.parseCoordSys("ICRS HOME SPHERICAL2");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Incorrect syntax: \"HOME SPHERICAL2\" was unexpected! Expected syntax: \"[(ECLIPTIC|FK4|FK5|GALACTIC|ICRS|UNKNOWNFRAME)] [(BARYCENTER|GEOCENTER|HELIOCENTER|LSR|TOPOCENTER|RELOCATABLE|UNKNOWNREFPOS)] [(CARTESIAN2|CARTESIAN3|SPHERICAL2)]\" ; an empty string is also allowed and will be interpreted as the coordinate system locally used.", e.getMessage());
+		}
+
+		// With a cartesian flavor:
+		try{
+			STCS.parseCoordSys("ICRS CARTESIAN2");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("a coordinate system expressed with a cartesian flavor MUST have an UNKNOWNFRAME and UNKNOWNREFPOS!", e.getMessage());
+		}
+		try{
+			STCS.parseCoordSys("LSR CARTESIAN3");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("a coordinate system expressed with a cartesian flavor MUST have an UNKNOWNFRAME and UNKNOWNREFPOS!", e.getMessage());
+		}
+		try{
+			CoordSys p = STCS.parseCoordSys("CARTESIAN2");
+			assertEquals(Frame.UNKNOWNFRAME, p.frame);
+			assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+			assertEquals(Flavor.CARTESIAN2, p.flavor);
+
+			p = STCS.parseCoordSys("CARTESIAN3");
+			assertEquals(Frame.UNKNOWNFRAME, p.frame);
+			assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+			assertEquals(Flavor.CARTESIAN3, p.flavor);
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail();
+		}
+
+		// Without spaces:
+		try{
+			STCS.parseCoordSys("icrsGeocentercarteSIAN2");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Incorrect syntax: \"icrsGeocentercarteSIAN2\" was unexpected! Expected syntax: \"[(ECLIPTIC|FK4|FK5|GALACTIC|ICRS|UNKNOWNFRAME)] [(BARYCENTER|GEOCENTER|HELIOCENTER|LSR|TOPOCENTER|RELOCATABLE|UNKNOWNREFPOS)] [(CARTESIAN2|CARTESIAN3|SPHERICAL2)]\" ; an empty string is also allowed and will be interpreted as the coordinate system locally used.", e.getMessage());
+		}
+	}
+
+	@Test
+	public void serializeCoordSys(){
+		try{
+			assertEquals("", STCS.toSTCS((CoordSys)null));
+
+			assertEquals("", STCS.toSTCS(new CoordSys()));
+
+			assertEquals("", STCS.toSTCS(new CoordSys(null, null, null)));
+			assertEquals("", STCS.toSTCS(new CoordSys(Frame.DEFAULT, RefPos.DEFAULT, Flavor.DEFAULT)));
+			assertEquals("", STCS.toSTCS(new CoordSys(Frame.UNKNOWNFRAME, RefPos.UNKNOWNREFPOS, Flavor.SPHERICAL2)));
+
+			assertEquals("", STCS.toSTCS(new CoordSys(null)));
+			assertEquals("", STCS.toSTCS(new CoordSys("")));
+			assertEquals("", STCS.toSTCS(new CoordSys("   	\n\r")));
+
+			assertEquals("ICRS", STCS.toSTCS(new CoordSys(Frame.ICRS, null, null)));
+			assertEquals("ICRS", STCS.toSTCS(new CoordSys(Frame.ICRS, RefPos.DEFAULT, Flavor.DEFAULT)));
+			assertEquals("ICRS", STCS.toSTCS(new CoordSys(Frame.ICRS, RefPos.UNKNOWNREFPOS, Flavor.SPHERICAL2)));
+
+			assertEquals("GEOCENTER", STCS.toSTCS(new CoordSys(null, RefPos.GEOCENTER, null)));
+			assertEquals("GEOCENTER", STCS.toSTCS(new CoordSys(Frame.DEFAULT, RefPos.GEOCENTER, Flavor.DEFAULT)));
+			assertEquals("GEOCENTER", STCS.toSTCS(new CoordSys(Frame.UNKNOWNFRAME, RefPos.GEOCENTER, Flavor.SPHERICAL2)));
+
+			assertEquals("CARTESIAN3", STCS.toSTCS(new CoordSys(null, null, Flavor.CARTESIAN3)));
+			assertEquals("CARTESIAN3", STCS.toSTCS(new CoordSys(Frame.DEFAULT, RefPos.UNKNOWNREFPOS, Flavor.CARTESIAN3)));
+			assertEquals("CARTESIAN3", STCS.toSTCS(new CoordSys(Frame.UNKNOWNFRAME, RefPos.UNKNOWNREFPOS, Flavor.CARTESIAN3)));
+
+			assertEquals("ICRS GEOCENTER", STCS.toSTCS(new CoordSys(Frame.ICRS, RefPos.GEOCENTER, null)));
+			assertEquals("ICRS GEOCENTER", STCS.toSTCS(new CoordSys(Frame.ICRS, RefPos.GEOCENTER, Flavor.DEFAULT)));
+
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys().toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys("").toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys(null).toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys("   	\n\t").toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys(null, null, null).toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys(Frame.DEFAULT, RefPos.DEFAULT, Flavor.DEFAULT).toFullSTCS());
+			assertEquals("ICRS UNKNOWNREFPOS SPHERICAL2", new CoordSys(Frame.ICRS, null, null).toFullSTCS());
+			assertEquals("ICRS UNKNOWNREFPOS SPHERICAL2", new CoordSys(Frame.ICRS, RefPos.DEFAULT, Flavor.DEFAULT).toFullSTCS());
+			assertEquals("UNKNOWNFRAME GEOCENTER SPHERICAL2", new CoordSys(Frame.UNKNOWNFRAME, RefPos.GEOCENTER, Flavor.DEFAULT).toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS CARTESIAN3", new CoordSys(Frame.DEFAULT, RefPos.DEFAULT, Flavor.CARTESIAN3).toFullSTCS());
+			assertEquals("ICRS GEOCENTER SPHERICAL2", new CoordSys(Frame.ICRS, RefPos.GEOCENTER, Flavor.DEFAULT).toFullSTCS());
+		}catch(ParseException pe){
+			pe.printStackTrace(System.err);
+			fail();
+		}
+	}
+
+	@Test
+	public void parseRegion(){
+		// TESTS WITH NO STC-S:
+		try{
+			STCS.parseRegion(null);
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Missing STC-S expression to parse!", e.getMessage());
+		}
+		try{
+			STCS.parseRegion("");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Missing STC-S expression to parse!", e.getMessage());
+		}
+		try{
+			STCS.parseRegion("   	\n\r");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Missing STC-S expression to parse!", e.getMessage());
+		}
+
+		// TESTS WITH A VALID EXPRESSION, EACH OF A DIFFERENT REGION TYPE:
+		String[] expressions = new String[]{" Position GALACTIC 10 20","Circle  	 ICRS    GEOCENTER	10	20	0.5 ","BOX cartesian2 3 3 2 2","Polygon 1 4 2 4 2 5 1 5","Union ICRS (Polygon 1 4 2 4 2 5 1 5 Polygon 3 4 4 4 4 5 3 5)","INTERSECTION ICRS (Polygon 1 4 2 4 2 5 1 5 Polygon 3 4 4 4 4 5 3 5)","NOT(Circle ICRS GEOCENTER 10 20 0.5)"};
+		try{
+			for(String e : expressions)
+				STCS.parseRegion(e);
+		}catch(Exception ex){
+			ex.printStackTrace(System.err);
+			fail();
+		}
+
+		// TEST WITH A MISSING PARAMETER:
+		expressions = new String[]{" Position GALACTIC 10 ","BOX cartesian2 3 3 2","NOT()"};
+		for(String e : expressions){
+			try{
+				STCS.parseRegion(e);
+				fail();
+			}catch(Exception ex){
+				assertTrue(ex instanceof ParseException);
+				assertTrue(ex.getMessage().startsWith("Unexpected End Of Expression! Expected syntax: \""));
+			}
+		}
+
+		// TEST WITH A WRONG COORDINATE SYSTEM (since it is optional in all these expressions, it will be considered as a coordinate...which is of course, not the case):
+		try{
+			STCS.parseRegion("Circle  	 HERE	10	20	0.5 ");
+			fail();
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertTrue(ex.getMessage().startsWith("Incorrect syntax: a coordinates pair (2 numerics separated by one or more spaces) was expected! Expected syntax: \"CIRCLE <coordSys> <coordPair> <radius>\", where coordPair=\"<numeric> <numeric>\", radius=\"<numeric>\" and coordSys=\"[(ECLIPTIC|FK4|FK5|GALACTIC|ICRS|UNKNOWNFRAME)] [(BARYCENTER|GEOCENTER|HELIOCENTER|LSR|TOPOCENTER|RELOCATABLE|UNKNOWNREFPOS)] [(CARTESIAN2|CARTESIAN3|SPHERICAL2)]\" ; an empty string is also allowed and will be interpreted as the coordinate system locally used."));
+		}
+
+		// TEST WITH EITHER A WRONG NUMERIC (L in lower case instead of 1) OR A MISSING OPENING PARENTHESIS:
+		expressions = new String[]{"Polygon 1 4 2 4 2 5 l 5","Union ICRS Polygon 1 4 2 4 2 5 1 5 Polygon 3 4 4 4 4 5 3 5)"};
+		for(String e : expressions){
+			try{
+				STCS.parseRegion(e);
+				fail();
+			}catch(Exception ex){
+				assertTrue(ex instanceof ParseException);
+				assertTrue(ex.getMessage().startsWith("Incorrect syntax: "));
+			}
+		}
+	}
+}
diff --git a/test/adql/parser/TestADQLParser.java b/test/adql/parser/TestADQLParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..10837d51c98c84a26d562efc5d48ccd053c588b8
--- /dev/null
+++ b/test/adql/parser/TestADQLParser.java
@@ -0,0 +1,43 @@
+package adql.parser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import adql.query.ADQLQuery;
+import adql.query.operand.StringConstant;
+
+public class TestADQLParser {
+
+	@BeforeClass
+	public static void setUpBeforeClass() throws Exception{}
+
+	@AfterClass
+	public static void tearDownAfterClass() throws Exception{}
+
+	@Before
+	public void setUp() throws Exception{}
+
+	@After
+	public void tearDown() throws Exception{}
+
+	@Test
+	public void test(){
+		ADQLParser parser = new ADQLParser();
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT 'truc''machin'  	'bidule' -- why not a comment now ^^\n'FIN' FROM foo;");
+			assertNotNull(query);
+			assertEquals("truc'machinbiduleFIN", ((StringConstant)(query.getSelect().get(0).getOperand())).getValue());
+			assertEquals("'truc''machinbiduleFIN'", query.getSelect().get(0).getOperand().toADQL());
+		}catch(Exception ex){
+			fail("String litteral concatenation is perfectly legal according to the ADQL standard.");
+		}
+	}
+
+}
diff --git a/test/tap/db/JDBCConnectionTest.java b/test/tap/db/JDBCConnectionTest.java
index 2cd74577a3600b882ab920049134a8d5a83acf5e..b645e85800bee3eb0f71d0ad592bce7cec49df76 100644
--- a/test/tap/db/JDBCConnectionTest.java
+++ b/test/tap/db/JDBCConnectionTest.java
@@ -32,12 +32,12 @@ import tap.metadata.TAPMetadata.STDSchema;
 import tap.metadata.TAPMetadata.STDTable;
 import tap.metadata.TAPSchema;
 import tap.metadata.TAPTable;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import testtools.DBTools;
 import adql.db.DBChecker;
 import adql.db.DBColumn;
 import adql.db.DBTable;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 import adql.parser.ADQLParser;
 import adql.parser.ParseException;
 import adql.query.ADQLQuery;
@@ -179,8 +179,8 @@ public class JDBCConnectionTest {
 		assertEquals("VARCHAR", pgJDBCConnection.getDBMSDatatype(null));
 		assertEquals("TEXT", sqliteJDBCConnection.getDBMSDatatype(null));
 
-		assertEquals("bytea", pgJDBCConnection.getDBMSDatatype(new TAPType(TAPDatatype.VARBINARY)));
-		assertEquals("BLOB", sqliteJDBCConnection.getDBMSDatatype(new TAPType(TAPDatatype.VARBINARY)));
+		assertEquals("bytea", pgJDBCConnection.getDBMSDatatype(new DBType(DBDatatype.VARBINARY)));
+		assertEquals("BLOB", sqliteJDBCConnection.getDBMSDatatype(new DBType(DBDatatype.VARBINARY)));
 	}
 
 	@Test
@@ -854,7 +854,7 @@ public class JDBCConnectionTest {
 		return true;
 	}
 
-	private static boolean equals(final TAPType type1, final TAPType type2){
+	private static boolean equals(final DBType type1, final DBType type2){
 		return type1 != null && type2 != null && type1.type == type2.type && type1.length == type2.length;
 	}
 
@@ -862,7 +862,7 @@ public class JDBCConnectionTest {
 		TAPMetadata tapMeta = new TAPMetadata();
 		TAPSchema tapSchema = new TAPSchema(STDSchema.TAPSCHEMA.toString());
 		TAPTable customColumns = (TAPTable)TAPMetadata.getStdTable(STDTable.COLUMNS).copy("Columns", STDTable.COLUMNS.label);
-		customColumns.addColumn("TestNewColumn", new TAPType(TAPDatatype.VARCHAR), "This is a fake column, just for test purpose.", null, null, null);
+		customColumns.addColumn("TestNewColumn", new DBType(DBDatatype.VARCHAR), "This is a fake column, just for test purpose.", null, null, null);
 		tapSchema.addTable(customColumns);
 		TAPTable addTable = new TAPTable("AdditionalTable");
 		addTable.addColumn("Blabla");
diff --git a/test/tap/formatter/JSONFormatTest.java b/test/tap/formatter/JSONFormatTest.java
index 527d7e2cfda74d37bc49f4e2b247de4345083370..3e6a84522088cea570b458db650900e7ce0ced9d 100644
--- a/test/tap/formatter/JSONFormatTest.java
+++ b/test/tap/formatter/JSONFormatTest.java
@@ -31,11 +31,12 @@ import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.parameters.TAPParameters;
 import testtools.DBTools;
 import uws.service.UserIdentifier;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef;
 
 /**
  * <p>Test the JSONFormat function {@link JSONFormat#writeResult(TableIterator, OutputStream, TAPExecutionReport, Thread)}.</p>
@@ -58,10 +59,10 @@ public class JSONFormatTest {
 		serviceConn = new ServiceConnectionTest();
 
 		resultingColumns = new TAPColumn[4];
-		resultingColumns[0] = new TAPColumn("ID", new TAPType(TAPDatatype.VARCHAR));
-		resultingColumns[1] = new TAPColumn("ra", new TAPType(TAPDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
-		resultingColumns[2] = new TAPColumn("deg", new TAPType(TAPDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
-		resultingColumns[3] = new TAPColumn("gmag", new TAPType(TAPDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
+		resultingColumns[0] = new TAPColumn("ID", new DBType(DBDatatype.VARCHAR));
+		resultingColumns[1] = new TAPColumn("ra", new DBType(DBDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
+		resultingColumns[2] = new TAPColumn("deg", new DBType(DBDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
+		resultingColumns[3] = new TAPColumn("gmag", new DBType(DBDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
 
 		if (!jsonFile.exists())
 			jsonFile.createNewFile();
@@ -223,6 +224,16 @@ public class JSONFormatTest {
 			return null;
 		}
 
+		@Override
+		public Collection<String> getGeometries(){
+			return null;
+		}
+
+		@Override
+		public Collection<FunctionDef> getUDFs(){
+			return null;
+		}
+
 		@Override
 		public TAPLog getLogger(){
 			return null;
diff --git a/test/tap/formatter/SVFormatTest.java b/test/tap/formatter/SVFormatTest.java
index d71c6accf79a64cf5343209af274af62265663ad..d50f0fecae68fc6f5c282b144d98ed17851161c5 100644
--- a/test/tap/formatter/SVFormatTest.java
+++ b/test/tap/formatter/SVFormatTest.java
@@ -28,12 +28,13 @@ import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.parameters.TAPParameters;
 import testtools.CommandExecute;
 import testtools.DBTools;
 import uws.service.UserIdentifier;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef;
 
 /**
  * <p>Test the SVFormat function {@link SVFormat#writeResult(TableIterator, OutputStream, TAPExecutionReport, Thread)}.</p>
@@ -56,10 +57,10 @@ public class SVFormatTest {
 		serviceConn = new ServiceConnectionTest();
 
 		resultingColumns = new TAPColumn[4];
-		resultingColumns[0] = new TAPColumn("ID", new TAPType(TAPDatatype.VARCHAR));
-		resultingColumns[1] = new TAPColumn("ra", new TAPType(TAPDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
-		resultingColumns[2] = new TAPColumn("deg", new TAPType(TAPDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
-		resultingColumns[3] = new TAPColumn("gmag", new TAPType(TAPDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
+		resultingColumns[0] = new TAPColumn("ID", new DBType(DBDatatype.VARCHAR));
+		resultingColumns[1] = new TAPColumn("ra", new DBType(DBDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
+		resultingColumns[2] = new TAPColumn("deg", new DBType(DBDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
+		resultingColumns[3] = new TAPColumn("gmag", new DBType(DBDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
 
 		if (!svFile.exists())
 			svFile.createNewFile();
@@ -217,6 +218,16 @@ public class SVFormatTest {
 			return null;
 		}
 
+		@Override
+		public Collection<String> getGeometries(){
+			return null;
+		}
+
+		@Override
+		public Collection<FunctionDef> getUDFs(){
+			return null;
+		}
+
 		@Override
 		public TAPLog getLogger(){
 			return null;
diff --git a/test/tap/formatter/TextFormatTest.java b/test/tap/formatter/TextFormatTest.java
index 959475b608ac2425a9297e0c6f2972723e224719..b763a29cc669f0580784a0c33c2499ceae68b19d 100644
--- a/test/tap/formatter/TextFormatTest.java
+++ b/test/tap/formatter/TextFormatTest.java
@@ -28,12 +28,13 @@ import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.parameters.TAPParameters;
 import testtools.CommandExecute;
 import testtools.DBTools;
 import uws.service.UserIdentifier;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef;
 
 /**
  * <p>Test the TestFormat function {@link TestFormat#writeResult(TableIterator, OutputStream, TAPExecutionReport, Thread)}.</p>
@@ -56,10 +57,10 @@ public class TextFormatTest {
 		serviceConn = new ServiceConnectionTest();
 
 		resultingColumns = new TAPColumn[4];
-		resultingColumns[0] = new TAPColumn("ID", new TAPType(TAPDatatype.VARCHAR));
-		resultingColumns[1] = new TAPColumn("ra", new TAPType(TAPDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
-		resultingColumns[2] = new TAPColumn("deg", new TAPType(TAPDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
-		resultingColumns[3] = new TAPColumn("gmag", new TAPType(TAPDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
+		resultingColumns[0] = new TAPColumn("ID", new DBType(DBDatatype.VARCHAR));
+		resultingColumns[1] = new TAPColumn("ra", new DBType(DBDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
+		resultingColumns[2] = new TAPColumn("deg", new DBType(DBDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
+		resultingColumns[3] = new TAPColumn("gmag", new DBType(DBDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
 
 		if (!textFile.exists())
 			textFile.createNewFile();
@@ -217,6 +218,16 @@ public class TextFormatTest {
 			return null;
 		}
 
+		@Override
+		public Collection<String> getGeometries(){
+			return null;
+		}
+
+		@Override
+		public Collection<FunctionDef> getUDFs(){
+			return null;
+		}
+
 		@Override
 		public TAPLog getLogger(){
 			return null;
diff --git a/test/tap/formatter/VOTableFormatTest.java b/test/tap/formatter/VOTableFormatTest.java
index 51e9dc8ab47732d008335527eba3564dd03488cc..5812072dc47999bffa5d98e75886da7b6c893b7f 100644
--- a/test/tap/formatter/VOTableFormatTest.java
+++ b/test/tap/formatter/VOTableFormatTest.java
@@ -28,13 +28,14 @@ import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.parameters.TAPParameters;
 import testtools.CommandExecute;
 import testtools.DBTools;
 import uk.ac.starlink.votable.DataFormat;
 import uws.service.UserIdentifier;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef;
 
 /**
  * <p>Test the VOTableFormat function {@link VOTableFormat#writeResult(TableIterator, OutputStream, TAPExecutionReport, Thread)}.</p>
@@ -57,10 +58,10 @@ public class VOTableFormatTest {
 		serviceConn = new ServiceConnectionTest();
 
 		resultingColumns = new TAPColumn[4];
-		resultingColumns[0] = new TAPColumn("ID", new TAPType(TAPDatatype.VARCHAR));
-		resultingColumns[1] = new TAPColumn("ra", new TAPType(TAPDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
-		resultingColumns[2] = new TAPColumn("deg", new TAPType(TAPDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
-		resultingColumns[3] = new TAPColumn("gmag", new TAPType(TAPDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
+		resultingColumns[0] = new TAPColumn("ID", new DBType(DBDatatype.VARCHAR));
+		resultingColumns[1] = new TAPColumn("ra", new DBType(DBDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
+		resultingColumns[2] = new TAPColumn("deg", new DBType(DBDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
+		resultingColumns[3] = new TAPColumn("gmag", new DBType(DBDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
 
 		if (!votableFile.exists())
 			votableFile.createNewFile();
@@ -222,6 +223,16 @@ public class VOTableFormatTest {
 			return null;
 		}
 
+		@Override
+		public Collection<String> getGeometries(){
+			return null;
+		}
+
+		@Override
+		public Collection<FunctionDef> getUDFs(){
+			return null;
+		}
+
 		@Override
 		public TAPLog getLogger(){
 			return null;
diff --git a/test/uws/service/UWSUrlTest.java b/test/uws/service/UWSUrlTest.java
index 673365076427418810a9b270f20168e44d216636..a61db70f0032176b4f983aa3c915dcbf9be21d0d 100644
--- a/test/uws/service/UWSUrlTest.java
+++ b/test/uws/service/UWSUrlTest.java
@@ -2,6 +2,7 @@ package uws.service;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.BufferedReader;
@@ -406,39 +407,53 @@ public class UWSUrlTest {
 		// CASE 1: http://localhost:8080/tapTest/path with url-pattern = /path/*
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2root);
-			assertEquals(uu.getBaseURI(), "/path");
-		}catch(NullPointerException e){
+			assertEquals("/path", uu.getBaseURI());
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/", uu.toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2root.getRequestURL());
 		}
 
 		// CASE 2: http://localhost:8080/tapTest/path/async with url-pattern = /path/*
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2async);
-			assertEquals(uu.getBaseURI(), "/path");
-		}catch(NullPointerException e){
+			assertEquals("/path", uu.getBaseURI());
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/async", uu.toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2async.getRequestURL());
 		}
 
-		// CASE 3: http://localhost:8080/tapTest/async with url-pattern = /*
+		// CASE 3: http://localhost:8080/tapTest with url-pattern = /*
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2root);
-			assertEquals(uu.getBaseURI(), "");
-		}catch(NullPointerException e){
+			assertEquals("", uu.getBaseURI());
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/", uu.toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2root.getRequestURL());
 		}
 
 		// CASE 4: http://localhost:8080/tapTest/async with url-pattern = /*
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2async);
-			assertEquals(uu.getBaseURI(), "");
-		}catch(NullPointerException e){
+			assertEquals("", uu.getBaseURI());
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/async", uu.toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2async.getRequestURL());
 		}
 
 		// CASE 5: http://localhost:8080/tapTest/path/async with url-pattern = /path/*
 		try{
 			new UWSUrl(requestWithServletPathNull);
-		}catch(NullPointerException e){
+			fail("RequestURL with no servlet path: this test should have failed!");
+		}catch(Exception e){
+			assertTrue(e instanceof NullPointerException);
 			assertEquals(e.getMessage(), "The extracted base UWS URI is NULL!");
 		}
 	}
@@ -449,18 +464,20 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2root);
 			uu.load(requestFromPath2root);
-			assertEquals(uu.getUwsURI(), "");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/path/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2root.getRequestURL());
 		}
 		// CASE 1b: Idem while loading http://localhost:8080/tapTest/path/async
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2root);
 			uu.load(requestFromPath2async);
-			assertEquals(uu.getUwsURI(), "/async");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/path/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2async.getRequestURL());
 		}
 
@@ -468,9 +485,10 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2async);
 			uu.load(requestFromPath2async);
-			assertEquals(uu.getUwsURI(), "/async");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/path/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2async.getRequestURL());
 		}
 
@@ -478,9 +496,10 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2async);
 			uu.load(requestFromPath2root);
-			assertEquals(uu.getUwsURI(), "");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/path/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2root.getRequestURL());
 		}
 
@@ -488,8 +507,8 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2root);
 			uu.load(requestFromRoot2root);
-			assertEquals(uu.getUwsURI(), "");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/async/123456A");
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/async/123456A", uu.jobSummary("async", "123456A").toString());
 		}catch(NullPointerException e){
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2root.getRequestURL());
 		}
@@ -498,9 +517,10 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2root);
 			uu.load(requestFromRoot2async);
-			assertEquals(uu.getUwsURI(), "/async");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2async.getRequestURL());
 		}
 
@@ -508,9 +528,10 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2async);
 			uu.load(requestFromRoot2async);
-			assertEquals(uu.getUwsURI(), "/async");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2async.getRequestURL());
 		}
 
@@ -518,9 +539,10 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2async);
 			uu.load(requestFromRoot2root);
-			assertEquals(uu.getUwsURI(), "");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2root.getRequestURL());
 		}
 
@@ -529,7 +551,8 @@ public class UWSUrlTest {
 			UWSUrl uu = new UWSUrl(requestFromRoot2async);
 			uu.load(requestFromPath2async);
 			assertFalse(uu.getUwsURI().equals(""));
-		}catch(NullPointerException e){
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2root.getRequestURL());
 		}
 	}