Skip to content

Commit

Permalink
#35: updated to a new vscommon version (#36)
Browse files Browse the repository at this point in the history
* #35: updated to a new vs common version
* #35: changed the version because the previous release breaks the interface
* #35: removed the KEYS keyword from generated queries because Exasol can't parse it correctly
  • Loading branch information
AnastasiiaSergienko authored Apr 16, 2020
1 parent f5236e2 commit 6982a40
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 41 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
<description>Common module for JDBC-based data access from Virtual Schemas.</description>
<url>https://github.com/exasol/virtual-schema-common-jdbc</url>
<properties>
<product.version>3.0.3</product.version>
<product.version>4.0.0</product.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
<junit.version>5.6.1</junit.version>
<junit.platform.version>1.6.1</junit.platform.version>
<maven.surefire.version>3.0.0-M4</maven.surefire.version>
<vscommon.version>8.0.2</vscommon.version>
<vscommon.version>9.0.0</vscommon.version>
<gpg.skip>true</gpg.skip>
</properties>
<licenses>
Expand Down
104 changes: 66 additions & 38 deletions src/main/java/com/exasol/adapter/dialects/SqlGenerationVisitor.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.exasol.adapter.dialects;

import java.util.ArrayList;
import java.util.List;
import java.util.*;

import com.exasol.adapter.AdapterException;
import com.exasol.adapter.adapternotes.ColumnAdapterNotesJsonConverter;
Expand Down Expand Up @@ -230,7 +229,7 @@ public String visit(final SqlFunctionAggregate function) throws AdapterException

@Override
public String visit(final SqlFunctionAggregateGroupConcat function) throws AdapterException {
validateSingleAgrumentFunctionParameter(function);
validateSingleArgumentFunctionParameter(function.getArguments(), "AGGREGATE GROUP CONCAT");
final StringBuilder builder = new StringBuilder();
builder.append(function.getFunctionName());
builder.append("(");
Expand All @@ -253,10 +252,10 @@ public String visit(final SqlFunctionAggregateGroupConcat function) throws Adapt
return builder.toString();
}

