PostgreSQL Function Resolution: Handling Ambiguous Candidate Sets

Function candidate resolution in PostgreSQL proceeds through strict filtering and scoring phases. The following configuration demonstrates how the dispatcher handles type coercion, base type mapping, and category matching when resolving overloaded signatures.

Initial Parameter and Signature Configuration

Actual argument OIDs: InputParams = (84500, 20, 20) Candidate signatures (parameter OID triplets): CandidatePool = [(84500, 1700, 1700), (1700, 1700, 1700), (1043, 1700, 1700)]

Querying the system catalog reveals the underlying type metadata:

SELECT oid, typname, typcategory, typispreferred 
FROM pg_type 
WHERE oid IN (84500, 20, 1700, 1043);

Result set:

  oid   | typname | typcat | preferred
--------+---------+--------+-----------
     20 | int8    | N      | f
   1043 | varchar | S      | f
   1700 | numeric | N      | f
  84500 | myblob  | S      | f

Phase 1: Exact Match Verification

The dispatcher first searches for a signature where parameter types exactly match the input OIDs. In this configuration, no direct hit exists, triggering the fallback resolution path.

Phase 2: Implicit Coercion Filter

Each candidate is evaluated against the IsImplicitlyConvertible() routine to verify if the actual arguments can safely promote to the candidate's expected types.

Evaluating CandidatePool[0]:

  • 84500 matches 84500 (exact)
  • 20 implicitly promotes to 1700
  • 20 implicitly promotes to 1700 -> Retained.

Evaluating CandidatePool[1]:

  • 84500 cannot implicitly convert to 1700 -> Discarded.

Evaluating CandidatePool[2]:

  • 84500 implicitly converts to 1043
  • 20 implicitly promotes to 1700
  • 20 implicitly promotes to 1700 -> Retained.

Updated candidate set: CandidatePool = [(84500, 1700, 1700), (1043, 1700, 1700)]

Phase 3: Base Type Resolution

The system maps each input OID to its underlying base type identifier to normalize composite or domain types.

  • 84500 (myblob) maps to 25 (text)
  • 20 (int8) remains 20
  • 20 (int8) remains 20 Base type vector: BaseVector = (25, 20, 20)

Phase 4: Base Type Scoring

A preliminary score is calculated by comparing BaseVector against each remaining candidate. A match increments the accumulator; a mismatch yields zero.

Comparison with first candidate (84500, 1700, 1700):

Base:  25    20    20
Cand:  84500 1700  1700
Score: 0     0     0  => Total: 0

Comparison with second candidate (1043, 1700, 1700):

Base:  25    20    20
Cand:  1043  1700  1700
Score: 0     0     0  => Total: 0

Both candidates yield identical scores and remain in the pool.

Phase 5: Category Classification

The dispatcher extracts the type category character for each base type: BaseVector = (25, 20, 20) maps to CategorySet = ('S', 'N', 'N')

Phase 6: Category and Preference Evaluation

The algorithm compares CategorySet against each candidate's type categories, factoring in preferred type flags.

First candidate (84500, 1700, 1700):

Input Base:     25    20    20
Cand Types:     84500 1700  1700
Cand Cats:      'S'   'N'   'N'
Input Cats:     'S'   'N'   'N'
Preferred Flags: f     f     f
Per-pos Score:  0     0     0  => Total: 0

Second candidate (1043, 1700, 1700):

Input Base:     25    20    20
Cand Types:     1043  1700  1700
Cand Cats:      'S'   'N'   'N'
Input Cats:     'S'   'N'   'N'
Preferred Flags: f     f     f
Per-pos Score:  0     0     0  => Total: 0

Resolution Outcome

The scoring mechanism produces a tie across all evaluated candidates. No signature emerges as strictly superior based on exactness, category alignment, or type preference. Since the argument list contains no unknown types to force a resolution, the dispatcher cannot break the deadlock. The function selection process terminates with an ambiguity error, requiring explicit type casting in the original query.

Tags: PostgreSQL function-resolution type-coercion database-internals overload-resolution

Posted on Mon, 15 Jun 2026 16:53:50 +0000 by d401tq