2012-04-13 10 views
5

Ho un file SQL che voglio analizzare ed eseguire in oracle usando la libreria python cx_Oracle. Il file SQL contiene sia classici DML/DDL e PL/SQL, ad es. esso può assomigliare a questo:Analizza file SQL con PL/SQL e DML/DDL utilizzando cx_Oracle in python

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); 

se uso questo file nella SQLDeveloper o SQL * Plus, che saranno divisi in 3 domande e giustiziati.

Tuttavia, cx_Oracle.connect (...). Cursor(). Execute (...) può richiedere solo UNA query alla volta, non un intero file. Non posso semplicemente dividere la stringa usando string.split(';') (come suggerito qui execute a sql script file from cx_oracle?), perché entrambi i commenti saranno divisi (e causeranno un errore) e il blocco PL/SQL non verrà eseguito come singolo comando, causando quindi un errore.

Nel forum Oracle (https://forums.oracle.com/forums/thread.jspa?threadID=841025) ho trovato che cx_Oracle non supporta l'intero file di analisi. La mia domanda è - c'è uno strumento per fare questo per me? Per esempio. una libreria Python che posso chiamare per dividere il mio file in query?

Modifica: le migliori soluzioni sembrano utilizzare direttamente SQL * Plus. Ho usato questo codice:

# open the file 
f = open(file_path, 'r') 
data = f.read() 
f.close() 

# add EXIT at the end so that SQL*Plus ends (there is no --no-interactive :(
data = "%s\n\nEXIT" % data 

# write result to a temp file (required, SQL*Plus takes a file name argument) 
f = open('tmp.file', 'w') 
f.write(data) 
f.close() 

# execute SQL*Plus 
output = subprocess.check_output(['sqlplus', '%s/%[email protected]%s' % (db_user, db_password, db_address), '@', 'tmp.file']) 

# if an error was found in the result, raise an Exception 
if output.find('ERROR at line') != -1: 
    raise Exception('%s\n\nStack:%s' % ('ERROR found in SQLPlus result', output)) 
+0

Stesso problema qui. Fondamentalmente, Oracle è braindead e in realtà non ha * alcuna * abilità incorporata per analizzare script SQL a più istruzioni, quindi SQL * Plus e SQL Developer e TOAD implementano tutti i propri parser * own * :-( –

risposta

2

È possibile eseguire più istruzioni allo stesso tempo ma è semi-hacky. Devi avvolgere le tue affermazioni ed eseguirle una alla volta.

>>> import cx_Oracle 
>>> 
>>> a = cx_Oracle.connect('schema/[email protected]') 
>>> curs = a.cursor() 
>>> SQL = (("""create table tmp_test (a date)"""), 
... ("""insert into tmp_test values (sysdate)""") 
...) 
>>> for i in SQL: 
...  print i 
... 
create table tmp_test (a date) 
insert into tmp_test values (sysdate) 
>>> for i in SQL: 
...  curs.execute(i) 
... 
>>> a.commit() 
>>> 

Come hai notato, questo non risolve il problema del punto e virgola, per il quale non esiste una risposta facile. Come la vedo io si hanno 3 opzioni:

  1. scrive un parser troppo complicata, che non credo sia una buona opzione a tutti.

  2. Non eseguire script SQL da Python; avere il codice in entrambi gli script SQL separati in modo che l'analisi sia semplice, in un file Python separato, incorporato nel codice Python, in una procedura nel database ... ecc. Questa è probabilmente la mia opzione preferita.

  3. Utilizzare subprocess e chiamare lo script in questo modo. Questa è l'opzione più semplice e veloce, ma non utilizza affatto lo cx_Oracle.

    >>> import subprocess 
    >>> cmdline = ['sqlplus','schema/[email protected]','@','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 
    >>> 
    
+0

Ciao, il sottoprocesso .call sembra una soluzione ragionevole (non posso usare la seconda scelta perché non riesco a controllare il file SQL) Tuttavia, quando eseguo sqlplus come sottoprocesso, aspetterà il comando "QUIT". Il mio script Python deve essere non interattivo (ci saranno diversi file SQL eseguiti in questo modo). Come cambieresti in modo che non aspetti lo stdin? –

+0

@Savannah, non puoi aggiungere lo smettere agli script SQL? Chi li genera dovrebbe essere – Ben

+0

Ho aggiunto qualcosa come data = "% s \ n \ nEXIT"% dati allo script che corro e ora funziona –