Antecedentes : estoy trabajando en http://sqlfiddle.com (mi sitio) y estoy tratando de evitar una posible vía de abuso allí. Espero que al preguntar sobre un problema que estoy abordando actualmente, sin querer empeore el posible abuso, pero ¿qué puede hacer? Confío en ustedes amigos.
Me gustaría evitar que cualquier usuario emita llamadas explícitas de 'confirmación' dentro de un bloque de transacciones dado. Desde el contexto de SQL Fiddle, el bloque de transacción es el código que se ejecuta en el panel del lado derecho. Básicamente, estoy repitiendo y ejecutando una lista de comandos SQL de texto sin formato, y quiero asegurarme de que todos los cambios que realicen se revertirán al final del lote. Normalmente, sus cambios se revierten, pero a veces hay una declaración explícita de 'confirmación' dentro del texto, por lo que, por supuesto, mi reversión no funcionará. Es muy probable que esta confirmación explícita de un usuario que intente romper el esquema en SQL Fiddle, por lo que otras personas que trabajen en ella verán errores.
Resultado deseado principal : Me gustaría deshabilitar confirmaciones explícitas en el nivel JDBC, si es posible. Esto se debe a que tengo que admitir múltiples proveedores de back-end de bases de datos y, por supuesto, cada uno tiene sus peculiaridades en el nivel bajo.
Opción alternativa : si no es posible configurar JDBC para deshabilitar confirmaciones explícitas, entonces estaría abierto a soluciones para detectar confirmaciones explícitas mientras se procesa un lote para cada uno de los siguientes backends: SQL Server, Oracle, MySQL y PostgreSQL.
Para SQL Server, pensé en esta solución: analizar el plan de consulta XML para la instrucción antes de ejecutarla y verificar la presencia de una entrada que coincida con esta XPath:
//*[@StatementType="COMMIT TRANSACTION"]
Creo que esto funcionaría bastante bien para SQL Server. Sin embargo, este enfoque no funciona para los otros tipos de bases de datos. La salida del plan de ejecución XML de Oracle para confirmaciones explícitas no hace referencia al hecho de que está ejecutando una declaración de confirmación (sino que simplemente repite la salida del plan de ejecución de las consultas que está confirmando). PostgreSQL y MySQL no proporcionan ningún resultado del plan de ejecución (XML o de otro tipo) para confirmaciones explícitas.
Eso me deja con verificar la declaración real de la palabra "commit". Esto funcionaría, excepto que puede haber todo tipo de variaciones posibles:
declare @sql varchar(50)
set @sql = 'com' + 'mit'
exec(@sql);
Lo anterior es un ejemplo para SQL Server (que podría solucionar), pero supongo que cosas similares serían posibles para Oracle, MySQL y PostgreSQL. ¿Estoy equivocado en esa suposición? ¿Quizás no permitirían declaraciones de compromiso "dinámicas"? Siéntase libre de usar SQL Fiddle (preferiblemente no el esquema de muestra o alguien con quien probablemente esté trabajando) para ver si puede hacer que ocurra algo similar en Oracle, MySQL y PostgreSQL. Si no, tal vez la simple detección de cadenas podría funcionar para ellos.
Otra posibilidad
Se me ocurrió otra opción: si conoces una forma de configurar cualquiera de estas bases de datos en modo de solo lectura, por ejemplo, mientras que en ese modo no se puede comprometer nada, eso también podría funcionar. Todavía tendría que permitir que se inicien las transacciones y que se ejecute el código dentro de ellas, siempre y cuando no se pueda confirmar nada en ese modo. ¿Es eso posible?
Actualizar
Lo que aprendí recientemente: esto en realidad no es un problema con PostgreSQL. Aparentemente, las confirmaciones emitidas dentro de un bloque de transacción no se aplicarán si ese mismo bloque finalmente se revierte (en Postgres). ¡Así que hurra por Postgres!
Gracias al enlace de Phil a la publicación SO, creo que puedo usar el hack DEFERRABLE INICIALMENTE DEFERIDO para que Oracle logre lo que busco (se generará un error si se emite un commit, pero puedo solucionarlo). Esto debería abordar Oracle. (Pensé por un momento que las transacciones anidadas podrían funcionar aquí, pero ¿no parece que Oracle admite transacciones anidadas? De todos modos, no pude encontrar nada que funcionara de esta manera).
Realmente todavía no tengo una solución para MySQL. Intenté usar transacciones anidadas, pero eso no parece funcionar. Estoy pensando seriamente en enfoques más drásticos para MySQL, como no permitir nada más que SELECCIONAR en el lado derecho o soltar / recrear la base de datos después de cada consulta. Sin embargo, ninguno de los dos suena bien.
Resolución
Entonces, he implementado las soluciones descritas para SQL Server y Oracle, y como mencioné, esto no es realmente un problema para PostgreSQL. Para MySQL, he tomado el paso algo desafortunado de restringir el panel de consulta para que solo seleccione declaraciones. DDL y DML para MySQL simplemente deberán ingresarse en el panel de esquema (el lado izquierdo). Espero que esto no rompa muchos viejos violines, pero creo que simplemente es lo que hay que hacer para garantizar la coherencia de los datos. ¡Gracias!