Crear función a través de MySQLdb

¿Cómo puedo definir una función o un procedimiento de múltiples sentencias al usar la librería MySQLdb en python?

Ejemplo:

import MySQLdb db = MySQLdb.connect(db='service') c = db.cursor() c.execute("""DELIMITER // CREATE FUNCTION trivial_func (radius float) RETURNS FLOAT BEGIN IF radius > 1 THEN RETURN 0.0; ELSE RETURN 1.0; END IF; END // DELIMITER ;""") 

Lo que crea el siguiente rastreo:

 Traceback (most recent call last): File "proof.py", line 21, in  DELIMITER ;""") File "build/bdist.macosx-10.5-i386/egg/MySQLdb/cursors.py", line 173, in execute File "build/bdist.macosx-10.5-i386/egg/MySQLdb/connections.py", line 35, in defaulterrorhandler _mysql_exceptions.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER //\nCREATE FUNCTION trivial_func (radius float) \n RETURNS FLOAT\n\n ' at line 1") 

Si copio el mismo SQL directamente en un cliente de shell mysql, funciona como se esperaba

El comando DELIMITER es un cliente de shell MySQL integrado, y es reconocido solo por ese progtwig (y el buscador de consultas de MySQL). No es necesario usar DELIMITER si ejecuta sentencias de SQL directamente a través de una API.

El propósito de DELIMITER es ayudarlo a evitar la ambigüedad sobre la terminación de la CREATE FUNCTION , cuando la sentencia en sí misma puede contener caracteres de punto y coma. Esto es importante en el cliente shell, donde, de forma predeterminada, un punto y coma termina una statement SQL. Debe establecer el terminador de la instrucción en algún otro carácter para enviar el cuerpo de una función (o activador o procedimiento).

 CREATE FUNCTION trivial_func (radius float) RETURNS FLOAT BEGIN IF radius > 1 THEN RETURN 0.0; <-- does this semicolon terminate RETURN or CREATE FUNCTION? ELSE RETURN 1.0; END IF; END 

Dado que la API normalmente le permite enviar una statement SQL a la vez, no hay ambigüedad: la interfaz sabe que cualquier punto y coma dentro del cuerpo de la definición de su función no termina la statement CREATE FUNCTION . Por lo tanto, no es necesario cambiar el terminador de la sentencia con DELIMITER .

Para agregar a la respuesta de Bill Karwin, el siguiente ejemplo de código de Python se puede usar para ejecutar correctamente una cadena donde se usa DELIMITER, como un script de creación de base de datos.

 import MySQLdb db = MySQLdb.connect(db='service') cursor = db.cursor() dbString = """DELIMITER // CREATE FUNCTION trivial_func (radius float) RETURNS FLOAT BEGIN IF radius > 1 THEN RETURN 0.0; ELSE RETURN 1.0; END IF; END // DELIMITER ;""" # Find special delimiters delimiters = re.compile('DELIMITER *(\S*)',re.I) result = delimiters.split(dbString) # Insert default delimiter and separate delimiters and sql result.insert(0,';') delimiter = result[0::2] section = result[1::2] # Split queries on delimiters and execute for i in range(len(delimiter)): queries = section[i].split(delimiter[i]) for query in queries: if not query.strip(): continue cursor.execute(query) 

Esto ejecutará una statement delimitada a la vez, cambiando los delimitadores cuando sea necesario.

Basado en el comentario de @AaronS. Este script leerá en un archivo SQL, lo dividirá en comandos SQL discretos y manejará los delimitadores que encuentre.

  queries = [] delimiter = ';' query = '' with open('import.sql', 'r') as f: for line in f.readlines(): line = line.strip() if line.startswith('DELIMITER'): delimiter = line[10:] else: query += line+'\n' if line.endswith(delimiter): # Get rid of the delimiter, remove any blank lines and add this query to our list queries.append(query.strip().strip(delimiter)) query = '' for query in queries: if not query.strip(): continue cursor.execute(query) cursor.close()