All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.community.dialect.InformixDialect Maven / Gradle / Ivy

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.community.dialect;

import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.Replacer;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.CaseLeastGreatestEmulation;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.community.dialect.identity.InformixIdentityColumnSupport;
import org.hibernate.community.dialect.pagination.FirstLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.community.dialect.pagination.SkipFirstLimitHandler;
import org.hibernate.community.dialect.sequence.InformixSequenceSupport;
import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.community.dialect.unique.InformixUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.sql.SqmTranslator;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.sql.StandardSqmTranslatorFactory;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.community.dialect.sequence.SequenceInformationExtractorInformixDatabaseImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.type.StandardBasicTypes;

import java.sql.Types;

import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;

/**
 * Dialect for Informix 7.31.UD3 with Informix
 * JDBC driver 2.21JC3 and above.
 *
 * @author Steve Molitor
 */
public class InformixDialect extends Dialect {

	private final int version;
	private final UniqueDelegate uniqueDelegate;
	private final LimitHandler limitHandler;

	public InformixDialect(DialectResolutionInfo info) {
		this( info.getDatabaseMajorVersion() );
		registerKeywords( info );
	}

	public InformixDialect() {
		this( 7 );
	}

	/**
	 * Creates new InformixDialect instance. Sets up the JDBC /
	 * Informix type mappings.
	 */
	public InformixDialect(int version) {
		super();
		this.version = version;

		registerColumnType( Types.TINYINT, "smallint" );
		registerColumnType( Types.BIGINT, "int8" );

		//Ingres ignores the precision argument in
		//float(n) and just always defaults to
		//double precision.
		//TODO: return 'smallfloat' when n <= 24

		registerColumnType( Types.TIME, "datetime hour to second" );
		registerColumnType( Types.TIMESTAMP, "datetime year to fraction($p)" );
		registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "datetime year to fraction($p)" );

		//these types have no defined length
		registerColumnType( Types.BINARY, "byte" );
		registerColumnType( Types.VARBINARY, "byte" );

		registerColumnType( Types.VARCHAR, 255, "varchar($l)" );
		registerColumnType( Types.VARCHAR, 32_739, "lvarchar($l)" );
		registerColumnType( Types.VARCHAR, "text" );

		uniqueDelegate = new InformixUniqueDelegate( this );

