hibernate/hibernate-orm · error · HibernateException

PostgreSQL supports only one REF_CURSOR parameter, but multi

Error message

PostgreSQL supports only one REF_CURSOR parameter, but multiple were registered

What it means

When rendering function-style calls on PostgreSQL, PostgreSQLCallableStatementSupport iterates remaining parameter registrations and rejects any additional ParameterMode.REF_CURSOR beyond the special first-position one, because a PostgreSQL function call cannot materialize multiple refcursor results in one callable statement. The check is explicit (supportsProcedures == false branch) and throws HibernateException.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/procedure/internal/PostgreSQLCallableStatementSupport.java:147

		else {
			jdbcParameterOffset = 1;
			startIndex = 0;
			callMode = CallMode.CALL;
		}

		final StringBuilder buffer = new StringBuilder( callMode.start.length() + callMode.end.length() + procedureName.length() + paramStringSizeEstimate )
				.append( callMode.start );
		buffer.append( procedureName );

		if ( startIndex == registrations.size() ) {
			buffer.append( '(' );
		}
		else {
			char sep = '(';
			for ( int i = startIndex; i < registrations.size(); i++ ) {
				final ProcedureParameterImplementor<?> parameter = registrations.get( i );
				if ( !supportsProcedures && parameter.getMode() == ParameterMode.REF_CURSOR ) {
					throw new HibernateException(
							"PostgreSQL supports only one REF_CURSOR parameter, but multiple were registered" );
				}
				buffer.append( sep );
				final JdbcCallParameterRegistration registration = parameter.toJdbcParameterRegistration(
						i + jdbcParameterOffset,
						procedureCall
				);
				final OutputableType<?> type = registration.getParameterType();
				final String castType;
				if ( parameter.getName() != null ) {
					buffer.append( parameter.getName() ).append( " => " );
				}
				if ( type != null
					&& type.getJdbcType() instanceof AbstractPostgreSQLStructJdbcType structJdbcType ) {
					// We have to cast struct type parameters so that PostgreSQL understands nulls
					castType = structJdbcType.getStructTypeName();
					buffer.append( "cast(" );
				}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Collapse to one cursor returning a composite/RECORD row type that carries what the multiple cursors carried.
  2. Return TABLE(...) / SETOF and read results with getResultList(), or issue one call per cursor.
  3. If multiple refcursors are unavoidable, use native SQL with explicit cursors (BEGIN; SELECT f(); FETCH ALL IN "cur1"; COMMIT;) instead of the procedure API.

Example fix

-- before: function returning two refcursors (Oracle style)
CREATE FUNCTION order_report(uid bigint) RETURNS refcursor AS ...

-- after: single cursor of a composite type
CREATE TYPE order_line AS (id bigint, qty int);
CREATE FUNCTION order_report(uid bigint)
    RETURNS TABLE(order_id bigint, lines order_line[]) AS $$ ... $$ LANGUAGE plpgsql;
Defensive patterns

Strategy: validation

Validate before calling

long refCursorCount = proc.getParameters().stream()
        .filter( p -> p.getMode() == ParameterMode.REF_CURSOR )
        .count();
if ( refCursorCount > 1 ) {
    throw new IllegalArgumentException(
            "PostgreSQL supports one REF_CURSOR per call; restructure to a single cursor or multiple calls" );
}

Prevention

When it happens

Trigger: Registering two or more REF_CURSOR parameters, e.g. Oracle-style proc.registerStoredProcedureParameter(1, void.class, ParameterMode.REF_CURSOR) and again at position 2, on PostgreSQL before procedure support applies.

Common situations: Direct ports of Oracle reporting packages that return several cursors (header + lines pattern); cross-dialect DAOs written against Oracle and pointed at PostgreSQL.

Related errors


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