Commit 475fcb65 authored by gmantele's avatar gmantele
Browse files

[ADQL] Fix identification of UDFs using a list of function definitions.

Functions whose some parameters are another function were not correctly
identified: since the inner functions were not yet identified, their type
was UNKNOWN and so it makes the identification of the parent function
much easier since an UNKNOWN parameter is not checked. But, it was a
problem if the parameter occurs to be finally of the wrong type.
parent 09e81ed7
Loading
Loading
Loading
Loading
+19 −17
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 ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Copyright 2011,2013-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
 * Copyright 2011-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
 *                       Astronomisches Rechen Institut (ARI)
 */

@@ -97,7 +97,7 @@ import adql.search.SimpleSearchHandler;
 * </i></p>
 * 
 * @author Gr&eacute;gory Mantelet (CDS;ARI)
 * @version 1.4 (08/2015)
 * @version 1.4 (03/2016)
 */
public class DBChecker implements QueryChecker {

@@ -813,24 +813,26 @@ public class DBChecker implements QueryChecker {
			 *       for a later resolution try. */
			for(ADQLObject result : sHandler){
				udf = (UserDefinedFunction)result;
				// if the type of not all parameters are resolved, postpone the resolution:
				if (!isAllParamTypesResolved(udf))
					toResolveLater.add(udf);
				// otherwise:
				else{
					// 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))
					if (match < 0)
						errors.addException(new UnresolvedFunctionException(udf));
					// ...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++){
			/* Note: we need to iterate from the end in order to resolve first the most wrapped functions
			 *       (e.g. fct1(fct2(...)) ; fct2 must be resolved before fct1). */
			for(int i = toResolveLater.size() - 1; i >= 0; i--){
				udf = toResolveLater.get(i);
				// search for a match:
				match = binSearch.search(udf, allowedUdfs);
@@ -866,7 +868,7 @@ public class DBChecker implements QueryChecker {
	 */
	protected final boolean isAllParamTypesResolved(final ADQLFunction fct){
		for(ADQLOperand op : fct.getParameters()){
			if (op.isNumeric() == op.isString())
			if (op.isGeometry() == op.isNumeric() && op.isNumeric() == op.isString())
				return false;
		}
		return true;
+25 −0
Original line number Diff line number Diff line
@@ -675,6 +675,31 @@ public class TestDBChecker {
			assertEquals("Type mismatch! A numeric value was expected instead of \"titi()\".", ex.getErrors().next().getMessage());
		}

		// Try with functions wrapped on 2 levels:
		// i.e. fct1('blabla', fct2(fct3('blabla')))
		FunctionDef[] complexFcts = new FunctionDef[3];
		complexFcts[0] = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("str", new DBType(DBDatatype.VARCHAR)),new FunctionParam("num", new DBType(DBDatatype.INTEGER))});
		complexFcts[1] = new FunctionDef("fct2", new DBType(DBDatatype.INTEGER), new FunctionParam[]{new FunctionParam("str", new DBType(DBDatatype.VARCHAR))});
		complexFcts[2] = new FunctionDef("fct3", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("str", new DBType(DBDatatype.VARCHAR))});
		parser = new ADQLParser(new DBChecker(tables, Arrays.asList(complexFcts)));
		// With parameters of the good type:
		try{
			assertNotNull(parser.parseQuery("SELECT fct1('blabla', fct2(fct3('blabla'))) FROM foo"));
		}catch(ParseException pe){
			pe.printStackTrace();
			fail("Types are matching: this test should have succeeded!");
		}
		// With parameters of the bad type:
		try{
			parser.parseQuery("SELECT fct2(fct1('blabla', fct3('blabla'))) FROM foo");
			fail("Parameters types are not matching: the parsing should have failed!");
		}catch(ParseException pe){
			assertEquals(UnresolvedIdentifiersException.class, pe.getClass());
			assertEquals(1, ((UnresolvedIdentifiersException)pe).getNbErrors());
			ParseException innerPe = ((UnresolvedIdentifiersException)pe).getErrors().next();
			assertEquals("Unresolved function: \"fct1('blabla', fct3('blabla'))\"! No UDF has been defined or found with the signature: fct1(STRING, STRING).", innerPe.getMessage());
		}

		// CLEAR ALL UDFs AND ALLOW UNKNOWN FUNCTION:
		parser = new ADQLParser(new DBChecker(tables, null));