		limitHandler = getVersion() < 10
				? FirstLimitHandler.INSTANCE
				//according to the Informix documentation for
				//version 11 and above, parameters are supported
				//but I have not tested this at all!
				: new SkipFirstLimitHandler( getVersion() >= 11 );
	}

	@Override
	public int getVersion() {
		return version;
	}

	@Override
	public int getDefaultDecimalPrecision() {
		//the maximum
		return 32;
	}

	@Override
	public int getDefaultTimestampPrecision() {
		//the maximum
		return 5;
	}

	@Override
	public void initializeFunctionRegistry(QueryEngine queryEngine) {
		super.initializeFunctionRegistry( queryEngine );

		CommonFunctionFactory.instr( queryEngine );
		CommonFunctionFactory.substr( queryEngine );
		CommonFunctionFactory.substring_substr( queryEngine );
		//also natively supports ANSI-style substring()
		CommonFunctionFactory.trunc( queryEngine );
		CommonFunctionFactory.trim2( queryEngine );
		CommonFunctionFactory.space( queryEngine );
		CommonFunctionFactory.reverse( queryEngine );
		CommonFunctionFactory.octetLength( queryEngine );
		CommonFunctionFactory.degrees( queryEngine );
		CommonFunctionFactory.radians( queryEngine );
		CommonFunctionFactory.sinh( queryEngine );
		CommonFunctionFactory.tanh( queryEngine );
		CommonFunctionFactory.cosh( queryEngine );
		CommonFunctionFactory.moreHyperbolic( queryEngine );
		CommonFunctionFactory.log10( queryEngine );
		CommonFunctionFactory.initcap( queryEngine );
		CommonFunctionFactory.yearMonthDay( queryEngine );
		CommonFunctionFactory.ceiling_ceil( queryEngine );
		CommonFunctionFactory.concat_pipeOperator( queryEngine );
		CommonFunctionFactory.ascii( queryEngine );
		CommonFunctionFactory.char_chr( queryEngine );
		CommonFunctionFactory.addMonths( queryEngine );
		CommonFunctionFactory.monthsBetween( queryEngine );
		CommonFunctionFactory.stddev( queryEngine );
		CommonFunctionFactory.variance( queryEngine );

		queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
				"locate",
				queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ),
				"instr(?2,?1)",
				"instr(?2,?1,?3)"
		).setArgumentListSignature("(pattern, string[, start])");

		//coalesce() and nullif() both supported since Informix 12

		queryEngine.getSqmFunctionRegistry().register( "least", new CaseLeastGreatestEmulation( true ) );
		queryEngine.getSqmFunctionRegistry().register( "greatest", new CaseLeastGreatestEmulation( false ) );
	}

	@Override
	public SqmTranslatorFactory getSqmTranslatorFactory() {
		return new StandardSqmTranslatorFactory() {
			@Override
			public SqmTranslator createSelectTranslator(
					SqmSelectStatement sqmSelectStatement,
					QueryOptions queryOptions,
					DomainParameterXref domainParameterXref,
					QueryParameterBindings domainParameterBindings,
					LoadQueryInfluencers loadQueryInfluencers,
					SqlAstCreationContext creationContext) {
				return new InformixSqmToSqlAstConverter<>(
						sqmSelectStatement,
						queryOptions,
						domainParameterXref,
						domainParameterBindings,
						loadQueryInfluencers,
						creationContext
				);
			}
		};
	}

	@Override
	public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
		return new StandardSqlAstTranslatorFactory() {
			@Override
			protected  SqlAstTranslator buildTranslator(
					SessionFactoryImplementor sessionFactory, Statement statement) {
				return new InformixSqlAstTranslator<>( sessionFactory, statement );
			}
		};
	}

	/**
	 * Informix has no extract() function, but we can
	 * partially emulate it by using the appropriate
	 * named functions, and by using to_char() with
	 * a format string.
	 *
	 * The supported fields are
	 * {@link TemporalUnit#HOUR},
	 * {@link TemporalUnit#MINUTE},
	 * {@link TemporalUnit#SECOND},
	 * {@link TemporalUnit#DAY},
	 * {@link TemporalUnit#MONTH},
	 * {@link TemporalUnit#YEAR},
	 * {@link TemporalUnit#QUARTER},
	 * {@link TemporalUnit#DAY_OF_MONTH},
	 * {@link TemporalUnit#DAY_OF_WEEK}.
	 */
	@Override
	public String extractPattern(TemporalUnit unit) {
		switch (unit) {
			case SECOND:
				return "to_number(to_char(?2,'%S'))";
			case MINUTE:
				return "to_number(to_char(?2,'%M'))";
			case HOUR:
				return "to_number(to_char(?2,'%H'))";
			case DAY_OF_WEEK:
				return "(weekday(?2)+1)";
			case DAY_OF_MONTH:
				return "day(?2)";
			default:
				//I think week() returns the ISO week number
				return "?1(?2)";
		}
	}

	@Override
	public String getAddColumnString() {
		return "add";
	}

	/**
	 * Informix constraint name must be at the end.
	 * 

* {@inheritDoc} */ @Override public String getAddForeignKeyConstraintString( String constraintName, String[] foreignKey, String referencedTable, String[] primaryKey, boolean referencesPrimaryKey) { final StringBuilder result = new StringBuilder( 30 ) .append( " add constraint " ) .append( " foreign key (" ) .append( String.join( ", ", foreignKey ) ) .append( ") references " ) .append( referencedTable ); if ( !referencesPrimaryKey ) { result.append( " (" ) .append( String.join( ", ", primaryKey ) ) .append( ')' ); } result.append( " constraint " ).append( constraintName ); return result.toString(); } public String getAddForeignKeyConstraintString( String constraintName, String foreignKeyDefinition) { return " add constraint " + foreignKeyDefinition + " constraint " + constraintName; } /** * Informix constraint name must be at the end. *

* {@inheritDoc} */ @Override public String getAddPrimaryKeyConstraintString(String constraintName) { return " add constraint primary key constraint " + constraintName + " "; } @Override public SequenceSupport getSequenceSupport() { return InformixSequenceSupport.INSTANCE; } @Override public String getQuerySequencesString() { return "select systables.tabname as sequence_name,syssequences.* from syssequences join systables on syssequences.tabid=systables.tabid where tabtype='Q'"; } @Override public SequenceInformationExtractor getSequenceInformationExtractor() { return SequenceInformationExtractorInformixDatabaseImpl.INSTANCE; } @Override public LimitHandler getLimitHandler() { return limitHandler; } @Override public boolean supportsOrderByInSubquery() { // This is just a guess return false; } @Override public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() { return EXTRACTOR; } private static final ViolatedConstraintNameExtractor EXTRACTOR = new TemplatedViolatedConstraintNameExtractor( sqle -> { String constraintName; switch ( JdbcExceptionHelper.extractErrorCode( sqle ) ) { case -268: constraintName = extractUsingTemplate( "Unique constraint (", ") violated.", sqle.getMessage() ); break; case -691: constraintName = extractUsingTemplate( "Missing key in referenced table for referential constraint (", ").", sqle.getMessage() ); break; case -692: constraintName = extractUsingTemplate( "Key value for constraint (", ") is still being referenced.", sqle.getMessage() ); break; default: return null; } // strip table-owner because Informix always returns constraint names as "." final int i = constraintName.indexOf( '.' ); if ( i != -1 ) { constraintName = constraintName.substring( i + 1 ); } return constraintName; } ); @Override public boolean supportsCurrentTimestampSelection() { return true; } @Override public boolean isCurrentTimestampSelectStringCallable() { return false; } @Override public String getCurrentTimestampSelectString() { return "select distinct current timestamp from informix.systables"; } @Override public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { return new LocalTemporaryTableStrategy( new IdTable( rootEntityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ), () -> new TempIdTableExporter( true, this::getTypeName ) { @Override protected String getCreateCommand() { return "create temp table"; } @Override protected String getCreateOptions() { return "with no log"; } }, AfterUseAction.NONE, TempTableDdlTransactionHandling.NONE, runtimeModelCreationContext.getSessionFactory() ); } @Override public UniqueDelegate getUniqueDelegate() { return uniqueDelegate; } @Override public IdentityColumnSupport getIdentityColumnSupport() { return new InformixIdentityColumnSupport(); } @Override public void appendBooleanValueString(SqlAppender appender, boolean bool) { appender.appendSql( bool ? "'t'" : "'f'" ); } @Override public String currentDate() { return "today"; } @Override public String currentTimestamp() { return "current"; } @Override public void appendDatetimeFormat(SqlAppender appender, String format) { //Informix' own variation of MySQL appender.appendSql( datetimeFormat( format ).result() ); } public static Replacer datetimeFormat(String format) { return new Replacer( format, "'", "" ) .replace("%", "%%") //year .replace("yyyy", "%Y") .replace("yyy", "%Y") .replace("yy", "%y") .replace("y", "Y") //month of year .replace("MMMM", "%B") .replace("MMM", "%b") .replace("MM", "%m") .replace("M", "%c") //???? //day of week .replace("EEEE", "%A") .replace("EEE", "%a") .replace("ee", "%w") .replace("e", "%w") //day of month .replace("dd", "%d") .replace("d", "%e") //am pm .replace("aa", "%p") //????? .replace("a", "%p") //????? //hour .replace("hh", "%I") .replace("HH", "%H") .replace("h", "%I") .replace("H", "%H") //minute .replace("mm", "%M") .replace("m", "%M") //second .replace("ss", "%S") .replace("s", "%S") //fractional seconds .replace("SSSSSS", "%F50") //5 is the max .replace("SSSSS", "%F5") .replace("SSSS", "%F4") .replace("SSS", "%F3") .replace("SS", "%F2") .replace("S", "%F1"); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy