Commit 04a00fe2 authored by gmantele's avatar gmantele
Browse files

[ADQL] Fix a sub-query bug. No DBTable was set on an ADQLTable having a

sub-query. Without this information, it was impossible to resolve columns makingreference to sub-queries of the clause FROM. See the JUnit test case for a
concrete example.
(error raised by Hendrik Heinl - ARI/GAVO)
parent 19026c1b
Loading
Loading
Loading
Loading
+17 −8
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ package adql.query.from;
 * 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-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
 * Copyright 2012-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
 *                       Astronomisches Rechen Institut (ARI)
 */

@@ -39,7 +39,7 @@ import adql.query.TextPosition;
 * A table reference may have an alias (MUST if it is a sub-query).
 * 
 * @author Gr&eacute;gory Mantelet (CDS;ARI)
 * @version 06/2015
 * @version 2.1 (07/2016)
 */
public class ADQLTable implements ADQLObject, FromContent {

@@ -165,6 +165,7 @@ public class ADQLTable implements ADQLObject, FromContent {
	 * 
	 * @return	The position of this {@link ADQLTable}.
	 */
	@Override
	public final TextPosition getPosition(){
		return position;
	}
@@ -174,6 +175,7 @@ public class ADQLTable implements ADQLObject, FromContent {
	 * 
	 * @param pos	Position of this {@link ADQLTable}.
	 */
	@Override
	public final void setPosition(final TextPosition pos){
		position = pos;
	}
@@ -465,18 +467,15 @@ public class ADQLTable implements ADQLObject, FromContent {
	}

	/**
	 * <p>Sets the {@link DBTable} corresponding to this {@link ADQLTable}.</p>
	 * <p><i>
	 * 	<u>Note:</u> This function will do nothing if this {@link ADQLTable} is a sub query.
	 * </i></p>
	 * Sets the {@link DBTable} corresponding to this {@link ADQLTable}.
	 * 
	 * @param dbLink Its corresponding {@link DBTable}.
	 */
	public final void setDBLink(DBTable dbLink){
		if (!isSubQuery())
		this.dbLink = dbLink;
	}

	@Override
	public SearchColumnList getDBColumns(){
		SearchColumnList list = new SearchColumnList();
		if (isSubQuery() && dbLink == null)
@@ -488,12 +487,14 @@ public class ADQLTable implements ADQLObject, FromContent {
		return list;
	}

	@Override
	public ArrayList<ADQLTable> getTables(){
		ArrayList<ADQLTable> tables = new ArrayList<ADQLTable>();
		tables.add(this);
		return tables;
	}

	@Override
	public ArrayList<ADQLTable> getTablesByAlias(final String alias, final boolean caseSensitive){
		ArrayList<ADQLTable> tables = new ArrayList<ADQLTable>();

@@ -515,19 +516,23 @@ public class ADQLTable implements ADQLObject, FromContent {
		return tables;
	}

	@Override
	public ADQLObject getCopy() throws Exception{
		return new ADQLTable(this);
	}

	@Override
	public String getName(){
		return hasAlias() ? alias : (isSubQuery() ? "{subquery}" : getTableName());
	}

	@Override
	public ADQLIterator adqlIterator(){
		return new ADQLIterator(){

			private boolean subQueryGot = !isSubQuery();

			@Override
			public ADQLObject next(){
				if (!subQueryGot){
					subQueryGot = true;
@@ -536,10 +541,12 @@ public class ADQLTable implements ADQLObject, FromContent {
					throw new NoSuchElementException();
			}

			@Override
			public boolean hasNext(){
				return !subQueryGot;
			}

			@Override
			public void replace(ADQLObject replacer) throws UnsupportedOperationException, IllegalStateException{
				if (!subQueryGot)
					throw new IllegalStateException("replace(ADQLObject) impossible: next() has not yet been called !");
@@ -553,6 +560,7 @@ public class ADQLTable implements ADQLObject, FromContent {
					throw new UnsupportedOperationException("Impossible to replace a sub-query (" + subQuery.toADQL() + ") by a " + replacer.getClass().getName() + " (" + replacer.toADQL() + ") !");
			}

			@Override
			public void remove(){
				if (!subQueryGot)
					throw new IllegalStateException("remove() impossible: next() has not yet been called !");
@@ -562,6 +570,7 @@ public class ADQLTable implements ADQLObject, FromContent {
		};
	}

	@Override
	public String toADQL(){
		return (isSubQuery() ? ("(" + subQuery.toADQL() + ")") : getFullTableName()) + ((alias == null) ? "" : (" AS " + (isCaseSensitive(IdentifierField.ALIAS) ? ("\"" + alias + "\"") : alias)));
	}
+44 −0
Original line number Diff line number Diff line
package adql.db;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;

import org.junit.Before;
import org.junit.Test;

import adql.parser.ADQLParser;
import adql.query.ADQLQuery;
import tap.metadata.TAPMetadata;
import tap.metadata.TAPTable;
import tap.metadata.TableSetParser;

public class TestSeveralSubQueries {

	@Before
	public void setUp() throws Exception{}

	@Test
	public void test(){
		try{
			TableSetParser tsParser = new TableSetParser();
			TAPMetadata esaMetaData = tsParser.parse(new File("test/adql/db/subquery_test_tables.xml"));
			ArrayList<DBTable> esaTables = new ArrayList<DBTable>(esaMetaData.getNbTables());
			Iterator<TAPTable> itTables = esaMetaData.getTables();
			while(itTables.hasNext())
				esaTables.add(itTables.next());

			ADQLParser adqlParser = new ADQLParser(new DBChecker(esaTables));
			ADQLQuery query = adqlParser.parseQuery("SELECT sel2.*,t1.h_m, t1.j_m, t1.k_m\nFROM (\n  SELECT sel1.*, t3.*\n  FROM (\n  	SELECT *\n    FROM table2 AS t2\n	WHERE 1=CONTAINS(POINT('ICRS', t2.ra, t2.dec), CIRCLE('ICRS', 56.75, 24.1167, 15.))\n  ) AS sel1 JOIN table3 AS t3 ON t3.oid2=sel1.oid2\n) AS sel2 JOIN table1 AS t1 ON sel2.oid=t1.oid");
			assertEquals("SELECT sel2.* , t1.h_m , t1.j_m , t1.k_m\nFROM (SELECT sel1.* , t3.*\nFROM (SELECT *\nFROM table2 AS t2\nWHERE 1 = CONTAINS(POINT('ICRS', t2.ra, t2.dec), CIRCLE('ICRS', 56.75, 24.1167, 15.))) AS sel1 INNER JOIN table3 AS t3 ON ON t3.oid2 = sel1.oid2) AS sel2 INNER JOIN table1 AS t1 ON ON sel2.oid = t1.oid", query.toADQL());

		}catch(Exception ex){
			ex.printStackTrace(System.err);
			fail("No error expected! (see console for more details)");
		}
	}

}
+301 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8"?>
<vod:tableset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:vod="http://www.ivoa.net/xml/VODataService/v1.1" xsi:type="vod:TableSet" xsi:schemaLocation="http://www.ivoa.net/xml/VODataService/v1.1 http://www.ivoa.net/xml/VODataService/v1.1">
	<schema>
		<name>public</name>
		<table type="base_table">
			<name>table1</name>
			<column std="false">
				<name>oid</name>
				<unit></unit>
				<ucd>meta.id;meta.main</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">BIGINT</dataType>
				<flag>indexed</flag>
			</column>
			<column std="false">
				<name>ra</name>
				<unit>deg</unit>
				<ucd>pos.eq.ra;meta.main</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
				<flag>indexed</flag>
			</column>
			<column std="false">
				<name>dec</name>
				<unit>Angle[deg]</unit>
				<ucd>pos.eq.dec;meta.main</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
				<flag>indexed</flag>
			</column>
			<column std="false">
				<name>h_m</name>
				<unit>mag</unit>
				<ucd>phot.mag;em.IR.H</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">REAL</dataType>
			</column>
			<column std="false">
				<name>j_m</name>
				<unit>mag</unit>
				<ucd>phot.mag;em.IR.J</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">REAL</dataType>
			</column>
			<column std="false">
				<name>k_m</name>
				<unit>mag</unit>
				<ucd>phot.mag;em.IR.K</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">REAL</dataType>
			</column>
		</table>
		<table type="base_table">
			<name>table2</name>
			<column std="false">
				<name>oid2</name>
				<unit></unit>
				<ucd>meta.id;meta.main</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">BIGINT</dataType>
				<flag>indexed</flag>
			</column>
			<column std="false">
				<name>ra</name>
				<unit>deg</unit>
				<ucd>pos.eq.ra;meta.main</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
				<flag>indexed</flag>
			</column>
			<column std="false">
				<name>ra_error</name>
				<unit>mas</unit>
				<ucd>stat.error;pos.eq.ra</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
			</column>
			<column std="false">
				<name>dec</name>
				<unit>deg</unit>
				<ucd>pos.eq.dec;meta.main</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
				<flag>indexed</flag>
			</column>
			<column std="false">
				<name>dec_error</name>
				<unit>mas</unit>
				<ucd>stat.error;pos.eq.dec</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
			</column>
			<column std="false">
				<name>parallax</name>
				<unit>mas</unit>
				<ucd>pos.parallax</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
				<flag>indexed</flag>
			</column>
			<column std="false">
				<name>parallax_error</name>
				<unit>mas</unit>
				<ucd>stat.error;pos.parallax</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
				<flag>indexed</flag>
			</column>
			<column std="false">
				<name>magnitude</name>
				<unit>mag</unit>
				<ucd>phot.mag;stat.mean;em.opt</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
				<flag>indexed</flag>
			</column>
			<column std="false">
				<name>pmdec</name>
				<unit>mas/year</unit>
				<ucd>pos.pm;pos.eq.dec</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
				<flag>indexed</flag>
			</column>
			<column std="false">
				<name>pmdec_error</name>
				<unit>mas/year</unit>
				<ucd>stat.error;pos.pm;pos.eq.dec</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
			</column>
			<column std="false">
				<name>pmra</name>
				<unit>mas/year</unit>
				<ucd>pos.pm;pos.eq.ra</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
				<flag>indexed</flag>
			</column>
			<column std="false">
				<name>pmra_error</name>
				<unit>mas/year</unit>
				<ucd>stat.error;pos.pm;pos.eq.ra</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
			</column>
		</table>
		<table type="base_table">
			<name>table3</name>
			<column std="false">
				<name>oid</name>
				<unit></unit>
				<ucd>meta.id</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">BIGINT</dataType>
			</column>
			<column std="false">
				<name>oid2</name>
				<description></description>
				<unit></unit>
				<ucd>meta.id</ucd>
				<utype></utype>
				<dataType xsi:type="vod:TAPType">BIGINT</dataType>
			</column>
		</table>
	</schema>
	<schema>
		<name>tap_schema</name>
		<table type="base_table">
			<name>tables</name>
			<column std="false">
				<name>description</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>schema_name</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>size</name>
				<dataType xsi:type="vod:TAPType">INTEGER</dataType>
			</column>
			<column std="false">
				<name>table_name</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>table_type</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>utype</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
		</table>
		<table type="base_table">
			<name>columns</name>
			<column std="false">
				<name>column_name</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>datatype</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>description</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>indexed</name>
				<dataType xsi:type="vod:TAPType">INTEGER</dataType>
			</column>
			<column std="false">
				<name>principal</name>
				<dataType xsi:type="vod:TAPType">INTEGER</dataType>
			</column>
			<column std="false">
				<name>schema_name</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>size</name>
				<dataType xsi:type="vod:TAPType">INTEGER</dataType>
			</column>
			<column std="false">
				<name>std</name>
				<dataType xsi:type="vod:TAPType">INTEGER</dataType>
			</column>
			<column std="false">
				<name>table_name</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>ucd</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>unit</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>utype</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
		</table>
		<table type="base_table">
			<name>keys</name>
			<column std="false">
				<name>description</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>from_table</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>key_id</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>target_table</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>utype</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
		</table>
		<table type="base_table">
			<name>schemas</name>
			<column std="false">
				<name>description</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>schema_name</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>utype</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
		</table>
		<table type="base_table">
			<name>key_columns</name>
			<column std="false">
				<name>from_column</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>key_id</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
			<column std="false">
				<name>target_column</name>
				<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
			</column>
		</table>
	</schema>
</vod:tableset>