Skip to content
Snippets Groups Projects
Commit e136017d authored by Grégory Mantelet's avatar Grégory Mantelet
Browse files

[ADQL] Fix the SQL translation of concatenations for MySQL and MS-SQLServer.

As @vforchi said:

> The ANSI standard `||` is supported only by Oracle and Postgres: MySQL uses
> `CONCAT` and SQLServer uses `+`.

_This commit resolves the issue #70 ._
parent 1a1c8a3a
No related branches found
No related tags found
No related merge requests found
......@@ -5,6 +5,8 @@ import adql.db.DBType.DBDatatype;
import adql.db.STCS.Region;
import adql.parser.ParseException;
import adql.query.IdentifierField;
import adql.query.operand.ADQLOperand;
import adql.query.operand.Concatenation;
import adql.query.operand.function.geometry.AreaFunction;
import adql.query.operand.function.geometry.BoxFunction;
import adql.query.operand.function.geometry.CentroidFunction;
......@@ -34,20 +36,23 @@ import adql.query.operand.function.geometry.RegionFunction;
* 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 2017 - Astronomisches Rechen Institut (ARI)
* Copyright 2017-2019 - Astronomisches Rechen Institut (ARI),
* UDS/Centre de Données astronomiques de Strasbourg (CDS)
*/
/**
* <p>Translates all ADQL objects into an SQL interrogation query designed for MySQL.</p>
* Translates all ADQL objects into an SQL interrogation query designed for
* MySQL.
*
* <p><i><b>Important</b>:
* The geometrical functions are translated exactly as in ADQL.
* You will probably need to extend this translator to correctly manage the geometrical functions.
* You will probably need to extend this translator to correctly manage the
* geometrical functions.
* </i></p>
*
* @author Gr&eacute;gory Mantelet (ARI)
* @version 2.1 (08/2017)
* @since 2.1
* @author Gr&eacute;gory Mantelet (ARI;CDS)
* @version 1.5 (03/2019)
* @since 1.4
*/
public class MySQLTranslator extends JDBCTranslator {
......@@ -125,6 +130,28 @@ public class MySQLTranslator extends JDBCTranslator {
return str.append(id);
}
/* ********************************************************************** */
/* * * */
/* * GENERAL TRANSLATIONS * */
/* * * */
/* ********************************************************************** */
@Override
public String translate(Concatenation concat) throws TranslationException{
StringBuffer translated = new StringBuffer();
for(ADQLOperand op : concat){
if (translated.length() == 0)
translated.append("CONCAT(");
else
translated.append(", ");
translated.append(translate(op));
}
translated.append(")");
return translated.toString();
}
/* ********************************************************************** */
/* * * */
/* * TYPE MANAGEMENT * */
......@@ -145,7 +172,8 @@ public class MySQLTranslator extends JDBCTranslator {
if (params != null && params.length > 0){
try{
lengthParam = Integer.parseInt(params[0]);
}catch(NumberFormatException nfe){}
}catch(NumberFormatException nfe){
}
}
// SMALLINT
......
package adql.translator;
/*
* 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 2017-2019 - Astronomisches Rechen Institut (ARI),
* UDS/Centre de Données astronomiques de Strasbourg (CDS)
*/
import java.util.Iterator;
import adql.db.DBColumn;
......@@ -17,6 +37,8 @@ import adql.query.from.ADQLJoin;
import adql.query.from.ADQLTable;
import adql.query.from.FromContent;
import adql.query.operand.ADQLColumn;
import adql.query.operand.ADQLOperand;
import adql.query.operand.Concatenation;
import adql.query.operand.function.MathFunction;
import adql.query.operand.function.geometry.AreaFunction;
import adql.query.operand.function.geometry.BoxFunction;
......@@ -32,36 +54,38 @@ import adql.query.operand.function.geometry.PolygonFunction;
import adql.query.operand.function.geometry.RegionFunction;
/**
* <p>MS SQL Server translator.</p>
*
* MS SQL Server translator.
*
* <p><b>Important:</b>
* This translator works correctly ONLY IF {@link SQLServer_ADQLQueryFactory} has been used
* to create any ADQL query this translator is asked to translate.
* This translator works correctly ONLY IF {@link SQLServer_ADQLQueryFactory}
* has been used to create any ADQL query this translator is asked to
* translate.
* </p>
*
*
* TODO See how case sensitivity is supported by MS SQL Server and modify this translator accordingly.
*
*
* TODO Extend this class for each MS SQL Server extension supporting geometry and particularly
* {@link #translateGeometryFromDB(Object)}, {@link #translateGeometryToDB(adql.db.STCS.Region)} and all this other
* translate(...) functions for the ADQL's geometrical functions.
*
*
* TODO Check MS SQL Server datatypes (see {@link #convertTypeFromDB(int, String, String, String[])},
* {@link #convertTypeToDB(DBType)}).
*
*
* <p><i><b>Important note:</b>
* Geometrical functions are not translated ; the translation returned for them is their ADQL expression.
* Geometrical functions are not translated ; the translation returned for them
* is their ADQL expression.
* </i></p>
*
* @author Gr&eacute;gory Mantelet (ARI)
* @version 1.4 (09/2017)
*
* @author Gr&eacute;gory Mantelet (ARI;CDS)
* @version 1.5 (03/2019)
* @since 1.4
*
*
* @see SQLServer_ADQLQueryFactory
*/
public class SQLServerTranslator extends JDBCTranslator {
/** <p>Indicate the case sensitivity to apply to each SQL identifier (only SCHEMA, TABLE and COLUMN).</p>
*
*
* <p><i>Note:
* In this implementation, this field is set by the constructor and never modified elsewhere.
* It would be better to never modify it after the construction in order to keep a certain consistency.
......@@ -80,7 +104,7 @@ public class SQLServerTranslator extends JDBCTranslator {
/**
* Builds an SQLServerTranslator which always translates in SQL all identifiers (schema, table and column) in the specified case sensitivity ;
* in other words, schema, table and column names will all be surrounded or not by double quotes in the SQL translation.
*
*
* @param allCaseSensitive <i>true</i> to translate all identifiers in a case sensitive manner (surrounded by double quotes), <i>false</i> for case insensitivity.
*/
public SQLServerTranslator(final boolean allCaseSensitive){
......@@ -89,7 +113,7 @@ public class SQLServerTranslator extends JDBCTranslator {
/**
* Builds an SQLServerTranslator which will always translate in SQL identifiers with the defined case sensitivity.
*
*
* @param catalog <i>true</i> to translate catalog names with double quotes (case sensitive in the DBMS), <i>false</i> otherwise.
* @param schema <i>true</i> to translate schema names with double quotes (case sensitive in the DBMS), <i>false</i> otherwise.
* @param table <i>true</i> to translate table names with double quotes (case sensitive in the DBMS), <i>false</i> otherwise.
......@@ -111,7 +135,7 @@ public class SQLServerTranslator extends JDBCTranslator {
* For SQL Server, {@link #translate(ClauseSelect)} must be overridden for
* TOP/LIMIT handling. We must not add the LIMIT at the end of the query, it
* must go in the SELECT.
*
*
* @see #translate(ClauseSelect)
*/
@Override
......@@ -151,6 +175,19 @@ public class SQLServerTranslator extends JDBCTranslator {
return sql;
}
@Override
public String translate(Concatenation concat) throws TranslationException{
StringBuffer translated = new StringBuffer();
for(ADQLOperand op : concat){
if (translated.length() > 0)
translated.append(" + ");
translated.append(translate(op));
}
return translated.toString();
}
@Override
public String translate(final ADQLJoin join) throws TranslationException{
StringBuffer sql = new StringBuffer(translate(join.getLeftTable()));
......@@ -229,7 +266,7 @@ public class SQLServerTranslator extends JDBCTranslator {
/**
* Generate an ADQL column of the given table and with the given metadata.
*
*
* @param table Parent table of the column to generate.
* @param colMeta DB metadata of the column to generate.
* @param joinedColumn The joined column (i.e. the ADQL column listed in a
......@@ -238,7 +275,7 @@ public class SQLServerTranslator extends JDBCTranslator {
* <i>If NULL, an {@link ADQLColumn} instance will be
* created from scratch using the ADQL name of the
* given DB metadata.</i>
*
*
* @return The generated column.
*/
protected ADQLColumn generateJoinColumn(final FromContent table, final DBColumn colMeta, final ADQLColumn joinedColumn){
......@@ -361,7 +398,8 @@ public class SQLServerTranslator extends JDBCTranslator {
if (params != null && params.length > 0){
try{
lengthParam = Integer.parseInt(params[0]);
}catch(NumberFormatException nfe){}
}catch(NumberFormatException nfe){
}
}
// SMALLINT
......
package adql.translator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Test;
import adql.parser.ADQLParser;
import adql.parser.ParseException;
import adql.query.ADQLQuery;
public class TestMySQLTranslator {
@Test
public void testConcat(){
try{
MySQLTranslator translator = new MySQLTranslator();
// Test with an easy translation:
ADQLQuery query = (new ADQLParser()).parseQuery("SELECT 'abc' || ' ' || 'def' FROM aTable");
assertEquals("SELECT CONCAT('abc', ' ', 'def') AS `concat`", translator.translate(query.getSelect()));
// Test with an easy translation:
query = (new ADQLParser()).parseQuery("SELECT 'a||b||c' || ' ' || 'd+e|f' FROM aTable");
assertEquals("SELECT CONCAT('a||b||c', ' ', 'd+e|f') AS `concat`", translator.translate(query.getSelect()));
}catch(ParseException pe){
pe.printStackTrace();
fail("The given ADQL query is completely correct. No error should have occurred while parsing it. (see the console for more details)");
}catch(TranslationException te){
te.printStackTrace();
fail("No error was expected from this translation. (see the console for more details)");
}
}
}
......@@ -83,4 +83,26 @@ public class TestSQLServerTranslator {
}
}
@Test
public void testConcat(){
try{
SQLServerTranslator translator = new SQLServerTranslator();
// Test with an easy translation:
ADQLQuery query = (new ADQLParser(new SQLServer_ADQLQueryFactory())).parseQuery("SELECT 'abc' || ' ' || 'def' FROM aTable");
assertEquals("SELECT 'abc' + ' ' + 'def' AS \"concat\"", translator.translate(query.getSelect()));
// Test with an easy translation:
query = (new ADQLParser(new SQLServer_ADQLQueryFactory())).parseQuery("SELECT 'a||b||c' || ' ' || 'd+e|f' FROM aTable");
assertEquals("SELECT 'a||b||c' + ' ' + 'd+e|f' AS \"concat\"", translator.translate(query.getSelect()));
}catch(ParseException pe){
pe.printStackTrace();
fail("The given ADQL query is completely correct. No error should have occurred while parsing it. (see the console for more details)");
}catch(TranslationException te){
te.printStackTrace();
fail("No error was expected from this translation. (see the console for more details)");
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment