为plpgsql函数返回复合类型时发生意外行为

时间:2019-12-17 16:00:54

标签: plpgsql composite-types

因此,我在plpgsql上遇到了一些问题,我不知道自己可能做错了什么。我创建了一个复合类型,该类型在plpgsql函数中用作返回值,以便在另一个函数中获取它。问题在于,这与我预期的方式不符。

这是我的复合类型:

CREATE TYPE sync.sync_conn_data AS (
  sync_conn_name VARCHAR,
  sync_active BOOLEAN,
  sync_use_ssl BOOLEAN,
  lcl_ctry VARCHAR,
  lcl_dbhostaddr VARCHAR,
  lcl_dbname VARCHAR,
  lcl_dbuser VARCHAR,
  lcl_dbpasswd VARCHAR,
  rmte_ctry VARCHAR,
  rmte_dbhostaddr VARCHAR,
  rmte_dbname VARCHAR,
  rmte_dbuser VARCHAR,
  rmte_dbpasswd VARCHAR,
  timezone VARCHAR
);

这是返回它的函数:

CREATE OR REPLACE FUNCTION sync.sync_connect (
    param_name VARCHAR = 'sync_db'::VARCHAR
)
RETURNS sync.sync_conn_data AS
$body$
DECLARE
    sync_connrcd sync.sync_conn_data;
BEGIN
    WITH all_conn_prms AS (
        SELECT (CASE WHEN param_name = 'local_country' THEN param_value ELSE NULL END) AS local_country,
               (CASE WHEN param_name = 'sync_active' THEN param_value ELSE NULL END) AS sync_active,
               (CASE WHEN param_name = 'sync_use_ssl' THEN param_value ELSE NULL END) AS sync_use_ssl,
               (CASE WHEN param_name = 'ago_dbhostaddr' THEN param_value ELSE NULL END) AS ago_dbhostaddr,
               (CASE WHEN param_name = 'ago_dbname' THEN param_value ELSE NULL END) AS ago_dbname,
               (CASE WHEN param_name = 'ago_dbuser' THEN param_value ELSE NULL END) AS ago_dbuser,
               (CASE WHEN param_name = 'ago_dbpasswd' THEN param_value ELSE NULL END) AS ago_dbpasswd,
               (CASE WHEN param_name = 'cub_dbhostaddr' THEN param_value ELSE NULL END) AS cub_dbhostaddr, 
               (CASE WHEN param_name = 'cub_dbname' THEN param_value ELSE NULL END) AS cub_dbname,
               (CASE WHEN param_name = 'cub_dbuser' THEN param_value ELSE NULL END) AS cub_dbuser,
               (CASE WHEN param_name = 'cub_dbpasswd' THEN param_value ELSE NULL END) AS cub_dbpasswd
        FROM sync.sync_config_params
        WHERE param_name IN ('local_country','sync_active','sync_use_ssl','ago_dbhostaddr','ago_dbname','ago_dbuser','ago_dbpasswd','cub_dbhostaddr','cub_dbname','cub_dbuser','cub_dbpasswd')
    ),
    fltd_conn_prms AS (
        SELECT string_agg(all_conn_prms.local_country, ',') AS local_country,
               string_agg(all_conn_prms.sync_active, ',') AS sync_active,
               string_agg(all_conn_prms.sync_use_ssl, ',') AS sync_use_ssl,
               string_agg(all_conn_prms.ago_dbhostaddr, ',') AS ago_dbhostaddr,
               string_agg(all_conn_prms.ago_dbname, ',') AS ago_dbname,
               string_agg(all_conn_prms.ago_dbuser, ',') AS ago_dbuser,
               string_agg(all_conn_prms.ago_dbpasswd, ',') AS ago_dbpasswd,
               string_agg(all_conn_prms.cub_dbhostaddr, ',') AS cub_dbhostaddr,
               string_agg(all_conn_prms.cub_dbname, ',') AS cub_dbname,
               string_agg(all_conn_prms.cub_dbuser, ',') AS cub_dbuser,
               string_agg(all_conn_prms.cub_dbpasswd, ',') AS cub_dbpasswd
        FROM all_conn_prms
    )
    SELECT coalesce(sync_conn_name, 'sync_db') AS sync_conn_name,
           fltd_conn_prms.sync_active,
           fltd_conn_prms.sync_use_ssl,
           fltd_conn_prms.local_country AS lcl_ctry,
           (CASE fltd_conn_prms.local_country WHEN 'AGO' THEN fltd_conn_prms.ago_dbhostaddr WHEN 'CUB' THEN fltd_conn_prms.cub_dbhostaddr ELSE NULL END) AS lcl_dbhostaddr,
           (CASE fltd_conn_prms.local_country WHEN 'AGO' THEN fltd_conn_prms.ago_dbname WHEN 'CUB' THEN fltd_conn_prms.cub_dbname ELSE NULL END) AS lcl_dbname,
           (CASE fltd_conn_prms.local_country WHEN 'AGO' THEN fltd_conn_prms.ago_dbuser WHEN 'CUB' THEN fltd_conn_prms.cub_dbuser ELSE NULL END) AS lcl_dbuser,
           (CASE fltd_conn_prms.local_country WHEN 'AGO' THEN fltd_conn_prms.ago_dbpasswd WHEN 'CUB' THEN fltd_conn_prms.cub_dbpasswd ELSE NULL END) AS lcl_dbpasswd,
           (CASE fltd_conn_prms.local_country WHEN 'AGO' THEN 'CUB' WHEN 'CUB' THEN 'AGO' ELSE NULL END) AS rmte_ctry,
           (CASE fltd_conn_prms.local_country WHEN 'AGO' THEN fltd_conn_prms.cub_dbhostaddr WHEN 'CUB' THEN fltd_conn_prms.ago_dbhostaddr ELSE NULL END) AS rmte_dbhostaddr,
           (CASE fltd_conn_prms.local_country WHEN 'AGO' THEN fltd_conn_prms.cub_dbname WHEN 'CUB' THEN fltd_conn_prms.ago_dbname ELSE NULL END) AS rmte_dbname,
           (CASE fltd_conn_prms.local_country WHEN 'AGO' THEN fltd_conn_prms.cub_dbuser WHEN 'CUB' THEN fltd_conn_prms.ago_dbuser ELSE NULL END) AS rmte_dbuser,
           (CASE fltd_conn_prms.local_country WHEN 'AGO' THEN fltd_conn_prms.cub_dbpasswd WHEN 'CUB' THEN fltd_conn_prms.ago_dbpasswd ELSE NULL END) AS rmte_dbpasswd,
           current_setting('TIMEZONE') AS timezone
    INTO sync_connrcd
    FROM fltd_conn_prms;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'Exception text...';
    END IF;

    RETURN sync_connrcd;
