Antecedentes : estou trabalhando em http://sqlfiddle.com (meu site) e estou tentando evitar uma avenida de abuso possível lá. Espero que, perguntando sobre um problema que estou abordando no momento, não torne inadvertidamente o abuso potencial pior, mas o que você pode fazer? Eu confio em vocês.
Gostaria de impedir que qualquer usuário emita chamadas explícitas de 'confirmação' dentro de um determinado bloco de transações. No contexto do SQL Fiddle, o bloco de transação é o código que é executado no painel do lado direito. Basicamente, estou fazendo um loop e executando uma lista de comandos SQL em texto sem formatação e quero garantir que todas as alterações feitas sejam revertidas no final do lote. Normalmente, as alterações são revertidas, mas às vezes há uma declaração explícita de 'commit' no texto e, portanto, é claro que minha reversão não funcionará. Essa confirmação explícita é muito provável de um usuário tentar quebrar o esquema no SQL Fiddle, para que outras pessoas que trabalhem nele vejam erros.
Resultado Desejado Principal : Gostaria de desativar confirmações explícitas no nível JDBC, se possível. Isso ocorre porque eu tenho que dar suporte a vários fornecedores de back-end de banco de dados e, é claro, cada um tem suas peculiaridades no nível mais baixo.
Opção de fallback : se não for possível configurar o JDBC para desativar confirmações explícitas, eu estaria aberto a soluções para detectar confirmações explícitas ao processar um lote para cada um dos seguintes back-ends: SQL Server, Oracle, MySQL e PostgreSQL.
Para o SQL Server, pensei nesta solução: analise o plano de consulta XML da instrução antes de executá-la e verifique a presença de uma entrada que corresponda a esse XPath:
//*[@StatementType="COMMIT TRANSACTION"]
Eu acho que isso funcionaria muito bem para o SQL Server. No entanto, essa abordagem não funciona para os outros tipos de banco de dados. A saída do plano de execução XML da Oracle para confirmações explícitas não faz referência ao fato de que você está executando uma instrução de confirmação (mas simplesmente repete a saída do plano de execução das consultas que está sendo confirmada). O PostgreSQL e o MySQL não fornecem nenhum resultado do plano de execução (XML ou outro) para confirmações explícitas.
Isso me deixa checando a declaração real da palavra "commit". Isso funcionaria, exceto que pode haver todos os tipos de variações possíveis:
declare @sql varchar(50)
set @sql = 'com' + 'mit'
exec(@sql);
O exemplo acima é um exemplo para o SQL Server (com o qual eu poderia solucionar), mas acho que coisas semelhantes seriam possíveis para Oracle, MySQL e PostgreSQL. Estou errado nessa suposição? Talvez eles não permitam declarações de confirmação "dinâmicas"? Sinta-se à vontade para usar o SQL Fiddle (de preferência não o esquema de amostra ou alguém em quem provavelmente esteja trabalhando) para ver se é possível fazer algo semelhante acontecer no Oracle, MySQL e PostgreSQL. Caso contrário, talvez a detecção simples de strings possa funcionar para eles.
Ainda outra possibilidade
Outra opção me ocorreu - se você souber uma maneira de definir qualquer um desses bancos de dados no modo somente leitura, enquanto nesse modo nada poderia ser comprometido, isso também funcionaria. Eu ainda precisaria permitir que as transações fossem iniciadas e que o código fosse executado dentro delas, desde que nada pudesse ser confirmado nesse modo. Isso é possível?
Atualizar
O que eu aprendi recentemente - isso realmente não é um problema com o PostgreSQL. Aparentemente, as confirmações emitidas dentro de um bloco de transação não serão aplicadas se o mesmo bloco for revertido (no Postgres). Então viva o Postgres!
Graças ao link de Phil para o post do SO, acho que posso usar o hack DEFERRABLE INICIALMENTE DEFERIDO para o Oracle para realizar o que estou procurando (um erro será gerado se um commit for emitido, mas posso solucionar isso). Isso deve abordar o Oracle. (Pensei por um momento que transações aninhadas poderiam funcionar aqui, mas não parece que o Oracle suporta transações aninhadas? De qualquer forma, não consegui encontrar nada que funcionasse dessa maneira).
Ainda não tem uma solução para o MySQL. Tentei usar transações aninhadas, mas isso não parece funcionar. Estou pensando seriamente em abordagens mais drásticas para o MySQL, como não permitir nada além de SELECTs no lado direito ou soltar / recriar o banco de dados após cada consulta. Nem parece bom embora.
Resolução
Portanto, agora implementei as soluções descritas para SQL Server e Oracle e, como mencionei, isso não é realmente um problema para o PostgreSQL. Para o MySQL, dei o passo um tanto infeliz de restringir o painel de consulta para selecionar apenas instruções. DDL e DML para MySQL simplesmente precisam ser inseridos no painel do esquema (no lado esquerdo). Espero que isso não acabe com muitos problemas antigos, mas acho que é o que deve ser feito para garantir a consistência dos dados. Obrigado!