package liquibase.database.core;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import liquibase.CatalogAndSchema;
import liquibase.database.AbstractJdbcDatabase;
import liquibase.database.DatabaseConnection;
import liquibase.database.OfflineConnection;
import liquibase.exception.DatabaseException;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.executor.ExecutorService;
import liquibase.logging.LogFactory;
import liquibase.statement.core.GetViewDefinitionStatement;
import liquibase.statement.core.RawSqlStatement;
import liquibase.structure.DatabaseObject;
public class InformixDatabase extends AbstractJdbcDatabase {
private static final String PRODUCT_NAME = "Informix Dynamic Server";
private static final String INTERVAL_FIELD_QUALIFIER = "HOUR TO FRACTION(5)";
private static final String DATETIME_FIELD_QUALIFIER = "YEAR TO FRACTION(5)";
private final Set<String> systemTablesAndViews = new HashSet<String>();
private static final Pattern CREATE_VIEW_AS_PATTERN = Pattern.compile("^CREATE\\s+.*?VIEW\\s+.*?AS\\s+",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
public InformixDatabase() {
super.setCurrentDateTimeFunction("CURRENT " + DATETIME_FIELD_QUALIFIER);
super.sequenceNextValueFunction = "%s.NEXTVAL";
systemTablesAndViews.add("systables");
systemTablesAndViews.add("syscolumns");
systemTablesAndViews.add("sysindices");
systemTablesAndViews.add("systabauth");
systemTablesAndViews.add("syscolauth");
systemTablesAndViews.add("sysviews");
systemTablesAndViews.add("sysusers");
systemTablesAndViews.add("sysdepend");
systemTablesAndViews.add("syssynonyms");
systemTablesAndViews.add("syssyntable");
systemTablesAndViews.add("sysconstraints");
systemTablesAndViews.add("sysreferences");
systemTablesAndViews.add("syschecks");
systemTablesAndViews.add("sysdefaults");
systemTablesAndViews.add("syscoldepend");
systemTablesAndViews.add("sysprocedures");
systemTablesAndViews.add("sysprocbody");
systemTablesAndViews.add("sysprocplan");
systemTablesAndViews.add("sysprocauth");
systemTablesAndViews.add("sysblobs");
systemTablesAndViews.add("sysopclstr");
systemTablesAndViews.add("systriggers");
systemTablesAndViews.add("systrigbody");
systemTablesAndViews.add("sysdistrib");
systemTablesAndViews.add("sysfragments");
systemTablesAndViews.add("sysobjstate");
systemTablesAndViews.add("sysviolations");
systemTablesAndViews.add("sysfragauth");
systemTablesAndViews.add("sysroleauth");
systemTablesAndViews.add("sysxtdtypes");
systemTablesAndViews.add("sysattrtypes");
systemTablesAndViews.add("sysxtddesc");
systemTablesAndViews.add("sysinherits");
systemTablesAndViews.add("syscolattribs");
systemTablesAndViews.add("syslogmap");
systemTablesAndViews.add("syscasts");
systemTablesAndViews.add("sysxtdtypeauth");
systemTablesAndViews.add("sysroutinelangs");
systemTablesAndViews.add("syslangauth");
systemTablesAndViews.add("sysams");
systemTablesAndViews.add("systabamdata");
systemTablesAndViews.add("sysopclasses");
systemTablesAndViews.add("syserrors");
systemTablesAndViews.add("systraceclasses");
systemTablesAndViews.add("systracemsgs");
systemTablesAndViews.add("sysaggregates");
systemTablesAndViews.add("syssequences");
systemTablesAndViews.add("sysdirectives");
systemTablesAndViews.add("sysxasourcetypes");
systemTablesAndViews.add("sysxadatasources");
systemTablesAndViews.add("sysseclabelcomponents");
systemTablesAndViews.add("sysseclabelcomponentelements");
systemTablesAndViews.add("syssecpolicies");
systemTablesAndViews.add("syssecpolicycomponents");
systemTablesAndViews.add("syssecpolicyexemptions");
systemTablesAndViews.add("sysseclabels");
systemTablesAndViews.add("sysseclabelnames");
systemTablesAndViews.add("sysseclabelauth");
systemTablesAndViews.add("syssurrogateauth");
systemTablesAndViews.add("sysproccolumns");
systemTablesAndViews.add("sysdomains");
systemTablesAndViews.add("sysindexes");
super.sequenceNextValueFunction = "%s.NEXTVAL";
super.sequenceCurrentValueFunction = "%s.CURRVAL";
}
@Override
protected Set<String> getSystemViews() {
return systemTablesAndViews;
}
@Override
public int getPriority() {
return PRIORITY_DEFAULT;
}
@Override
protected String getDefaultDatabaseProductName() {
return "Informix";
}
@Override
public Integer getDefaultPort() {
return 1526;
}
@Override
public void setConnection(final DatabaseConnection connection) {
super.setConnection(connection);
if (!(connection instanceof OfflineConnection)) {
try {
/*
* TODO Maybe there is a better place for this.
* For each session this statement has to be executed,
* to allow newlines in quoted strings
*/
ExecutorService.getInstance().getExecutor(this).execute(new RawSqlStatement("EXECUTE PROCEDURE IFX_ALLOW_NEWLINE('T');"));
} catch (Exception e) {
throw new UnexpectedLiquibaseException("Could not allow newline characters in quoted strings with IFX_ALLOW_NEWLINE", e);
}
}
}
@Override
public String getDefaultDriver(final String url) {
if (url.startsWith("jdbc:informix-sqli")) {
return "com.informix.jdbc.IfxDriver";
}
return null;
}
@Override
public String getShortName() {
return "informix";
}
@Override
public boolean isCorrectDatabaseImplementation(final DatabaseConnection conn)
throws DatabaseException {
return PRODUCT_NAME.equals(conn.getDatabaseProductName());
}
@Override
public boolean supportsInitiallyDeferrableColumns() {
return false;
}
/*
* Informix calls them Dbspaces
*/
@Override
public boolean supportsTablespaces() {
return true;
}
@Override
public String getViewDefinition(CatalogAndSchema schema, final String viewName) throws DatabaseException {
schema = schema.customize(this);
List<Map<String, ?>> retList = ExecutorService.getInstance().getExecutor(this).queryForList(new GetViewDefinitionStatement(schema.getCatalogName(), schema.getSchemaName(), viewName));
// building the view definition from the multiple rows
StringBuilder sb = new StringBuilder();
for (Map rowMap : retList) {
String s = (String) rowMap.get("VIEWTEXT");
sb.append(s);
}
return CREATE_VIEW_AS_PATTERN.matcher(sb.toString()).replaceFirst("");
}
@Override
public String getAutoIncrementClause(final BigInteger startWith, final BigInteger incrementBy) {
return "";
}
@Override
public String getDateLiteral(final String isoDate) {
if (isTimeOnly(isoDate)) {
return "INTERVAL (" + super.getDateLiteral(isoDate).replaceAll("'", "") + ") " + INTERVAL_FIELD_QUALIFIER;
} else if (isDateOnly(isoDate)){
return super.getDateLiteral(isoDate);
} else {
return "DATETIME (" + super.getDateLiteral(isoDate).replaceAll("'", "") + ") " + DATETIME_FIELD_QUALIFIER;
}
}
@Override
public boolean supportsRestrictForeignKeys() {
// TODO dont know if this correct
return false;
}
@Override
public String escapeObjectName(final String catalogName, final String schemaName, final String objectName, final Class<? extends DatabaseObject> objectType) {
String name = super.escapeObjectName(catalogName, schemaName, objectName, objectType);
if (name == null) {
return null;
}
if (name.matches(".*\\..*\\..*")) {
name = name.replaceFirst("\\.", ":"); //informix uses : to separate catalog and schema. Like "catalog:schema.table"
}
return name;
}
@Override
public String getSystemSchema(){
return "informix";
}
@Override
public String quoteObject(String objectName, Class<? extends DatabaseObject> objectType) {
return objectName;
}
@Override
protected String getConnectionSchemaName() {
if (getConnection() == null || getConnection() instanceof OfflineConnection) {
return null;
}
try {
String schemaName = ExecutorService.getInstance().getExecutor(this).queryForObject(new RawSqlStatement("select username from sysmaster:informix.syssessions where sid = dbinfo('sessionid')"), String.class);
if (schemaName != null) {
return schemaName.trim();
}
} catch (Exception e) {
LogFactory.getInstance().getLog().info("Error getting connection schema", e);
}
return null;
}
@Override
public boolean supportsCatalogInObjectName(final Class<? extends DatabaseObject> type) {
return true;
}
}