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

HSQLLegacySqlAstTranslator.visitConflictClause() rejects HQL INSERT ... ON CONFLICT clauses that name a constraint as the conflict target. HSQLDB supports upsert conflict clauses keyed by columns, but it has no 'ON CONFLICT ON CONSTRAINT <name>' syntax, so when conflictClause.isDoUpdate() and getConstraintName() != null Hibernate throws IllegalQueryOperationException during query translation.

Source

Thrown at hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacySqlAstTranslator.java:93

		}
	}

	@Override
	protected void renderDerivedTableReference(DerivedTableReference tableReference) {
		if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
			// No need for a lateral keyword for functions
			tableReference.accept( this );
		}
		else {
			super.renderDerivedTableReference( tableReference );
		}
	}

	@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 renderExpressionAsClauseItem(Expression expression) {
		expression.accept( this );
	}

	@Override
	public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
		final boolean isNegated = booleanExpressionPredicate.isNegated();
		if ( isNegated ) {
			appendSql( "not(" );
		}
		booleanExpressionPredicate.getExpression().accept( this );
		if ( isNegated ) {
			appendSql( CLOSE_PARENTHESIS );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Change the conflict target to a column list: 'on conflict (name) do update set ...' — supported on HSQLDB
  2. Drop the conflict clause on HSQLDB and handle the unique-violation (DataIntegrityViolationException/ConstraintViolationException) yourself with a follow-up UPDATE
  3. Move the upsert to a native query for the HSQLDB profile

Example fix

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

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

Strategy: validation

Validate before calling

static boolean usesConstraintConflictTarget(String hql) {
    return hql.toLowerCase().matches("(?s).*on\\s+conflict\\s+on\\s+constraint.*");
}
if ( session.getJdbcServices().getDialect() instanceof HSQLLegacyDialect
        && usesConstraintConflictTarget(hql) ) {
    throw new IllegalArgumentException("HSQLDB supports only column-target conflict clauses");
}

Type guard

static boolean isLegacyHsqldb(Dialect d) { return d instanceof HSQLLegacyDialect; }

Try / catch

try {
    session.createQuery(hql).executeUpdate();
} catch (IllegalQueryOperationException e) {
    if ( String.valueOf(e.getMessage()).contains("constraint name") ) {
        // rewrite conflict target to a column list and retry
    }
    throw e;
}

Prevention

When it happens

Trigger: Running an HQL insert with a constraint-named conflict target on HSQLLegacyDialect, e.g. 'insert into Person (id, name) select ... on conflict on constraint uk_person_name do update set name = excluded.name'. Only the DO UPDATE form with a constraint name is rejected; DO NOTHING and column-target forms pass through.

Common situations: Sharing one upsert @NamedQuery or criteria InsertSelectStatement across HSQLDB (tests) and PostgreSQL/SQLite (production), then the constraint-name form leaks into the HSQLDB test run; migrating upsert statements from a dialect that supports named constraints to HSQLDB in-memory tests.

Related errors


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