hibernate/hibernate-orm · error · IllegalQueryOperationException

Insert conflict 'do update' clause with constraint name is n

Error message

Insert conflict 'do update' clause with constraint name is not supported

What it means

SybaseASELegacySqlAstTranslator.visitConflictClause() throws IllegalQueryOperationException when an HQL insert statement carries an ON CONFLICT 'do update' clause that names a constraint ('on conflict for constraint <name> do update'). Sybase ASE has no native upsert, and while the translator can pass through a plain key-based conflict clause, the constraint-name variant is explicitly rejected during query translation. The check only fires when isDoUpdate() and getConstraintName() are both set - 'do nothing' with a constraint name and 'do update' keyed by columns are not rejected here.

Source

Thrown at hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacySqlAstTranslator.java:123

	}

	@Override
	protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
		if ( statement.getFromClause().getRoots().isEmpty() ) {
			appendSql( " from " );
			renderDmlTargetTableExpression( statement.getTargetTable() );
			renderTableReferenceIdentificationVariable( statement.getTargetTable() );
		}
		else {
			visitFromClause( statement.getFromClause() );
		}
	}

	@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" );
			}
		}
	}

	// Sybase ASE does not allow CASE expressions where all result arms contain plain parameters.
	// At least one result arm must provide some type context for inference,
	// so we cast the first result arm if we encounter this condition

	@Override
	protected void visitAnsiCaseSearchedExpression(
			CaseSearchedExpression caseSearchedExpression,
			Consumer<Expression> resultRenderer) {
		if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( caseSearchedExpression ) ) {
			final List<CaseSearchedExpression.WhenFragment> whenFragments = caseSearchedExpression.getWhenFragments();
			final Expression firstResult = whenFragments.get( 0 ).getResult();
			super.visitAnsiCaseSearchedExpression(
					caseSearchedExpression,
					e -> {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Drop the 'for constraint <name>' phrase and key the conflict by columns: 'on conflict (id) do update set ...'
  2. Use 'on conflict do nothing' (optionally with the constraint name) if skipping duplicates is acceptable
  3. On ASE, implement the upsert manually: try an UPDATE, then INSERT the rows that updated zero rows, inside a transaction
  4. Catch IllegalQueryOperationException at query creation and fall back to the manual update-then-insert path

Example fix

// before
insert into Person(id, name) values(:id, :name)
on conflict for constraint pk_person do update set name = excluded.name

// after
insert into Person(id, name) values(:id, :name)
on conflict (id) do update set name = excluded.name
Defensive patterns

Strategy: try-catch

Validate before calling

static String stripConflictConstraintName(String hql) {
    return hql.replaceAll('for constraint \\w+', '');
}
// use stripConflictConstraintName(hql) before createMutationQuery on Sybase ASE

Try / catch

try {
    factory.createMutationQuery(upsertHql).execute();
}
catch (IllegalQueryOperationException e) {
    if (String.valueOf(e.getMessage()).contains('do update')) {
        // fallback: manual upsert on ASE
        int updated = em.createQuery('update Person p set p.name = :n where p.id = :id')
            .setParameter('n', name).setParameter('id', id).executeUpdate();
        if (updated == 0) {
            em.persist(new Person(id, name));
        }
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Building a MutationQuery with HQL like 'insert into Person(id, name) values(:id,:name) on conflict for constraint pk_person do update set name = excluded.name' against a Sybase ASE (legacy dialect) connection; Jakarta Persistence 3.2 style upsert statements reused across databases.

Common situations: An upsert statement written and tested on PostgreSQL (where naming the constraint is common) executed against Sybase ASE in another environment; shared repository code covering multiple databases; migration of batch import jobs to ASE.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/1bafb36f9e7fa0c3. Report an issue: GitHub.