lightdb plpgsql函数in/inout参数示例
lightdb支持oracle pl/sql以及开源postgresql Plpgsql两种过程性(增强)语言。本文讲解pgpgsql函数出参的典型用法及限制。
注:匿名块实际上走的是plorasql,而非plpgsql,即使调用的是plpgsql过程。
本文我们假设对于函数、存储过程的调用是进行逻辑处理,而不是返回结果集或游标,这通常是两种上下文场景。
1、如果有多个出参,则不能带return预定义类型或必须return record
zjh@postgres=# CREATE OR REPLACE FUNCTION public.f_square(inout v_a int, inout v_str varchar) returns int LANGUAGE 'plpgsql' AS $BODY$ declare lv_result int; begin v_a := v_a * v_a; v_str := v_str || '..ret'; end $BODY$; CREATE FUNCTION zjh@postgres=# select f_square(10,'a'); ERROR: invalid input syntax for type integer: "(100,a..ret)" CONTEXT: PL/pgSQL function f_square(integer,character varying) while casting return value to function's return type
除了事务外,该限制和oracle存在明显的不兼容性。
zjh@postgres=# CREATE OR REPLACE FUNCTION public.f_square(inout v_a int) returns int LANGUAGE 'plpgsql' AS $BODY$ declare lv_result int; begin v_a := v_a * v_a; -- v_str := v_str || '..ret'; end $BODY$; CREATE FUNCTION zjh@postgres=# zjh@postgres=# zjh@postgres=# select f_square(10); f_square ---------- 100 (1 row)
zjh@postgres=# CREATE OR REPLACE FUNCTION public.f_square(inout v_a int, inout v_str varchar) returns record LANGUAGE 'plpgsql' AS $BODY$ declare lv_result int; begin v_a := v_a * v_a; v_str := v_str || '..ret'; end $BODY$; CREATE FUNCTION zjh@postgres=# select f_square(10,'a'); f_square -------------- (100,a..ret) (1 row)
2、plpgsql匿名块或过程中使用perform调用函数未修改出参值
zjh@postgres=# CREATE OR REPLACE FUNCTION public.f_square(inout v_a int, inout v_str varchar) returns record LANGUAGE 'plpgsql' AS $BODY$ declare lv_result int; begin v_a := v_a * v_a; v_str := v_str || '..ret';
raise notice 'v_str=%',v_str; end $BODY$; CREATE FUNCTION zjh@postgres=# declare v_a int :=10; v_str varchar(100) := 'b'; begin perform f_square(v_a,v_str);
raise notice 'v_a=%,v_str=%',v_a,v_str; end; / NOTICE: v_str=b..ret NOTICE: v_a=10,v_str=b DO
zjh@postgres=# declare v_a int :=10; v_str varchar(100) := 'b'; begin select * into v_a,v_str from f_square(v_a,v_str);
raise notice 'v_a=%,v_str=%',v_a,v_str; end; / NOTICE: v_str=b..ret NOTICE: v_a=100,v_str=b..ret DO
为什么通过perform调用不生效,直接select 函数(v1,v2),select into o1,o2 from 函数(v1,v2)能返回呢?因为本质上in/out/inout都是传值处理,所有的出参事实上都是通过return scalar或return record实现。exec_stmt_execsql处理完了into才丢弃SPI_tuptable,exec_stmt_perform则立刻丢弃。
postgresql函数OUT和INOUT使用方法 https://blog.csdn.net/llj318/article/details/122387617
实现oracle的v_ret := fnc(in a, out b,inout c);调用兼容支持
declare v_a int :=10; v_str varchar(100) := 'b'; v_ret int; begin v_ret := f_square(v_a,v_str); -- 因为pg不支持带出参返回非record的函数定义,所以自然也就不支持oracle的这种调用。 raise notice 'v_a=%,v_str=%',v_a,v_str; end; 其实只要实现PLpgSQL_execstate.retval即可(因为record存储在SPITupleTable *eval_tuptable成员中,所以不存在破坏)。 typedef struct PLpgSQL_execstate { PLpgSQL_function *func; /* function being executed */ TriggerData *trigdata; /* if regular trigger, data about firing */ EventTriggerData *evtrigdata; /* if event trigger, data about firing */ Datum retval; bool retisnull; Oid rettype; /* type of current retval */ /* temporary state for results from evaluation of query or expr */ SPITupleTable *eval_tuptable; uint64 eval_processed;
lightdb将在23.3版本支持该兼容性,内测版已经支持。
调用存储过程获取出参
zjh@postgres=# create or replace procedure proce_inouttest(in nD1 bigint, inout szD varchar, out nD2 integer) zjh@postgres-# as zjh@postgres$# $$ zjh@postgres$# begin zjh@postgres$# nD1:=99; zjh@postgres$# szD:='qaz'; zjh@postgres$# nD2:=88; zjh@postgres$# end; zjh@postgres$# $$ zjh@postgres-# language plpgsql; zjh@postgres=# call proce_inouttest(1,'3',1); szd | nd2 -----+----- qaz | 88 (1 row)
CALL executes a procedure.
If the procedure has any output parameters, then a result row will be returned, containing the values of those parameters.
zjh@postgres=# DO $$ zjh@postgres$# DECLARE myvar int := 5; zjh@postgres$# BEGIN zjh@postgres$# CALL triple(myvar); zjh@postgres$# RAISE NOTICE 'myvar = %', myvar; -- prints 15 zjh@postgres$# END; zjh@postgres$# $$; NOTICE: myvar = 15
在plpgsql中,存储过程是支持出参赋值的。参见http://www.light-pg.com/docs/lightdb/13.8-22.3/plpgsql-control-structures.html#PLPGSQL-STATEMENTS-CALLING-PROCEDURE。在请求返回的时候,OUT重新赋值回去的。这一点存储过程和函数的行为不一样。
libpq调用存储过程
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <sys/types.h> #include "libpq-fe.h" /* for ntohl/htonl */ #include <netinet/in.h> #include <arpa/inet.h> static void exit_nicely(PGconn *conn) { PQfinish(conn); exit(1); } void test_call_sp() { const char *conninfo; PGconn *conn; PGresult *res; const char *paramValues[3]; int paramLengths[3]; int paramFormats[3]; uint32_t binaryIntVal; uint64_t bigbinaryIntVal; int nFields; int i, j; conninfo = "postgresql:///postgres?host=localhost&port=18888"; /* Make a connection to the database */ conn = PQconnectdb(conninfo); /* Check to see that the backend connection was successfully made */ if (PQstatus(conn) != CONNECTION_OK) { fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn)); exit_nicely(conn); } /* Convert integer value "2" to network byte order */ binaryIntVal = htonl((uint32_t) 2); bigbinaryIntVal = htonl((uint64_t) 2); /* Set up parameter arrays for PQexecParams */ paramValues[0] = (char *) &bigbinaryIntVal; paramLengths[0] = sizeof(bigbinaryIntVal); paramFormats[0] = 1; /* binary */ paramValues[1] = "2"; paramLengths[1] = 1; paramFormats[1] = 0; /* test */ paramValues[2] = (char *) &binaryIntVal; paramLengths[2] = sizeof(binaryIntVal); paramFormats[2] = 1; /* binary */ res = PQexecParams(conn, "call proce_inouttest($1,$2,$3)", 3, /* one param */ NULL, /* let the backend deduce param type */ paramValues, paramLengths, paramFormats, 0); /* ask for text results */ if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } nFields = PQnfields(res); for (i = 0; i < nFields; i++) printf("%-15s", PQfname(res, i)); printf("\n\n"); /* next, print out the instances */ for (i = 0; i < PQntuples(res); i++) { for (j = 0; j < nFields; j++) printf("%-15s", PQgetvalue(res, i, j)); printf("\n"); } PQclear(res); /* close the connection to the database and cleanup */ PQfinish(conn); return 0; }
create or replace procedure proce_inouttest(in nD1 bigint, inout szD varchar, out nD2 integer) as $$ begin nD1:=99; szD:='qaz'; nD2:=88; end; $$ language plpgsql;
SQL(注意不是psql)对于存储过程out/inout参数返回值的处理,与一般的select查询返回值处理一样,使用PQnfields、PQntuples、PQgetvalue等函数对结果PGresult结构体进行处理。
输出如下:
szd nd2
qaz 88 --名字为啥是qaz?
其它
注:postgresql不支持oracle的select bulk collect和fetch bulk collect,但是如果返回多行,没有指定strict的情况下,只返回第1行,但是不会报错。
通过数组也可以支持该特性。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2021-07-15 PostgreSQL查看索引、表对应的relfileno