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}