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