2015-04-15 2595 views
3

我只是想阻止用户运行没有WHERE子句的UPDATE语句。我下面提供无效的SQL类型:sqlKind = UNINITIALIZED - PLSQL错误

create or replace PROCEDURE secure_update(update_query IN varchar2) 
IS 
    msg varchar2(30000); 
    flag char(1); 
    qry varchar2(30000); 
BEGIN 

    IF upper(update_query) LIKE 'UPDATE%SET%WHERE%=%' THEN 
    flag := '1'; 
    ELSE 
    flag := '0'; 
    END IF; 

    IF (flag = '1') THEN 
    --qry := update_query; 
    execute immediate update_query into msg; 
    END IF; 

    dbms_output.put_line(msg); 

END; 

这就是我如何执行它

EXEC secure_update 
(' 
UPDATE dummy_table 
SET col1 = ''whatever'' 
WHERE pk = ''1234'' 
') 

我不断收到这个消息,我的PLSQL:

无效的SQL类型:sqlKind =未初始化

你能帮我找出如何克服这个呃ROR?

+0

当使用'动态sql'时,尝试将varchar值放在两个两个引号内'''whatever'''我的意思是用''''''' – jfun

+0

除了你看到的错误之外,不平衡单引号,而'exec'必须在一行,我不确定你的目标。如果你想检查一个'where'子句,所以调用者不能一次更新所有记录,是什么阻止他们做'where 1 = 1'? –

+0

这个plsql用于实验目的。在这一点上我没有考虑安全 – user311509

回答

2

这工作,请看到的变化,不要使用到子句中执行即时

create or replace PROCEDURE secure_update(update_query IN varchar2) 
IS 
    msg varchar2(30000); 
    flag char(1); 
    qry varchar2(30000); 
BEGIN 

    IF upper(update_query) LIKE 'UPDATE%SET%WHERE%=%' THEN 
    flag := '1'; 
    dbms_output.put_line('updated succesfully'); 
    ELSE 
    flag := '0'; 
    dbms_output.put_line('no where clause in update'); 
    END IF; 

    IF (flag = '1') THEN 
    --qry := update_query; 
    execute immediate update_query ; 
    END IF; 

    END; 

,如果你想在更新使用VARCHAR,那么请参阅本

[email protected] 16-APR-15> select * from test2; 

A  B 
----- ----- 
a  b 

code to execute procedure : 

declare 
lsql varchar2(100):= 'update test2 set a=''z'' where b=''b'' '; 
begin 
secure_update(lsql); 
end; 

output:  updated succesfully 

[email protected] 16-APR-15> select * from test2; 

A  B 
----- ----- 
z  b 

declare 
lsql varchar2(100):= 'update test2 set a=''z'''; 
begin 
secure_update(lsql); 
end; 

output 

no where clause in update  

另一个例子

[email protected] 16-APR-15> select * from test1; 

     VAL1  VAL2  VAL3 
---------- ---------- ---------- 
     2   2   4 
     3   2   4 
     123   2   3 
     42   3 

[email protected] 16-APR-15> exec secure_update('update test1 set val1=555 where val1=2'); 
updated succesfully 

PL/SQL procedure successfully completed. 

[email protected] 16-APR-15> select * from test1; 

     VAL1  VAL2  VAL3 
---------- ---------- ---------- 
     555   2   4 
     3   2   4 
     123   2   3 
     42   3 


[email protected] 16-APR-15> exec secure_update('update test1 set val1=555'); 
no where clause in update 

PL/SQL procedure successfully completed. 
0

UPDATE声明不应执行into什么,它应该只是执行。此代码演示错误,但错误消息与您发布的错误消息略有不同。

create table dummy_table(col1 varchar2(100), pk number); 

declare 
    msg varchar2(30000); 
begin 
    execute immediate q'[ 
     UPDATE dummy_table 
     SET col1 = 'whatever' 
     WHERE pk = '1234' 
    ]' into msg; 
end; 
/

ORA-01007: variable not in select list 
ORA-06512: at line 4 

删除into msg它会工作。另外,可以使用替代引用机制来避免转义引号。

+0

顺便说一下,您是否在尝试构建一个通用程序来处理任何类型的动态SQL语句?如果是这样,你可能需要做很多工作。只要弄清楚它是什么类型的声明并获得适当的反馈信息就很复杂。我正在研究这个系统[这里](https://github.com/jonheller1/plsql_lexer)。它几个星期不会准备好,但它可能会让你知道任务有多困难。 –

1

SQL Developer(和SQL * Plus,但可能不是其他客户端!)需要exec命令在一条线上。你没有告诉你会从调用了前面的错误:

EXEC secure_update 
Error report - 
ORA-06550: line 1, column 7: 
PLS-00306: wrong number or types of arguments in call to 'SECURE_UPDATE' 
ORA-06550: line 1, column 7: 
PL/SQL: Statement ignored 
06550. 00000 - "line %s, column %s:\n%s" 
*Cause: Usually a PL/SQL compilation error. 
*Action: 

Error starting at line : 25 in command - 
(' 
UPDATE dummy_table 
SET col1 = ''whatever'' 
WHERE pk = ''1234'' 
') 
Error at Command Line : 25 Column : 2 
Error report - 
SQL Error: Invalid SQL type: sqlKind = UNINITIALIZED 

的过程被称为不带参数,因为根本不存在在同一行的实际exec,使你得到一个PLS -00306。然后剩余部分被解释为一个单独的命令,它会得到您报告的'无效SQL类型'错误。 (在SQL Developer中,即在SQL * Plus中,第一部分的PLS-00306相同,其余为ORA-00928: missing SELECT keyword)。

您可以将整个语句插入一行:

EXEC secure_update ('UPDATE dummy_table SET col1 = ''whatever'' WHERE pk = ''1234'''); 

或者使用一个明确的匿名块,而不是exec速记:

BEGIN 
    secure_update 
    ('UPDATE dummy_table 
    SET col1 = ''whatever'' 
    WHERE pk = ''1234'' 
    '); 
    dbms_output.put_line('after'); 
END; 
/

还请注意,我不得不搬到无论如何,UPDATE上线,因为你的支票不允许任何空格(包括换行符)在命令的开始。你的程序中的into msg没有做任何事情,但似乎没有造成问题。如果您想查看更新了多少行,请在execute immediate之后使用SQL%ROWCOUNT。