blob: 40ec68ada71efd20ee70d850f2801004c6a7bf49 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.geode.connectors.jdbc.internal;
import java.sql.Blob;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
import org.apache.geode.connectors.jdbc.JdbcConnectorException;
import org.apache.geode.pdx.FieldType;
import org.apache.geode.pdx.PdxInstance;
import org.apache.geode.pdx.WritablePdxInstance;
public class SqlToPdxInstance {
private PdxInstance pdxTemplate;
private final Map<String, PdxFieldInfo> columnToPdxFieldMap = new HashMap<>();
// for unit testing
PdxInstance getPdxTemplate() {
return pdxTemplate;
}
public void setPdxTemplate(PdxInstance template) {
this.pdxTemplate = template;
}
public void addMapping(String columnName, String pdxFieldName, FieldType pdxFieldType) {
columnToPdxFieldMap.put(columnName, new PdxFieldInfo(pdxFieldName, pdxFieldType));
}
// for unit testing
Map<String, PdxFieldInfo> getColumnToPdxFieldMap() {
return columnToPdxFieldMap;
}
static class PdxFieldInfo {
private final String name;
private final FieldType type;
public PdxFieldInfo(String name, FieldType type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public FieldType getType() {
return type;
}
}
public PdxInstance create(ResultSet resultSet) throws SQLException {
if (!resultSet.next()) {
return null;
}
WritablePdxInstance result = pdxTemplate.createWriter();
ResultSetMetaData metaData = resultSet.getMetaData();
final int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);
PdxFieldInfo fieldInfo = this.columnToPdxFieldMap.get(columnName);
if (fieldInfo == null) {
throw new JdbcConnectorException(
"The jdbc-mapping does not contain the column name \"" + columnName + "\"."
+ " This is probably caused by a column being added to the table after the jdbc-mapping was created.");
}
Object fieldValue = getFieldValue(resultSet, i, fieldInfo.getType(), metaData);
result.setField(fieldInfo.getName(), fieldValue);
}
if (resultSet.next()) {
throw new JdbcConnectorException(
"Multiple rows returned for query: " + resultSet.getStatement());
}
return result;
}
/**
* @throws SQLException if the column value get fails
*/
Object getFieldValue(ResultSet resultSet, int columnIndex, FieldType fieldType,
ResultSetMetaData metaData)
throws SQLException {
switch (fieldType) {
case STRING:
return resultSet.getString(columnIndex);
case CHAR:
return getCharValue(resultSet, columnIndex);
case SHORT:
return resultSet.getShort(columnIndex);
case INT:
return resultSet.getInt(columnIndex);
case LONG:
return resultSet.getLong(columnIndex);
case FLOAT:
return resultSet.getFloat(columnIndex);
case DOUBLE:
return resultSet.getDouble(columnIndex);
case BYTE:
return resultSet.getByte(columnIndex);
case BOOLEAN:
return resultSet.getBoolean(columnIndex);
case DATE:
return getDateValue(resultSet, columnIndex, metaData);
case BYTE_ARRAY:
return getByteArrayValue(resultSet, columnIndex, metaData);
case BOOLEAN_ARRAY:
return convertJdbcObjectToJavaType(boolean[].class, resultSet.getObject(columnIndex));
case CHAR_ARRAY:
return convertJdbcObjectToJavaType(char[].class, resultSet.getObject(columnIndex));
case SHORT_ARRAY:
return convertJdbcObjectToJavaType(short[].class, resultSet.getObject(columnIndex));
case INT_ARRAY:
return convertJdbcObjectToJavaType(int[].class, resultSet.getObject(columnIndex));
case LONG_ARRAY:
return convertJdbcObjectToJavaType(long[].class, resultSet.getObject(columnIndex));
case FLOAT_ARRAY:
return convertJdbcObjectToJavaType(float[].class, resultSet.getObject(columnIndex));
case DOUBLE_ARRAY:
return convertJdbcObjectToJavaType(double[].class, resultSet.getObject(columnIndex));
case STRING_ARRAY:
return convertJdbcObjectToJavaType(String[].class, resultSet.getObject(columnIndex));
case OBJECT_ARRAY:
return convertJdbcObjectToJavaType(Object[].class, resultSet.getObject(columnIndex));
case ARRAY_OF_BYTE_ARRAYS:
return convertJdbcObjectToJavaType(byte[][].class, resultSet.getObject(columnIndex));
case OBJECT:
return getObjectValue(resultSet, columnIndex, metaData);
default:
throw new IllegalStateException("unhandled pdx field type: " + fieldType);
}
}
private Object getObjectValue(ResultSet resultSet, int columnIndex, ResultSetMetaData metaData)
throws SQLException {
Object v;
if (isBlobColumn(columnIndex, metaData)) {
v = getBlobData(resultSet, columnIndex);
} else {
v = resultSet.getObject(columnIndex);
if (v instanceof java.util.Date) {
if (v instanceof java.sql.Date) {
java.sql.Date sqlDate = (java.sql.Date) v;
v = new java.util.Date(sqlDate.getTime());
} else if (v instanceof java.sql.Time) {
java.sql.Time sqlTime = (java.sql.Time) v;
v = new java.util.Date(sqlTime.getTime());
} else if (v instanceof java.sql.Timestamp) {
java.sql.Timestamp sqlTimestamp = (java.sql.Timestamp) v;
v = new java.util.Date(sqlTimestamp.getTime());
}
}
}
return v;
}
private Object getByteArrayValue(ResultSet resultSet, int columnIndex, ResultSetMetaData metaData)
throws SQLException {
byte[] byteData;
if (isBlobColumn(columnIndex, metaData)) {
byteData = getBlobData(resultSet, columnIndex);
} else {
byteData = resultSet.getBytes(columnIndex);
}
return byteData;
}
private Object getCharValue(ResultSet resultSet, int columnIndex) throws SQLException {
char charValue = 0;
String columnValue = resultSet.getString(columnIndex);
if (columnValue != null && columnValue.length() > 0) {
charValue = columnValue.charAt(0);
}
return charValue;
}
private java.util.Date getDateValue(ResultSet resultSet, int columnIndex,
ResultSetMetaData metaData)
throws SQLException {
java.util.Date sqlDate;
int columnType = metaData.getColumnType(columnIndex);
switch (columnType) {
case Types.DATE:
sqlDate = resultSet.getDate(columnIndex);
break;
case Types.TIME:
case Types.TIME_WITH_TIMEZONE:
sqlDate = resultSet.getTime(columnIndex);
break;
default:
sqlDate = resultSet.getTimestamp(columnIndex);
break;
}
java.util.Date pdxDate = null;
if (sqlDate != null) {
pdxDate = new java.util.Date(sqlDate.getTime());
}
return pdxDate;
}
private boolean isBlobColumn(int columnIndex, ResultSetMetaData metaData) throws SQLException {
int columnType = metaData.getColumnType(columnIndex);
return Types.BLOB == columnType;
}
/**
* If the given column contains a Blob returns its data as a byte array;
* otherwise return null.
*
* @throws JdbcConnectorException if blob is too big to fit in a byte array
*/
private byte[] getBlobData(ResultSet resultSet, int columnIndex) throws SQLException {
Blob blob = resultSet.getBlob(columnIndex);
if (blob == null) {
return null;
}
try {
long blobLength = blob.length();
if (blobLength > Integer.MAX_VALUE) {
throw new JdbcConnectorException(
"Blob of length " + blobLength + " is too big to be converted to a byte array.");
}
return blob.getBytes(1, (int) blobLength);
} finally {
blob.free();
}
}
private <T> T convertJdbcObjectToJavaType(Class<T> javaType, Object jdbcObject) {
try {
return javaType.cast(jdbcObject);
} catch (ClassCastException classCastException) {
throw JdbcConnectorException.createException("Could not convert "
+ jdbcObject.getClass().getTypeName() + " to " + javaType.getTypeName(),
classCastException);
}
}
}