2012-04-13 15 views
5

Mam plik SQL, który chcę przeanalizować i wykonać w oracle przy użyciu biblioteki python cx_Oracle. Plik SQL zawiera zarówno klasyczne DML/DDL i PL/SQL, np. może to wyglądać tak:Parsować plik SQL z PL/SQL i DML/DDL używając cx_Oracle w pythonie

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

jeśli mogę użyć tego pliku w sqldeveloper lub SQL * Plus, zostanie on podzielony na 3 pytania i stracony.

Jednak cx_Oracle.connect (...). Cursor(). Execute (...) może wykonać tylko JEDNĄ kwerendę naraz, a nie cały plik. Nie mogę po prostu podzielić ciągu znaków za pomocą string.split(';') (zgodnie z sugestią tutaj execute a sql script file from cx_oracle?), ponieważ zarówno komentarz zostanie podzielony (i spowoduje błąd), a blok PL/SQL nie zostanie wykonany jako pojedyncze polecenie, powodując błąd.

Na forum Oracle (https://forums.oracle.com/forums/thread.jspa?threadID=841025) Stwierdziłem, że sam cx_Oracle nie obsługuje takich rzeczy, jak parsowanie całego pliku. Moje pytanie brzmi - czy istnieje narzędzie, które może to dla mnie zrobić? Na przykład. biblioteka Pythona, którą mogę wywołać, aby podzielić mój plik na zapytania?

Edycja: Najlepsze rozwiązania wydają się korzystać bezpośrednio z SQL * Plus. Użyłem tego kodu:

# 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

sam problem tutaj. Zasadniczo, Oracle jest braindead i nie ma * żadnej * wbudowanej możliwości analizowania skryptów SQL wielu oświadczeń, więc SQL * Plus i SQL Developer oraz TOAD implementują swoje * własne * parsery :-( –

Odpowiedz

2

Możliwe jest wykonywanie wielu instrukcji w tym samym czasie, ale jest pół-hacky. Musisz zawijać wyciągi i wykonywać je pojedynczo.

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

Jak już zauważyłeś, nie rozwiązuje to problemu średniego, dla którego nie ma łatwej odpowiedzi. Jak widzę, masz 3 opcje:

  1. Napisz zbyt skomplikowany analizator składni, który moim zdaniem nie jest dobrym rozwiązaniem.

  2. Nie należy wykonywać skryptów SQL z Python; mieć kod w oddzielnych skryptach SQL, więc parsowanie jest łatwe, w osobnym pliku Pythona, osadzonym w kodzie Pythona, w procedurze w bazie danych ... itd. Jest to prawdopodobnie moja preferowana opcja.

  3. Użyj subprocess i wywołaj skrypt w ten sposób. Jest to najprostsza i najszybsza opcja, ale w ogóle nie korzysta z 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

Witam, podproces .call wydaje się być rozsądnym rozwiązaniem (nie mogę użyć drugiego wyboru, ponieważ nie mogę kontrolować pliku SQL), ale gdy uruchomię sqlplus jako podproces, będzie czekać na polecenie "QUIT" .Program My Python musi być nieinteraktywny (w ten sposób będzie wykonywanych kilka plików SQL). Jak byś go zmienił, aby nie czekał na stdin? –

+0

@Savannah, nie możesz dodać quitu do skryptów SQL? Ktokolwiek je wygeneruje, powinien być upewniając się, że wychodzą. – Ben

+0

Dodałem coś takiego jak dane = "% s \ n \ nEXIT"% danych do skryptu, który uruchamiam i działa teraz. –

Powiązane problemy