hibernate/hibernate-orm · error · UnsupportedOperationException

GaussDB only supports REF_CURSOR parameters as the first par

Error message

GaussDB only supports REF_CURSOR parameters as the first parameter

What it means

Dialect.getResultSet(CallableStatement, position) retrieves REF_CURSOR out-parameters from stored procedures. GaussDB returns refcursors through a single mechanism that only exposes the first parameter, so GaussDBDialect throws UnsupportedOperationException whenever Hibernate asks for a refcursor at any position other than 1.

Source

Thrown at hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/GaussDBDialect.java:901

	@Override
	public boolean supportsUnboundedLobLocatorMaterialization() {
		return false;
	}

	@Override
	public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() {
		return SelectItemReferenceStrategy.POSITION;
	}

	@Override
	public CallableStatementSupport getCallableStatementSupport() {
		return GaussDBCallableStatementSupport.INSTANCE;
	}

	@Override
	public ResultSet getResultSet(CallableStatement statement, int position) throws SQLException {
		if ( position != 1 ) {
			throw new UnsupportedOperationException( "GaussDB only supports REF_CURSOR parameters as the first parameter" );
		}
		return (ResultSet) statement.getObject( 1 );
	}

	@Override
	public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException {
		throw new UnsupportedOperationException( "GaussDB only supports accessing REF_CURSOR parameters by position" );
	}

	@Override
	public boolean qualifyIndexName() {
		return false;
	}

	@Override
	public IdentityColumnSupport getIdentityColumnSupport() {
		return GaussDBIdentityColumnSupport.INSTANCE;
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Declare exactly one REF_CURSOR parameter and make it the first parameter of the procedure call (register it at position 1)
  2. Return additional result sets inside the cursor (e.g. as rows tagged by type, or a refcursor set column) instead of multiple cursor parameters
  3. Split the multi-cursor procedure into several single-cursor procedures and call each separately
  4. If you need all cursors in one round trip, fetch them via a native CallableStatement using the driver-specific pattern and bypass the dialect hook

Example fix

// before (two refcursors -> second one throws on GaussDB)
StoredProcedureQuery q = em.createStoredProcedureQuery("get_data");
q.registerStoredProcedureParameter(1, void.class, ParameterMode.REF_CURSOR);
q.registerStoredProcedureParameter(2, void.class, ParameterMode.REF_CURSOR);
q.execute();

// after (single refcursor at position 1, second set via separate call)
StoredProcedureQuery q = em.createStoredProcedureQuery("get_data_a");
q.registerStoredProcedureParameter(1, void.class, ParameterMode.REF_CURSOR);
q.execute();
List<?> a = q.getResultList();
Defensive patterns

Strategy: validation

Validate before calling

// before executing, verify at most one REF_CURSOR and at position 1
long cursorCount = parameters.stream().filter(p -> p.mode == ParameterMode.REF_CURSOR).count();
if (sessionFactory.getJdbcServices().getDialect() instanceof GaussDBDialect && cursorCount > 1) {
    throw new IllegalArgumentException("GaussDB supports a single REF_CURSOR as first parameter only");
}

Type guard

static boolean refCursorLayoutSafe(Dialect d, List<Param> params) {
    if (d instanceof GaussDBDialect) {
        return params.stream().filter(p -> p.mode == ParameterMode.REF_CURSOR).count() <= 1
            && params.get(0).mode == ParameterMode.REF_CURSOR;
    }
    return true;
}

Try / catch

try {
    query.execute();
} catch (UnsupportedOperationException e) {
    if (e.getMessage().contains("first parameter")) {
        // split into multiple single-cursor procedure calls
    } else throw e;
}

Prevention

When it happens

Trigger: A StoredProcedureQuery/@NamedStoredProcedureQuery registered with two or more REF_CURSOR parameters (or a refcursor registered at a position after 1), then calling getResultList()/execute() - Hibernate iterates refcursor positions and the second call with position 2 throws.

Common situations: Migrating Oracle-style procedures returning multiple cursors to GaussDB; reusable procedure-call wrappers that register cursors after scalar OUT parameters; tests written against PostgreSQL getResultSet behavior being run on GaussDB.

Related errors


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