Commit 37e26a11 authored by gmantele's avatar gmantele
Browse files

[ADQL,TAP] Add the ability to declare columns and UDF with an UNKNOWN type.

(merge with branch 'unknownFctType')
parent c57d2fe4
Loading
Loading
Loading
Loading
+130 −5
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ package adql.db;
 * 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 - Astronomisches Rechen Institut (ARI)
 * Copyright 2014-2015 - Astronomisches Rechen Institut (ARI)
 */

/**
@@ -32,7 +32,7 @@ package adql.db;
 * It is used to set the attribute type/datatype of this class.</p>
 *  
 * @author Gr&eacute;gory Mantelet (ARI)
 * @version 1.3 (10/2014)
 * @version 1.4 (07/2015)
 * @since 1.3
 */
public class DBType {
@@ -41,11 +41,40 @@ public class DBType {
	 * List of all datatypes declared in the IVOA recommendation of TAP (in the section UPLOAD).
	 * 
	 * @author Gr&eacute;gory Mantelet (ARI)
	 * @version 1.3 (10/2014)
	 * @version 1.4 (06/2015)
	 * @since 1.3
	 */
	public static enum DBDatatype{
		SMALLINT, INTEGER, BIGINT, REAL, DOUBLE, BINARY, VARBINARY, CHAR, VARCHAR, BLOB, CLOB, TIMESTAMP, POINT, REGION;
		SMALLINT, INTEGER, BIGINT, REAL, DOUBLE, BINARY, VARBINARY, CHAR, VARCHAR, BLOB, CLOB, TIMESTAMP, POINT, REGION,
		/** @since 1.4 */
		UNKNOWN;

		/** String to return when {@link #toString()} is called.
		 * @since 1.4*/
		private String strExp = this.name();

		@Override
		public String toString(){
			return strExp;
		}

		/**
		 * <p>This function lets define the name of the type as provided
		 * <b>ONLY FOR {@link #UNKNOWN} {@link DBDatatype}</b>.</p>
		 * 
		 * <p><i><b>Important:</b>
		 * 	If this {@link DBDatatype} is not {@link #UNKNOWN} or if the given name is NULL or empty,
		 * 	this function has no effect.
		 * </i></p>
		 * 
		 * @param typeName	User type name.
		 * 
		 * @since 1.4
		 */
		public void setCustomType(final String typeName){
			if (this == UNKNOWN && typeName != null && typeName.trim().length() > 0)
				strExp = "?" + typeName.trim() + "?";
		}
	}

	/** Special value in case no length/size is specified. */
@@ -79,6 +108,23 @@ public class DBType {
		this.length = length;
	}

	/**
	 * <p>Tells whether this type is a numeric.</p>
	 * 
	 * <p><i>Concerned types:
	 * 	{@link DBDatatype#SMALLINT SMALLINT}, {@link DBDatatype#INTEGER INTEGER}, {@link DBDatatype#BIGINT BIGINT},
	 * 	{@link DBDatatype#REAL REAL}, {@link DBDatatype#DOUBLE DOUBLE}, {@link DBDatatype#BINARY BINARY},
	 * 	{@link DBDatatype#VARBINARY VARBINARY} and {@link DBDatatype#BLOB BLOB}.
	 * </i></p>
	 * 
	 * <p><i><b>Important note</b>:
	 * 	Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything.
	 * 	That's why, this function will also returned <code>true</code> if the type is
	 * 	{@link DBDatatype#UNKNOWN UNKNOWN}.
	 * </i></p>
	 * 
	 * @return	<code>true</code> if this type is a numeric, <code>false</code> otherwise.
	 */
	public boolean isNumeric(){
		switch(type){
			case SMALLINT:
@@ -91,42 +137,121 @@ public class DBType {
			case BINARY:
			case VARBINARY:
			case BLOB:
			case UNKNOWN:
				return true;
			default:
				return false;
		}
	}

	/**
	 * <p>Tells whether this type is a list of bytes.</p>
	 * 
	 * <p><i>Concerned types:
	 * 	{@link DBDatatype#BINARY BINARY}, {@link DBDatatype#VARBINARY VARBINARY} and {@link DBDatatype#BLOB BLOB}.
	 * </i></p>
	 * 
	 * <p><i><b>Important note</b>:
	 * 	Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything.
	 * 	That's why, this function will also returned <code>true</code> if the type is
	 * 	{@link DBDatatype#UNKNOWN UNKNOWN}.
	 * </i></p>
	 * 
	 * @return	<code>true</code> if this type is a binary, <code>false</code> otherwise.
	 */
	public boolean isBinary(){
		switch(type){
			case BINARY:
			case VARBINARY:
			case BLOB:
			case UNKNOWN:
				return true;
			default:
				return false;
		}
	}

	/**
	 * <p>Tells whether this type is about characters.</p>
	 * 
	 * <p><i>Concerned types:
	 * 	{@link DBDatatype#CHAR CHAR}, {@link DBDatatype#VARCHAR VARCHAR}, {@link DBDatatype#CLOB CLOB}
	 * 	and {@link DBDatatype#TIMESTAMP TIMESTAMP}.
	 * </i></p>
	 * 
	 * <p><i><b>Important note</b>:
	 * 	Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything.
	 * 	That's why, this function will also returned <code>true</code> if the type is
	 * 	{@link DBDatatype#UNKNOWN UNKNOWN}.
	 * </i></p>
	 * 
	 * @return	<code>true</code> if this type is a string, <code>false</code> otherwise.
	 */
	public boolean isString(){
		switch(type){
			case CHAR:
			case VARCHAR:
			case CLOB:
			case TIMESTAMP:
			case UNKNOWN:
				return true;
			default:
				return false;
		}
	}

	/**
	 * <p>Tells whether this type is a geometrical region.</p>
	 * 
	 * <p><i>Concerned types:
	 * 	{@link DBDatatype#POINT POINT} and {@link DBDatatype#REGION REGION}.
	 * </i></p>
	 * 
	 * <p><i><b>Important note</b>:
	 * 	Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything.
	 * 	That's why, this function will also returned <code>true</code> if the type is
	 * 	{@link DBDatatype#UNKNOWN UNKNOWN}.
	 * </i></p>
	 * 
	 * @return	<code>true</code> if this type is a geometry, <code>false</code> otherwise.
	 */
	public boolean isGeometry(){
		return (type == DBDatatype.POINT || type == DBDatatype.REGION);
		return (type == DBDatatype.POINT || type == DBDatatype.REGION || type == DBDatatype.UNKNOWN);
	}

	/**
	 * <p>Tell whether this type has been resolved or not.</p>
	 * 
	 * <p><i>Concerned type:
	 * 	{@link DBDatatype#UNKNOWN UNKNOWN}.
	 * </i></p>
	 * 
	 * @return	<code>true</code> if this type has NOT been resolved, <code>false</code> otherwise.
	 * 
	 * @since 1.4
	 */
	public boolean isUnknown(){
		return type == DBDatatype.UNKNOWN;
	}

	/**
	 * <p>Tell whether this {@link DBType} is compatible with the given one.</p>
	 * 
	 * <p>
	 * 	Two {@link DBType}s are said compatible if they are both binary, numeric, geometric or string.
	 * 	If one of the two types is {@link DBDatatype#UNKNOWN unknown}, this function will consider them
	 * 	as compatible and will return <code>true</code>.
	 * </p>
	 * 
	 * @param t	The type to compare to.
	 * 
	 * @return	<code>true</code> if this type is compatible with the given one, <code>false</code> otherwise.
	 */
	public boolean isCompatible(final DBType t){
		if (t == null)
			return false;
		else if (isUnknown() || t.isUnknown())
			return true;
		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())
+45 −21
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ import adql.query.operand.function.UserDefinedFunction;
 * </p>
 * 
 * @author Gr&eacute;gory Mantelet (ARI)
 * @version 1.3 (02/2015)
 * @version 1.4 (07/2015)
 * 
 * @since 1.3
 */
@@ -86,6 +86,14 @@ public class FunctionDef implements Comparable<FunctionDef> {
	protected final boolean isNumeric;
	/** Indicate whether the return type is a geometry. */
	protected final boolean isGeometry;
	/** Indicate whether the return type is an unknown type.
	 * <p><i><u>Note:</u>
	 * 	If <code>true</code>, {@link #isString}, {@link #isNumeric}
	 * 	and {@link #isGeometry} are <code>false</code>. Otherwise,
	 * 	at least one of these attributes is set to <code>true</code>.
	 * </i></p>
	 * @since 1.4 */
	protected final boolean isUnknown;

	/** Total number of parameters. */
	public final int nbParams;
@@ -122,7 +130,7 @@ public class FunctionDef implements Comparable<FunctionDef> {
	 * <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)
	 * @version 1.4 (07/2015)
	 * @since 1.3
	 */
	public static final class FunctionParam {
@@ -135,15 +143,13 @@ public class FunctionDef implements Comparable<FunctionDef> {
		 * 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>
		 * @param paramType	Type of the parameter to create. <i>If NULL, an {@link DBDatatype#UNKNOWN UNKNOWN} type will be created and set instead.</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;
			this.type = (paramType == null) ? new DBType(DBDatatype.UNKNOWN) : paramType;
		}
	}

@@ -193,13 +199,11 @@ public class FunctionDef implements Comparable<FunctionDef> {
		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;
		this.returnType = (returnType != null) ? returnType : new DBType(DBDatatype.UNKNOWN);
		isUnknown = this.returnType.isUnknown();
		isNumeric = isUnknown || this.returnType.isNumeric();
		isString = isUnknown || this.returnType.isString();
		isGeometry = isUnknown || this.returnType.isGeometry();

		// Serialize in Strings (serializedForm and compareForm) this function definition:
		StringBuffer bufSer = new StringBuffer(name), bufCmp = new StringBuffer(name.toLowerCase());
@@ -244,6 +248,20 @@ public class FunctionDef implements Comparable<FunctionDef> {
		return isGeometry;
	}

	/**
	 * <p>Tell whether this function returns an unknown type.</p>
	 * 
	 * <p>
	 * 	If this function returns <code>true</code>, {@link #isNumeric()}, {@link #isString()} and {@link #isGeometry()}
	 * 	<b>MUST ALL</b> return <code>false</code>. Otherwise, one of these 3 last functions MUST return <code>true</code>.
	 * </p> 
	 * 
	 * @return	<i>true</i> if this function returns an unknown/unresolved/unsupported type, <i>false</i> otherwise.
	 */
	public final boolean isUnknown(){
		return isUnknown;
	}

	/**
	 * Get the number of parameters required by this function.
	 * 
@@ -357,8 +375,10 @@ public class FunctionDef implements Comparable<FunctionDef> {
			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() + "\"!");
				if (returnType == null){
					returnType = new DBType(DBDatatype.UNKNOWN);
					returnType.type.setCustomType(m.group(4));
				}
			}

			// Get the parameters, if any:
@@ -384,9 +404,10 @@ public class FunctionDef implements Comparable<FunctionDef> {
						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
						if (paramType == null){
							paramType = new DBType(DBDatatype.UNKNOWN);
							paramType.type.setCustomType(m.group(2) + ((m.group(3) == null) ? "" : m.group(3)));
						}
						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.
@@ -410,7 +431,8 @@ public class FunctionDef implements Comparable<FunctionDef> {
	 *              	<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.
	 * @return	The object representation of the specified datatype
	 *        	or NULL if the specified datatype can not be resolved.
	 */
	private static DBType parseType(String datatype, int length){
		if (datatype == null)
@@ -519,7 +541,9 @@ public class FunctionDef implements Comparable<FunctionDef> {
		// 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 (fct.getParameter(i).isNumeric() && fct.getParameter(i).isString() && fct.getParameter(i).isGeometry())
					comp = 0;
				else 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;
+3 −3
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ package adql.translator;
 * 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),
 * Copyright 2012-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
 *                       Astronomisches Rechen Institut (ARI)
 */

@@ -48,7 +48,7 @@ import adql.query.operand.function.geometry.PolygonFunction;
 * The other functions are managed by {@link PostgreSQLTranslator}.</p>
 * 
 * @author Gr&eacute;gory Mantelet (CDS;ARI)
 * @version 1.3 (11/2014)
 * @version 1.4 (07/2015)
 */
public class PgSphereTranslator extends PostgreSQLTranslator {

@@ -202,7 +202,7 @@ public class PgSphereTranslator extends PostgreSQLTranslator {
	public DBType convertTypeFromDB(final int dbmsType, final String rawDbmsTypeName, String dbmsTypeName, final String[] params){
		// If no type is provided return VARCHAR:
		if (dbmsTypeName == null || dbmsTypeName.trim().length() == 0)
			return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
			return null;

		// Put the dbmsTypeName in lower case for the following comparisons:
		dbmsTypeName = dbmsTypeName.toLowerCase();
+4 −4
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ package adql.translator;
 * 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),
 * Copyright 2012-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
 *                       Astronomisches Rechen Institut (ARI)
 */

@@ -50,7 +50,7 @@ import adql.query.operand.function.geometry.RegionFunction;
 * </i></p>
 * 
 * @author Gr&eacute;gory Mantelet (CDS;ARI)
 * @version 1.3 (11/2014)
 * @version 1.4 (07/2015)
 * 
 * @see PgSphereTranslator
 */
@@ -196,7 +196,7 @@ public class PostgreSQLTranslator extends JDBCTranslator {
	public DBType convertTypeFromDB(final int dbmsType, final String rawDbmsTypeName, String dbmsTypeName, final String[] params){
		// If no type is provided return VARCHAR:
		if (dbmsTypeName == null || dbmsTypeName.trim().length() == 0)
			return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
			return null;

		// Put the dbmsTypeName in lower case for the following comparisons:
		dbmsTypeName = dbmsTypeName.toLowerCase();
@@ -247,7 +247,7 @@ public class PostgreSQLTranslator extends JDBCTranslator {
			return new DBType(DBDatatype.TIMESTAMP);
		// Default:
		else
			return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
			return null;
	}

	@Override
+10 −8
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ package tap.data;
 * 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 - Astronomisches Rechen Institut (ARI)
 * Copyright 2014-2015 - Astronomisches Rechen Institut (ARI)
 */

import java.sql.ResultSet;
@@ -42,7 +42,7 @@ import adql.translator.JDBCTranslator;
 * </i></p>
 * 
 * @author Gr&eacute;gory Mantelet (ARI)
 * @version 2.0 (11/2014)
 * @version 2.1 (07/2015)
 * @since 2.0
 */
public class ResultSetTableIterator implements TableIterator {
@@ -363,7 +363,7 @@ public class ResultSetTableIterator implements TableIterator {
				if (o instanceof Timestamp)
					o = ISO8601Format.format(((Timestamp)o).getTime());
				// if the column value is a geometrical object, it must be serialized in STC-S:
				else if (translator != null && colType.isGeometry()){
				else if (translator != null && colType != null && colType.isGeometry()){
					Region region = translator.translateGeometryFromDB(o);
					if (region != null)
						o = region.toSTCS();
@@ -409,7 +409,8 @@ public class ResultSetTableIterator implements TableIterator {
	 * @param dbmsType	DBMS column data-type 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 DBType} object.
	 * @return	The best suited {@link DBType} object,
	 *        	or an {@link DBDatatype#UNKNOWN UNKNOWN} type if none can be found.
	 * 
	 * @see JDBCTranslator#convertTypeFromDB(int, String, String, String[])
	 * @see #defaultTypeConversion(String, String[], String)
@@ -417,7 +418,7 @@ public class ResultSetTableIterator implements TableIterator {
	protected DBType convertType(final int dbmsType, String dbmsTypeName, final String dbms) throws DataReadException{
		// If no type is provided return VARCHAR:
		if (dbmsTypeName == null || dbmsTypeName.trim().length() == 0)
			return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
			return new DBType(DBDatatype.UNKNOWN);

		// Extract the type prefix and lower-case it:
		int startParamIndex = dbmsTypeName.indexOf('('), endParamIndex = dbmsTypeName.indexOf(')');
@@ -468,7 +469,8 @@ public class ResultSetTableIterator implements TableIterator {
	 * @param params		The eventual type parameters (e.g. char string length).
	 * @param dbms			The targeted DBMS.
	 * 
	 * @return	The corresponding ADQL/TAP type. <i>NEVER NULL</i>
	 * @return	The corresponding ADQL/TAP type. <i>NEVER NULL ;
	 *        	an {@link DBDatatype#UNKNOWN UNKNOWN} type is returned in case no match can be found.</i>
	 */
	protected final DBType defaultTypeConversion(final String dbmsTypeName, final String[] params, final String dbms){
		// Get the length parameter (always in first position):
@@ -495,7 +497,7 @@ public class ResultSetTableIterator implements TableIterator {
				return new DBType(DBDatatype.BLOB);
			// Default:
			else
				return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
				return new DBType(DBDatatype.UNKNOWN);
		}
		// CASE: OTHER DBMS
		else{
@@ -537,7 +539,7 @@ public class ResultSetTableIterator implements TableIterator {
				return new DBType(DBDatatype.TIMESTAMP);
			// Default:
			else
				return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
				return new DBType(DBDatatype.UNKNOWN);
		}
	}

Loading