EXCEPTION
    WHEN others THEN
        RAISE NOTICE 'SQLERRM -> %', quote_nullable(SQLERRM);
        INSERT INTO sync.sync_exec_log (log_type, log_msg) VALUES (1, SQLERRM);
        RETURN sync_connrcd;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;

这是我期望结果的功能:

CREATE OR REPLACE FUNCTION sync.validate_config (
    // input params...
)
RETURNS pg_catalog.void AS
$body$
DECLARE
    conn_data sync.sync_conn_data;
    // ... 
BEGIN
    SELECT sync.sync_connect('sync_cfg_mgmt') INTO conn_data;

    IF conn_data.lcl_ctry IS NULL OR conn_data.lcl_dbname IS NULL OR conn_data.lcl_dbhostaddr IS NULL OR conn_data.lcl_dbuser IS NULL OR conn_data.lcl_dbpasswd IS NULL OR conn_data.rmte_ctry IS NULL OR conn_data.rmte_dbname IS NULL OR conn_data.rmte_dbhostaddr IS NULL OR conn_data.rmte_dbuser IS NULL OR conn_data.rmte_dbpasswd IS NULL THEN
        RAISE EXCEPTION 'No data obtained';
    END IF;

    // more code ...
EXCEPTION
    WHEN others THEN
        RAISE NOTICE 'SQLERRM -> %', quote_nullable(SQLERRM);
        INSERT INTO sync.sync_exec_log (log_type, log_msg) VALUES (1, quote_nullable(SQLERRM));
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;

一个有趣的事实是,使用PostgreSQL的EMS SQL Manager进行调试,此代码可以完美地工作,但是当通过任何客户端执行功能sync.validate_config()时,都会抛出未获得任何数据的异常,“导致所有conn_data对象中的字段为null,但第一个字段除外,该字段包含所有对象信息。这是在函数中得到结果后将这个倍数RAISE NOTICE放入的结果:

