在python中使用cx_Oracle使用PL/SQL和DML/DDL解析SQL文件

在python中使用cx_Oracle使用PL/SQL和DML/DDL解析SQL文件,python,oracle,cx-oracle,Python,Oracle,Cx Oracle,我有一个SQL文件,我想使用cx\u oraclepython库在oracle中解析和执行它。SQL文件包含经典的DML/DDL和PL/SQL,例如,它可以如下所示: create.sql: -- This is some ; malicious comment CREATE TABLE FOO(id numeric); BEGIN INSERT INTO FOO VALUES(1); INSERT INTO FOO VALUES(2); INSERT INTO FOO VALUE

我有一个SQL文件,我想使用
cx\u oracle
python库在oracle中解析和执行它。SQL文件包含经典的DML/DDL和PL/SQL,例如,它可以如下所示:

create.sql

-- This is some ; malicious comment
CREATE TABLE FOO(id numeric);

BEGIN
  INSERT INTO FOO VALUES(1);
  INSERT INTO FOO VALUES(2);
  INSERT INTO FOO VALUES(3);
END;
/
CREATE TABLE BAR(id numeric);
如果我在SQLDeveloper或SQL*Plus中使用此文件,它将被拆分为3个查询并执行

但是,cx_Oracle.connect(…).cursor().execute(…)一次只能执行一个查询,而不能执行整个文件。我不能简单地使用
string.split(“;”)
(如本文所建议的)拆分字符串,因为注释将被拆分(并将导致错误),而PL/SQL块将不会作为单个命令执行,从而导致错误

在Oracle论坛()上,我发现cx_Oracle本身不支持解析整个文件。我的问题是,有没有一个工具可以帮我做到这一点?我可以调用python库将我的文件拆分为查询

编辑:最好的解决方案似乎直接使用SQL*Plus。我使用了以下代码:

#打开文件
f=打开(文件路径“r”)
data=f.read()
f、 关闭()
#在末尾添加EXIT,以便SQL*Plus结束(没有--no interactive:(
data=“%s\n\nEXIT”%data
#将结果写入临时文件(必需,SQL*Plus采用文件名参数)
f=打开('tmp.file','w')
f、 写入(数据)
f、 关闭()
#执行SQL*Plus
output=子进程。检查\u输出(['sqlplus','%s/%s@%s'(db\u用户,db\u密码,db\u地址),'@','tmp.file'])
#如果在结果中发现错误,则引发异常
如果输出.find('ERROR at line')!=-1:
引发异常(“%s\n\n堆栈:%s%”(在SQLPlus结果中发现错误,输出))

可以同时执行多条语句,但这是一种半黑客操作。您需要包装语句,然后一次执行一条语句

导入cx\u Oracle >>> >>>a=cx\U Oracle.connect('架构/pw@db') >>>curs=a.cursor() >>>SQL=(“创建表tmp_测试(a日期)”), …(“插入tmp_测试值(系统日期)”) ... ) >>>对于SQL中的i: …打印i ... 创建tmp_测试表(日期) 插入tmp_测试值(sysdate) >>>对于SQL中的i: …游标执行(i) ... >>>a.提交() >>> 正如您所指出的,这并不能解决分号问题,因为分号问题没有简单的答案。在我看来,您有3个选项:

  • 编写一个过于复杂的解析器,我认为这根本不是一个好的选择

  • 不要从Python中执行SQL脚本;将代码放在单独的SQL脚本中,以便在单独的Python文件中、嵌入在Python代码中、在数据库中的过程中等方便地进行解析。这可能是我的首选选项

  • 使用
    subprocess
    并以这种方式调用脚本。这是最简单、最快速的选项,但根本不使用
    cx\u Oracle

    >>> import subprocess
    >>> cmdline = ['sqlplus','schema/pw@db','@','tmp_test.sql']
    >>> subprocess.call(cmdline)
    
    SQL*Plus: Release 9.2.0.1.0 - Production on Fri Apr 13 09:40:41 2012
    
    Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.
    
    
    Connected to:
    Oracle Database 11g Release 11.2.0.1.0 - 64bit Production
    
    SQL>
    SQL> CREATE TABLE FOO(id number);
    
    Table created.
    
    SQL>
    SQL> BEGIN
      2    INSERT INTO FOO VALUES(1);
      3    INSERT INTO FOO VALUES(2);
      4    INSERT INTO FOO VALUES(3);
      5  END;
      6  /
    
    PL/SQL procedure successfully completed.
    
    SQL> CREATE TABLE BAR(id number);
    
    Table created.
    
    SQL>
    SQL> quit
    Disconnected from Oracle Database 11g Release 11.2.0.1.0 - 64bit Production
    0
    >>>
    

  • 您好,subprocess.call似乎是一个合理的解决方案(我不能使用第二个选项,因为我无法控制SQL文件)。但是,当我作为subprocess运行sqlplus时,它将等待“QUIT”命令。我的Python脚本需要非交互式(将以这种方式执行多个SQL文件)。您将如何更改它,使其不会等待stdin?@Savannah,您不能将quit添加到SQL脚本中吗?生成它们的人应该确保它们退出。我添加了类似data=“%s\n\nEXIT”的内容%data添加到我运行的脚本中,它现在可以工作了。使用subprocess.Popen而不是call将允许您从Python结束会话,而无需将exit命令添加到SQL脚本中-请参见这里的相同问题。基本上,Oracle是死板的,实际上没有任何内置的解析多语句SQL脚本的能力,因此SQLPlus和SQL Developer和TOAD都实现了自己的解析器:-(