Skip to content

Commit

Permalink
FMWK-298 Support byte array parameter in PreparedStatement (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
reugn authored Dec 20, 2023
1 parent 7266ca5 commit c1740df
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 53 deletions.
28 changes: 8 additions & 20 deletions src/main/java/com/aerospike/jdbc/AerospikePreparedStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

import static com.aerospike.jdbc.util.PreparedStatement.parseParameters;
import static java.lang.String.format;
import static java.util.Objects.isNull;

public class AerospikePreparedStatement extends AerospikeStatement implements PreparedStatement {

Expand All @@ -50,9 +49,8 @@ private Object[] buildSqlParameters(String sql) {

@Override
public ResultSet executeQuery() throws SQLException {
String preparedQueryString = prepareQueryString();
logger.info(() -> "executeQuery: " + preparedQueryString);
AerospikeQuery query = parseQuery(preparedQueryString);
logger.info(() -> format("executeQuery: %s, params: %s", sqlStatement, Arrays.toString(sqlParameters)));
AerospikeQuery query = parseQuery(sqlStatement, Arrays.asList(sqlParameters));
runQuery(query);
return resultSet;
}
Expand Down Expand Up @@ -114,7 +112,7 @@ public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException

@Override
public void setString(int parameterIndex, String x) throws SQLException {
setObject(parameterIndex, format("\"%s\"", x));
setObject(parameterIndex, x);
}

@Override
Expand Down Expand Up @@ -180,25 +178,15 @@ public void setObject(int parameterIndex, Object x) throws SQLException {

@Override
public boolean execute() throws SQLException {
String preparedQueryString = prepareQueryString();
logger.info(() -> "execute: " + preparedQueryString);
AerospikeQuery query = parseQuery(preparedQueryString);
logger.info(() -> format("execute: %s, params: %s", sqlStatement, Arrays.toString(sqlParameters)));
AerospikeQuery query = parseQuery(sqlStatement, Arrays.asList(sqlParameters));
runQuery(query);
return query.getQueryType() == QueryType.SELECT;
}

private String prepareQueryString() {
String preparedQueryString = sqlStatement;
for (Object value : sqlParameters) {
String replacement = isNull(value) ? "?" : value.toString();
preparedQueryString = preparedQueryString.replaceFirst("\\?", replacement);
}
return preparedQueryString;
}

@Override
public void addBatch() throws SQLException {
addBatch(prepareQueryString());
throw new SQLFeatureNotSupportedException(BATCH_NOT_SUPPORTED_MESSAGE);
}

@Override
Expand Down Expand Up @@ -228,7 +216,7 @@ public void setArray(int parameterIndex, Array x) throws SQLException {

@Override
public ResultSetMetaData getMetaData() throws SQLException {
AerospikeQuery query = parseQuery(prepareQueryString());
AerospikeQuery query = parseQuery(sqlStatement, Arrays.asList(sqlParameters));
List<DataColumn> columns = ((AerospikeDatabaseMetadata) connection.getMetaData())
.getSchemaBuilder()
.getSchema(query.getCatalogTable());
Expand Down Expand Up @@ -262,7 +250,7 @@ public void setURL(int parameterIndex, URL url) throws SQLException {

@Override
public ParameterMetaData getParameterMetaData() throws SQLException {
AerospikeQuery query = parseQuery(prepareQueryString());
AerospikeQuery query = parseQuery(sqlStatement, Arrays.asList(sqlParameters));
List<DataColumn> columns = ((AerospikeDatabaseMetadata) connection.getMetaData())
.getSchemaBuilder()
.getSchema(query.getCatalogTable());
Expand Down
13 changes: 7 additions & 6 deletions src/main/java/com/aerospike/jdbc/AerospikeStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.Collection;
import java.util.logging.Logger;

import static java.lang.String.format;
Expand All @@ -24,9 +25,9 @@

public class AerospikeStatement implements Statement, SimpleWrapper {

private static final Logger logger = Logger.getLogger(AerospikeStatement.class.getName());
protected static final String BATCH_NOT_SUPPORTED_MESSAGE = "Batch update is not supported";

private static final String BATCH_NOT_SUPPORTED_MESSAGE = "Batch update is not supported";
private static final Logger logger = Logger.getLogger(AerospikeStatement.class.getName());
private static final String AUTO_GENERATED_KEYS_NOT_SUPPORTED_MESSAGE = "Auto-generated keys are not supported";

protected final IAerospikeClient client;
Expand All @@ -48,7 +49,7 @@ public AerospikeStatement(IAerospikeClient client, AerospikeConnection connectio
@Override
public ResultSet executeQuery(String sql) throws SQLException {
logger.info(() -> "executeQuery: " + sql);
AerospikeQuery query = parseQuery(sql);
AerospikeQuery query = parseQuery(sql, null);
runQuery(query);
return resultSet;
}
Expand All @@ -59,11 +60,11 @@ protected void runQuery(AerospikeQuery query) {
updateCount = result.getRight();
}

protected AerospikeQuery parseQuery(String sql) throws SQLException {
protected AerospikeQuery parseQuery(String sql, Collection<Object> sqlParameters) throws SQLException {
sql = sql.replace("\n", " ");
AerospikeQuery query;
try {
query = AerospikeQuery.parse(sql);
query = AerospikeQuery.parse(sql, sqlParameters);
} catch (Exception e) {
query = AuxStatementParser.parse(sql);
}
Expand Down Expand Up @@ -142,7 +143,7 @@ public void setCursorName(String name) throws SQLException {
@Override
public boolean execute(String sql) throws SQLException {
logger.info(() -> "execute: " + sql);
AerospikeQuery query = parseQuery(sql);
AerospikeQuery query = parseQuery(sql, null);
runQuery(query);
return query.getQueryType() == QueryType.SELECT;
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/aerospike/jdbc/model/AerospikeQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ public AerospikeQuery() {
this.queryType = QueryType.UNKNOWN;
}

public static AerospikeQuery parse(String sql) throws SqlParseException {
public static AerospikeQuery parse(String sql, Collection<Object> sqlParameters) throws SqlParseException {
SqlParser parser = SqlParser.create(sql, sqlParserConfig);
SqlNode parsed = parser.parseQuery();
return parsed.accept(new AerospikeSqlVisitor());
return parsed.accept(new AerospikeSqlVisitor(sqlParameters));
}

public String getCatalog() {
Expand Down
26 changes: 22 additions & 4 deletions src/main/java/com/aerospike/jdbc/model/AerospikeSqlVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,30 @@
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.util.SqlVisitor;

import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Iterator;
import java.util.stream.Collectors;

import static com.aerospike.jdbc.util.Constants.UNSUPPORTED_QUERY_TYPE_MESSAGE;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;

public class AerospikeSqlVisitor implements SqlVisitor<AerospikeQuery> {

private static final String QUERY_PLACEHOLDER = "?";

private final AerospikeQuery query;
private final Iterator<Object> sqlParametersIterator;

public AerospikeSqlVisitor() {
this(null);
}

public AerospikeSqlVisitor(@Nullable Collection<Object> sqlParameters) {
query = new AerospikeQuery();
sqlParametersIterator = sqlParameters != null ? sqlParameters.iterator() : null;
}

@Override
Expand All @@ -45,13 +57,13 @@ public AerospikeQuery visit(SqlCall sqlCall) {
SqlUpdate sql = (SqlUpdate) sqlCall;
query.setQueryType(QueryType.UPDATE);
query.setTable(requireNonNull(sql.getTargetTable()).toString());
query.setValues(sql.getSourceExpressionList().stream()
.map(this::parseValue).collect(Collectors.toList()));
if (sql.getCondition() != null) {
query.setPredicate(parseWhere((SqlBasicCall) sql.getCondition()));
}
query.setColumns(sql.getTargetColumnList().stream()
.map(SqlNode::toString).collect(Collectors.toList()));
query.setValues(sql.getSourceExpressionList().stream()
.map(this::parseValue).collect(Collectors.toList()));
} else if (sqlCall instanceof SqlInsert) {
SqlInsert sql = (SqlInsert) sqlCall;
query.setQueryType(QueryType.INSERT);
Expand Down Expand Up @@ -97,8 +109,10 @@ public AerospikeQuery visit(SqlCall sqlCall) {
} else {
throw new UnsupportedOperationException(UNSUPPORTED_QUERY_TYPE_MESSAGE);
}
} catch (UnsupportedOperationException e) {
throw e;
} catch (Exception e) {
throw new UnsupportedOperationException(UNSUPPORTED_QUERY_TYPE_MESSAGE);
throw new UnsupportedOperationException(UNSUPPORTED_QUERY_TYPE_MESSAGE, e);
}
return query;
}
Expand Down Expand Up @@ -142,7 +156,7 @@ private QueryPredicate parseWhere(SqlBasicCall where) {
}
} else if (where.getOperator() instanceof SqlLikeOperator) {
String binName = where.getOperandList().get(0).toString();
String expression = unwrapString(where.getOperandList().get(1).toString());
String expression = parseValue(where.getOperandList().get(1)).toString();
return new QueryPredicateLike(binName, expression);
} else if (where.getOperator() instanceof SqlBetweenOperator) {
return new QueryPredicateRange(
Expand All @@ -168,6 +182,10 @@ private Object parseValue(SqlNode sqlNode) {
}
} else if (sqlNode instanceof SqlIdentifier) {
return unwrapString(sqlNode.toString());
} else if (sqlNode instanceof SqlDynamicParam
&& unwrapString(sqlNode.toString()).equals(QUERY_PLACEHOLDER)) {
checkState(sqlParametersIterator != null, "SQL parameters is null");
return sqlParametersIterator.next();
}
throw new UnsupportedOperationException(UNSUPPORTED_QUERY_TYPE_MESSAGE);
}
Expand Down
25 changes: 21 additions & 4 deletions src/main/java/com/aerospike/jdbc/predicate/QueryPredicateBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,45 @@ protected QueryPredicateBase(
}

protected static Exp.Type getValueType(Object value) {
if (value instanceof String) {
if (value == null) {
return Exp.Type.NIL;
} else if (value instanceof String) {
return Exp.Type.STRING;
} else if (value instanceof Long) {
} else if (value instanceof Long || value instanceof Integer
|| value instanceof Short || value instanceof Byte) {
return Exp.Type.INT;
} else if (value instanceof Double) {
} else if (value instanceof Double || value instanceof Float) {
return Exp.Type.FLOAT;
} else if (value instanceof Boolean) {
return Exp.Type.BOOL;
} else if (value instanceof byte[]) {
return Exp.Type.BLOB;
} else {
return Exp.Type.STRING;
}
}

protected Exp getValueExp(Object value) {
if (value instanceof String) {
if (value == null) {
return Exp.nil();
} else if (value instanceof String) {
return Exp.val((String) value);
} else if (value instanceof Long) {
return Exp.val((long) value);
} else if (value instanceof Integer) {
return Exp.val((int) value);
} else if (value instanceof Short) {
return Exp.val((short) value);
} else if (value instanceof Byte) {
return Exp.val((byte) value);
} else if (value instanceof Double) {
return Exp.val((double) value);
} else if (value instanceof Float) {
return Exp.val((float) value);
} else if (value instanceof Boolean) {
return Exp.val((boolean) value);
} else if (value instanceof byte[]) {
return Exp.val((byte[]) value);
} else {
return Exp.val(value.toString());
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/aerospike/jdbc/util/PreparedStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ public static Iterable<String> splitQueries(String sql) {
}
}

if (currentQuery.length() > 0 && currentQuery.toString().trim().length() > 0) {
if (currentQuery.length() > 0 && !currentQuery.toString().trim().isEmpty()) {
appendNotEmpty(queries, currentQuery.toString());
}

return queries;
}

private static void appendNotEmpty(Collection<String> queries, String query) {
if (query.trim().length() > 0) {
if (!query.trim().isEmpty()) {
queries.add(query);
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/test/java/com/aerospike/jdbc/JdbcBaseTest.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.aerospike.jdbc;

import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;

import java.sql.Connection;
import java.sql.DriverManager;
Expand All @@ -25,7 +25,7 @@ public abstract class JdbcBaseTest {

protected static Connection connection;

@BeforeSuite
@BeforeClass
public static void connectionInit() throws Exception {
logger.info("connectionInit");
Class.forName("com.aerospike.jdbc.AerospikeDriver").newInstance();
Expand All @@ -34,7 +34,7 @@ public static void connectionInit() throws Exception {
connection.setNetworkTimeout(Executors.newSingleThreadExecutor(), 5000);
}

@AfterSuite
@AfterClass
public static void connectionClose() throws SQLException {
logger.info("connectionClose");
connection.close();
Expand Down
Loading

0 comments on commit c1740df

Please sign in to comment.