2009-04-22 16 views
6

Ho uno script IronPython che esegue una serie di istruzioni SQL su un database SQL Server. le istruzioni sono stringhe di grandi dimensioni che contengono effettivamente più istruzioni, separate dalla parola chiave "GO". Funziona quando vengono eseguiti da SQL Management Studio e alcuni altri strumenti, ma non in ADO. Così ho diviso le stringhe utilizzando il modulo da 2,5 "re" in questo modo:regex per l'analisi delle istruzioni SQL

splitter = re.compile(r'\bGO\b', re.IGNORECASE) 
for script in splitter.split(scriptBlob): 
    if(script): 
     [... execute the query ...] 

Questo rompe nel raro caso in cui ci sia la parola "andare" in un commento o una stringa. Come diamine avrei lavorato su quello? cioè analizza correttamente questa stringa in due script:

-- this is a great database script! go team go! 
INSERT INTO myTable(stringColumn) VALUES ('go away!') 
/* 
    here are some comments that go with this script. 
*/ 
GO 
INSERT INTO myTable(stringColumn) VALUES ('this is the next script') 

EDIT:

ho cercato di più e trovato questa documentazione di SQL: http://msdn.microsoft.com/en-us/library/ms188037(SQL.90).aspx

Come si è visto, GO deve essere sulla propria linea come suggerito da alcune risposte. Comunque può essere seguito da un numero intero di "conteggio" che in realtà eseguirà il batch di istruzioni più volte (qualcuno lo ha effettivamente usato prima ??) e può essere seguito da commenti a riga singola sulla stessa riga (ma non a multi-linea, ho provato questo) Così l'espressione regolare magia sarebbe simile:.

"(?m)^\s*GO\s*\d*\s*$" 

solo che questa non tiene conto di:

  • una possibile singola riga di commento ("--" seguito da qualsiasi carattere tranne una interruzione di riga) alla fine.
  • l'intera riga all'interno di un commento multi-linea più grande.

Non sono interessato a catturare l'argomento "contare" e utilizzarlo. Ora che ho un po 'di documentazione tecnica, sono allettantemente vicino a scrivere questo "su specifiche" e non dovrò mai più preoccuparmi di questo.

+0

ho upmodded risposte di tutti, grazie per l'aiuto! Ho concesso la risposta a mcassano perché è stato il primo a suggerire che GO potesse essere sempre sulla propria linea, il che mi ha portato a cercare la documentazione per quel comando e alla fine ha portato a una soluzione molto più semplice. –

risposta

8

"GO" è sempre su una linea da solo? Potresti semplicemente dividere in "^ GO $".

+0

dopo aver attivato la corrispondenza multilinea, ho inserito anche uno spazio bianco opzionale, per ogni evenienza. –

+0

Penso che di solito sia sulla sua stessa linea e questa potrebbe essere una soluzione abbastanza buona per questo script. Anche se rigorosamente, questo non sarebbe protetto da un GO sulla propria linea all'interno di un commento multilinea o di una stringa multilinea (anche se molto raro). –

+0

Il mio script aggiornato ora rimedia a questo difetto. –

5

poiché è possibile inserire commenti all'interno di commenti, commenti annidati, commenti all'interno di query, ecc., Non esiste un modo corretto di eseguirlo con espressioni regex.

Proprio immagine il seguente script:

INSERT INTO table (name) VALUES (
-- GO NOW GO 
'GO to GO /* GO */ GO' + 
/* some comment 'go go go' 
-- */ 'GO GO' /* 
GO */ 
) 

Questo senza menzionare:

INSERT INTO table (go) values ('xxx') GO 

L'unico modo sarebbe quello di costruire un parser stateful, invece. Uno che legge un char alla volta e ha un flag che verrà impostato quando si trova all'interno di una stringa delimitatore/delimitato da virgolette/etc e reimpostato al termine, in modo che il codice possa ignorare le istanze "GO" quando si trovano all'interno di esse.

+1

Un parser sarebbe la soluzione migliore, ma se si può garantire che GO sia sempre su una linea da solo, si è abbastanza sicuri, specialmente perché SQL92 non ha commenti multilinea. –

+1

mi hai appena fatto impazzire. –

5