private void validateSingleAgrumentFunctionParameter(final SqlFunctionAggregateGroupConcat function) {
if ((function.getArguments().size() != 1) || (function.getArguments().get(0) == null)) {
private void validateSingleArgumentFunctionParameter(final List<SqlNode> arguments, final String functionName) {
if ((arguments.size() != 1) || (arguments.get(0) == null)) {
throw new IllegalArgumentException(
"Function AGGREGATE GROUP CONCAT must have exactly one non-NULL parameter.");
"Function " + functionName + " must have exactly one non-NULL parameter.");
}
}

Expand All @@ -272,14 +271,14 @@ public String visit(final SqlFunctionScalar function) throws AdapterException {
functionNameInSourceSystem = this.dialect.getScalarFunctionAliases().get(function.getFunction());
} else {
if (this.dialect.getBinaryInfixFunctionAliases().containsKey(function.getFunction())) {
assert(argumentsSql.size() == 2);
assert (argumentsSql.size() == 2);
String realFunctionName = function.getFunctionName();
if (this.dialect.getBinaryInfixFunctionAliases().containsKey(function.getFunction())) {
realFunctionName = this.dialect.getBinaryInfixFunctionAliases().get(function.getFunction());
}
return "(" + argumentsSql.get(0) + " " + realFunctionName + " " + argumentsSql.get(1) + ")";
} else if (this.dialect.getPrefixFunctionAliases().containsKey(function.getFunction())) {
assert(argumentsSql.size() == 1);
assert (argumentsSql.size() == 1);
String realFunctionName = function.getFunctionName();
if (this.dialect.getPrefixFunctionAliases().containsKey(function.getFunction())) {
realFunctionName = this.dialect.getPrefixFunctionAliases().get(function.getFunction());
Expand Down Expand Up @@ -320,43 +319,48 @@ public String visit(final SqlFunctionScalarCase function) throws AdapterExceptio

@Override
public String visit(final SqlFunctionScalarCast function) throws AdapterException {
validateSingleAgrumentFunctionParameter(function);
final StringBuilder builder = new StringBuilder();
builder.append("CAST");
builder.append("(");
builder.append(function.getArguments().get(0).accept(this));
builder.append(" AS ");
builder.append(function.getDataType());
builder.append(")");
return builder.toString();
}

private void validateSingleAgrumentFunctionParameter(final SqlFunctionScalarCast function) {
if ((function.getArguments().size() != 1) || (function.getArguments().get(0) == null)) {
throw new IllegalArgumentException("Function CAST must have exactly one non-NULL parameter.");
}
final String expression = function.getArguments().get(0).accept(this);
return function.toSimpleSql() + "(" + expression + " AS " + function.getDataType() + ")";
}

@Override
public String visit(final SqlFunctionScalarExtract function) throws AdapterException {
validateSingleAgrumentFunctionParameter(function);
final String expression = function.getArguments().get(0).accept(this);
return function.getFunctionName() + "(" + function.getToExtract() + " FROM " + expression + ")";
return function.toSimpleSql() + "(" + function.getToExtract() + " FROM " + expression + ")";
}

private void validateSingleAgrumentFunctionParameter(final SqlFunctionScalarExtract function) {
if ((function.getArguments().size() != 1) || (function.getArguments().get(0) == null)) {
throw new IllegalArgumentException("Function EXTRACT must have exactly one non-NULL parameter.");
@Override
public String visit(final SqlFunctionScalarJsonValue function) throws AdapterException {
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("JSON_VALUE(");
stringBuilder.append(function.getArguments().get(0).accept(this));
stringBuilder.append(", ");
stringBuilder.append(function.getArguments().get(1).accept(this));
stringBuilder.append(" RETURNING ");
stringBuilder.append(function.getReturningDataType().toString());
stringBuilder.append(" ");
final SqlFunctionScalarJsonValue.Behavior emptyBehavior = function.getEmptyBehavior();
stringBuilder.append(emptyBehavior.getBehaviorType());
final Optional<SqlNode> emptyBehaviorExpression = emptyBehavior.getExpression();
if (emptyBehaviorExpression.isPresent()) {
stringBuilder.append(" ");
stringBuilder.append(emptyBehaviorExpression.get().accept(this));
}
stringBuilder.append(" ON EMPTY ");
final SqlFunctionScalarJsonValue.Behavior errorBehavior = function.getErrorBehavior();
stringBuilder.append(errorBehavior.getBehaviorType());
final Optional<SqlNode> errorBehaviorExpression = errorBehavior.getExpression();
if (errorBehaviorExpression.isPresent()) {
stringBuilder.append(" ");
stringBuilder.append(errorBehaviorExpression.get().accept(this));
}
stringBuilder.append(" ON ERROR)");
return stringBuilder.toString();
}

@Override
public String visit(final SqlLimit limit) {
String offsetSql = "";
if (limit.getOffset() != 0) {
offsetSql = " OFFSET " + limit.getOffset();
}
return "LIMIT " + limit.getLimit() + offsetSql;
return limit.toSimpleSql();
}

@Override
Expand All @@ -370,9 +374,7 @@ public String visit(final SqlLiteralBool literal) {

@Override
public String visit(final SqlLiteralDate literal) {
return "DATE '" + literal.getValue() + "'"; // This gets always executed
// as
// TO_DATE('2015-02-01','YYYY-MM-DD')
return "DATE '" + literal.getValue() + "'"; // This gets always executed as TO_DATE('2015-02-01','YYYY-MM-DD')
}

@Override
Expand Down Expand Up @@ -440,7 +442,7 @@ public String visit(final SqlOrderBy orderBy) throws AdapterException {
* @param isAscending true if the desired sort order is ascending, false if descending
* @param defaultNullSorting default null sorting of dialect
* @return true, if the data source would position nulls at end of the resultset if NULLS FIRST/LAST is not
* specified explicitly.
* specified explicitly.
*/
private boolean nullsAreAtEndByDefault(final boolean isAscending, final SqlDialect.NullSorting defaultNullSorting) {
if (defaultNullSorting == SqlDialect.NullSorting.NULLS_SORTED_AT_END) {
Expand Down Expand Up @@ -485,6 +487,32 @@ public String visit(final SqlPredicateInConstList predicate) throws AdapterExcep
return predicate.getExpression().accept(this) + " IN (" + String.join(", ", argumentsSql) + ")";
}

@Override
public String visit(final SqlPredicateIsJson function) throws AdapterException {
return visitSqlPredicateJson(function.getExpression(), function.toSimpleSql(), function.getTypeConstraint(),
function.getKeyUniquenessConstraint());
}

// We remove KEYS keyword from the query, because Exasol database can't parse it correctly in some cases.
// According to the SQL standard, KEYS is optional.
private String visitSqlPredicateJson(final SqlNode expression, final String functionToSimpleSql,
final String typeConstraint, final String keyUniquenessConstraint) throws AdapterException {
final StringBuilder stringBuilder = new StringBuilder();
final String expressionString = expression.accept(this);
stringBuilder.append(expressionString);
stringBuilder.append(functionToSimpleSql);
stringBuilder.append(typeConstraint);
stringBuilder.append(" ");
stringBuilder.append(keyUniquenessConstraint.replace(" KEYS", ""));
return stringBuilder.toString();
}

@Override
public String visit(final SqlPredicateIsNotJson function) throws AdapterException {
return visitSqlPredicateJson(function.getExpression(), function.toSimpleSql(), function.getTypeConstraint(),
function.getKeyUniquenessConstraint());
}

@Override
public String visit(final SqlPredicateLess predicate) throws AdapterException {
return predicate.getLeft().accept(this) + " < " + predicate.getRight().accept(this);
Expand Down Expand Up @@ -543,6 +571,6 @@ public String visit(final SqlPredicateIsNotNull predicate) throws AdapterExcepti
protected String getTypeNameFromColumn(final SqlColumn column) throws AdapterException {
final ColumnAdapterNotesJsonConverter converter = ColumnAdapterNotesJsonConverter.getInstance();
return converter.convertFromJsonToColumnAdapterNotes(column.getMetadata().getAdapterNotes(), column.getName())
.getTypeName();
.getTypeName();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.exasol.adapter.dialects;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.when;

import java.util.*;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import com.exasol.adapter.AdapterException;
import com.exasol.adapter.AdapterProperties;
import com.exasol.adapter.dialects.dummy.DummySqlDialect;
import com.exasol.adapter.metadata.DataType;
import com.exasol.adapter.sql.*;

class SqlGenerationVisitorTest {
private static SqlGenerationVisitor sqlGenerationVisitor;

@BeforeAll
static void setUp() {
final Map<String, String> rawProperties = new HashMap<>();
final AdapterProperties adapterProperties = new AdapterProperties(rawProperties);
final SqlDialect sqlDialect = new DummySqlDialect(null, adapterProperties);
final SqlGenerationContext context = new SqlGenerationContext("", "TEXT_SCHEMA_NAME", false);
sqlGenerationVisitor = new SqlGenerationVisitor(sqlDialect, context);
}

@Test
void testVisitSqlFunctionScalarCast() throws AdapterException {
final DataType dataType = DataType.createDecimal(18, 0);
final List<SqlNode> arguments = new ArrayList<>();
arguments.add(new SqlLiteralDouble(20));
final SqlFunctionScalarCast sqlFunctionScalarCast = new SqlFunctionScalarCast(dataType, arguments);
assertThat(sqlGenerationVisitor.visit(sqlFunctionScalarCast), equalTo("CAST(20.0 AS DECIMAL(18, 0))"));
}

@Test
void testVisitSqlFunctionScalarExtract() throws AdapterException {
final List<SqlNode> arguments = new ArrayList<>();
arguments.add(new SqlLiteralTimestamp("2019-02-12 12:07:00"));
final SqlFunctionScalarExtract sqlFunctionScalarExtract = new SqlFunctionScalarExtract("SECOND", arguments);
assertThat(sqlGenerationVisitor.visit(sqlFunctionScalarExtract),
equalTo("EXTRACT(SECOND FROM TIMESTAMP '2019-02-12 12:07:00')"));
}

@Test
void testVisitSqlFunctionScalarJsonValue() throws AdapterException {
final List<SqlNode> arguments = new ArrayList<>();
arguments.add(new SqlLiteralString("{\"a\": 1}"));
arguments.add(new SqlLiteralString("$.a"));
final SqlFunctionScalarJsonValue.Behavior emptyBehavior = new SqlFunctionScalarJsonValue.Behavior(
SqlFunctionScalarJsonValue.BehaviorType.DEFAULT, Optional.of(new SqlLiteralString("*** error ***")));
final SqlFunctionScalarJsonValue.Behavior errorBehavior = new SqlFunctionScalarJsonValue.Behavior(
SqlFunctionScalarJsonValue.BehaviorType.DEFAULT, Optional.of(new SqlLiteralString("*** error ***")));
final SqlFunctionScalarJsonValue sqlFunctionScalarJsonValue = new SqlFunctionScalarJsonValue(
ScalarFunction.JSON_VALUE, arguments, DataType.createVarChar(1000, DataType.ExaCharset.UTF8),
emptyBehavior, errorBehavior);
assertThat(sqlGenerationVisitor.visit(sqlFunctionScalarJsonValue),
equalTo("JSON_VALUE('{\"a\": 1}', '$.a' RETURNING VARCHAR(1000) UTF8 "
+ "DEFAULT '*** error ***' ON EMPTY DEFAULT '*** error ***' ON ERROR)"));
}

@Test
void testVisitSqlLimit() {
final SqlLimit sqlLimit = new SqlLimit(5, 10);
assertThat(sqlGenerationVisitor.visit(sqlLimit), equalTo("LIMIT 5 OFFSET 10"));
}

@Test
void testVisitSqlPredicateIsJson() throws AdapterException {
final SqlNode expressionMock = Mockito.mock(SqlNode.class);
when(expressionMock.accept(sqlGenerationVisitor)).thenReturn("SELECT '{\"a\": 1}'");
final SqlPredicateIsJson sqlPredicateIsJson = new SqlPredicateIsJson(expressionMock,
AbstractSqlPredicateJson.TypeConstraints.OBJECT,
AbstractSqlPredicateJson.KeyUniquenessConstraint.WITH_UNIQUE_KEYS);
assertThat(sqlGenerationVisitor.visit(sqlPredicateIsJson),
equalTo("SELECT '{\"a\": 1}' IS JSON OBJECT WITH UNIQUE"));
}

@Test
void testVisitSqlPredicateIsNotJson() throws AdapterException {
final SqlNode expressionMock = Mockito.mock(SqlNode.class);
when(expressionMock.accept(sqlGenerationVisitor)).thenReturn("SELECT '{\"a\": 1}'");
final SqlPredicateIsNotJson sqlPredicateIsNotJson = new SqlPredicateIsNotJson(expressionMock,
AbstractSqlPredicateJson.TypeConstraints.OBJECT,
AbstractSqlPredicateJson.KeyUniquenessConstraint.WITH_UNIQUE_KEYS);
assertThat(sqlGenerationVisitor.visit(sqlPredicateIsNotJson),
equalTo("SELECT '{\"a\": 1}' IS NOT JSON OBJECT WITH UNIQUE"));
}

@Test
void testVisitSqlLiteralDate() {
final SqlLiteralDate sqlLiteralDate = new SqlLiteralDate("2015-12-01");
assertThat(sqlGenerationVisitor.visit(sqlLiteralDate), equalTo("DATE '2015-12-01'"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public <R> R accept(final SqlNodeVisitor<R> visitor) throws AdapterException {
}

@Override
String toSimpleSql() {
public String toSimpleSql() {
return DUMMY_SELECT;
}
}

0 comments on commit 6982a40

Please sign in to comment.