当没有记录时,FOUND变量设置为“true”

时间:2017-01-09 15:58:36

标签: postgresql plpgsql

PLPGSQL函数中,我有一些行来检查记录是否存在。好吧,即使没有记录,FOUND变量也会设置为TRUE。非常奇怪。

我正在测试的行是:

query:='SELECT i.identity_id FROM app.identity as i,' || quote_ident(schema_name) || '.users as u,' || quote_ident(schema_name) || '.udata as ud WHERE i.identity_id=' || p_identity_id || ' and u.identity_id=i.identity_id and ud.identity_id=i.identity_id';
        RAISE WARNING 'query=%',query;
        PERFORM query;
        RAISE WARNING 'FOUND=%',FOUND;
        IF FOUND 
        THEN
            RAISE WARNING 'Record found, rising an exception';
            RAISE EXCEPTION USING ERRCODE = 'AA002';
        END IF;

这是输出:

WARNING:  query=SELECT i.identity_id FROM app.identity as i,"comp-1049007476".users as u,"comp-1049007476".udata as ud WHERE i.identity_id=-1615382132 and u.identity_id=i.identity_id and ud.identity_id=i.identity_id
WARNING:  FOUND=t
WARNING:  Record found, rising an exception

这是pg_log

 2017-01-09 09:47:21.906 CST > LOG:  execute <unnamed>: SELECT app.create_identity($1::varchar,'') as identity_id
< 2017-01-09 09:47:21.906 CST > DETAIL:  parameters: $1 = 'someuser@domain.com'
< 2017-01-09 09:47:21.908 CST > WARNING:  new_identity_id=-1615382132
< 2017-01-09 09:47:21.908 CST > CONTEXT:  PL/pgSQL function app.create_identity(character varying,character varying) line 18 at RAISE
< 2017-01-09 09:47:21.917 CST > LOG:  execute <unnamed>: select app.add_user(       
                        $1::integer,
                        $2::varchar,                     
                        $3::integer,                     
                        $4::varchar,                     
                        $5::varchar,                     
                        $6::varchar,                     
                        $7::varchar,                     
                        $8::varchar,                     
                        $9::varchar,                     
                        $10::varchar,                    
                        $11::integer,                    
                        $12::integer,                    
                        $13::integer,                    
                        $14::integer,                    
                        $15::integer,                    
                        $16::integer,                    
                        $17::smallint,                   
                        $18::boolean,                    
                        $19::boolean,                    
                        $20::boolean,                    
                        $21::boolean,                    
                        $22::boolean                     
                        )
< 2017-01-09 09:47:21.917 CST > DETAIL:  parameters: $1 = '905220468', $2 = '763715373817831', $3 = '-1049007476', $4 = 'Some User', $5 = '44444', $6 = '', $7 = '', $8 = '', $9 = '', $10 = '', $11 = '-1615382132', $12 = '0', $13 = '1', $14 = '0', $15 = '0', $16 = '0', $17 = '0', $18 = 't', $19 = 'f', $20 = 'f', $21 = 'f', $22 = 'f'
< 2017-01-09 09:47:21.919 CST > WARNING:  query=SELECT i.identity_id FROM app.identity as i,"comp-1049007476".users as u,"comp-1049007476".udata as ud WHERE i.identity_id=-1615382132 and u.identity_id=i.identity_id and ud.identity_id=i.identity_id
< 2017-01-09 09:47:21.919 CST > CONTEXT:  PL/pgSQL function app.add_user(integer,character varying,integer,character varying,character varying,character varying,character varying,character varying,character varying,character varying,integer,integer,integer,integer,integer,integer,smallint,boolean,boolean,boolean,boolean,boolean) line 25 at RAISE
< 2017-01-09 09:47:21.919 CST > WARNING:  FOUND=t
< 2017-01-09 09:47:21.919 CST > CONTEXT:  PL/pgSQL function app.add_user(integer,character varying,integer,character varying,character varying,character varying,character varying,character varying,character varying,character varying,integer,integer,integer,integer,integer,integer,smallint,boolean,boolean,boolean,boolean,boolean) line 27 at RAISE
< 2017-01-09 09:47:21.919 CST > WARNING:  Record found, rising an exception
< 2017-01-09 09:47:21.919 CST > CONTEXT:  PL/pgSQL function app.add_user(integer,character varying,integer,character varying,character varying,character varying,character varying,character varying,character varying,character varying,integer,integer,integer,integer,integer,integer,smallint,boolean,boolean,boolean,boolean,boolean) line 30 at RAISE
< 2017-01-09 09:47:21.919 CST > ERROR:  Identity already registered

