Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f9230bf
Add casts to parameters in 'select' on Informix
gavinking Jun 29, 2025
250d495
Use a safe cast in DerbySqlAstTranslator
gavinking Jun 29, 2025
4320867
fix package of DerbyDialect
gavinking Jun 29, 2025
2006db1
fix a test to consistently use quoted column names
gavinking Jun 29, 2025
a625481
skip tests on Informix
gavinking Jun 29, 2025
f74c7ef
fix test on Informix
gavinking Jun 29, 2025
ac550f3
skip test on Informix
gavinking Jun 29, 2025
7a37a85
improve test
gavinking Jun 29, 2025
91dd51e
improve all the array tests
gavinking Jun 29, 2025
b0cd0b1
skip test on Informix
gavinking Jun 29, 2025
1865c7c
add support for SQL MERGE to InformixDialect
gavinking Jun 29, 2025
e7e231e
skip tests on Informix
gavinking Jun 29, 2025
74c0f90
fix up handling of casts on Informix
gavinking Jun 29, 2025
ffbc09b
fix test on Informix
gavinking Jun 29, 2025
5ee2b26
fix crazy impl of DialectFeatureChecks.CurrentTimestampHasMicrosecond…
gavinking Jun 29, 2025
9c85ba2
skip test on Informix
gavinking Jun 29, 2025
b20446e
emulation for FETCH ... WITH TIES on Informix
gavinking Jun 29, 2025
bf64bd7
disable assertions on Informix
gavinking Jun 29, 2025
9c298ff
skip tests on Informix
gavinking Jun 29, 2025
9513ada
properly handle the nested nature of the Informix ISAM error
gavinking Jun 29, 2025
b49dccb
disable tests on Informix
gavinking Jun 29, 2025
ba841c4
special handling for literal null inside a cast in select clause
gavinking Jun 29, 2025
e6eaa7d
very minor grammar fixes in comments
gavinking Jun 29, 2025
19da9fa
skip test on Informix
gavinking Jun 29, 2025
68ba41e
skip assertion on Informix
gavinking Jun 29, 2025
480d6f5
skip test on Informix
gavinking Jun 29, 2025
3b49683
get more FunctionTests working on Informix
gavinking Jun 30, 2025
563e1e3
more consistent exception message format
gavinking Jun 30, 2025
d696c16
use @SkipForDialect like in all other tests
gavinking Jun 30, 2025
6945dd0
enhancements and cleanups in DialectFeatureChecks
gavinking Jun 30, 2025
c4b4c7a
add a cast around string functions with parameter args on Informix
gavinking Jun 30, 2025
1aa2fc1
Informix rejects Integer.MAX_VALUE, Integer.MIN_VALUE
gavinking Jun 30, 2025
56e9294
add appropriate casts on parameters in binary numeric expressions on …
gavinking Jun 30, 2025
874f0d9
extract pattern for NATIVE on Informix
gavinking Jun 30, 2025
02a45cb
skip test on Informix
gavinking Jun 30, 2025
48f20d2
get 'extract(epoch)' working Informix
gavinking Jun 30, 2025
9ba64d1
re-disable test on Sybase and EDB
gavinking Jun 30, 2025
a60f852
disable tests on Informix
gavinking Jun 30, 2025
fd93ee9
improve StandardFunctionTests
gavinking Jun 30, 2025
a107f6c
skip tests on Informix
gavinking Jun 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,7 @@ protected void renderPartitionItem(Expression expression) {
if ( expression instanceof Literal ) {
appendSql( "'0'" );
}
else if ( expression instanceof Summarization ) {
Summarization summarization = (Summarization) expression;
else if ( expression instanceof Summarization summarization ) {
appendSql( summarization.getKind().sqlText() );
appendSql( OPEN_PARENTHESIS );
renderCommaSeparated( summarization.getGroupings() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@
import static org.hibernate.internal.util.JdbcExceptionHelper.extractErrorCode;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING;
import static org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers.impliedOrInvariant;
import static org.hibernate.type.SqlTypes.BIGINT;
import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.FLOAT;
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
Expand Down Expand Up @@ -214,8 +213,8 @@ protected String columnType(int sqlTypeCode) {
switch ( sqlTypeCode ) {
case TINYINT:
return "smallint";
case BIGINT:
return "int8";
// case BIGINT:
// return "int8";
case TIME:
return "datetime hour to second";
case TIMESTAMP:
Expand Down Expand Up @@ -252,7 +251,7 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
);

ddlTypeRegistry.addDescriptor(
CapacityDependentDdlType.builder( VARCHAR, columnType( LONG32VARCHAR ), "varchar(255)",this )
CapacityDependentDdlType.builder( VARCHAR, columnType( LONG32VARCHAR ), "lvarchar",this )
.withTypeCapacity( 255, "varchar($l)" )
.withTypeCapacity( getMaxVarcharLength(), columnType( VARCHAR ) )
.build()
Expand Down Expand Up @@ -340,6 +339,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.initcap();
functionFactory.yearMonthDay();
functionFactory.ceiling_ceil();
functionFactory.concat_pipeOperator();
functionFactory.ascii();
functionFactory.char_chr();
functionFactory.addMonths();
Expand All @@ -355,14 +355,6 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio

functionRegistry.registerAlternateKey( "var_samp", "variance" );

// the pipe operator or concat() function returns strings with trailing whitespace
functionRegistry.patternDescriptorBuilder( "concat", "cast(?1||?2... as varchar(255))" )
.setInvariantType( stringBasicType )
.setMinArgumentCount( 1 )
.setArgumentTypeResolver( impliedOrInvariant( typeConfiguration, STRING ) )
.setArgumentListSignature( "(STRING string0[, STRING string1[, ...]])" )
.register();

if ( getVersion().isSameOrAfter( 12 ) ) {
functionFactory.locate_charindex();
}
Expand Down Expand Up @@ -443,6 +435,8 @@ public String extractPattern(TemporalUnit unit) {
case HOUR -> "to_number(to_char(?2,'%H'))";
case DAY_OF_WEEK -> "(weekday(?2)+1)";
case DAY_OF_MONTH -> "day(?2)";
case EPOCH -> "(to_number(cast(cast(sum(?2-datetime(1970-1-1) year to day) as interval day(9) to day) as varchar(12)))*86400+to_number(cast(cast(sum(cast(?2 as datetime hour to second)-datetime(00:00:00) hour to second) as interval second(6) to second) as varchar(9))))";
case NATIVE -> "((to_number(cast(cast(sum(?2) as interval day(9) to day) as varchar(12)))*86400+mod(to_number(cast(cast(sum(?2) as interval second(6) to second) as varchar(9))),86400)+to_number(cast(cast(sum(?2) as interval fraction to fraction) as varchar(6))))*1e3)";
default -> "?1(?2)";
};
}
Expand Down Expand Up @@ -591,26 +585,35 @@ public boolean supportsValuesListForInsert() {

@Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) ->
switch ( extractErrorCode( sqlException ) ) {
case -378, -233, -107, -113, -134, -143, -144, -154 ->
//TODO: which of these are these are really LockTimeoutExceptions
// rather than the more generic LockAcquisitionException?
new LockAcquisitionException( message, sqlException, sql );
case -239, -268 ->
new ConstraintViolationException( message, sqlException, sql, ConstraintViolationException.ConstraintKind.UNIQUE,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) );
case -691, -692 ->
new ConstraintViolationException( message, sqlException, sql, ConstraintViolationException.ConstraintKind.FOREIGN_KEY,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) );
case -703, -391 ->
new ConstraintViolationException( message, sqlException, sql, ConstraintViolationException.ConstraintKind.NOT_NULL,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) );
case -530 ->
new ConstraintViolationException( message, sqlException, sql, ConstraintViolationException.ConstraintKind.CHECK,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) );
default -> null;
};
return (exception, message, sql) -> switch ( extractErrorCode( exception ) ) {
case -239, -268 ->
new ConstraintViolationException( message, exception, sql, ConstraintViolationException.ConstraintKind.UNIQUE,
getViolatedConstraintNameExtractor().extractConstraintName( exception ) );
case -691, -692 ->
new ConstraintViolationException( message, exception, sql, ConstraintViolationException.ConstraintKind.FOREIGN_KEY,
getViolatedConstraintNameExtractor().extractConstraintName( exception ) );
case -703, -391 ->
new ConstraintViolationException( message, exception, sql, ConstraintViolationException.ConstraintKind.NOT_NULL,
getViolatedConstraintNameExtractor().extractConstraintName( exception ) );
case -530 ->
new ConstraintViolationException( message, exception, sql, ConstraintViolationException.ConstraintKind.CHECK,
getViolatedConstraintNameExtractor().extractConstraintName( exception ) );
default -> {
// unwrap the ISAM error, if any
if ( exception.getCause() instanceof SQLException cause && cause != exception ) {
yield switch ( extractErrorCode( cause ) ) {
case -107, -113, -134, -143, -144, -154 ->
//TODO: which of these are these are really LockTimeoutExceptions
// rather than the more generic LockAcquisitionException?
new LockAcquisitionException( message, exception, sql );
default -> null;
};
}
else {
yield null;
}
}
};
}

