I have a function in PL/pgSQL that accepts a record
type parameter, and returns 1 record type as well:
DROP FUNCTION IF EXISTS public.f_obtem_record_auth(record);
CREATE OR REPLACE FUNCTION public.f_obtem_record_auth(
rec RECORD
-- trabalhar com apenas um aparametro do tipo record
-- return record
)
RETURNS TABLE(
/*auth TEXT
, code INTEGER */
auth RECORD
)
LANGUAGE plpgsql
AS $function$
DECLARE
vrecordauth RECORD;
BEGIN
-- LOGIN IPP
-- TRATAR ERROS, CODIGOS <> 200 OU '' PARA TODAS AS BANDEIRAS
IF (rec.codigo_bandeira = '00001') THEN
WITH sub_primeira_req AS (
-- my code
), sub_segunda_req AS (
-- my code
), sub_terceira_req AS (
-- my code
), sub_quarta_req AS (
SELECT
-- my code
)
SELECT
jtr."AuthProxyJWT" auth,
sub.code
INTO vrecordauth
FROM sub_quarta_req sub
, JSONB_TO_RECORD(sub.cookies) AS jtr (
"AuthProxyJWT" TEXT
);
auth := vrecordauth;
RETURN NEXT;
-- AUTENTICAÇÃO IPP PEGAR NO NOT PAD++
ELSE
RAISE EXCEPTION 'Código bandeira: [%] desconhecido. Entre em contato com o desenvolvedor', PCodigoEmpresa;
END IF;
END;
$function$;
I want it to accept a record
parameter, and naturally I will be able to access nested fields. Example:
rec.user
rec.password
This part is well resolved and works, the part that doesn’t seem to work is this:
RETURNS TABLE(
/*auth TEXT
, code INTEGER */
auth RECORD
)
So I made a second function to test the first:
DROP FUNCTION IF EXISTS public.f_testa_login();
CREATE OR REPLACE FUNCTION public.f_testa_login()
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
rec RECORD;
VRecordAuth RECORD;
BEGIN
SELECT
CAST('' AS TEXT) usuario
, CAST('' AS TEXT) senha
, CAST('' AS TEXT) auth
, CAST('' AS TEXT) cookies
, CAST('' AS TEXT) ltpatoken
, CAST('' AS TEXT) subjsession
, CAST('' AS TEXT) javax
, CAST('' AS TEXT) codigo_empresa
, CAST('' AS TEXT) codigo_fornecedor
, CAST('' AS TEXT) codigo_produto
INTO VRecordAuth;
FOR rec IN
SELECT
-- my code
FROM bandeira b
-- my code
WHERE e.registro_ativo
AND fpe.registro_ativo
AND pe.registro_ativo
AND p.registro_ativo
GROUP BY
-- my group by
ORDER BY
-- my order by
LOOP
IF (rec.nome_bandeira = 'SHELL') THEN
IF (VRecordAuth.usuario != rec.usuario OR VRecordAuth.senha != rec.senha OR VRecordAuth.auth = '') THEN
SELECT
CAST(rec.usuario AS TEXT)
, CAST(rec.senha AS TEXT)
, auth
INTO VRecordAuth
FROM f_obtem_record_auth(rec)
AS auth_record(auth RECORD);
RAISE NOTICE 'Usuario: %', auth_record;
END IF;
END IF;
END LOOP;
END;
$function$
;
SELECT * FROM f_testa_login()
I tried sending record
type parameters (stored in var rec
) and I sent this rec as a parameter in my function f_obtem_record_auth()
and I inserted the result into a variable (VRecordAuth
) of type record
too, to access the fields of this result later, and that’s where the problem lies, when I print VRecordAuth
I get the error:
ERROR: record "auth" is not assigned yet Detalhe: The tuple structure of a not-yet-assigned record is indeterminate. Onde: PL/pgSQL function f_obtem_record_auth(record) line 76 at RETURN NEXT SQL statement "SELECT CAST(rec.usuario AS TEXT) , CAST(rec.senha AS TEXT) , auth FROM f_obtem_record_auth(rec) AS auth_record(auth RECORD)" PL/pgSQL function f_testa_login() line 94 at SQL statement
But when I change the return from the f_obtem_recor_auth()
function to:
RETURNS TABLE(
auth TEXT
, code INTEGER
)
everything works as expected and in my test function I can access the returns from the f_obtem_record_auth()
function.
Why is this happening?
Why do I get the error mentioned above?
Why can’t I just use a table return with a record type containing several fields?
I’m a beginner in programming so be patient with me.
2
Answers
Q: Why is this happening? Why do I get the error mentioned above? Why can’t I just use a table return with a record type containing several fields?
Note that
record
is not a type, but a pseudo-type.You may still use it to return results from a function, but not in combination with
TABLE
. The declaration isRETURNS SETOF record
Here’s a very simple example
When calling this function from an SQL query, it is necessary to fully specify a "column definition list" in the query, so that the query planner knows the count and types of the columns from these records.
Example:
See for more:
Immediate fix
The immediate problem that causes the error is buried in your function
f_obtem_record_auth()
here:Adding another
ROW
wrapper makes it work:fiddle
Doesn’t make it a good idea though. Much too convoluted. There are simpler solutions …
Also, I think we found a bug:
fiddle
Basics
record
is not an actual data type. The manual:You assume:
No. You must pass a well-known row type (= composite type) containing all the fields that are hard-coded in the function body. Or you must cast the anonymous record to such a well-known row type, before you can access its fields.
Cases where such a construct makes sense are few and far between. I have never had such a case. But it’s possible.