Include column name in build_attrmap_by_position's error reports.
authorTom Lane <[email protected]>
Fri, 7 Mar 2025 18:24:09 +0000 (13:24 -0500)
committerTom Lane <[email protected]>
Fri, 7 Mar 2025 18:24:20 +0000 (13:24 -0500)
Formerly we only provided the column number, but it's frequently
more useful to mention the column name.  The input tupdesc often
doesn't have useful column names, but the output tupdesc usually
contains user-supplied names, so report that one.

Author: Marcos Pegoraro <[email protected]>
Co-authored-by: jian he <[email protected]>
Co-authored-by: Tom Lane <[email protected]>
Co-authored-by: Erik Wienhold <[email protected]>
Reviewed-by: Vladlen Popolitov <[email protected]>
Discussion: https://postgr.es/m/CAB-JLwanky28gjAMdnMh1CjyO1b2zLdr6UOA1-oY9G7PVL9KKQ@mail.gmail.com

src/backend/access/common/attmap.c
src/pl/plpgsql/src/expected/plpgsql_record.out
src/pl/plpgsql/src/sql/plpgsql_record.sql
src/test/regress/expected/plpgsql.out

index 4b6cfe05c0230873fccdb1a8a188f621fdd30daf..4901ebecef73f2a66e06eaa3999998a5edbb06fd 100644 (file)
@@ -96,33 +96,31 @@ build_attrmap_by_position(TupleDesc indesc,
    same = true;
    for (i = 0; i < n; i++)
    {
-       Form_pg_attribute att = TupleDescAttr(outdesc, i);
-       Oid         atttypid;
-       int32       atttypmod;
+       Form_pg_attribute outatt = TupleDescAttr(outdesc, i);
 
-       if (att->attisdropped)
+       if (outatt->attisdropped)
            continue;           /* attrMap->attnums[i] is already 0 */
        noutcols++;
-       atttypid = att->atttypid;
-       atttypmod = att->atttypmod;
        for (; j < indesc->natts; j++)
        {
-           att = TupleDescAttr(indesc, j);
-           if (att->attisdropped)
+           Form_pg_attribute inatt = TupleDescAttr(indesc, j);
+
+           if (inatt->attisdropped)
                continue;
            nincols++;
 
            /* Found matching column, now check type */
-           if (atttypid != att->atttypid ||
-               (atttypmod != att->atttypmod && atttypmod >= 0))
+           if (outatt->atttypid != inatt->atttypid ||
+               (outatt->atttypmod != inatt->atttypmod && outatt->atttypmod >= 0))
                ereport(ERROR,
                        (errcode(ERRCODE_DATATYPE_MISMATCH),
                         errmsg_internal("%s", _(msg)),
-                        errdetail("Returned type %s does not match expected type %s in column %d.",
-                                  format_type_with_typemod(att->atttypid,
-                                                           att->atttypmod),
-                                  format_type_with_typemod(atttypid,
-                                                           atttypmod),
+                        errdetail("Returned type %s does not match expected type %s in column \"%s\" (position %d).",
+                                  format_type_with_typemod(inatt->atttypid,
+                                                           inatt->atttypmod),
+                                  format_type_with_typemod(outatt->atttypid,
+                                                           outatt->atttypmod),
+                                  NameStr(outatt->attname),
                                   noutcols)));
            attrMap->attnums[i] = (AttrNumber) (j + 1);
            j++;
index 6974c8f4a441c137f1483ae0811c0fc7ef0eef67..e5de7143606ab8d9f975542b564b642bdf91b8d1 100644 (file)
@@ -28,7 +28,7 @@ create or replace function retc(int) returns two_int8s language plpgsql as
 $$ begin return row($1,1); end $$;
 select retc(42);
 ERROR:  returned record type does not match expected record type
-DETAIL:  Returned type integer does not match expected type bigint in column 1.
+DETAIL:  Returned type integer does not match expected type bigint in column "q1" (position 1).
 CONTEXT:  PL/pgSQL function retc(integer) while casting return value to function's return type
 -- nor extra columns
 create or replace function retc(int) returns two_int8s language plpgsql as
@@ -50,7 +50,7 @@ create or replace function retc(int) returns two_int8s language plpgsql as
 $$ declare r record; begin r := row($1,1); return r; end $$;
 select retc(42);
 ERROR:  returned record type does not match expected record type
-DETAIL:  Returned type integer does not match expected type bigint in column 1.
+DETAIL:  Returned type integer does not match expected type bigint in column "q1" (position 1).
 CONTEXT:  PL/pgSQL function retc(integer) while casting return value to function's return type
 create or replace function retc(int) returns two_int8s language plpgsql as
 $$ declare r record; begin r := row($1::int8, 1::int8, 42); return r; end $$;
@@ -386,7 +386,7 @@ DETAIL:  Number of returned columns (2) does not match expected column count (3)
 CONTEXT:  PL/pgSQL function returnsrecord(integer) while casting return value to function's return type
 select * from returnsrecord(42) as r(x int, y bigint);  -- fail
 ERROR:  returned record type does not match expected record type
-DETAIL:  Returned type integer does not match expected type bigint in column 2.
+DETAIL:  Returned type integer does not match expected type bigint in column "y" (position 2).
 CONTEXT:  PL/pgSQL function returnsrecord(integer) while casting return value to function's return type
 -- same with an intermediate record variable
 create or replace function returnsrecord(int) returns record language plpgsql as
@@ -409,7 +409,7 @@ DETAIL:  Number of returned columns (2) does not match expected column count (3)
 CONTEXT:  PL/pgSQL function returnsrecord(integer) while casting return value to function's return type
 select * from returnsrecord(42) as r(x int, y bigint);  -- fail
 ERROR:  returned record type does not match expected record type
-DETAIL:  Returned type integer does not match expected type bigint in column 2.
+DETAIL:  Returned type integer does not match expected type bigint in column "y" (position 2).
 CONTEXT:  PL/pgSQL function returnsrecord(integer) while casting return value to function's return type
 -- should work the same with a missing column in the actual result value
 create table has_hole(f1 int, f2 int, f3 int);
@@ -434,7 +434,7 @@ DETAIL:  Number of returned columns (2) does not match expected column count (3)
 CONTEXT:  PL/pgSQL function returnsrecord(integer) while casting return value to function's return type
 select * from returnsrecord(42) as r(x int, y bigint);  -- fail
 ERROR:  returned record type does not match expected record type
-DETAIL:  Returned type integer does not match expected type bigint in column 2.
+DETAIL:  Returned type integer does not match expected type bigint in column "y" (position 2).
 CONTEXT:  PL/pgSQL function returnsrecord(integer) while casting return value to function's return type
 -- same with an intermediate record variable
 create or replace function returnsrecord(int) returns record language plpgsql as
@@ -457,7 +457,7 @@ DETAIL:  Number of returned columns (2) does not match expected column count (3)
 CONTEXT:  PL/pgSQL function returnsrecord(integer) while casting return value to function's return type
 select * from returnsrecord(42) as r(x int, y bigint);  -- fail
 ERROR:  returned record type does not match expected record type
-DETAIL:  Returned type integer does not match expected type bigint in column 2.
+DETAIL:  Returned type integer does not match expected type bigint in column "y" (position 2).
 CONTEXT:  PL/pgSQL function returnsrecord(integer) while casting return value to function's return type
 -- check access to a field of an argument declared "record"
 create function getf1(x record) returns int language plpgsql as
@@ -545,6 +545,7 @@ begin
   return next h;
   return next row(5,6);
   return next row(7,8)::has_hole;
+  return query select 9, 10;
 end$$;
 select returnssetofholes();
  returnssetofholes 
@@ -554,7 +555,8 @@ select returnssetofholes();
  (3,4)
  (5,6)
  (7,8)
-(5 rows)
+ (9,10)
+(6 rows)
 
 create or replace function returnssetofholes() returns setof has_hole language plpgsql as
 $$
@@ -575,6 +577,16 @@ select returnssetofholes();
 ERROR:  returned record type does not match expected record type
 DETAIL:  Number of returned columns (3) does not match expected column count (2).
 CONTEXT:  PL/pgSQL function returnssetofholes() line 3 at RETURN NEXT
+create or replace function returnssetofholes() returns setof has_hole language plpgsql as
+$$
+begin
+  return query select 1, 2.0;  -- fails
+end$$;
+select returnssetofholes();
+ERROR:  structure of query does not match function result type
+DETAIL:  Returned type numeric does not match expected type integer in column "f3" (position 2).
+CONTEXT:  SQL statement "select 1, 2.0"
+PL/pgSQL function returnssetofholes() line 3 at RETURN QUERY
 -- check behavior with changes of a named rowtype
 create table mutable(f1 int, f2 text);
 create function sillyaddone(int) returns int language plpgsql as
index 96dcc414e9205c4cafa94678a8468abf8f0fc8a3..4fbed38b8bbb99b1629b21c70e01b610d17930f5 100644 (file)
@@ -338,6 +338,7 @@ begin
   return next h;
   return next row(5,6);
   return next row(7,8)::has_hole;
+  return query select 9, 10;
 end$$;
 select returnssetofholes();
 
@@ -356,6 +357,13 @@ begin
 end$$;
 select returnssetofholes();
 
+create or replace function returnssetofholes() returns setof has_hole language plpgsql as
+$$
+begin
+  return query select 1, 2.0;  -- fails
+end$$;
+select returnssetofholes();
+
 -- check behavior with changes of a named rowtype
 create table mutable(f1 int, f2 text);
 
index c5f73fef2970baa77d03d5f8b6759e61a2e7341d..d8ce39dba3c16b062121b0af397ef7945b542a8a 100644 (file)
@@ -3779,7 +3779,7 @@ end;
 $$ language plpgsql;
 select compos();
 ERROR:  returned record type does not match expected record type
-DETAIL:  Returned type unknown does not match expected type character varying in column 2.
+DETAIL:  Returned type unknown does not match expected type character varying in column "y" (position 2).
 CONTEXT:  PL/pgSQL function compos() while casting return value to function's return type
 -- ... but this does
 create or replace function compos() returns compostype as $$