Se GO è sempre su una riga da solo è possibile utilizzare divisa in questo modo:

#!/usr/bin/python 

import re 

sql = """-- this is a great database script! go team go! 
INSERT INTO myTable(stringColumn) VALUES ('go away!') 
/* 
    here are some comments that go with this script. 
*/ 
GO 5 --this is a test 
INSERT INTO myTable(stringColumn) VALUES ('this is the next script')""" 

statements = re.split("(?m)^\s*GO\s*(?:[0-9]+)?\s*(?:--.*)?$", sql) 

for statement in statements: 
    print "the statement is\n%s\n" % (statement) 
  • (?m) accende accostamenti più righe, cioè ^ e $ sarà abbinare inizio e fine linea (al posto di inizio e fine della stringa).
  • ^ partite all'inizio di una linea
  • \s* corrisponde a zero o più spazi bianchi (spazio, tab, ecc)
  • GO partite GO letterale
  • \s* partite come prima
  • (?:[0-9]+)? corrisponde opzionale numero intero (con eventuali zeri)
  • \s* partite come prima
  • (?:--.*)? corrisponde un commento opzionale end-of-line
  • $ partite alla fine di una linea

La spaccatura consumerà la linea GO, quindi non dovrete preoccuparvi di questo. Questo ti lascerà un elenco di dichiarazioni.

Questa divisione modificata ha un problema: non ti restituirà il numero dopo il GO, se ciò è importante, direi che è ora di passare a un parser di qualche forma.

+0

Questo è un buon suggerimento, e grazie per la ripartizione dettagliata. –

+0

Chas, vedere le modifiche sopra: come potrei verificare un commento a riga singola alla fine della linea GO? Per favore, scusami l'inesperienza delle regex. –

2

Questo non rileva se GO viene mai utilizzato come nome di variabile all'interno di alcune istruzioni, ma dovrebbe occuparsi di quelle all'interno di commenti o stringhe.

EDIT: Questo ora funziona se GO fa parte della dichiarazione, fintanto che non si trova in essa la propria linea. utilizzo

import re 

line_comment = r'(?:--|#).*$' 
block_comment = r'/\*[\S\s]*?\*/' 
singe_quote_string = r"'(?:\\.|[^'\\])*'" 
double_quote_string = r'"(?:\\.|[^"\\])*"' 
go_word = r'^[^\S\n]*(?P<GO>GO)[^\S\n]*\d*[^\S\n]*(?:(?:--|#).*)?$' 

full_pattern = re.compile(r'|'.join((
    line_comment, 
    block_comment, 
    singe_quote_string, 
    double_quote_string, 
    go_word, 
)), re.IGNORECASE | re.MULTILINE) 

def split_sql_statements(statement_string): 
    last_end = 0 
    for match in full_pattern.finditer(statement_string): 
     if match.group('GO'): 
      yield statement_string[last_end:match.start()] 
      last_end = match.end() 
    yield statement_string[last_end:] 

Esempio:

statement_string = r""" 
-- this is a great database script! go team go! 
INSERT INTO go(go) VALUES ('go away!') 
go 7 -- foo 
INSERT INTO go(go) VALUES (
    'I have to GO " with a /* comment to GO inside a /* GO string /*' 
) 
/* 
    here are some comments that go with this script. 
    */ 
    GO 
    INSERT INTO go(go) VALUES ('this is the next script') 
""" 

for statement in split_sql_statements(statement_string): 
    print '=======' 
    print statement 

uscita:

======= 

-- this is a great database script! go team go! 
INSERT INTO go(go) VALUES ('go away!') 

======= 

INSERT INTO go(go) VALUES (
    'I have to GO " with a /* comment to GO inside a /* GO string /*' 
) 
/* 
    here are some comments that go with this script. 
    */ 

======= 

    INSERT INTO go(go) VALUES ('this is the next script') 
+0

non riesce su "I have to GO" con un commento/* su GO all'interno di/* GO stringa/* 'GO – nosklo

+0

No, non troverà tutte le corrispondenze non sovrapposte, quindi l'intera stringa a virgoletta singola verrà rilevato come una corrispondenza –

+0

Non fallire quando lo eseguo ... –

Problemi correlati