/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.seasar.extension.jdbc.dialect;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import javax.persistence.GenerationType;
import javax.persistence.TemporalType;
import org.seasar.extension.jdbc.PropertyMeta;
import org.seasar.extension.jdbc.SelectForUpdateType;
import org.seasar.extension.jdbc.ValueType;
import org.seasar.extension.jdbc.types.BytesType;
import org.seasar.extension.jdbc.types.SerializableType;
import org.seasar.extension.jdbc.types.ValueTypes;
import org.seasar.extension.jdbc.types.BytesType.Trait;
import org.seasar.framework.util.tiger.Pair;
/**
* PostgreSQL用の方言をあつかうクラスです。
*
* @author higa
*
*/
public class PostgreDialect extends StandardDialect {
/**
* 一意制約違反を表すSQLステート
*/
protected static final String uniqueConstraintViolationCode = "23505";
/**
* BLOB用の値タイプです。
*/
protected final static ValueType BLOB_TYPE = new BytesType(
new PostgreTrait());
/**
* オブジェクトをシリアライズしたBLOB用の値タイプです。
*/
public final static ValueType SERIALIZABLE_BLOB_TYPE = new SerializableType(
new PostgreTrait());
@Override
public String getName() {
return "postgre";
}
@Override
public boolean supportsLimit() {
return true;
}
@Override
public boolean needsParameterForResultSet() {
return true;
}
@Override
public String convertLimitSql(String sql, int offset, int limit) {
StringBuilder buf = new StringBuilder(sql.length() + 20);
buf.append(sql);
if (limit > 0) {
buf.append(" limit ");
buf.append(limit);
}
if (offset > 0) {
buf.append(" offset ");
buf.append(offset);
}
return buf.toString();
}
@Override
public ValueType getValueType(PropertyMeta propertyMeta) {
final Class<?> clazz = propertyMeta.getPropertyClass();
if (propertyMeta.isLob()) {
if (clazz == String.class) {
return ValueTypes.STRING;
} else if (clazz == byte[].class) {
return BLOB_TYPE;
} else if (Serializable.class.isAssignableFrom(clazz)) {
return SERIALIZABLE_BLOB_TYPE;
}
}
final ValueType valueType = getValueTypeInternal(clazz);
if (valueType != null) {
return valueType;
}
return super.getValueType(propertyMeta);
}
@Override
public ValueType getValueType(Class<?> clazz, boolean lob,
TemporalType temporalType) {
if (lob) {
if (clazz == String.class) {
return ValueTypes.STRING;
} else if (clazz == byte[].class) {
return BLOB_TYPE;
} else if (Serializable.class.isAssignableFrom(clazz)) {
return SERIALIZABLE_BLOB_TYPE;
}
}
return super.getValueType(clazz, lob, temporalType);
}
@Override
protected ValueType getValueTypeInternal(Class<?> clazz) {
if (List.class.isAssignableFrom(clazz)) {
return ValueTypes.POSTGRE_RESULT_SET;
}
return null;
}
@Override
public GenerationType getDefaultGenerationType() {
return GenerationType.IDENTITY;
}
@Override
public boolean supportsIdentity() {
return true;
}
@Override
public boolean supportsGetGeneratedKeys() {
return false;
}
@Override
public String getIdentitySelectString(final String tableName,
final String columnName) {
return new String(new StringBuilder(64).append("select currval('")
.append(tableName).append('_').append(columnName).append(
"_seq')"));
}
@Override
public boolean supportsSequence() {
return true;
}
@Override
public String getSequenceNextValString(final String sequenceName,
final int allocationSize) {
return "select nextval('" + sequenceName + "')";
}
@Override
public boolean supportsForUpdate(final SelectForUpdateType type,
boolean withTarget) {
return type == SelectForUpdateType.NORMAL;
}
@Override
public String getForUpdateString(final SelectForUpdateType type,
final int waitSeconds, final Pair<String, String>... aliases) {
final StringBuilder buf = new StringBuilder(100).append(" for update");
if (aliases.length > 0) {
buf.append(" of ");
for (final Pair<String, String> alias : aliases) {
buf.append(alias.getFirst()).append(", ");
}
buf.setLength(buf.length() - 2);
}
return new String(buf);
}
@Override
public boolean supportsOuterJoinForUpdate() {
return false;
}
@Override
public boolean isUniqueConstraintViolation(Throwable t) {
final String state = getSQLState(t);
return uniqueConstraintViolationCode.equals(state);
}
/**
* {@link Blob}を扱うトレイトです。
*
* @author koichik
*/
public static class PostgreTrait implements Trait {
public int getSqlType() {
return Types.BLOB;
}
public void set(final PreparedStatement ps, final int parameterIndex,
final byte[] bytes) throws SQLException {
ps.setBlob(parameterIndex, new BlobImpl(bytes));
}
public void set(final CallableStatement cs, final String parameterName,
final byte[] bytes) throws SQLException {
cs.setBytes(parameterName, bytes);
}
public byte[] get(final ResultSet rs, final int columnIndex)
throws SQLException {
return BytesType.toBytes(rs.getBlob(columnIndex));
}
public byte[] get(final ResultSet rs, final String columnName)
throws SQLException {
return BytesType.toBytes(rs.getBlob(columnName));
}
public byte[] get(final CallableStatement cs, final int columnIndex)
throws SQLException {
return BytesType.toBytes(cs.getBlob(columnIndex));
}
public byte[] get(final CallableStatement cs, final String columnName)
throws SQLException {
return BytesType.toBytes(cs.getBlob(columnName));
}
}
/**
* {@link Blob}の簡易実装クラスです。
*
* @author koichik
*/
public static class BlobImpl implements Blob {
/** バイト列 */
protected byte[] bytes;
/**
* インスタンスを構築します。
*
* @param bytes
* バイト列
*/
public BlobImpl(final byte[] bytes) {
this.bytes = bytes;
}
public InputStream getBinaryStream() throws SQLException {
return new ByteArrayInputStream(bytes);
}
public byte[] getBytes(final long pos, final int length)
throws SQLException {
if (length == bytes.length) {
return bytes;
}
final byte[] result = new byte[length];
System.arraycopy(bytes, 0, result, 0, length);
return result;
}
public long length() throws SQLException {
return bytes.length;
}
public long position(final Blob pattern, final long start)
throws SQLException {
throw new UnsupportedOperationException("position");
}
public long position(final byte[] pattern, final long start)
throws SQLException {
throw new UnsupportedOperationException("position");
}
public OutputStream setBinaryStream(final long pos) throws SQLException {
throw new UnsupportedOperationException("setBinaryStream");
}
public int setBytes(final long pos, final byte[] bytes,
final int offset, final int len) throws SQLException {
throw new UnsupportedOperationException("setBytes");
}
public int setBytes(final long pos, final byte[] bytes)
throws SQLException {
throw new UnsupportedOperationException("setBytes");
}
public void truncate(final long len) throws SQLException {
throw new UnsupportedOperationException("truncate");
}
}
}