001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019 020package org.nuxeo.ecm.core.storage.sql.jdbc.db; 021 022import java.io.Serializable; 023import java.sql.JDBCType; 024import java.sql.PreparedStatement; 025import java.sql.ResultSet; 026import java.sql.SQLException; 027import java.sql.Types; 028import org.nuxeo.ecm.core.storage.sql.ColumnType; 029import org.nuxeo.ecm.core.storage.sql.jdbc.dialect.Dialect; 030import org.nuxeo.ecm.core.storage.sql.jdbc.dialect.Dialect.JDBCInfo; 031 032/** 033 * An SQL {@code column}. 034 * 035 * @author Florent Guillaume 036 */ 037public class Column implements Serializable { 038 039 private static final long serialVersionUID = 1L; 040 041 protected final Table table; 042 043 protected final Dialect dialect; 044 045 protected final String physicalName; 046 047 private final String quotedName; 048 049 private final String freeVariableSetter; 050 051 /** The abstract type. */ 052 private final ColumnType type; 053 054 /** 055 * The JDBC {@link java.sql.Types} type. Used for: - comparison with database introspected type - switch() to get 056 * from result set or set to prepared statement - setNull to prepared statement 057 */ 058 private int jdbcType; 059 060 /** The JDBC type string. */ 061 private final String jdbcTypeString; 062 063 /* 064 * {@see java.sql.Array.getBaseType() value is 0 if this is not an array column 065 */ 066 private int jdbcBaseType; 067 068 /* 069 * {@see java.sql.Array.getBaseTypeName() value is null if this is not an array column 070 */ 071 private final String jdbcBaseTypeString; 072 073 private final String key; 074 075 private boolean identity; 076 077 private boolean primary; 078 079 private boolean nullable = true; 080 081 private String defaultValue; 082 083 /** For foreign key reference. */ 084 private Table foreignTable; 085 086 private String foreignKey; 087 088 /** 089 * Creates a new column with the given name and type. 090 * 091 * @param table the column's table 092 * @param physicalName the column physical name 093 * @param type the column's type 094 * @param key the associated field name 095 */ 096 public Column(Table table, String physicalName, ColumnType type, String key) { 097 this.table = table; 098 dialect = table.getDialect(); 099 this.physicalName = physicalName; 100 this.type = type; 101 JDBCInfo jdbcInfo = dialect.getJDBCTypeAndString(type); 102 jdbcType = jdbcInfo.jdbcType; 103 jdbcTypeString = jdbcInfo.string; 104 jdbcBaseType = jdbcInfo.jdbcBaseType; 105 jdbcBaseTypeString = jdbcInfo.jdbcBaseTypeString; 106 this.key = key; 107 quotedName = dialect.openQuote() + physicalName + dialect.closeQuote(); 108 freeVariableSetter = dialect.getFreeVariableSetterForType(type); 109 } 110 111 /** 112 * Creates a column from an existing column and an aliased table. 113 */ 114 public Column(Column column, Table table) { 115 this(table, column.physicalName, column.type, column.key); 116 } 117 118 public Table getTable() { 119 return table; 120 } 121 122 public String getPhysicalName() { 123 return physicalName; 124 } 125 126 public String getQuotedName() { 127 return quotedName; 128 } 129 130 public String getFullQuotedName() { 131 return table.getQuotedName() + '.' + quotedName; 132 } 133 134 public int getJdbcType() { 135 return jdbcType; 136 } 137 138 public int getJdbcBaseType() { 139 return jdbcBaseType; 140 } 141 142 public ColumnType getType() { 143 return type; 144 } 145 146 public ColumnType getBaseType() { 147 ColumnType baseType; 148 if (type == ColumnType.ARRAY_BLOBID) { 149 baseType = ColumnType.BLOBID; 150 } else if (type == ColumnType.ARRAY_BOOLEAN) { 151 baseType = ColumnType.BOOLEAN; 152 } else if (type == ColumnType.ARRAY_CLOB) { 153 baseType = ColumnType.CLOB; 154 } else if (type == ColumnType.ARRAY_DOUBLE) { 155 baseType = ColumnType.DOUBLE; 156 } else if (type == ColumnType.ARRAY_INTEGER) { 157 baseType = ColumnType.INTEGER; 158 } else if (type == ColumnType.ARRAY_LONG) { 159 baseType = ColumnType.LONG; 160 } else if (type == ColumnType.ARRAY_STRING) { 161 baseType = ColumnType.STRING; 162 } else if (type == ColumnType.ARRAY_TIMESTAMP) { 163 baseType = ColumnType.TIMESTAMP; 164 } else { 165 baseType = type; 166 } 167 return baseType; 168 } 169 170 public String getFreeVariableSetter() { 171 return freeVariableSetter; 172 } 173 174 public boolean isArray() { 175 return type.isArray(); 176 } 177 178 public boolean isOpaque() { 179 return type == ColumnType.FTINDEXED || type == ColumnType.FTSTORED; 180 } 181 182 public String checkJdbcType(int actual, String actualName, int actualSize) { 183 int expected = jdbcType; 184 if (actual == expected) { 185 return null; 186 } 187 if (dialect.isAllowedConversion(expected, actual, actualName, actualSize)) { 188 return null; 189 } 190 return String.format("SQL type mismatch for %s: expected %s, database has %s / %s(%s)", getFullQuotedName(), 191 getJDBCTypeName(expected), getJDBCTypeName(actual), actualName, actualSize); 192 } 193 194 protected static String getJDBCTypeName(int expected) { 195 try { 196 return JDBCType.valueOf(expected).getName(); 197 } catch (IllegalArgumentException e) { 198 return String.valueOf(expected); 199 } 200 } 201 202 public String getKey() { 203 return key; 204 } 205 206 public void setIdentity(boolean identity) { 207 this.identity = identity; 208 } 209 210 public boolean isIdentity() { 211 return identity; 212 } 213 214 public void setPrimary(boolean primary) { 215 this.primary = primary; 216 } 217 218 public boolean isPrimary() { 219 return primary; 220 } 221 222 public void setNullable(boolean nullable) { 223 this.nullable = nullable; 224 } 225 226 public boolean isNullable() { 227 return nullable; 228 } 229 230 public String getDefaultValue() { 231 return defaultValue; 232 } 233 234 public void setDefaultValue(String defaultValue) { 235 this.defaultValue = defaultValue; 236 } 237 238 public void setReferences(Table foreignTable, String foreignKey) { 239 this.foreignTable = foreignTable; 240 this.foreignKey = foreignKey; 241 } 242 243 public Table getForeignTable() { 244 return foreignTable; 245 } 246 247 public String getForeignKey() { 248 return foreignKey; 249 } 250 251 public String getSqlTypeString() { 252 return jdbcTypeString; 253 } 254 255 public String getSqlBaseTypeString() { 256 return jdbcBaseTypeString; 257 } 258 259 public void setToPreparedStatement(PreparedStatement ps, int index, Serializable value) throws SQLException { 260 if (value == null) { 261 ps.setNull(index, jdbcType); 262 return; 263 } 264 if ((jdbcType == Types.ARRAY) && !(value instanceof Object[])) { 265 throw new SQLException("Expected an array value instead of: " + value); 266 } 267 dialect.setToPreparedStatement(ps, index, value, this); 268 } 269 270 public Serializable getFromResultSet(ResultSet rs, int index) throws SQLException { 271 Serializable result = dialect.getFromResultSet(rs, index, this); 272 if (rs.wasNull()) { 273 result = null; 274 } 275 return result; 276 } 277 278 @Override 279 public String toString() { 280 return getClass().getSimpleName() + '(' + physicalName + ')'; 281 } 282 283}