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