/** * Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com) * * Licensed 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. * * For more information: http://www.orientechnologies.com */ package com.orientechnologies.orient.jdbc; import com.orientechnologies.orient.core.db.record.OIdentifiable; import com.orientechnologies.orient.core.db.record.ORecordLazyList; import com.orientechnologies.orient.core.metadata.schema.OClass; import com.orientechnologies.orient.core.metadata.schema.OProperty; import com.orientechnologies.orient.core.metadata.schema.OType; import com.orientechnologies.orient.core.record.impl.OBlob; import com.orientechnologies.orient.core.record.impl.ODocument; import java.math.BigDecimal; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Types; import java.util.*; /** * @author Roberto Franchini (CELI Srl - franchini@celi.it) * @author Salvatore Piccione (TXT e-solutions SpA - salvo.picci@gmail.com) */ @SuppressWarnings("boxing") public class OrientJdbcResultSetMetaData implements ResultSetMetaData { private final static Map<OType, Integer> typesSqlTypes = new HashMap<OType, Integer>(); static { typesSqlTypes.put(OType.STRING, Types.VARCHAR); typesSqlTypes.put(OType.INTEGER, Types.INTEGER); typesSqlTypes.put(OType.FLOAT, Types.FLOAT); typesSqlTypes.put(OType.SHORT, Types.SMALLINT); typesSqlTypes.put(OType.BOOLEAN, Types.BOOLEAN); typesSqlTypes.put(OType.LONG, Types.BIGINT); typesSqlTypes.put(OType.DOUBLE, Types.DOUBLE); typesSqlTypes.put(OType.DECIMAL, Types.DECIMAL); typesSqlTypes.put(OType.DATE, Types.DATE); typesSqlTypes.put(OType.DATETIME, Types.TIMESTAMP); typesSqlTypes.put(OType.BYTE, Types.TINYINT); typesSqlTypes.put(OType.SHORT, Types.SMALLINT); // NOT SURE ABOUT THE FOLLOWING MAPPINGS typesSqlTypes.put(OType.BINARY, Types.BINARY); typesSqlTypes.put(OType.EMBEDDED, Types.JAVA_OBJECT); typesSqlTypes.put(OType.EMBEDDEDLIST, Types.ARRAY); typesSqlTypes.put(OType.EMBEDDEDMAP, Types.JAVA_OBJECT); typesSqlTypes.put(OType.EMBEDDEDSET, Types.ARRAY); typesSqlTypes.put(OType.LINK, Types.JAVA_OBJECT); typesSqlTypes.put(OType.LINKLIST, Types.ARRAY); typesSqlTypes.put(OType.LINKMAP, Types.JAVA_OBJECT); typesSqlTypes.put(OType.LINKSET, Types.ARRAY); typesSqlTypes.put(OType.TRANSIENT, Types.NULL); } private final String[] fieldNames; private final OrientJdbcResultSet resultSet; public OrientJdbcResultSetMetaData(OrientJdbcResultSet orientJdbcResultSet, List<String> fieldNames) { resultSet = orientJdbcResultSet; this.fieldNames = fieldNames.toArray(new String[] {}); } public static Integer getSqlType(final OType iType) { return typesSqlTypes.get(iType); } public int getColumnCount() throws SQLException { final ODocument currentRecord = getCurrentRecord(); return currentRecord.fields(); } public String getCatalogName(final int column) throws SQLException { // return an empty String according to the method's documentation return ""; } public String getColumnClassName(final int column) throws SQLException { Object value = this.resultSet.getObject(column); if (value == null) return null; return value.getClass().getCanonicalName(); } public int getColumnDisplaySize(final int column) throws SQLException { return 0; } public String getColumnLabel(final int column) throws SQLException { return this.getColumnName(column); } public String getColumnName(final int column) throws SQLException { String fieldName = fieldNames[column - 1]; return fieldName; } public int getColumnType(final int column) throws SQLException { final ODocument currentRecord = getCurrentRecord(); if (column > fieldNames.length) return Types.NULL; String fieldName = fieldNames[column - 1]; // The OClass is not available so attempting to retrieve the OType from // the schema class // results in a NullPointerException // OClass oclass = currentRecord.getSchemaClass(); OType otype = currentRecord.fieldType(fieldName); if (otype == null) { Object value = currentRecord.field(fieldName); if (value == null) { return Types.NULL; } else if (value instanceof OBlob) { // Check if the type is a binary record or a collection of binary // records return Types.BINARY; } else if (value instanceof ORecordLazyList) { ORecordLazyList list = (ORecordLazyList) value; // check if all the list items are instances of ORecordBytes ListIterator<OIdentifiable> iterator = list.listIterator(); OIdentifiable listElement; boolean stop = false; while (iterator.hasNext() && !stop) { listElement = iterator.next(); if (!(listElement instanceof OBlob)) stop = true; } if (!stop) { return Types.BLOB; } } return this.getSQLTypeFromJavaClass(value); } else { if (otype == OType.EMBEDDED || otype == OType.LINK) { Object value = currentRecord.field(fieldName); if (value == null) { return Types.NULL; } // 1. Check if the type is another record or a collection of records if (value instanceof OBlob) { return Types.BINARY; } else { // the default type return typesSqlTypes.get(otype); } } else { if (otype == OType.EMBEDDEDLIST || otype == OType.LINKLIST) { Object value = currentRecord.field(fieldName); if (value == null) { return Types.NULL; } if (value instanceof ORecordLazyList) { ORecordLazyList list = (ORecordLazyList) value; // check if all the list items are instances of ORecordBytes ListIterator<OIdentifiable> iterator = list.listIterator(); OIdentifiable listElement; boolean stop = false; while (iterator.hasNext() && !stop) { listElement = iterator.next(); if (!(listElement instanceof OBlob)) stop = true; } if (stop) { return typesSqlTypes.get(otype); } else { return Types.BLOB; } } else { return typesSqlTypes.get(otype); // return Types.JAVA_OBJECT; } } else { return typesSqlTypes.get(otype); } } } } protected ODocument getCurrentRecord() throws SQLException { final ODocument currentRecord = this.resultSet.unwrap(ODocument.class); if (currentRecord == null) throw new SQLException("No current record"); return currentRecord; } private int getSQLTypeFromJavaClass(final Object value) { if (value instanceof Boolean) return typesSqlTypes.get(OType.BOOLEAN); else if (value instanceof Byte) return typesSqlTypes.get(OType.BYTE); else if (value instanceof Date) return typesSqlTypes.get(OType.DATETIME); else if (value instanceof Double) return typesSqlTypes.get(OType.DOUBLE); else if (value instanceof BigDecimal) return typesSqlTypes.get(OType.DECIMAL); else if (value instanceof Float) return typesSqlTypes.get(OType.FLOAT); else if (value instanceof Integer) return typesSqlTypes.get(OType.INTEGER); else if (value instanceof Long) return typesSqlTypes.get(OType.LONG); else if (value instanceof Short) return typesSqlTypes.get(OType.SHORT); else if (value instanceof String) return typesSqlTypes.get(OType.STRING); else if (value instanceof List) return typesSqlTypes.get(OType.EMBEDDEDLIST); else return Types.JAVA_OBJECT; } @Override public String getColumnTypeName(final int column) throws SQLException { final ODocument currentRecord = getCurrentRecord(); OType columnType = currentRecord.fieldType(fieldNames[column - 1]); if (columnType == null) return null; return columnType.toString(); } public int getPrecision(final int column) throws SQLException { return 0; } public int getScale(final int column) throws SQLException { return 0; } public String getSchemaName(final int column) throws SQLException { final ODocument currentRecord = getCurrentRecord(); if (currentRecord == null) return ""; else return currentRecord.getDatabase().getName(); } public String getTableName(final int column) throws SQLException { final OProperty p = getProperty(column); return p != null ? p.getOwnerClass().getName() : null; } public boolean isAutoIncrement(final int column) throws SQLException { return false; } public boolean isCaseSensitive(final int column) throws SQLException { final OProperty p = getProperty(column); return p != null ? p.getCollate().getName().equalsIgnoreCase("ci") : false; } public boolean isCurrency(final int column) throws SQLException { return false; } public boolean isDefinitelyWritable(final int column) throws SQLException { return false; } public int isNullable(final int column) throws SQLException { return columnNullableUnknown; } public boolean isReadOnly(final int column) throws SQLException { final OProperty p = getProperty(column); return p != null ? p.isReadonly() : false; } public boolean isSearchable(int column) throws SQLException { return true; } public boolean isSigned(final int column) throws SQLException { final ODocument currentRecord = getCurrentRecord(); return this.isANumericColumn(currentRecord.fieldType(fieldNames[column - 1])); } public boolean isWritable(final int column) throws SQLException { return !isReadOnly(column); } public boolean isWrapperFor(final Class<?> iface) throws SQLException { return false; } public <T> T unwrap(final Class<T> iface) throws SQLException { return null; } private boolean isANumericColumn(final OType type) { return type == OType.BYTE || type == OType.DOUBLE || type == OType.FLOAT || type == OType.INTEGER || type == OType.LONG || type == OType.SHORT; } protected OProperty getProperty(final int column) throws SQLException { final ODocument currentRecord = getCurrentRecord(); final OClass schemaClass = currentRecord.getSchemaClass(); if (schemaClass != null) { final String fieldName = fieldNames[column - 1]; return schemaClass.getProperty(fieldName); } return null; } }