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行,但是不會報錯。

通過數組也可以支持該特性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章