RAISE NOTICE '%', conn_data.sync_conn_name;
RAISE NOTICE '%', conn_data.sync_active;
RAISE NOTICE '%', conn_data.sync_use_ssl;
RAISE NOTICE '%', conn_data.lcl_ctry;
RAISE NOTICE '%', conn_data.lcl_dbhostaddr;
RAISE NOTICE '%', conn_data.lcl_dbname;
RAISE NOTICE '%', conn_data.lcl_dbuser;
RAISE NOTICE '%', conn_data.lcl_dbpasswd;
RAISE NOTICE '%', conn_data.rmte_ctry;
RAISE NOTICE '%', conn_data.rmte_dbhostaddr;
RAISE NOTICE '%', conn_data.rmte_dbname;
RAISE NOTICE '%', conn_data.rmte_dbuser;
RAISE NOTICE '%', conn_data.rmte_dbpasswd;
RAISE NOTICE '%', conn_data.timezone;

conn_data object info 拜托,有人知道这是怎么回事吗?

谢谢。

1 个答案:

答案 0 :(得分:0)

由于没有人回答我的问题,并且我有点着急,因此我寻找解决该问题的替代方法。我现在分享我找到的解决方案,以防其他任何人遇到这种问题。

我将de plpgsql hstore module用于sync.sync_connect()函数的返回类型,以这种方式:

CREATE OR REPLACE FUNCTION sync.sync_connect (
    sync_conn_name VARCHAR = 'sync_db'::VARCHAR
)
RETURNS hstore AS
$body$
DECLARE
    sync_connrcd sync.sync_conn_data;
BEGIN
    /* Same code as before, where sync_connrcd is filled */

    RAISE NOTICE 'conn_data -> %', row_to_json(sync_connrcd);
    RETURN hstore(sync_connrcd);
EXCEPTION
    WHEN others THEN
        RAISE NOTICE 'SQLERRM -> %', quote_nullable(SQLERRM);
        INSERT INTO sync.sync_exec_log (log_type, log_msg) VALUES (1, SQLERRM);
        RETURN hstore(sync_connrcd);
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;

然后,在sync.validate_config()函数中,通过以下方式将结果存储到hstore对象中:

CREATE OR REPLACE FUNCTION sync.validate_config (
    /* input params */
)
RETURNS pg_catalog.void AS
$body$
DECLARE
    conn_data hstore;
    /* more variables */
BEGIN
    SELECT sync.sync_connect('sync_cfg_mgmt') INTO conn_data;

    IF NOT exist(conn_data, 'lcl_ctry') OR NOT defined(conn_data, 'lcl_ctry') OR 
       NOT exist(conn_data, 'lcl_dbname') OR NOT defined(conn_data, 'lcl_dbname') OR 
       NOT exist(conn_data, 'lcl_dbhostaddr') OR NOT defined(conn_data, 'lcl_dbhostaddr') OR 
       NOT exist(conn_data, 'lcl_dbuser') OR NOT defined(conn_data, 'lcl_dbuser') OR 
       NOT exist(conn_data, 'lcl_dbpasswd') OR NOT defined(conn_data, 'lcl_dbpasswd') OR 
       NOT exist(conn_data, 'rmte_ctry') OR NOT defined(conn_data, 'rmte_ctry') OR 
       NOT exist(conn_data, 'rmte_dbname') OR NOT defined(conn_data, 'rmte_dbname') OR 
       NOT exist(conn_data, 'rmte_dbhostaddr') OR NOT defined(conn_data, 'rmte_dbhostaddr') OR 
       NOT exist(conn_data, 'rmte_dbuser') OR NOT defined(conn_data, 'rmte_dbuser') OR 
       NOT exist(conn_data, 'rmte_dbpasswd') OR NOT defined(conn_data, 'rmte_dbpasswd') THEN
        RAISE EXCEPTION 'No data obtained';
    END IF;

    /* more code */
EXCEPTION
    WHEN others THEN
        RAISE NOTICE 'SQLERRM -> %', quote_nullable(SQLERRM);
        INSERT INTO sync.sync_exec_log (log_type, log_msg) VALUES (1, SQLERRM);
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;

要访问对象中的任何值,请按照conn_data->'rmte_dbname'进行操作。

仅此而已,对我来说一切正常。