2009-04-22 21 views
6

Tengo un script de IronPython que ejecuta un conjunto de sentencias de SQL en una base de datos de SQL Server. las instrucciones son cadenas grandes que en realidad contienen varias instrucciones, separadas por la palabra clave "IR". Eso funciona cuando se ejecutan desde SQL Management Studio y algunas otras herramientas, pero no en ADO. Así que me separé las cadenas utilizando el módulo "re" 2.5, así:regex para analizar sentencias de SQL

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

Esto rompe en el caso raro de que no es la palabra "ir" en un comentario o una cadena. ¿Cómo diablos iba a trabajar en eso? es decir, analiza correctamente esta cadena en dos guiones:

-- 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:

busqué más y encontré esta documentación de SQL: http://msdn.microsoft.com/en-us/library/ms188037(SQL.90).aspx

Como resultado, GO debe haber en su propia línea como algunas respuestas sugeridas. Sin embargo, puede ir seguido de un número "conteo" que realmente ejecutará el lote de declaración tantas veces (¿alguien realmente lo ha usado antes?) Y puede ser seguido por comentarios de una sola línea en la misma línea (pero no un multilínea, he probado esto) Así que la expresión regular magia sería algo como:.

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

Salvo esto no tiene en cuenta:

  • un posible comentario de una sola línea ("--" seguido de cualquier personaje excepto un salto de línea) al final.
  • toda la línea dentro de un comentario más grande de varias líneas.

No me preocupa capturar el argumento "contar" y usarlo. Ahora que tengo cierta documentación técnica, estoy tentadoramente cerca de escribir esto "a especificaciones" y nunca tener que preocuparme por ello nuevamente.

+0

I upmodded respuestas de todo el mundo, gracias por la ayuda! Le concedí la respuesta a mcassano porque él fue el primero en sugerir que GO podría estar siempre en su propia línea, lo que me llevó a buscar la documentación para ese comando y, finalmente, a una solución mucho más fácil. –

Respuesta

8

¿"GO" siempre está en línea por sí mismo? Podrías dividir en "^ GO $".

+0

después de activar el emparejamiento multilínea, también incluí espacios en blanco opcionales, por las dudas. –

+0

Creo que generalmente está en su propia línea, y eso podría ser una buena solución para este script. Aunque estrictamente, eso no protegería contra un GO en su propia línea dentro de un comentario multilínea o una cadena multilínea (también muy raro). –

+0

Mi script actualizado ahora soluciona este error. –

5

ya que puede tener comentarios dentro de comentarios, comentarios anidados, comentarios dentro de consultas, etc., no hay una manera sensata de hacerlo con expresiones regulares.

Sólo IMMAGINE el siguiente script:

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

Eso sin mencionar:

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

La única manera sería construir un analizador de estado en su lugar. Uno que lee un char a la vez, y tiene un indicador que se establecerá cuando está dentro de una cadena/etc delimitada por comentarios/comillas y se restablece cuando termina, por lo que el código puede ignorar instancias "IR" cuando están dentro de esos.

+1

Un analizador sería la mejor solución, pero si puede garantizar que GO siempre está en línea, es bastante seguro, ya que SQL92 no tiene comentarios de líneas múltiples. –

+1

me has dejado sin palabras. –

5

Si GO es siempre en una línea por sí mismo puede utilizar dividida así:

#!/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) enciende emparejamientos de varias líneas, es decir ^ y $ coincidirá con inicio y final de la línea (en lugar de inicio y final de la cadena).
  • ^ partidos al comienzo de una línea
  • \s* coincide con cero o más espacios en blanco (espacios, tabulaciones, etc.)
  • GO coincide con una GO literal
  • \s* partidos como antes
  • (?:[0-9]+)? coincide con una opcional número entero (con posibles ceros a la izquierda)
  • \s* coincide con el anterior
  • (?:--.*)? partidos, también un comentario al final de la línea de
  • $ partidos al final de una línea

La división consumirá la línea GO, por lo que no tendrá que preocuparse por ello. Esto te dejará con una lista de declaraciones.

Esta división modificada tiene un problema: no le devolverá el número después del GO, si eso es importante, diría que es hora de pasar a un analizador de alguna forma.

+0

Esta es una buena sugerencia, y gracias por el desglose detallado. –

+0

Chas, ver las ediciones anteriores. ¿Cómo verificaría un comentario de una sola línea al final de la línea GO? Disculpe mi inexperiencia de expresiones regulares. –

2

Esto no detectará si GO alguna vez se utiliza como nombre de variable dentro de alguna instrucción, pero debe ocuparse de los comentarios o cadenas internas.

EDIT: Esto ahora funciona si GO es parte de la declaración, siempre que no se encuentra en su propia línea. el uso

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:] 

Ejemplo:

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 

de salida:

======= 

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

falla en 'Tengo que IR' con un comentario */para IR dentro de/* GO cadena/* 'GO – nosklo

+0

No, no. Encontrará todas las coincidencias no superpuestas, por lo que toda la cadena de comillas simples se detectará como una coincidencia –

+0

No fallaré cuando lo ejecute ... –

Cuestiones relacionadas