From 04a00fe2973ef275389a34b7442504ddbc5ba5f0 Mon Sep 17 00:00:00 2001 From: gmantele <gmantele@ari.uni-heidelberg.de> Date: Wed, 13 Jul 2016 17:38:19 +0200 Subject: [PATCH] [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) --- src/adql/query/from/ADQLTable.java | 25 +- test/adql/db/TestSeveralSubQueries.java | 44 ++++ test/adql/db/subquery_test_tables.xml | 301 ++++++++++++++++++++++++ 3 files changed, 362 insertions(+), 8 deletions(-) create mode 100644 test/adql/db/TestSeveralSubQueries.java create mode 100644 test/adql/db/subquery_test_tables.xml diff --git a/src/adql/query/from/ADQLTable.java b/src/adql/query/from/ADQLTable.java index 0d592ea..baa2599 100644 --- a/src/adql/query/from/ADQLTable.java +++ b/src/adql/query/from/ADQLTable.java @@ -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é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; + 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))); } diff --git a/test/adql/db/TestSeveralSubQueries.java b/test/adql/db/TestSeveralSubQueries.java new file mode 100644 index 0000000..bf9f485 --- /dev/null +++ b/test/adql/db/TestSeveralSubQueries.java @@ -0,0 +1,44 @@ +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)"); + } + } + +} diff --git a/test/adql/db/subquery_test_tables.xml b/test/adql/db/subquery_test_tables.xml new file mode 100644 index 0000000..526ef6e --- /dev/null +++ b/test/adql/db/subquery_test_tables.xml @@ -0,0 +1,301 @@ +<?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> -- GitLab