为什么我确定记录不存在?好吧,我把我知道的值放在表格中,但为了以防万一,我确认:

dev=> SELECT i.identity_id FROM app.identity as i,"comp-1049007476".users as u,"comp-1049007476".udata as ud WHERE i.identity_id=-1615382132 and u.identity_id=i.identity_id and ud.identity_id=i.identity_id;
 identity_id 
-------------
(0 rows)

dev=> 

参见,0记录。所以,大问题是,为什么FOUND变量谎言?这里发生了什么?

这是我的功能的完整代码:

CREATE OR REPLACE FUNCTION app.add_user(sess_identity_id int,session_str varchar,sess_company_id int,
    p_full_name         varchar,
    p_mob_phone         varchar,
    p_home_phone        varchar,
    p_work_phone        varchar,
    p_phone_ext         varchar,
    p_position          varchar,
    p_notes             varchar,
    p_identity_id       integer,
    p_user_id           integer,
    p_group_id          integer,
    p_shift_id          integer,
    p_boss_id           integer,
    p_crreated_by       integer,
    p_timezone          smallint,
    p_utype             boolean,
    p_create_root       boolean,
    p_has_subord        boolean,
    p_inactive          boolean,
    p_generic           boolean 
)
RETURNS integer as $$
DECLARE
    v_session                   bigint;
    schema_name                 varchar;
    query                       varchar;
    current_ts                  integer;
    v_user_id                   integer;
    v_generic                   integer;
    v_udata_id                  integer;
    _c                          text;
BEGIN
        SELECT extract(epoch from now())::integer into current_ts;
        SELECT session_str::bigint INTO v_session;
        schema_name:='comp' || sess_company_id;

        IF NOT EXISTS (
            SELECT 1 FROM app.session WHERE app.session.identity_id=sess_identity_id AND app.session.session=v_session
        ) THEN
            RAISE EXCEPTION USING ERRCODE = 'AA001';
        END IF;

        query:='SELECT i.identity_id FROM app.identity as i,' || quote_ident(schema_name) || '.users as u,' || quote_ident(schema_name) || '.udata as ud WHERE i.identity_id=' || p_identity_id || ' and u.identity_id=i.identity_id and ud.identity_id=i.identity_id';
        RAISE WARNING 'query=%',query;
        PERFORM query;
        RAISE WARNING 'FOUND=%',FOUND;
        IF FOUND 
        THEN
            RAISE WARNING 'Record found, rising an exception';
            RAISE EXCEPTION USING ERRCODE = 'AA002';
        END IF;

        query:='SELECT u.user_id,u.generic FROM ' || quote_ident(schema_name) || '.users as u,identity as i WHERE i.email=p_email AND i.identity_id=u.identity_id';
        RAISE WARNING 'query=%',query;
        EXECUTE query INTO v_user_id,v_generic;
        IF NOT FOUND
        THEN
            EXECUTE 'INSERT INTO $1.users(
                identity_id,
                group_id,
                shift_id,
                boss_id,
                created_by,
                date_inserted
                timezone,
                utype,
                create_root,
                has_subord,
                inactive,
                generic
            ) VALUES ($2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) RETURNING user_id' 
                INTO v_user_id 
                USING schema_name,p _identity_id,p_group_id,p_shift_id,p_boss_id,p_created_by,p_date_inserted,p_timezone,p_utype,p_create_root,p_has_subord,p_inactive,p_generc;
        END IF;
        if NOT v_generic THEN
            RAISE EXCEPTION USING ERRCODE = 'AA003';
        END IF;
        EXECUTE 'INSERT INTO $1.udata(user_id,identity_id,date_insert,full_name,mob_phone,home_phone,work_phone,phone_ext,position,notes) VALUES ($2,$3,$4,$5,$6,$7,$8,$9,$10,$11)'
                INTO v_udata_id
                USING schema_name,v_user_id,p_identity_id,current_ts,p_full_name,p_mob_phone,p_home_phone,p_work_phone,p_phone_ext,p_position,p_notes;
        RETURN v_udata_id;
EXCEPTION
    WHEN SQLSTATE 'AA001' THEN
        RAISE EXCEPTION USING ERRCODE = 'AA001', message = 'Invalid session';
    WHEN SQLSTATE 'AA002' THEN
        RAISE EXCEPTION USING ERRCODE = 'AA002', message = 'Identity already registered';
    WHEN SQLSTATE 'AA003' THEN
        RAISE EXCEPTION USING ERRCODE = 'AA003', message = 'Not a generic user, can not add email address';
    WHEN OTHERS THEN
        RAISE NOTICE 'add_user() failed with... error: % %',SQLSTATE,SQLERRM;
        GET STACKED DIAGNOSTICS _c = PG_EXCEPTION_CONTEXT;
        RAISE NOTICE 'context: >>%<<', _c;
        RAISE EXCEPTION USING MESSAGE = 'An error which is not handled by function';
END
$$ LANGUAGE plpgsql;

表结构,以防万一:

dev=> \d+ app.identity;
                                    Table "app.identity"
    Column     |          Type          | Modifiers | Storage  | Stats target | Description 
---------------+------------------------+-----------+----------+--------------+-------------
 identity_id   | integer                | not null  | plain    |              | 
 created_by    | integer                |           | plain    |              | 
 email         | character varying(128) |           | extended |              | 
 date_inserted | integer                |           | plain    |              | 
 password      | character varying(32)  |           | extended |              | 
 validated     | smallint               |           | plain    |              | 
Indexes:
    "identity_pkey" PRIMARY KEY, btree (identity_id)
    "identity_email_key" UNIQUE CONSTRAINT, btree (email)

dev=> \d+ "comp-1049007476".users;
                                                        Table "comp-1049007476.users"
    Column     |   Type   |                                 Modifiers                                 | Storage | Stats target | Description 
---------------+----------+---------------------------------------------------------------------------+---------+--------------+-------------
 user_id       | integer  | not null default nextval('"comp-1049007476".users_user_id_seq'::regclass) | plain   |              | 
 identity_id   | integer  | default 0                                                                 | plain   |              | 
 group_id      | integer  | default 0                                                                 | plain   |              | 
 shift_id      | integer  | default 0                                                                 | plain   |              | 
 boss_id       | integer  | default 0                                                                 | plain   |              | 
 created_by    | integer  | default 0                                                                 | plain   |              | 
 date_inserted | integer  | default 0                                                                 | plain   |              | 
 timezone      | smallint | default 0                                                                 | plain   |              | 
 utype         | boolean  | default false                                                             | plain   |              | 
 create_root   | boolean  | default false                                                             | plain   |              | 
 has_subord    | boolean  | default false                                                             | plain   |              | 
 inactive      | boolean  | default false                                                             | plain   |              | 
 generic       | boolean  | default false                                                             | plain   |              | 

dev-> \d+ "comp-1049007476".udata 
                                                                Table "comp-1049007476.udata"
    Column     |          Type          |                                 Modifiers                                  | Storage  | Stats target | Description 
---------------+------------------------+----------------------------------------------------------------------------+----------+--------------+-------------
 udata_id      | integer                | not null default nextval('"comp-1049007476".udata_udata_id_seq'::regclass) | plain    |              | 
 user_id       | integer                | default 0                                                                  | plain    |              | 
 identity_id   | integer                | default 0                                                                  | plain    |              | 
 date_inserted | integer                | default 0                                                                  | plain    |              | 
 full_name     | character varying(64)  | default ''::character varying                                              | extended |              | 
 mob_phone     | character varying(16)  | default ''::character varying                                              | extended |              | 
 home_phone    | character varying(16)  | default ''::character varying                                              | extended |              | 
 work_phone    | character varying(16)  | default ''::character varying                                              | extended |              | 
 phone_ext     | character varying(8)   | default ''::character varying                                              | extended |              | 
 position      | character varying(64)  | default ''::character varying                                              | extended |              | 
 notes         | character varying(128) | default ''::character varying                                              | extended |              | 

dev-> 

1 个答案:

答案 0 :(得分:6)

基本问题在于使用PERFORM语句。此语句专为无需结果处理的功能评估而设计。它不是为动态SQL设计的(其中SQL以字符串形式输入)。

PLpgSQL几乎将所有语句翻译为SELECTPERFORM也不例外。

PERFORM fx(10,20);

被翻译为:

SELECT fx(10,20);

您的代码:

variable := 'SELECT * FROM foo';
PERFORM variable;

被翻译为:

SELECT 'SELECT * FROM foo'; -- it is same like SELECT 'hello';

此查询返回一行,变量FOUND应为true。所以没有什么是奇怪的。

在PLpgSQL中,您应该在静态SQL和动态SQL之间存在很大差异。动态SQL(在运行时汇编查询)仅受EXECUTE语句支持。

使用PERFORM variable是对此语句的简单错误用法 - 但它会生成有效的SQL语句,可以在没有运行时错误的情况下进行评估。