package jef.database.dialect;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.persistence.PersistenceException;
import jef.common.log.LogUtil;
import jef.database.ConnectInfo;
import jef.database.DbCfg;
import jef.database.DbMetaData;
import jef.database.DbUtils;
import jef.database.DebugUtil;
import jef.database.ORMConfig;
import jef.database.dialect.ColumnType.Char;
import jef.database.dialect.handler.LimitHandler;
import jef.database.dialect.handler.LimitOffsetLimitHandler;
import jef.database.dialect.type.AColumnMapping;
import jef.database.dialect.type.AutoIncrementMapping;
import jef.database.exception.JDBCExceptionHelper;
import jef.database.exception.TemplatedViolatedConstraintNameExtracter;
import jef.database.exception.ViolatedConstraintNameExtracter;
import jef.database.jdbc.JDBCTarget;
import jef.database.jdbc.result.IResultSet;
import jef.database.meta.Column;
import jef.database.meta.DbProperty;
import jef.database.meta.Feature;
import jef.database.meta.SequenceInfo;
import jef.database.query.Func;
import jef.database.query.Scientific;
import jef.database.query.function.CastFunction;
import jef.database.query.function.NoArgSQLFunction;
import jef.database.query.function.StandardSQLFunction;
import jef.database.query.function.TemplateFunction;
import jef.database.support.RDBMS;
import jef.database.wrapper.populator.AbstractResultSetTransformer;
import jef.tools.JefConfiguration;
import jef.tools.StringUtils;
import jef.tools.collection.CollectionUtils;
import jef.tools.reflect.ClassEx;
/**
* HSQLDB的dialect
*
*/
public class HsqlDbMemDialect extends AbstractDialect {
protected static final String DRIVER_CLASS = "org.hsqldb.jdbc.JDBCDriver";
/**
* version is 18 for 1.8 or 20 for 2.0
*/
private int hsqldbVersion = 18;
public HsqlDbMemDialect() {
super();
super.loadKeywords("hsqldb_keywords.properties");
try {
final ClassEx props = ClassEx.forName("org.hsqldb.persist.HsqlDatabaseProperties");
final String versionString = (String) props.getDeclaredField("THIS_VERSION").get(null);
hsqldbVersion = Integer.parseInt(versionString.substring(0, 1)) * 10;
hsqldbVersion += Integer.parseInt(versionString.substring(2, 3));
} catch (Throwable e) {
// must be a very old version
}
features = CollectionUtils.identityHashSet();
features.add(Feature.ONE_COLUMN_IN_SINGLE_DDL);
features.add(Feature.COLUMN_ALTERATION_SYNTAX);
features.add(Feature.CURSOR_ENDS_ON_INSERT_ROW);
features.add(Feature.NOT_FETCH_NEXT_AUTOINCREAMENTD);
features.add(Feature.SUPPORT_SEQUENCE);
features.add(Feature.SUPPORT_COMMENT);
if (JefConfiguration.getBoolean(DbCfg.DB_ENABLE_ROWID, false)) {
features.add(Feature.SELECT_ROW_NUM);
}
registerNative(Func.now, "sysdate");
registerNative(Func.current_timestamp, new NoArgSQLFunction("current_timestamp", false), "systimestamp");
registerNative(Func.current_date, new NoArgSQLFunction("current_date", false), "curdate", "today");
registerNative(Func.current_time, new NoArgSQLFunction("current_time", false), "curtime");
registerNative(Func.upper, "ucase");
registerNative(Func.lower, "lcase");
registerNative(Scientific.cot);
registerNative(Scientific.degrees);
registerNative(Scientific.radians);
registerNative(Scientific.exp);
registerNative(Scientific.ln, "log");
registerNative(Scientific.log10);
registerNative(Scientific.power);
registerNative(Scientific.rand);
registerNative(new StandardSQLFunction("to_number"));
registerNative(new StandardSQLFunction("days"));
registerNative(new StandardSQLFunction("quarter"));
registerNative(new StandardSQLFunction("week"));
registerNative(new StandardSQLFunction("extract"));
registerNative(new StandardSQLFunction("uuid"));
registerAlias(Func.day, "days");
registerNative(Func.year);
registerNative(Func.month);
registerNative(Func.hour);
registerNative(Func.minute);
registerNative(Func.second);
registerNative(Func.decode);
registerNative(Func.coalesce);
registerNative(Func.nvl);
registerNative(Func.nullif);
registerNative(new StandardSQLFunction("ifnull"), "isnull");
registerNative(new StandardSQLFunction("nvl2"));
registerNative(new StandardSQLFunction("months_between"));
registerNative(Func.timestampadd, "dateadd");
registerNative(Func.timestampdiff, "datediff");
registerNative(Func.add_months);
registerNative(new StandardSQLFunction("date_add"));
registerAlias(Func.adddate, "date_add");
registerNative(new StandardSQLFunction("date_sub"));
registerAlias(Func.subdate, "date_sub");
registerNative(Func.trunc, "truncate");
registerNative(Func.mod);
registerNative(Func.ceil, "ceiling");
registerNative(Func.floor);
registerNative(Func.round);
registerNative(Func.locate);
registerNative(Func.lpad);
registerNative(Func.rpad);
registerNative(Func.ltrim);
registerNative(Func.rtrim);
registerNative(Func.trim);
registerNative(new NoArgSQLFunction("localtime", false));
registerNative(new NoArgSQLFunction("localtimestamp", false));
registerNative(new StandardSQLFunction("bitand"));
registerNative(new StandardSQLFunction("bitandnot"));
registerNative(new StandardSQLFunction("bitnot"));
registerNative(new StandardSQLFunction("bitor"));
registerNative(new StandardSQLFunction("bitxor"));
registerNative(new StandardSQLFunction("ascii"));
registerNative(new StandardSQLFunction("bit_length"));
registerNative(new StandardSQLFunction("octet_length"));
registerNative(new StandardSQLFunction("character_length"));
registerAlias(Func.length, "character_length");
registerCompatible(Func.lengthb, new TemplateFunction("lengthb", "bit_length(%s)/8"));
registerNative(new StandardSQLFunction("char"));// int转char
registerNative(new StandardSQLFunction("difference"));
registerNative(new StandardSQLFunction("soundex"));
registerNative(new StandardSQLFunction("length"));
registerNative(Func.concat);
registerNative(Func.translate);
registerNative(Func.substring, "substr");
registerNative(new StandardSQLFunction("concat_ws"));
registerNative(new StandardSQLFunction("insert"));// INSERT ( <char
// value expr 1>,
// <offset>,
// <length>, <char
// value expr 2> )
registerNative(new StandardSQLFunction("instr"));
registerNative(new StandardSQLFunction("hextoraw"));
registerNative(new StandardSQLFunction("rawtohex"));
registerNative(new StandardSQLFunction("left"));
registerNative(new StandardSQLFunction("right"));
registerNative(new StandardSQLFunction("overlay"));
// registerNative(Func.date);
registerCompatible(Func.date, new TemplateFunction("time", "cast(%s as date)"));
registerNative(Func.time);
registerNative(Func.datediff);
registerNative(Func.cast);
registerNative(new StandardSQLFunction("position"));
registerNative(new StandardSQLFunction("regexp_matches"));
registerNative(new StandardSQLFunction("regexp_substring"));
registerNative(new StandardSQLFunction("repeat"));
registerNative(new StandardSQLFunction("reverse"));
registerNative(Func.replace);
registerCompatible(Func.str, new CastFunction("str", "varchar(500)"));
setProperty(DbProperty.ADD_COLUMN, "ADD COLUMN");
setProperty(DbProperty.MODIFY_COLUMN, "ALTER");
setProperty(DbProperty.DROP_COLUMN, "DROP COLUMN");
setProperty(DbProperty.CHECK_SQL, "select 1 from (VALUES(0))");
setProperty(DbProperty.SELECT_EXPRESSION, "SELECT %s FROM (VALUES(0))");
setProperty(DbProperty.SEQUENCE_FETCH, "CALL NEXT VALUE FOR %s");
setProperty(DbProperty.WRAP_FOR_KEYWORD, "\"\"");
setProperty(DbProperty.GET_IDENTITY_FUNCTION, "CALL IDENTITY()");
setProperty(DbProperty.MAX_SEQUENCE_VALUE, "999999999");
typeNames.put(Types.TINYINT, "tinyint", 0);
typeNames.put(Types.INTEGER, "integer", 0);
typeNames.put(Types.BOOLEAN, "boolean", 0,"bool");
}
public RDBMS getName() {
return RDBMS.hsqldb;
}
public String getDriverClass(String url) {
return DRIVER_CLASS;
}
@Override
public String generateUrl(String host, int port, String pathOrName) {
if (StringUtils.isEmpty(host)) {
// 生成内存格式的URL
return "jdbc:hsqldb:mem:" + pathOrName;
} else {
// 生成形如的URL
// jdbc:hsqldb:hsql://localhost:9001/testDbName
if (port <= 0)
port = 9001;
if (!pathOrName.startsWith("/"))
pathOrName = "/" + pathOrName;
return "jdbc:hsqldb:hsql://" + host + ":" + port + pathOrName;
}
}
@Override
protected String getComment(ColumnType.AutoIncrement column, boolean flag) {
return "int generated by default as identity (start with 1)";
}
// 1 内存
// jdbc:hsqldb:mem:myDbName
//
// 2 进程(In-Process)模式:从应用程序启动数据库。因为所有数据被写入到文件中,所以即使应用程序退出后,数据也不会被销毁。
// jdbc:hsqldb:file:/C:/testdb/testDbName
// jdbc:hsqldb:file:/opt/db/myDbName
// jdbc:hsqldb:file:myDbName
//
//
// 3 远程
// jdbc:hsqldb:hsql://localhost:9001/testDbName
public void parseDbInfo(ConnectInfo connectInfo) {
String url = connectInfo.getUrl();
String lower = url.toLowerCase();
if (lower.startsWith("jdbc:hsqldb:mem:")) {
String dbName = url.substring(16);
connectInfo.setDbname(dbName);
} else if (lower.startsWith("jdbc:hsqldb:hsql:")) {
String path = url.substring(19);
int index = path.indexOf('/');
String hostport = path.substring(0, index);
String dbname = path.substring(index + 1);
connectInfo.setHost(hostport);
connectInfo.setDbname(dbname);
} else if (lower.startsWith("jdbc:hsqldb:file:")) {
String path = url.substring(17);
path = path.replace('\\', '/');
connectInfo.setDbname(StringUtils.substringAfterLastIfExist(path, "/"));
} else {
throw new IllegalArgumentException(url);
}
}
@Override
public ColumnType getProprtMetaFromDbType(Column column) {
int type = column.getDataTypeCode();
if (type == Types.TINYINT || type == Types.SMALLINT || type == Types.INTEGER || type == Types.BIGINT) {
if (column.getColumnDef() != null && column.getColumnDef().startsWith("GENERATED")) {
return new ColumnType.AutoIncrement(column.getColumnSize() / 4);// moni
} else {
return new ColumnType.Int(column.getColumnSize() / 4);
}
} else if ("CHARACTER".equals(column.getDataType())) {
return new Char(column.getColumnSize());
}
return super.getProprtMetaFromDbType(column);
}
@Override
public long getColumnAutoIncreamentValue(AutoIncrementMapping mapping, JDBCTarget db) {
String tableName = mapping.getMeta().getTableName(false).toLowerCase();
String seqname = tableName + "_" + mapping.lowerColumnName() + "_seq";
String sql = String.format("select nextval('%s') from dual", seqname);
if (ORMConfig.getInstance().isDebugMode()) {
LogUtil.show(sql + " | " + db.getTransactionId());
}
try {
Statement st = db.createStatement();
ResultSet rs = null;
try {
rs = st.executeQuery(sql);
rs.next();
return rs.getLong(1);
} finally {
DbUtils.close(rs);
DbUtils.close(st);
}
} catch (SQLException e) {
throw new PersistenceException(e);
}
}
private final LimitHandler limit = new LimitOffsetLimitHandler();
@Override
public LimitHandler getLimitHandler() {
return limit;
}
private static ViolatedConstraintNameExtracter EXTRACTER_18 = new TemplatedViolatedConstraintNameExtracter() {
/**
* Extract the name of the violated constraint from the given
* SQLException.
*
* @param sqle
* The exception that was the result of the constraint
* violation.
* @return The extracted constraint name.
*/
public String extractConstraintName(SQLException sqle) {
String constraintName = null;
int errorCode = JDBCExceptionHelper.extractErrorCode(sqle);
if (errorCode == -8) {
// constraintName = extractUsingTemplate("Integrity constraint violation ", " table:", sqle.getMessage());
return sqle.getMessage();
} else if (errorCode == -9) {
// constraintName = extractUsingTemplate("Violation of unique index: ", " in statement [", sqle.getMessage());
return sqle.getMessage();
} else if (errorCode == -104) {
// constraintName = extractUsingTemplate("Unique constraint violation: ", " in statement [", sqle.getMessage());
return sqle.getMessage();
} else if (errorCode == -177) {
// constraintName = extractUsingTemplate("Integrity constraint violation - no parent ", " table:", sqle.getMessage());
return sqle.getMessage();
}
return constraintName;
}
};
/**
* HSQLDB 2.0 messages have changed messages may be localized - therefore
* use the common, non-locale element " table: "
*/
private static ViolatedConstraintNameExtracter EXTRACTER_20 = new TemplatedViolatedConstraintNameExtracter() {
public String extractConstraintName(SQLException sqle) {
String constraintName = null;
int errorCode = JDBCExceptionHelper.extractErrorCode(sqle);
if (errorCode == -8) {
constraintName = extractUsingTemplate("; ", " table: ", sqle.getMessage());
} else if (errorCode == -9) {
constraintName = extractUsingTemplate("; ", " table: ", sqle.getMessage());
} else if (errorCode == -104) {
constraintName = extractUsingTemplate("; ", " table: ", sqle.getMessage());
} else if (errorCode == -177) {
constraintName = extractUsingTemplate("; ", " table: ", sqle.getMessage());
}
return "Unique constraint violation: " + constraintName;
}
};
@Override
public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
return hsqldbVersion < 20 ? EXTRACTER_18 : EXTRACTER_20;
}
@Override
public List<SequenceInfo> getSequenceInfo(DbMetaData conn, String schema, String seqName) {
String sql="select SEQUENCE_CATALOG,SEQUENCE_SCHEMA,SEQUENCE_NAME,MINIMUM_VALUE,MAXIMUM_VALUE,INCREMENT,START_WITH,NEXT_VALUE from INFORMATION_SCHEMA.sequences where SEQUENCE_SCHEMA like ? and SEQUENCE_NAME like ?";
schema=StringUtils.isBlank(schema) ? "%" : schema.toUpperCase();
seqName=StringUtils.isBlank(seqName) ? "%" : seqName.toUpperCase();
seqName=seqName.toUpperCase();
try {
return conn.selectBySql(sql, new AbstractResultSetTransformer<List<SequenceInfo>>() {
@Override
public List<SequenceInfo> transformer(IResultSet rs) throws SQLException {
List<SequenceInfo> result=new ArrayList<SequenceInfo>();
while(rs.next()) {
SequenceInfo seq=new SequenceInfo();
seq.setCatalog(rs.getString(1));
seq.setSchema(rs.getString(2));
seq.setName(rs.getString(3));
seq.setMinValue(rs.getLong(4));
// seq.setMaxValue(rs.getLong(5));
seq.setStartValue(rs.getLong(6));
seq.setStep(rs.getInt(7));
seq.setCurrentValue(rs.getLong(8));
result.add(seq);
}
return result;
}
}, 0, Arrays.asList(schema,seqName));
} catch (SQLException e) {
DebugUtil.setSqlState(e, sql);
LogUtil.exception(e);
}
return null;
}
}