![JAR search and dependency download from the Maven repository](/logo.png)
nl.topicus.jdbc.statement.ValueBinderExpressionVisitorAdapter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spanner-jdbc Show documentation
Show all versions of spanner-jdbc Show documentation
JDBC Driver for Google Cloud Spanner
package nl.topicus.jdbc.statement;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Date;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import nl.topicus.jdbc.shaded.org.apache.commons.lang3.math.NumberUtils;
import nl.topicus.jdbc.shaded.com.google.api.client.util.DateTime;
import nl.topicus.jdbc.shaded.com.google.cloud.ByteArray;
import nl.topicus.jdbc.shaded.com.google.cloud.spanner.ValueBinder;
import nl.topicus.jdbc.shaded.com.google.common.io.CharStreams;
import nl.topicus.jdbc.shaded.com.google.common.primitives.Booleans;
import nl.topicus.jdbc.shaded.com.google.common.primitives.Doubles;
import nl.topicus.jdbc.shaded.com.google.common.primitives.Longs;
import nl.topicus.jdbc.util.CloudSpannerConversionUtil;
class ValueBinderExpressionVisitorAdapter extends AbstractSpannerExpressionVisitorAdapter {
private static final Pattern SPLIT_ON_COMMA_PATTERN =
Pattern.compile(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
private ValueBinder binder;
ValueBinderExpressionVisitorAdapter(ParameterStore parameterStore, ValueBinder binder,
String column) {
super(parameterStore, column);
this.binder = binder;
}
@Override
protected void setValue(Object value, Integer sqlType) {
R res = setSingleValue(value, sqlType);
if (res == null && value != null)
res = setArrayValue(value);
if (res == null && value != null) {
throw new IllegalArgumentException(
"Unsupported parameter type: " + value.getClass().getName() + " - " + value.toString());
}
}
private R setSingleValue(Object value, Integer sqlType) {
if (value == null) {
return setNullValue(sqlType);
} else if (Boolean.class.isAssignableFrom(value.getClass())) {
return binder.to((Boolean) value);
} else if (Byte.class.isAssignableFrom(value.getClass())) {
return binder.to(((Byte) value).longValue());
} else if (Short.class.isAssignableFrom(value.getClass())) {
return binder.to(((Short) value).longValue());
} else if (Integer.class.isAssignableFrom(value.getClass())) {
return binder.to(((Integer) value).longValue());
} else if (Long.class.isAssignableFrom(value.getClass())) {
return binder.to(((Long) value).longValue());
} else if (Float.class.isAssignableFrom(value.getClass())) {
return binder.to(((Float) value).doubleValue());
} else if (Double.class.isAssignableFrom(value.getClass())) {
return binder.to(((Double) value).doubleValue());
} else if (BigDecimal.class.isAssignableFrom(value.getClass())) {
return binder.to(((BigDecimal) value).doubleValue());
} else if (Date.class.isAssignableFrom(value.getClass())) {
Date dateValue = (Date) value;
return binder.to(CloudSpannerConversionUtil.toCloudSpannerDate(dateValue));
} else if (Timestamp.class.isAssignableFrom(value.getClass())) {
Timestamp timeValue = (Timestamp) value;
if (timeValue.equals(CloudSpannerPreparedStatement.getSpannerCommitTimestamp())) {
return binder.to(CloudSpannerPreparedStatement.SPANNER_COMMIT_TIMESTAMP_PLACEHOLDER);
} else {
return binder.to(CloudSpannerConversionUtil.toCloudSpannerTimestamp(timeValue));
}
} else if (String.class.isAssignableFrom(value.getClass())) {
String stringVal = (String) value;
if (stringVal.startsWith("{") && stringVal.endsWith("}")) {
R res = splitAndSetArrayValue(stringVal);
if (res != null) {
return res;
}
}
return binder.to(stringVal);
} else if (Readable.class.isAssignableFrom(value.getClass())) {
try {
Readable readable = (Readable) value;
return binder.to(CharStreams.toString(readable));
} catch (IOException e) {
throw new IllegalArgumentException("Could not read from readable", e);
}
} else if (Character.class.isAssignableFrom(value.getClass())) {
return binder.to(((Character) value).toString());
} else if (Character[].class.isAssignableFrom(value.getClass())) {
List list = Arrays.asList((Character[]) value);
String s = list.stream().map(Object::toString).reduce("", String::concat);
return binder.to(s);
} else if (char[].class.isAssignableFrom(value.getClass())) {
return binder.to(String.valueOf((char[]) value));
} else if (URL.class.isAssignableFrom(value.getClass())) {
return binder.to(((URL) value).toString());
} else if (byte[].class.isAssignableFrom(value.getClass())) {
return binder.to(ByteArray.copyFrom((byte[]) value));
} else if (ByteArrayInputStream.class.isAssignableFrom(value.getClass())) {
try {
return binder.to(ByteArray.copyFrom((ByteArrayInputStream) value));
} catch (IOException e) {
throw new IllegalArgumentException(
"Could not copy bytes from input stream: " + e.getMessage(), e);
}
} else if (Array.class.isAssignableFrom(value.getClass())) {
try {
return setArrayValue(((Array) value).getArray());
} catch (SQLException e) {
throw new IllegalArgumentException(
"Unsupported parameter type: " + value.getClass().getName() + " - " + value.toString());
}
}
return null;
}
private R splitAndSetArrayValue(String arrayString) {
// Split on comma's, do not include '{' and '}'
String[] array =
SPLIT_ON_COMMA_PATTERN.split(arrayString.substring(1, arrayString.length() - 1));
// Trim the array elements
for (int i = 0; i < array.length; i++)
array[i] = array[i] == null ? null : array[i].trim();
// Try to determine the data type of the array
if (isStringArray(array)) {
return setArrayValue(convertStringArray(array));
} else if (isBooleanArray(array)) {
return setArrayValue(convertBooleanArray(array));
} else if (isLongArray(array)) {
return setArrayValue(convertLongArray(array));
} else if (isDoubleArray(array)) {
return setArrayValue(convertDoubleArray(array));
} else if (isDateArray(array)) {
return setArrayValue(convertDateArray(array));
} else if (isTimestampArray(array)) {
return setArrayValue(convertTimestampArray(array));
}
return null;
}
private boolean isStringArray(String[] array) {
for (String val : array) {
if (val != null && !(val.startsWith("\"") && val.endsWith("\""))) {
return false;
}
}
return true;
}
private String[] convertStringArray(String[] array) {
int index = 0;
String[] res = new String[array.length];
for (String val : array) {
if (val != null) {
res[index] = val.substring(1, val.length() - 1);
}
index++;
}
return res;
}
private boolean isBooleanArray(String[] array) {
for (String val : array) {
if (val != null && !(val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false"))) {
return false;
}
}
return true;
}
private Boolean[] convertBooleanArray(String[] array) {
int index = 0;
Boolean[] res = new Boolean[array.length];
for (String val : array) {
if (val != null) {
res[index] = Boolean.valueOf(val);
}
index++;
}
return res;
}
private boolean isLongArray(String[] array) {
for (String val : array) {
if (!NumberUtils.isDigits(val)) {
return false;
}
}
return true;
}
private Long[] convertLongArray(String[] array) {
int index = 0;
Long[] res = new Long[array.length];
for (String val : array) {
if (val != null) {
res[index] = NumberUtils.createNumber(val).longValue();
}
index++;
}
return res;
}
private boolean isDoubleArray(String[] array) {
for (String val : array) {
if (!NumberUtils.isCreatable(val)) {
return false;
}
}
return true;
}
private Double[] convertDoubleArray(String[] array) {
int index = 0;
Double[] res = new Double[array.length];
for (String val : array) {
if (val != null) {
res[index] = NumberUtils.createNumber(val).doubleValue();
}
index++;
}
return res;
}
private boolean isDateArray(String[] array) {
for (String val : array) {
if (val != null && !(val.startsWith("{d \"") && val.endsWith("\"}"))) {
return false;
}
}
return true;
}
private Date[] convertDateArray(String[] array) {
int index = 0;
Date[] res = new Date[array.length];
for (String val : array) {
if (val != null) {
String date = val.substring(4, val.length() - 2);
try {
res[index] = new Date(DateTime.parseRfc3339(date).getValue());
} catch (Exception e) {
throw new IllegalArgumentException(String.format(
"Invalid date value: '%s'. Date values must be specified in the format yyyy-MM-dd.",
date), e);
}
}
index++;
}
return res;
}
private boolean isTimestampArray(String[] array) {
for (String val : array) {
if (val != null && !(val.startsWith("{ts \"") && val.endsWith("\"}"))) {
return false;
}
}
return true;
}
private Timestamp[] convertTimestampArray(String[] array) {
int index = 0;
Timestamp[] res = new Timestamp[array.length];
for (String val : array) {
if (val != null) {
StringBuilder date =
new StringBuilder(val.substring(5, val.length() - 2).replace(' ', 'T'));
if (date.charAt(date.length() - 1) != 'Z')
date.append('Z');
try {
res[index] = nl.topicus.jdbc.shaded.com.google.cloud.Timestamp.parseTimestamp(date.toString()).toSqlTimestamp();
} catch (Exception e) {
throw new IllegalArgumentException(String.format(
"Invalid timestamp value: '%s'. Timestamp values must be specified in the format yyyy-MM-dd HH:mm:ss.SSS",
val.substring(5, val.length() - 2)), e);
}
}
index++;
}
return res;
}
private R setArrayValue(Object value) {
if (boolean[].class.isAssignableFrom(value.getClass())) {
return binder.toBoolArray((boolean[]) value);
} else if (Boolean[].class.isAssignableFrom(value.getClass())) {
return binder.toBoolArray(Booleans.toArray(Arrays.asList((Boolean[]) value)));
} else if (short[].class.isAssignableFrom(value.getClass())) {
long[] l = new long[((short[]) value).length];
Arrays.parallelSetAll(l, i -> ((short[]) value)[i]);
return binder.toInt64Array(l);
} else if (Short[].class.isAssignableFrom(value.getClass())) {
return binder.toInt64Array(Longs.toArray(Arrays.asList((Short[]) value)));
} else if (int[].class.isAssignableFrom(value.getClass())) {
long[] l = new long[((int[]) value).length];
Arrays.parallelSetAll(l, i -> ((int[]) value)[i]);
return binder.toInt64Array(l);
} else if (Integer[].class.isAssignableFrom(value.getClass())) {
return binder.toInt64Array(Longs.toArray(Arrays.asList((Integer[]) value)));
} else if (long[].class.isAssignableFrom(value.getClass())) {
return binder.toInt64Array((long[]) value);
} else if (Long[].class.isAssignableFrom(value.getClass())) {
return binder.toInt64Array(Longs.toArray(Arrays.asList((Long[]) value)));
} else if (float[].class.isAssignableFrom(value.getClass())) {
double[] l = new double[((float[]) value).length];
Arrays.parallelSetAll(l, i -> ((float[]) value)[i]);
return binder.toFloat64Array(l);
} else if (Float[].class.isAssignableFrom(value.getClass())) {
return binder.toFloat64Array(Doubles.toArray(Arrays.asList((Float[]) value)));
} else if (double[].class.isAssignableFrom(value.getClass())) {
return binder.toFloat64Array((double[]) value);
} else if (Double[].class.isAssignableFrom(value.getClass())) {
return binder.toFloat64Array(Doubles.toArray(Arrays.asList((Double[]) value)));
} else if (BigDecimal[].class.isAssignableFrom(value.getClass())) {
return binder.toFloat64Array(Doubles.toArray(Arrays.asList((BigDecimal[]) value)));
} else if (Date[].class.isAssignableFrom(value.getClass())) {
return binder.toDateArray(CloudSpannerConversionUtil.toCloudSpannerDates((Date[]) value));
} else if (Timestamp[].class.isAssignableFrom(value.getClass())) {
return binder.toTimestampArray(
CloudSpannerConversionUtil.toCloudSpannerTimestamps((Timestamp[]) value));
} else if (String[].class.isAssignableFrom(value.getClass())) {
return binder.toStringArray(Arrays.asList((String[]) value));
} else if (byte[][].class.isAssignableFrom(value.getClass())) {
return binder.toBytesArray(CloudSpannerConversionUtil.toCloudSpannerBytes((byte[][]) value));
}
return null;
}
private R setNullValue(Integer sqlType) {
if (sqlType == null) {
return binder.to((String) null);
}
switch (sqlType) {
case Types.BIGINT:
return binder.to((Long) null);
case Types.BINARY:
return binder.to((ByteArray) null);
case Types.BLOB:
return binder.to((ByteArray) null);
case Types.BOOLEAN:
return binder.to((Boolean) null);
case Types.CHAR:
return binder.to((String) null);
case Types.CLOB:
return binder.to((String) null);
case Types.DATE:
return binder.to((nl.topicus.jdbc.shaded.com.google.cloud.Date) null);
case Types.DECIMAL:
return binder.to((Double) null);
case Types.DOUBLE:
return binder.to((Double) null);
case Types.FLOAT:
return binder.to((Double) null);
case Types.INTEGER:
return binder.to((Long) null);
case Types.LONGNVARCHAR:
return binder.to((String) null);
case Types.LONGVARBINARY:
return binder.to((ByteArray) null);
case Types.LONGVARCHAR:
return binder.to((String) null);
case Types.NCHAR:
return binder.to((String) null);
case Types.NCLOB:
return binder.to((String) null);
case Types.NUMERIC:
return binder.to((Double) null);
case Types.NVARCHAR:
return binder.to((String) null);
case Types.REAL:
return binder.to((Double) null);
case Types.SMALLINT:
return binder.to((Long) null);
case Types.SQLXML:
return binder.to((String) null);
case Types.TIME:
return binder.to((nl.topicus.jdbc.shaded.com.google.cloud.Timestamp) null);
case Types.TIMESTAMP:
return binder.to((nl.topicus.jdbc.shaded.com.google.cloud.Timestamp) null);
case Types.TINYINT:
return binder.to((Long) null);
case Types.VARBINARY:
return binder.to((ByteArray) null);
case Types.VARCHAR:
return binder.to((String) null);
default:
throw new IllegalArgumentException("Unsupported sql type for setting to null: " + sqlType);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy