在创建掩码返回案例语句中使用非确定性函数时出现问题

Posted

技术标签:

【中文标题】在创建掩码返回案例语句中使用非确定性函数时出现问题【英文标题】:Issue while using a non-deterministic function in create mask return case statement 【发布时间】:2021-12-31 15:14:01 【问题描述】:

我正在尝试在如下表上创建一个 IBMi DB2 掩码:

CREATE or replace MASK IESWEBSERP.MASK_PWD_WSCONTROL ON IESWEBSERP.WSCONTROL
    FOR COLUMN WSVNDPWD    RETURN                                         
    CASE   
        WHEN (  VERIFY_GROUP_FOR_USER(SESSION_USER, 'ICC', 'IICCGRP')=1)
            THEN WSVNDPWD                                                              
        WHEN (  SESSION_USER  in ('QSQSRVR', 'QTMHHTTP', 'PROFOUNDJS', 'IESWEBSVR'))                  
            THEN WSVNDPWD  
        WHEN            IESWEBSERP.IES_CHECK_AUTH( WSDTALIB )  = 1        
            THEN WSVNDPWD
        WHEN     (WSVNDID  = IESWEBSERP.web_login_id )         
            THEN WSVNDPWD 

        ELSE    'MASKED'
    END                                                                 
ENABLE ;  

问题在于 RETURN CASE 中使用的 IESWEBSERP.IES_CHECK_AUTH 用户定义函数。

根据文档,我了解 RETURN CASE 语句不能使用非确定性函数。所以下面是这个 udf 是如何定义的:

Create or REPLACE Function IESWEBSERP.IES_CHECK_AUTH(                                
            DATA_LIBRARY CHAR(10))                                             
     returns dec(1,0)                                                
     language SQL
     NO EXTERNAL ACTION
     DETERMINISTIC
     NOT FENCED
     SECURED                                                     
  BEGIN

    declare q char(1) ;
    declare back char(1) ;
    declare @sqlStmt1 varchar(500) ;
    declare @sqlStmt varchar(500) ;
    declare myCursor cursor for myStatement;
    declare myCursor1 cursor for myStatement1;
    set q = '''';
    set @sqlStmt1 =  'select count(*) from QSYS2.TABLES WHERE TABLE_SCHEMA = ' 
                    concat q concat TRIM(DATA_LIBRARY) concat q  
                    concat ' and TABLE_NAME = ' concat q concat 'MSTCONTL' concat q;
    prepare myStatement1  from @SqlStmt1;                    
    open myCursor1;
    Fetch myCursor1 into back;
    close myCursor1;
    IF ( back = '1' )
        THEN 
            SET back = '0' ;
            set @sqlStmt =  'select count(*) from ' concat TRIM(DATA_LIBRARY) concat '.MSTCONTL 
            where USRID = SESSION_USER and (USRC2 =' concat q concat 'U' concat q 
            concat 'or USRC2 = ' concat q concat 'P' concat q concat')'; 

            prepare myStatement  from @SqlStmt;
            open myCursor;
            Fetch myCursor into back;
            close myCursor;
            IF ( back = '1' )
              THEN SET back = '1' ;
            ELSE SET back = '0' ;
               

            END IF ;
 
       ELSE SET back = '0' ;
    END IF ;

    return(back );
END;   

UDF 所做的只是检查当前会话用户的用户记录的字段 MSTCONTL.USRC2 的值是“U”还是“P”。如果是这样,它会返回一个值“1”,这将允许用户在没有任何掩码的情况下查看字段 WSVNDPWD 的值。

但问题是,如果 MSTCONTL.USRC2 的值更新为 U 或 P 以外的值,该函数仍将返回“1”(如果之前也返回了“1”)。我相信这是因为函数被定义为 DETERMINISTIC。

如果我将函数设为非 DETERMINSTIC,则创建掩码语句将失败。所以我不确定如何处理这种情况。我想要函数的“动态”结果。请问有人可以建议如何处理吗?

【问题讨论】:

【参考方案1】:

SQL 语句有两种确定性:全局和语句。通过使用上面的 deterministic 关键字,您可以使函数具有全局确定性,这意味着它期望每次使用相同参数的调用总是返回与第一次调用相同的结果。

您可以将其更改为 statement deterministic,我怀疑这会满足您的需求。语句确定性意味着此调用每次在此特定语句调用中使用时都将返回相同的结果,但在下次调用此语句时可能会更改。这意味着每次使用此语句时,它都会重新查找以缓存该值,并重复使用该值,直到此特定调用完成。

语句确定性和非确定性之间的最大区别在于,如果有人在非确定性函数中更改此列的值,理论上它可以在处理结果的中途更改掩码。语句确定性不允许这样做,但它确实允许在语句的两次单独运行之间更改掩码结果。

请注意,https://www.ibm.com/docs/en/i/7.4?topic=statements-create-mask#fntarg_1 此处的文档确实说在 SQL 掩码过程中不建议使用语句确定性,但它没有给出原因。我怀疑这是出于性能原因,因此您必须确定可能的性能或其他影响对于您的情况是否值得。

【讨论】:

哇,非常感谢!!你赢得了赏金!

以上是关于在创建掩码返回案例语句中使用非确定性函数时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

创建案例表达式时出现问题

为啥在我的 Postgres 函数中使用 IF 语句时出现语法错误?

尝试使用 if 语句创建函数时出现 Postgresql 语法错误

无法从函数返回的变量索引到空数组,或访问返回的正则表达式数据时出现问题

在块中返回非指针对象时出现问题

在 C# 中使用非原始类型重新创建 C++ 联合类型时出现对齐错误