Oracle PL/SQL 语句抛出错误

Posted

技术标签:

【中文标题】Oracle PL/SQL 语句抛出错误【英文标题】:Oracle PL/SQL statement throwing errors 【发布时间】:2011-11-09 16:59:00 【问题描述】:

我通过 ADO 将以下 PL/SQL 发送到远程 oracle 11gr2 服务器。 它的目的是检查用户是否存在。然后,如果确实如此,则终止其所有连接。最后它丢弃了用户。

DECLARE   
   i INTEGER;  
BEGIN  
   select count(1) into i from dba_users where username='<schema>';
   if i=0 THEN 
     FOR c IN (SELECT s.sid,s.serial# FROM v$session s WHERE s.username = '<schema>') LOOP    
       EXECUTE IMMEDIATE 'alter system kill session ''' ||c.sid || ',' || c.serial# || '''';  
     END LOOP;   
     drop user <schema> Cascade;   
   END IF;  
END;

经过多次调整后我收到的错误消息仍然是:

错误:[Microsoft][Oracle 的 ODBC 驱动程序][Oracle]ORA-06550:第 1 行, 第 286 列:PLS-00103:预期时遇到符号“DROP” 以下之一:

( begin case declare else elsif end exit for goto if loop mod null pragma raise return select update while with

它不喜欢在 IF 语句中放置 drop 的语法。有谁知道让这个运行正常吗?

编辑: 需要明确的是,我通常不会以这种方式执行此语句。但由于环境原因,这是唯一可行的方法,不会造成任何安全风险。我知道我违反了几乎所有的良好做法,但这次是必要的!

【问题讨论】:

强烈建议您在存储过程中执行此操作。这将完成几件事:1) 您将从问题中删除 jet 解析器并让 Oracle 在内部处理它。 2)您可以构造 SP,使其简单地返回 1 或 0 作为成功/失败,从而拒绝任何注入尝试。此外,请注意 Ollie 的建议并使用绑定参数来进一步保护任何 sql 事务。 @wave:另外,使用count(1) 比使用count(*) 没有优势。此外,SQL 解析引擎无论如何都会将您的count(1) 更改为count(*)。请参阅 Tom Kyte 的许多关于此的文章:Select Count(1): How it works 【参考方案1】:

您不能直接在 PL/SQL 中发出 DDL(即 DROP)语句,您需要使用动态 SQL 运行 DROP 语句。

实现这一点的最简单方法是使用EXECUTE IMMEDIATE 语句(与您已经将它用于ALTER SESSION 命令的方式类似): http://download.oracle.com/docs/cd/B12037_01/appdev.101/b10807/13_elems017.htm

DECLARE
   i INTEGER;
BEGIN
   SELECT COUNT( 1 )
     INTO i
     FROM dba_users
    WHERE username = '<schema>';

   IF i = 0
   THEN
      FOR c IN ( SELECT s.sid,
                        s.serial#
                   FROM v$session s
                  WHERE s.username = '<schema>' )
      LOOP
         EXECUTE IMMEDIATE 'alter system kill session ''' || 
                           c.sid || ',' || c.serial# || '''';
      END LOOP;

      EXECUTE IMMEDIATE 'DROP USER :username CASCADE'
      USING '<schema>';   
   END IF;
END;

顺便说一句,您可能希望考虑使用绑定变量而不是连接动态 SQL 中的值,因为它可以提高性能,尤其是在循环中。

例如

EXECUTE IMMEDIATE 'alter system kill session '':sid'','':serial'''
USING c.sid,
      c.serial#;

希望对你有帮助...

【讨论】:

您不能在 DDL 中使用绑定变量,因此您不能以这种方式参数化 ALTER SYSTEMDROP USER 命令。您必须连接用户名。您可能希望使用DBMS_ASSERT.SCHEMA_NAME 函数来验证传入的用户名是一个简单的模式名称,以防止 SQL 注入攻击,此外还要执行诸如记录调用和验证用户名是否合理(即您不这样做)之类的操作。不希望有人意外删除 Oracle 提供的帐户) 您不能在 DDL 中使用绑定变量(即 alter systemdrop user)。 这在摆脱绑定部分后起到了作用。非常感谢。 啊,是的,我在想什么。您不能对 DDL 使用绑定变量。对不起,当我发布答案时,我正冲出门。答案的原则是正确的,为绑定变量红鲱鱼道歉。 Justin 是正确的,您仍然应该使用 DBMS_ASSET 包来保护您的过程免受 SQL 注入。

以上是关于Oracle PL/SQL 语句抛出错误的主要内容,如果未能解决你的问题,请参考以下文章

Oracle中RAISE异常

ORACLE:错误错误(6,3):PL/SQL:SQL 语句被忽略和错误(8,3):PL/SQL:ORA-00933:SQL 命令未在过程中正确结束

Oracle:使用 SQL 或 PL/SQL 查找动态 SQL 中的错误位置

通过 PL/SQL 手动处理交互式网格并不断抛出错误

基本 Oracle 触发器审计表

ORACLE APEX PL SQL 过程错误