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