@Override
Expand Down Expand Up @@ -735,6 +738,9 @@ public String castPattern(CastType from, CastType to) {
return "case ?1 when 't' then 1 when 'f' then 0 else null end";
}
}
if ( from == CastType.STRING && to == CastType.BOOLEAN ) {
return buildStringToBooleanCast( "'t'", "'f'" );
}
return super.castPattern( from, to );
}

Expand Down Expand Up @@ -977,14 +983,13 @@ public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType
@Override
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
final DdlType descriptor = typeConfiguration.getDdlTypeRegistry().getDescriptor( sqlType );
if ( descriptor == null ) {
// just cast it to an arbitrary SQL type,
// which we expect to be ignored by higher layers
return "null::int";
}
else {
return "null::" + castType( descriptor );
}
final String castType =
descriptor != null
? castType( descriptor )
// just cast it to an arbitrary SQL type,
// which we expect to be ignored by higher layers
: "integer";
return "cast(null as " + castType + ")";
}

private static String castType(DdlType descriptor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,25 @@
import java.util.List;

import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAstTranslatorWithMerge;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.FunctionExpression;
import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectClause;
Expand All @@ -28,12 +37,28 @@
*
* @author Christian Beikov
*/
public class InformixSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> {
public class InformixSqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslatorWithMerge<T> {

public InformixSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
super( sessionFactory, statement );
}

@Override
protected void visitQueryClauses(QuerySpec querySpec) {
visitSelectClause( querySpec.getSelectClause() );
visitFromClause( querySpec.getFromClause() );
if ( !hasFrom( querySpec.getFromClause() )
&& hasWhere( querySpec.getWhereClauseRestrictions() ) ) {
append( " from " );
append( getDual() );
}
visitWhereClause( querySpec.getWhereClauseRestrictions() );
visitGroupByClause( querySpec, getDialect().getGroupBySelectItemReferenceStrategy() );
visitHavingClause( querySpec );
visitOrderBy( querySpec.getSortSpecifications() );
visitOffsetFetchClause( querySpec );
}

@Override
public void visitSelectClause(SelectClause selectClause) {
getClauseStack().push( Clause.SELECT );
Expand All @@ -49,13 +74,21 @@ public void visitSelectClause(SelectClause selectClause) {

}

@Override
protected void renderSelectExpression(Expression expression) {
renderSelectExpressionWithCastedOrInlinedPlainParameters( expression );
}

@Override
protected void visitSqlSelections(SelectClause selectClause) {
if ( supportsSkipFirstClause() ) {
renderSkipFirstClause( (QuerySpec) getQueryPartStack().getCurrent() );
}
else {
renderFirstClause( (QuerySpec) getQueryPartStack().getCurrent() );
final QuerySpec querySpec = (QuerySpec) getQueryPartStack().getCurrent();
if ( isRowsOnlyFetchClauseType( querySpec ) ) {
if ( supportsSkipFirstClause() ) {
renderSkipFirstClause( querySpec );
}
else {
renderFirstClause( querySpec );
}
}
if ( selectClause.isDistinct() ) {
appendSql( "distinct " );
Expand Down Expand Up @@ -136,17 +169,17 @@ else if ( expression instanceof Summarization ) {
}
}

@Override
protected void renderNull(Literal literal) {
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.NO_UNTYPED ) {
renderCasted( literal );
}
else {
int sqlType = literal.getExpressionType().getSingleJdbcMapping().getJdbcType().getJdbcTypeCode();
String nullString = getDialect().getSelectClauseNullString( sqlType, getSessionFactory().getTypeConfiguration() );
appendSql( nullString );
}
}
// @Override
// protected void renderNull(Literal literal) {
// if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.NO_UNTYPED ) {
// renderCasted( literal );
// }
// else {
// int sqlType = literal.getExpressionType().getSingleJdbcMapping().getJdbcType().getJdbcTypeCode();
// String nullString = getDialect().getSelectClauseNullString( sqlType, getSessionFactory().getTypeConfiguration() );
// appendSql( nullString );
// }
// }

@Override
protected void renderInsertIntoNoColumns(TableInsertStandard tableInsert) {
Expand All @@ -161,4 +194,124 @@ private boolean supportsParameterOffsetFetchExpression() {
private boolean supportsSkipFirstClause() {
return getDialect().getVersion().isSameOrAfter( 11 );
}

@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}

@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
}

@Override
public void visitValuesTableReference(ValuesTableReference tableReference) {
emulateValuesTableReferenceColumnAliasing( tableReference );
}

protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
// Check if current query part is already row numbering to avoid infinite recursion
return useOffsetFetchClause( queryPart ) && getQueryPartForRowNumbering() != queryPart
&& getDialect().supportsWindowFunctions() && !isRowsOnlyFetchClauseType( queryPart );
}

@Override
public void visitQueryGroup(QueryGroup queryGroup) {
if ( shouldEmulateFetchClause( queryGroup ) ) {
emulateFetchOffsetWithWindowFunctions( queryGroup, true );
}
else {
super.visitQueryGroup( queryGroup );
}
}

@Override
public void visitQuerySpec(QuerySpec querySpec) {
if ( shouldEmulateFetchClause( querySpec ) ) {
emulateFetchOffsetWithWindowFunctions( querySpec, true );
}
else {
super.visitQuerySpec( querySpec );
}
}

@Override
protected void visitArithmeticOperand(Expression expression) {
if ( expression instanceof SqmParameterInterpretation
&& expression.getExpressionType() != null
&& expression.getExpressionType().getJdbcTypeCount() == 1 ) {
final String castType =
switch ( expression.getExpressionType().getSingleJdbcMapping().getCastType() ) {
case FLOAT, DOUBLE -> "float" ;
case INTEGER -> "integer" ;
case LONG -> "bigint";
default -> null;
};
if ( castType != null ) {
append( "cast(" );
}
super.visitArithmeticOperand( expression );
if ( castType != null ) {
append( " as " );
append( castType );
append( ")" );
}
}
else {
super.visitArithmeticOperand( expression );
}
}

@Override
public void visitSelfRenderingExpression(SelfRenderingExpression expression) {
final boolean isStringFunctionWithParameterArg =
expression instanceof FunctionExpression fn
&& expression.getExpressionType() != null
&& expression.getExpressionType().getJdbcTypeCount() == 1
&& expression.getExpressionType().getSingleJdbcMapping().getJdbcType().isString()
&& fn.getArguments().stream().anyMatch( arg -> arg instanceof SqmParameterInterpretation );
if ( isStringFunctionWithParameterArg ) {
append( "cast(" );
}
super.visitSelfRenderingExpression( expression );
if ( isStringFunctionWithParameterArg ) {
append( " as lvarchar)" );
}
}

private void caseArgument(Expression expression) {
// concatenation inside a case must be cast to varchar(255)
// or we get a bunch of trailing whitespace
final boolean concat =
expression instanceof FunctionExpression fn
&& fn.getFunctionName().equals( "concat" );
if ( concat ) {
append( "cast(" );
}
expression.accept( this );
if ( concat ) {
append( " as varchar(255))");
}
}

@Override
protected void visitCaseSearchedExpression(CaseSearchedExpression caseSearchedExpression, boolean inSelect) {
visitAnsiCaseSearchedExpression( caseSearchedExpression, this::caseArgument );
}

@Override
protected void visitCaseSimpleExpression(CaseSimpleExpression caseSimpleExpression, boolean inSelect) {
visitAnsiCaseSimpleExpression( caseSimpleExpression, this::caseArgument );
}
}
Loading