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 */
019package org.nuxeo.ecm.core.storage.sql.jdbc.dialect;
020
021import java.io.Serializable;
022import java.sql.Connection;
023import java.sql.DatabaseMetaData;
024import java.sql.PreparedStatement;
025import java.sql.ResultSet;
026import java.sql.SQLException;
027import java.sql.Types;
028import java.util.HashMap;
029import java.util.List;
030import java.util.Map;
031
032import org.nuxeo.ecm.core.storage.sql.ColumnType;
033import org.nuxeo.ecm.core.storage.sql.Model;
034import org.nuxeo.ecm.core.storage.sql.RepositoryDescriptor;
035import org.nuxeo.ecm.core.storage.sql.jdbc.JDBCLogger;
036import org.nuxeo.ecm.core.storage.sql.jdbc.db.Column;
037import org.nuxeo.ecm.core.storage.sql.jdbc.db.Database;
038import org.nuxeo.ecm.core.storage.sql.jdbc.db.Table;
039
040/**
041 * HSQLDB-specific dialect.
042 * <p>
043 * Not used for VCS, only for directories, so many features don't matter.
044 */
045public class DialectHSQLDB extends Dialect {
046
047    public DialectHSQLDB(DatabaseMetaData metadata, RepositoryDescriptor repositoryDescriptor) {
048        super(metadata, repositoryDescriptor);
049    }
050
051    @Override
052    public boolean supportsIfExistsAfterTableName() {
053        return true;
054    }
055
056    @Override
057    public JDBCInfo getJDBCTypeAndString(ColumnType type) {
058        switch (type.spec) {
059        case STRING:
060            if (type.isUnconstrained()) {
061                return jdbcInfo("VARCHAR", Types.VARCHAR);
062            } else if (type.isClob()) {
063                return jdbcInfo("CLOB", Types.CLOB);
064            } else {
065                return jdbcInfo("VARCHAR(%d)", type.length, Types.VARCHAR);
066            }
067        case BOOLEAN:
068            return jdbcInfo("BOOLEAN", Types.BOOLEAN);
069        case LONG:
070            return jdbcInfo("BIGINT", Types.BIGINT);
071        case DOUBLE:
072            return jdbcInfo("DOUBLE", Types.DOUBLE);
073        case TIMESTAMP:
074            return jdbcInfo("TIMESTAMP", Types.TIMESTAMP);
075        case BLOBID:
076            return jdbcInfo("VARCHAR(250)", Types.VARCHAR);
077            // -----
078        case NODEID:
079        case NODEIDFK:
080        case NODEIDFKNP:
081        case NODEIDFKMUL:
082        case NODEIDFKNULL:
083        case NODEIDPK:
084        case NODEVAL:
085            return jdbcInfo("VARCHAR(36)", Types.VARCHAR);
086        case SYSNAME:
087        case SYSNAMEARRAY:
088            return jdbcInfo("VARCHAR(250)", Types.VARCHAR);
089        case TINYINT:
090            return jdbcInfo("TINYINT", Types.TINYINT);
091        case INTEGER:
092            return jdbcInfo("INTEGER", Types.INTEGER);
093        case AUTOINC:
094            return jdbcInfo("INTEGER IDENTITY", Types.INTEGER);
095        case FTINDEXED:
096            throw new AssertionError(type);
097        case FTSTORED:
098            return jdbcInfo("CLOB", Types.CLOB);
099        case CLUSTERNODE:
100            return jdbcInfo("INTEGER", Types.INTEGER);
101        case CLUSTERFRAGS:
102            return jdbcInfo("VARCHAR", Types.VARCHAR);
103        }
104        throw new AssertionError(type);
105    }
106
107    @Override
108    public boolean isAllowedConversion(int expected, int actual, String actualName, int actualSize) {
109        // CLOB vs VARCHAR compatibility
110        if (expected == Types.VARCHAR && actual == Types.CLOB) {
111            return true;
112        }
113        if (expected == Types.CLOB && actual == Types.VARCHAR) {
114            return true;
115        }
116        // INTEGER vs BIGINT compatibility
117        if (expected == Types.BIGINT && actual == Types.INTEGER) {
118            return true;
119        }
120        if (expected == Types.INTEGER && actual == Types.BIGINT) {
121            return true;
122        }
123        return false;
124    }
125
126    @Override
127    public void setToPreparedStatement(PreparedStatement ps, int index, Serializable value, Column column)
128            throws SQLException {
129        switch (column.getJdbcType()) {
130        case Types.VARCHAR:
131        case Types.CLOB:
132            setToPreparedStatementString(ps, index, value, column);
133            return;
134        case Types.BOOLEAN:
135            ps.setBoolean(index, ((Boolean) value).booleanValue());
136            return;
137        case Types.TINYINT:
138        case Types.INTEGER:
139        case Types.BIGINT:
140            ps.setLong(index, ((Number) value).longValue());
141            return;
142        case Types.DOUBLE:
143            ps.setDouble(index, ((Double) value).doubleValue());
144            return;
145        case Types.TIMESTAMP:
146            setToPreparedStatementTimestamp(ps, index, value, column);
147            return;
148        default:
149            throw new SQLException("Unhandled JDBC type: " + column.getJdbcType());
150        }
151    }
152
153    @Override
154    @SuppressWarnings("boxing")
155    public Serializable getFromResultSet(ResultSet rs, int index, Column column) throws SQLException {
156        switch (column.getJdbcType()) {
157        case Types.VARCHAR:
158        case Types.CLOB:
159            return getFromResultSetString(rs, index, column);
160        case Types.BOOLEAN:
161            return rs.getBoolean(index);
162        case Types.TINYINT:
163        case Types.INTEGER:
164        case Types.BIGINT:
165            return rs.getLong(index);
166        case Types.DOUBLE:
167            return rs.getDouble(index);
168        case Types.TIMESTAMP:
169            return getFromResultSetTimestamp(rs, index, column);
170        }
171        throw new SQLException("Unhandled JDBC type: " + column.getJdbcType());
172    }
173
174    @Override
175    public String getCreateFulltextIndexSql(String indexName, String quotedIndexName, Table table,
176            List<Column> columns, Model model) {
177        throw new UnsupportedOperationException();
178    }
179
180    @Override
181    public String getDialectFulltextQuery(String query) {
182        throw new UnsupportedOperationException();
183    }
184
185    @Override
186    public FulltextMatchInfo getFulltextScoredMatchInfo(String fulltextQuery, String indexName, int nthMatch,
187            Column mainColumn, Model model, Database database) {
188        throw new UnsupportedOperationException();
189    }
190
191    @Override
192    public boolean getMaterializeFulltextSyntheticColumn() {
193        return false;
194    }
195
196    @Override
197    public int getFulltextIndexedColumns() {
198        return 2;
199    }
200
201    @Override
202    public boolean supportsUpdateFrom() {
203        return false;
204    }
205
206    @Override
207    public boolean doesUpdateFromRepeatSelf() {
208        return true;
209    }
210
211    @Override
212    public boolean supportsReadAcl() {
213        return false;
214    }
215
216    @Override
217    public String getUpdateReadAclsSql() {
218        throw new UnsupportedOperationException();
219    }
220
221    @Override
222    public String getRebuildReadAclsSql() {
223        throw new UnsupportedOperationException();
224    }
225
226    @Override
227    public String getClobCast(boolean inOrderBy) {
228        if (!inOrderBy) {
229            return "CAST(%s AS VARCHAR)";
230        }
231        return null;
232    }
233
234    @Override
235    public String getSecurityCheckSql(String idColumnName) {
236        throw new UnsupportedOperationException();
237    }
238
239    @Override
240    public String getInTreeSql(String idColumnName, String id) {
241        throw new UnsupportedOperationException();
242    }
243
244    @Override
245    public String getUpsertSql(List<Column> columns, List<Serializable> values, List<Column> outColumns,
246            List<Serializable> outValues) {
247        throw new UnsupportedOperationException();
248    }
249
250    @Override
251    public boolean supportsArrays() {
252        return false;
253    }
254
255    @Override
256    public String getSQLStatementsFilename() {
257        return "nuxeovcs/hsqldb.sql.txt"; // TODO VCS
258    }
259
260    @Override
261    public String getTestSQLStatementsFilename() {
262        return "nuxeovcs/hsqldb.test.sql.txt"; // TODO VCS
263    }
264
265    @Override
266    public Map<String, Serializable> getSQLStatementsProperties(Model model, Database database) {
267        return new HashMap<String, Serializable>();
268    }
269
270    @Override
271    public boolean isClusteringSupported() {
272        return false;
273    }
274
275    @Override
276    public String getClusterInsertInvalidations() {
277        throw new UnsupportedOperationException();
278    }
279
280    @Override
281    public String getClusterGetInvalidations() {
282        throw new UnsupportedOperationException();
283    }
284
285    @Override
286    public boolean supportsPaging() {
287        return true;
288    }
289
290    @SuppressWarnings("boxing")
291    @Override
292    public String addPagingClause(String sql, long limit, long offset) {
293        return sql + String.format(" LIMIT %d OFFSET %d", limit, offset);
294    }
295
296    @Override
297    public List<String> checkStoredProcedure(String procName, String procCreate, String ddlMode, Connection connection,
298            JDBCLogger logger, Map<String, Serializable> properties) throws SQLException {
299        throw new UnsupportedOperationException();
300    }
301
302}