/*
* 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.gen.internal.dialect;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import javax.persistence.GenerationType;
import org.seasar.extension.jdbc.PropertyMeta;
import org.seasar.extension.jdbc.ValueType;
import org.seasar.extension.jdbc.dialect.PostgreDialect.BlobImpl;
import org.seasar.extension.jdbc.gen.internal.sqltype.AbstractSqlType;
import org.seasar.extension.jdbc.gen.internal.sqltype.BigIntType;
import org.seasar.extension.jdbc.gen.internal.sqltype.BinaryType;
import org.seasar.extension.jdbc.gen.internal.sqltype.BooleanType;
import org.seasar.extension.jdbc.gen.internal.sqltype.DecimalType;
import org.seasar.extension.jdbc.gen.internal.sqltype.DoubleType;
import org.seasar.extension.jdbc.gen.internal.sqltype.FloatType;
import org.seasar.extension.jdbc.gen.internal.sqltype.IntegerType;
import org.seasar.extension.jdbc.gen.internal.sqltype.VarcharType;
import org.seasar.extension.jdbc.gen.provider.ValueTypeProvider;
import org.seasar.extension.jdbc.gen.sqltype.SqlType;
import org.seasar.framework.util.Base64Util;
/**
* PostgreSQLの方言を扱うクラスです。
*
* @author taedium
*/
public class PostgreGenDialect extends StandardGenDialect {
/** テーブルが見つからないことを示すSQLステート */
protected static String TABLE_NOT_FOUND_SQL_STATE = "42P01";
/** カラムが見つからないことを示すSQLステート */
protected static String COLUMN_NOT_FOUND_SQL_STATE = "42703";
/** シーケンスが見つからないことを示すSQLステート */
protected static String SEQUENCE_NOT_FOUND_SQL_STATE = "42P01";
/**
* インスタンスを構築します。
*/
public PostgreGenDialect() {
sqlTypeMap.put(Types.BIGINT, new BigIntType() {
@Override
public String getDataType(int length, int precision, int scale,
boolean identity) {
return identity ? "bigserial" : "bigint";
}
});
sqlTypeMap.put(Types.BINARY, new BinaryType("bytea"));
sqlTypeMap.put(Types.BOOLEAN, new BooleanType("bool"));
sqlTypeMap.put(Types.BLOB, new PostgreBlobType("oid"));
sqlTypeMap.put(Types.CLOB, new VarcharType("text"));
sqlTypeMap.put(Types.DECIMAL, new DecimalType("decimal($p,$s)"));
sqlTypeMap.put(Types.DOUBLE, new DoubleType("float8"));
sqlTypeMap.put(Types.FLOAT, new FloatType("float4"));
sqlTypeMap.put(Types.INTEGER, new IntegerType() {
@Override
public String getDataType(int length, int precision, int scale,
boolean identity) {
return identity ? "serial" : "integer";
}
});
columnTypeMap.put("bigint", PostgreColumnType.BIGINT);
columnTypeMap.put("bigserial", PostgreColumnType.BIGSERIAL);
columnTypeMap.put("bit", PostgreColumnType.BIT);
columnTypeMap.put("bool", PostgreColumnType.BOOL);
columnTypeMap.put("bpchar", PostgreColumnType.BPCHAR);
columnTypeMap.put("bytea", PostgreColumnType.BYTEA);
columnTypeMap.put("float4", PostgreColumnType.FLOAT4);
columnTypeMap.put("float8", PostgreColumnType.FLOAT8);
columnTypeMap.put("int2", PostgreColumnType.INT2);
columnTypeMap.put("int4", PostgreColumnType.INT4);
columnTypeMap.put("int8", PostgreColumnType.INT8);
columnTypeMap.put("money", PostgreColumnType.MONEY);
columnTypeMap.put("numeric", PostgreColumnType.NUMERIC);
columnTypeMap.put("oid", PostgreColumnType.OID);
columnTypeMap.put("serial", PostgreColumnType.SERIAL);
columnTypeMap.put("text", PostgreColumnType.TEXT);
columnTypeMap.put("timestamptz", PostgreColumnType.TIMESTAMPTZ);
columnTypeMap.put("timetz", PostgreColumnType.TIMETZ);
columnTypeMap.put("varbit", PostgreColumnType.VARBIT);
columnTypeMap.put("varchar", PostgreColumnType.VARCHAR);
}
@Override
public String getName() {
return "postgre";
}
@Override
public SqlType getSqlType(ValueTypeProvider valueTypeProvider,
PropertyMeta propertyMeta) {
if (propertyMeta.isLob()) {
if (propertyMeta.getPropertyClass() == String.class) {
return getSqlTypeInternal(Types.CLOB);
}
}
ValueType valueType = valueTypeProvider.provide(propertyMeta);
return getSqlTypeInternal(valueType.getSqlType());
}
@Override
public String getDefaultSchemaName(String userName) {
return null;
}
@Override
public GenerationType getDefaultGenerationType() {
return GenerationType.IDENTITY;
}
@Override
public boolean supportsSequence() {
return true;
}
@Override
public String getSequenceDefinitionFragment(String dataType,
long initialValue, int allocationSize) {
return "increment by " + allocationSize + " start with " + initialValue;
}
@Override
public String getIdentityColumnDefinition() {
return "not null";
}
@Override
public boolean isTableNotFound(Throwable throwable) {
String sqlState = getSQLState(throwable);
return TABLE_NOT_FOUND_SQL_STATE.equals(sqlState);
}
@Override
public boolean isColumnNotFound(Throwable throwable) {
String sqlState = getSQLState(throwable);
return COLUMN_NOT_FOUND_SQL_STATE.equals(sqlState);
}
@Override
public boolean isSequenceNotFound(Throwable throwable) {
String sqlState = getSQLState(throwable);
return SEQUENCE_NOT_FOUND_SQL_STATE.equals(sqlState);
}
@Override
public SqlBlockContext createSqlBlockContext() {
return new PostgreSqlBlockContext();
}
@Override
public boolean supportsIdentityInsert() {
return true;
}
@Override
public boolean supportsIdentity() {
return true;
}
@Override
public String getSequenceNextValString(String sequenceName,
int allocationSize) {
return "select nextval('" + sequenceName + "')";
}
@Override
public boolean supportsCommentInCreateTable() {
return false;
}
@Override
public boolean supportsCommentOn() {
return true;
}
/**
* PostgreSQL用の{@link ColumnType}の実装です。
*
* @author taedium
*/
public static class PostgreColumnType extends StandardColumnType {
private static PostgreColumnType BIT = new PostgreColumnType("bit($l)",
byte[].class);
private static PostgreColumnType BOOL = new PostgreColumnType("bool",
Boolean.class);
private static PostgreColumnType INT8 = new PostgreColumnType("int8",
Long.class);
private static PostgreColumnType BIGSERIAL = new PostgreColumnType(
"bigserial", Long.class);
private static PostgreColumnType BIGINT = new PostgreColumnType("oid",
byte[].class, true);
private static PostgreColumnType BYTEA = new PostgreColumnType("bytea",
byte[].class);
private static PostgreColumnType BPCHAR = new PostgreColumnType(
"char($l)", String.class);
private static PostgreColumnType NUMERIC = new PostgreColumnType(
"decimal($p,$s)", BigDecimal.class) {
@Override
public String getColumnDefinition(int length, int precision,
int scale, String defaultValue) {
precision = precision > 1000 ? 1000 : precision;
return super.getColumnDefinition(length, precision, scale,
defaultValue);
}
};
private static PostgreColumnType INT4 = new PostgreColumnType("int4",
Integer.class);
private static PostgreColumnType SERIAL = new PostgreColumnType(
"serial", Integer.class);
private static PostgreColumnType INT2 = new PostgreColumnType("int2",
Short.class);
private static PostgreColumnType FLOAT4 = new PostgreColumnType(
"float4", Float.class);
private static PostgreColumnType FLOAT8 = new PostgreColumnType(
"float8", Double.class);
private static PostgreColumnType MONEY = new PostgreColumnType("money",
Float.class);
private static PostgreColumnType OID = new PostgreColumnType("oid",
byte[].class, true);
private static PostgreColumnType TEXT = new PostgreColumnType("text",
String.class, true);
private static PostgreColumnType TIMETZ = new PostgreColumnType(
"timetz", Time.class);
private static PostgreColumnType TIMESTAMPTZ = new PostgreColumnType(
"timestamptz", Timestamp.class);
private static PostgreColumnType VARBIT = new PostgreColumnType(
"varbit", byte[].class);
private static PostgreColumnType VARCHAR = new PostgreColumnType(
"varchar($l)", String.class) {
@Override
public String getColumnDefinition(int length, int precision,
int scale, String defaultValue) {
length = length > 10485760 ? 10485760 : length;
return super.getColumnDefinition(length, precision, scale,
defaultValue);
}
};
/**
* インスタンスを構築します。
*
* @param dataType
* データ型
* @param attributeClass
* 属性のクラス
*/
public PostgreColumnType(String dataType, Class<?> attributeClass) {
super(dataType, attributeClass);
}
/**
* インスタンスを構築します。
*
* @param dataType
* データ型
* @param attributeClass
* 属性のクラス
* @param lob
* LOBの場合{@code true}
*/
public PostgreColumnType(String dataType, Class<?> attributeClass,
boolean lob) {
super(dataType, attributeClass, lob);
}
}
/**
* {@link StandardColumnType}の実装クラスです。
*
* @author taedium
*/
public static class PostgreSqlBlockContext implements SqlBlockContext {
/** ブロックの内側の場合{@code true} */
protected boolean inSqlBlock;
public void addKeyword(String keyword) {
if ("$$".equals(keyword)) {
inSqlBlock = !inSqlBlock;
}
}
public boolean isInSqlBlock() {
return inSqlBlock;
}
}
/**
* PostgreSQLのBLOB型です。
*
* @author taedium
*/
public static class PostgreBlobType extends AbstractSqlType {
/** 空のバイト配列 */
protected static byte[] EMPTY_BYTES = new byte[] {};
/**
* インスタンスを構築します。
*
* @param dataType
* データ型
*/
public PostgreBlobType(String dataType) {
super(dataType);
}
public void bindValue(PreparedStatement ps, int index, String value)
throws SQLException {
if (value == null) {
ps.setNull(index, Types.BLOB);
} else if (value.length() == 0) {
ps.setBlob(index, new BlobImpl(EMPTY_BYTES));
} else {
byte[] bytes = Base64Util.decode(value);
ps.setBlob(index, new BlobImpl(bytes));
}
}
public String getValue(ResultSet resultSet, int index)
throws SQLException {
Blob blob = resultSet.getBlob(index);
if (blob == null) {
return null;
}
final long length = blob.length();
if (length == 0) {
return Base64Util.encode(EMPTY_BYTES);
}
if (length > Integer.MAX_VALUE) {
throw new ArrayIndexOutOfBoundsException();
}
return Base64Util.encode(blob.getBytes(1L, (int) length));
}
}
}