/*
* 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.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.GenerationType;
import javax.persistence.TemporalType;
import org.seasar.extension.jdbc.PropertyMeta;
import org.seasar.extension.jdbc.ValueType;
import org.seasar.extension.jdbc.gen.dialect.GenDialect;
import org.seasar.extension.jdbc.gen.exception.UnsupportedSqlTypeRuntimeException;
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.BlobType;
import org.seasar.extension.jdbc.gen.internal.sqltype.BooleanType;
import org.seasar.extension.jdbc.gen.internal.sqltype.CharType;
import org.seasar.extension.jdbc.gen.internal.sqltype.ClobType;
import org.seasar.extension.jdbc.gen.internal.sqltype.DateType;
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.SmallIntType;
import org.seasar.extension.jdbc.gen.internal.sqltype.TimeType;
import org.seasar.extension.jdbc.gen.internal.sqltype.TimestampType;
import org.seasar.extension.jdbc.gen.internal.sqltype.VarcharType;
import org.seasar.extension.jdbc.gen.internal.util.ColumnUtil;
import org.seasar.extension.jdbc.gen.internal.util.TableUtil;
import org.seasar.extension.jdbc.gen.provider.ValueTypeProvider;
import org.seasar.extension.jdbc.gen.sqltype.SqlType;
import org.seasar.extension.jdbc.util.ConnectionUtil;
import org.seasar.framework.log.Logger;
import org.seasar.framework.util.CaseInsensitiveMap;
import org.seasar.framework.util.ResultSetUtil;
import org.seasar.framework.util.StatementUtil;
import org.seasar.framework.util.StringUtil;
/**
* 標準的な方言をあつかうクラスです。
*
* @author taedium
*/
public class StandardGenDialect implements GenDialect {
/** ロガー */
protected static Logger logger = Logger.getLogger(StandardGenDialect.class);
/** SQL型をキー、{@link SqlType}を値とするマップ */
protected Map<Integer, SqlType> sqlTypeMap = new HashMap<Integer, SqlType>();
/** カラムの型名をキー、{@link ColumnType}を値とするマップ */
@SuppressWarnings("unchecked")
protected Map<Object, ColumnType> columnTypeMap = new CaseInsensitiveMap();
/** カラムのSQL型をキー、{@link ColumnType}を値とするマップ。 */
protected Map<Integer, ColumnType> fallbackColumnTypeMap = new HashMap<Integer, ColumnType>();
/**
* インスタンスを構築します。
*/
public StandardGenDialect() {
sqlTypeMap.put(Types.BIGINT, new BigIntType());
sqlTypeMap.put(Types.BINARY, new BinaryType());
sqlTypeMap.put(Types.BLOB, new BlobType());
sqlTypeMap.put(Types.BOOLEAN, new BooleanType());
sqlTypeMap.put(Types.CHAR, new CharType());
sqlTypeMap.put(Types.CLOB, new ClobType());
sqlTypeMap.put(Types.DATE, new DateType());
sqlTypeMap.put(Types.DECIMAL, new DecimalType());
sqlTypeMap.put(Types.DOUBLE, new DoubleType());
sqlTypeMap.put(Types.FLOAT, new FloatType());
sqlTypeMap.put(Types.INTEGER, new IntegerType());
sqlTypeMap.put(Types.SMALLINT, new SmallIntType());
sqlTypeMap.put(Types.TIME, new TimeType());
sqlTypeMap.put(Types.TIMESTAMP, new TimestampType());
sqlTypeMap.put(Types.VARCHAR, new VarcharType());
columnTypeMap.put("bigint", StandardColumnType.BIGINT);
columnTypeMap.put("binary", StandardColumnType.BINARY);
columnTypeMap.put("bit", StandardColumnType.BIT);
columnTypeMap.put("blob", StandardColumnType.BLOB);
columnTypeMap.put("boolean", StandardColumnType.BOOLEAN);
columnTypeMap.put("char", StandardColumnType.CHAR);
columnTypeMap.put("clob", StandardColumnType.CLOB);
columnTypeMap.put("date", StandardColumnType.DATE);
columnTypeMap.put("decimal", StandardColumnType.DECIMAL);
columnTypeMap.put("double", StandardColumnType.DOUBLE);
columnTypeMap.put("float", StandardColumnType.FLOAT);
columnTypeMap.put("integer", StandardColumnType.INTEGER);
columnTypeMap.put("longvarbinary", StandardColumnType.LONGVARBINARY);
columnTypeMap.put("longvarchar", StandardColumnType.LONGVARCHAR);
columnTypeMap.put("numeric", StandardColumnType.NUMERIC);
columnTypeMap.put("real", StandardColumnType.REAL);
columnTypeMap.put("smallint", StandardColumnType.SMALLINT);
columnTypeMap.put("time", StandardColumnType.TIME);
columnTypeMap.put("timestamp", StandardColumnType.TIMESTAMP);
columnTypeMap.put("tinyint", StandardColumnType.TINYINT);
columnTypeMap.put("varbinary", StandardColumnType.VARBINARY);
columnTypeMap.put("varchar", StandardColumnType.VARCHAR);
fallbackColumnTypeMap.put(Types.BIGINT, StandardColumnType.BIGINT);
fallbackColumnTypeMap.put(Types.BINARY, StandardColumnType.BINARY);
fallbackColumnTypeMap.put(Types.BIT, StandardColumnType.BIT);
fallbackColumnTypeMap.put(Types.BLOB, StandardColumnType.BLOB);
fallbackColumnTypeMap.put(Types.BOOLEAN, StandardColumnType.BOOLEAN);
fallbackColumnTypeMap.put(Types.CHAR, StandardColumnType.CHAR);
fallbackColumnTypeMap.put(Types.CLOB, StandardColumnType.CLOB);
fallbackColumnTypeMap.put(Types.DATE, StandardColumnType.DATE);
fallbackColumnTypeMap.put(Types.DECIMAL, StandardColumnType.DECIMAL);
fallbackColumnTypeMap.put(Types.DOUBLE, StandardColumnType.DOUBLE);
fallbackColumnTypeMap.put(Types.FLOAT, StandardColumnType.FLOAT);
fallbackColumnTypeMap.put(Types.INTEGER, StandardColumnType.INTEGER);
fallbackColumnTypeMap.put(Types.LONGVARBINARY,
StandardColumnType.LONGVARBINARY);
fallbackColumnTypeMap.put(Types.LONGVARCHAR,
StandardColumnType.LONGVARCHAR);
fallbackColumnTypeMap.put(Types.NUMERIC, StandardColumnType.NUMERIC);
fallbackColumnTypeMap.put(Types.REAL, StandardColumnType.REAL);
fallbackColumnTypeMap.put(Types.SMALLINT, StandardColumnType.SMALLINT);
fallbackColumnTypeMap.put(Types.TIME, StandardColumnType.TIME);
fallbackColumnTypeMap
.put(Types.TIMESTAMP, StandardColumnType.TIMESTAMP);
fallbackColumnTypeMap.put(Types.TINYINT, StandardColumnType.TINYINT);
fallbackColumnTypeMap
.put(Types.VARBINARY, StandardColumnType.VARBINARY);
fallbackColumnTypeMap.put(Types.VARCHAR, StandardColumnType.VARCHAR);
}
public String getName() {
return null;
}
public String getDefaultSchemaName(String userName) {
return userName;
}
public SqlType getSqlType(int sqlType) {
return getSqlTypeInternal(sqlType);
}
public SqlType getSqlType(ValueTypeProvider valueTypeProvider,
PropertyMeta propertyMeta) {
ValueType valueType = valueTypeProvider.provide(propertyMeta);
return getSqlTypeInternal(valueType.getSqlType());
}
/**
* 内部的にSQL型を返します。
*
* @param sqlType
* JDBCのSQL型
* @return SQL型
*/
protected SqlType getSqlTypeInternal(int sqlType) {
SqlType type = sqlTypeMap.get(sqlType);
if (type != null) {
return type;
}
throw new UnsupportedSqlTypeRuntimeException(sqlType);
}
public ColumnType getColumnType(String typeName, int sqlType) {
ColumnType columnType = columnTypeMap.get(typeName);
return columnType != null ? columnType : fallbackColumnTypeMap
.get(sqlType);
}
public GenerationType getDefaultGenerationType() {
return GenerationType.TABLE;
}
public String getOpenQuote() {
return "\"";
}
public String getCloseQuote() {
return "\"";
}
public String quote(String value) {
if (value == null) {
return null;
}
return getOpenQuote() + value + getCloseQuote();
}
public String unquote(String value) {
String s = StringUtil.ltrim(value, getOpenQuote());
return StringUtil.rtrim(s, getCloseQuote());
}
public boolean supportsSequence() {
return false;
}
public boolean supportsGetIndexInfo(String catalogName, String schemaName,
String tableName) {
return true;
}
public String getSequenceDefinitionFragment(String dataType,
long initialValue, int allocationSize) {
throw new UnsupportedOperationException("getSequenceDefinitionFragment");
}
public String getSqlBlockDelimiter() {
return null;
}
public String getIdentityColumnDefinition() {
throw new UnsupportedOperationException("getIdentityDefinition");
}
public String getDropForeignKeySyntax() {
return "drop constraint";
}
public String getDropUniqueKeySyntax() {
return "drop constraint";
}
public boolean isTableNotFound(Throwable throwable) {
return false;
}
public boolean isColumnNotFound(Throwable throwable) {
return false;
}
public boolean isSequenceNotFound(Throwable throwable) {
return false;
}
public SqlBlockContext createSqlBlockContext() {
return new StandardSqlBlockContext();
}
public boolean supportsIdentityInsert() {
return false;
}
public boolean supportsIdentityInsertControlStatement() {
return false;
}
public String getIdentityInsertEnableStatement(String tableName) {
throw new UnsupportedOperationException("getIdentityInsertOnStatement");
}
public String getIdentityInsertDisableStatement(String tableName) {
throw new UnsupportedOperationException("getIdentityInsertOffStatement");
}
public boolean supportsNullableUnique() {
return true;
}
public boolean supportsIdentity() {
return false;
}
public String getSequenceNextValString(final String sequenceName,
final int allocationSize) {
throw new UnsupportedOperationException("getSequenceNextValString");
}
public boolean supportsCommentInCreateTable() {
return false;
}
public boolean supportsCommentOn() {
return false;
}
public boolean isJdbcCommentAvailable() {
return true;
}
public String getTableComment(Connection connection, String catalogName,
String schemaName, String tableName) throws SQLException {
throw new UnsupportedOperationException("getTableComment");
}
public Map<String, String> getColumnCommentMap(Connection connection,
String catalogName, String schemaName, String tableName)
throws SQLException {
throw new UnsupportedOperationException("getColumnCommentMap");
}
public boolean supportsReferentialDeleteRule() {
return true;
}
public boolean supportsReferentialUpdateRule() {
return true;
}
public boolean isAutoIncrement(Connection connection, String catalogName,
String schemaName, String tableName, String columnName)
throws SQLException {
String fullTableName = TableUtil.buildFullTableName(catalogName,
schemaName, tableName);
String sql = "select " + columnName + " from " + fullTableName
+ " where 1 = 0";
logger.debug(sql);
PreparedStatement ps = ConnectionUtil.prepareStatement(connection, sql);
try {
ResultSet rs = ps.executeQuery();
try {
ResultSetMetaData rsMetaData = rs.getMetaData();
return rsMetaData.isAutoIncrement(1);
} finally {
ResultSetUtil.close(rs);
}
} finally {
StatementUtil.close(ps);
}
}
/**
* 例外チェーンをたどって原因となった{@link SQLException#getSQLState() SQLステート}を返します。
* <p>
* 例外チェーンに{@link SQLException SQL例外}が存在しない場合や、SQLステートが設定されていない場合は
* <code>null</code>を返します。
* </p>
*
* @param t
* 例外
* @return 原因となった{@link SQLException#getSQLState() SQLステート}
*/
protected String getSQLState(Throwable t) {
SQLException cause = getCauseSQLException(t);
if (cause != null && !StringUtil.isEmpty(cause.getSQLState())) {
return cause.getSQLState();
}
return null;
}
/**
* 例外チェーンをたどって原因となった{@link SQLException#getErrorCode() ベンダー固有の例外コード}を返します。
* <p>
* 例外チェーンに{@link SQLException SQL例外}が存在しない場合や、例外コードが設定されていない場合は
* <code>null</code>を返します。
* </p>
*
* @param t
* 例外
* @return 原因となった{@link SQLException#getErrorCode() ベンダー固有の例外コード}
*/
protected Integer getErrorCode(Throwable t) {
SQLException cause = getCauseSQLException(t);
if (cause != null) {
return cause.getErrorCode();
}
return null;
}
/**
* 例外チェーンをたどって原因となった{@link SQLException SQL例外}を返します。
* <p>
* 例外チェーンにSQL例外が存在しない場合は<code>null</code>を返します。
* </p>
*
* @param t
* 例外
* @return 原因となった{@link SQLException SQL例外}
*/
protected SQLException getCauseSQLException(Throwable t) {
SQLException cause = null;
while (t != null) {
if (t instanceof SQLException) {
cause = SQLException.class.cast(t);
if (cause.getNextException() != null) {
cause = cause.getNextException();
t = cause;
continue;
}
}
t = t.getCause();
}
return cause;
}
/**
* 標準の{@link ColumnType}の実装クラスです。
*
* @author taedium
*/
public static class StandardColumnType implements ColumnType {
private static StandardColumnType BIGINT = new StandardColumnType(
"bigint", Long.class);
private static StandardColumnType BINARY = new StandardColumnType(
"binary", byte[].class);
private static StandardColumnType BIT = new StandardColumnType("bit",
Boolean.class);
private static StandardColumnType BLOB = new StandardColumnType("blob",
byte[].class, true);
private static StandardColumnType BOOLEAN = new StandardColumnType(
"boolean", Boolean.class);
private static StandardColumnType CHAR = new StandardColumnType(
"char($l)", String.class);
private static StandardColumnType CLOB = new StandardColumnType("clob",
String.class, true);
private static StandardColumnType DATE = new StandardColumnType("date",
java.sql.Date.class);
private static StandardColumnType DECIMAL = new StandardColumnType(
"decimal", BigDecimal.class);
private static StandardColumnType DOUBLE = new StandardColumnType(
"double", Double.class);
private static StandardColumnType FLOAT = new StandardColumnType(
"float", Float.class);
private static StandardColumnType INTEGER = new StandardColumnType(
"integer", Integer.class);
private static StandardColumnType LONGVARBINARY = new StandardColumnType(
"longvarbinary", byte[].class);
private static StandardColumnType LONGVARCHAR = new StandardColumnType(
"longvarchar", String.class);
private static StandardColumnType NUMERIC = new StandardColumnType(
"numeric", BigDecimal.class);
private static StandardColumnType REAL = new StandardColumnType("real",
Float.class);
private static StandardColumnType SMALLINT = new StandardColumnType(
"smallint", Short.class);
private static StandardColumnType TIME = new StandardColumnType("time",
java.sql.Time.class);
private static StandardColumnType TIMESTAMP = new StandardColumnType(
"timestamp", Timestamp.class);
private static StandardColumnType TINYINT = new StandardColumnType(
"tinyint", Short.class);
private static StandardColumnType VARBINARY = new StandardColumnType(
"varbinary($l)", byte[].class);
private static StandardColumnType VARCHAR = new StandardColumnType(
"varchar($l)", String.class);
/** カラム定義 */
protected String dataType;
/** 属性のクラス */
protected Class<?> attributeClass;
/** LOBの場合{@code true} */
protected boolean lob;
/** 時制の種別 */
protected TemporalType temporalType;
/**
* インスタンスを構築します。
*
* @param dataType
* データ型
* @param attributeClass
* 属性のクラス
*/
protected StandardColumnType(String dataType, Class<?> attributeClass) {
this(dataType, attributeClass, false);
}
/**
* インスタンスを構築します。
*
* @param dataType
* カラム定義
* @param attributeClass
* 属性のクラス
* @param lob
* LOBの場合{@code true}
*/
protected StandardColumnType(String dataType, Class<?> attributeClass,
boolean lob) {
this(dataType, attributeClass, lob, null);
}
/**
* インスタンスを構築します。
*
* @param dataType
* カラム定義
* @param attributeClass
* 属性のクラス
* @param lob
* LOBの場合{@code true}
* @param temporalType
* 時制の種別
*/
protected StandardColumnType(String dataType, Class<?> attributeClass,
boolean lob, TemporalType temporalType) {
this.dataType = dataType;
this.attributeClass = attributeClass;
this.lob = lob;
this.temporalType = temporalType;
}
public String getColumnDefinition(int length, int precision, int scale,
String defaultValue) {
String completeDataType = ColumnUtil.formatDataType(dataType,
length, precision, scale);
return getColumnDefinitionInternal(completeDataType, defaultValue);
}
/**
* カラム定義を返します。
*
* @param completeDataType
* 完全なデータ型
* @param defaultValue
* デフォルト値、存在しない場合は{@code null}
* @return カラム定義
*/
protected String getColumnDefinitionInternal(String completeDataType,
String defaultValue) {
if (defaultValue == null) {
return completeDataType;
}
return completeDataType + " default " + defaultValue;
}
public Class<?> getAttributeClass(int length, int precision, int scale) {
return attributeClass;
}
public boolean isLob() {
return lob;
}
public TemporalType getTemporalType() {
return temporalType;
}
}
/**
* 標準の{@link StandardColumnType}の実装クラスです。
*
* @author taedium
*/
public static class StandardSqlBlockContext implements SqlBlockContext {
/** SQLブロックの開始を表すキーワードの連なりのリスト */
protected List<List<String>> sqlBlockStartKeywordsList = new ArrayList<List<String>>();
/** 追加されたキーワードの連なり */
protected List<String> keywords = new ArrayList<String>();
/** SQLブロックの内側の場合{@code true} */
protected boolean inSqlBlock;
public void addKeyword(String keyword) {
if (!inSqlBlock) {
keywords.add(keyword);
check();
}
}
/**
* ブロックの内側かどうかチェックします。
*/
protected void check() {
for (List<String> startKeywords : sqlBlockStartKeywordsList) {
if (startKeywords.size() > keywords.size()) {
continue;
}
for (int i = 0; i < startKeywords.size(); i++) {
String word1 = startKeywords.get(i);
String word2 = keywords.get(i);
inSqlBlock = word1.equalsIgnoreCase(word2);
if (!inSqlBlock) {
break;
}
}
if (inSqlBlock) {
break;
}
}
}
public boolean isInSqlBlock() {
return inSqlBlock;
}
}
}