Skip to content

Commit

Permalink
jdbc: support for sub-set of JDBC escape syntax
Browse files Browse the repository at this point in the history
Add a driver SQL pre-processing before sending it to the server. The
driver supports sub-set of scalar functions defined by the spec
(appendix C), outer joins, escape clause for SQL LIKE operator, and
limit/offset clause. The processed result can be received using
Connection.nativeSQL() method.

Closes #79
Closes #76
Closes #81
Closes #83
Closes #84
Affects: #108
  • Loading branch information
nicktorwald committed Jan 14, 2020
1 parent 2c4baed commit d36814b
Show file tree
Hide file tree
Showing 13 changed files with 1,585 additions and 13 deletions.
416 changes: 416 additions & 0 deletions src/main/java/org/tarantool/jdbc/EscapeSyntaxParser.java

Large diffs are not rendered by default.

444 changes: 444 additions & 0 deletions src/main/java/org/tarantool/jdbc/EscapedFunctions.java

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion src/main/java/org/tarantool/jdbc/SQLConnection.java
Expand Up @@ -63,12 +63,15 @@ public class SQLConnection implements TarantoolConnection {
private DatabaseMetaData cachedMetadata;
private int resultSetHoldability = UNSET_HOLDABILITY;

private final EscapeSyntaxParser escapeSyntaxParser;

public SQLConnection(String url, Properties properties) throws SQLException {
this.url = url;
this.properties = properties;

try {
client = makeSqlClient(makeAddress(properties), makeConfigFromProperties(properties));
escapeSyntaxParser = new EscapeSyntaxParser(this);
} catch (Exception e) {
throw new SQLException("Couldn't initiate connection using " + SQLDriver.diagProperties(properties), e);
}
Expand Down Expand Up @@ -189,7 +192,7 @@ public CallableStatement prepareCall(String sql,
@Override
public String nativeSQL(String sql) throws SQLException {
checkNotClosed();
throw new SQLFeatureNotSupportedException();
return escapeSyntaxParser.translate(sql, true);
}

@Override
Expand Down
9 changes: 5 additions & 4 deletions src/main/java/org/tarantool/jdbc/SQLDatabaseMetadata.java
Expand Up @@ -6,6 +6,7 @@
import org.tarantool.Version;
import org.tarantool.jdbc.type.TarantoolSqlType;
import org.tarantool.util.ServerVersion;
import org.tarantool.util.StringUtils;
import org.tarantool.util.TupleTwo;

import java.sql.Connection;
Expand Down Expand Up @@ -179,17 +180,17 @@ public String getSQLKeywords() throws SQLException {

@Override
public String getNumericFunctions() throws SQLException {
return "";
return StringUtils.toCsvList(EscapedFunctions.NumericFunction.values());
}

@Override
public String getStringFunctions() throws SQLException {
return "";
return StringUtils.toCsvList(EscapedFunctions.StringFunction.values());
}

@Override
public String getSystemFunctions() throws SQLException {
return "";
return StringUtils.toCsvList(EscapedFunctions.SystemFunction.values());
}

@Override
Expand Down Expand Up @@ -274,7 +275,7 @@ public boolean supportsGroupByBeyondSelect() throws SQLException {

@Override
public boolean supportsLikeEscapeClause() throws SQLException {
return false;
return true;
}

@Override
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/tarantool/jdbc/SQLPreparedStatement.java
Expand Up @@ -43,7 +43,7 @@ public class SQLPreparedStatement extends SQLStatement implements PreparedStatem

public SQLPreparedStatement(SQLConnection connection, String sql, int autoGeneratedKeys) throws SQLException {
super(connection);
this.sql = sql;
this.sql = translateQuery(sql);
this.parameters = new HashMap<>();
this.autoGeneratedKeys = autoGeneratedKeys;
setPoolable(true);
Expand All @@ -55,7 +55,7 @@ public SQLPreparedStatement(SQLConnection connection,
int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
super(connection, resultSetType, resultSetConcurrency, resultSetHoldability);
this.sql = sql;
this.sql = translateQuery(sql);
this.parameters = new HashMap<>();
this.autoGeneratedKeys = NO_GENERATED_KEYS;
setPoolable(true);
Expand Down
14 changes: 10 additions & 4 deletions src/main/java/org/tarantool/jdbc/SQLStatement.java
Expand Up @@ -45,6 +45,7 @@ public class SQLStatement implements TarantoolStatement {
private List<String> batchQueries = new ArrayList<>();

private boolean isCloseOnCompletion;
private boolean useEscapeProcessing = true;

private final int resultSetType;
private final int resultSetConcurrency;
Expand Down Expand Up @@ -91,7 +92,7 @@ protected SQLStatement(SQLConnection sqlConnection,
@Override
public ResultSet executeQuery(String sql) throws SQLException {
checkNotClosed();
if (!executeInternal(NO_GENERATED_KEYS, sql)) {
if (!executeInternal(NO_GENERATED_KEYS, translateQuery(sql))) {
throw new SQLException("No results were returned", SQLStates.NO_DATA.getSqlState());
}
return resultSet;
Expand All @@ -106,7 +107,7 @@ public int executeUpdate(String sql) throws SQLException {
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
checkNotClosed();
JdbcConstants.checkGeneratedKeysConstant(autoGeneratedKeys);
if (executeInternal(autoGeneratedKeys, sql)) {
if (executeInternal(autoGeneratedKeys, translateQuery(sql))) {
throw new SQLException(
"Result was returned but nothing was expected",
SQLStates.TOO_MANY_RESULTS.getSqlState()
Expand Down Expand Up @@ -166,7 +167,8 @@ public void setMaxRows(int maxRows) throws SQLException {

@Override
public void setEscapeProcessing(boolean enable) throws SQLException {
throw new SQLFeatureNotSupportedException();
checkNotClosed();
useEscapeProcessing = enable;
}

@Override
Expand Down Expand Up @@ -208,7 +210,7 @@ public void setCursorName(String name) throws SQLException {
@Override
public boolean execute(String sql) throws SQLException {
checkNotClosed();
return executeInternal(NO_GENERATED_KEYS, sql);
return executeInternal(NO_GENERATED_KEYS, translateQuery(sql));
}

@Override
Expand Down Expand Up @@ -511,4 +513,8 @@ protected SQLResultSet executeGeneratedKeys(List<Integer> generatedKeys) throws
return createResultSet(SQLResultHolder.ofQuery(Collections.singletonList(sqlMetaData), rows));
}

protected String translateQuery(String sql) throws SQLException {
return useEscapeProcessing ? connection.nativeSQL(sql) : sql;
}

}
3 changes: 2 additions & 1 deletion src/main/java/org/tarantool/util/SQLStates.java
Expand Up @@ -7,7 +7,8 @@ public enum SQLStates {
CONNECTION_DOES_NOT_EXIST("08003"),
INVALID_PARAMETER_VALUE("22023"),
INVALID_CURSOR_STATE("24000"),
INVALID_TRANSACTION_STATE("25000");
INVALID_TRANSACTION_STATE("25000"),
SYNTAX_ERROR("42000");

private final String sqlState;

Expand Down
9 changes: 9 additions & 0 deletions src/main/java/org/tarantool/util/StringUtils.java
@@ -1,5 +1,8 @@
package org.tarantool.util;

import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StringUtils {

public static boolean isEmpty(String string) {
Expand All @@ -18,4 +21,10 @@ public static boolean isNotBlank(String string) {
return !isBlank(string);
}

public static String toCsvList(Enum<?>[] values) {
return Stream.of(values)
.map(Enum::name)
.collect(Collectors.joining(","));
}

}
27 changes: 27 additions & 0 deletions src/main/java/org/tarantool/util/ThrowingBiFunction.java
@@ -0,0 +1,27 @@
package org.tarantool.util;

/**
* Represents a function that accepts two arguments and
* produces a result or throws an exception.
*
* @param <T> type of the first argument to the function
* @param <U> type of the second argument to the function
* @param <R> type of the result of the function
* @param <E> type of the exception in case of error
*/
@FunctionalInterface
public interface ThrowingBiFunction<T, U, R, E extends Exception> {

/**
* Applies this function to the given arguments.
*
* @param argument1 first argument
* @param argument2 second argument
*
* @return function result
*
* @throws E if any error occurs
*/
R apply(T argument1, U argument2) throws E;

}

0 comments on commit d36814b

Please sign in to comment.