Desative confirmações explícitas no JDBC, detecte-as no SQL ou coloque o banco de dados em um estado somente leitura

12

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!

Jake Feasel
fonte
Vale ressaltar - eu também fiz muita pesquisa analisando os gatilhos "pré-confirmação". Parece que eles não existem, então infelizmente isso também não é uma opção.
Jake Feasel
2
Para a Oracle, isso parece uma boa maneira sorrateira de capturar COMMITs: stackoverflow.com/a/6463800/790702 O que ele não menciona é que você também deve ser capaz de capturar a violação de restrição em seu código, para interromper a segunda situação ocorrendo.
Philᵀᴹ
@ Phil, o único problema é que eu não tenho (e realmente não quero) controle sobre a definição de cada uma das tabelas (aquelas definidas no painel esquerdo). Assim que DEFERRABLE cláusula INICIALMENTE diferidos não estaria lá (a menos que haja alguma maneira de fazer isso automaticamente para todas as tabelas? - digamos através de um gatilho sistema que responde a instruções CREATE TABLE Ugh)
Jake Feasel
1
@NickChammas Preciso desativar as confirmações para manter o esquema (conforme definido pelo painel do lado esquerdo) em um estado fixo. Pode haver várias pessoas escrevendo consultas no mesmo esquema tentando resolver o mesmo problema. Para impedir que eles interfiram entre si, preciso garantir que a estrutura e os dados não sejam alterados. Isso é realizado por meio de transações de reversão, mas isso não acontece se o código do lado direito for confirmado.
Jake Feasel
2
O @RickJames DDL cria confirmações implícitas no MySQL e Oracle, mas não no PostgreSQL ou SQL Server. Os mecanismos que eu implementei após este post tratam dessa preocupação. Tanto quanto entrar em SQL arbitrário - esse é o ponto!
Jake Feasel

Respostas:

3

Para a Oracle, essa parece uma boa maneira de capturar COMMITs:

/programming//a/6463800/790702

O que ele não menciona é que você também deve poder capturar a violação de restrição no seu código, para impedir que a segunda situação ocorra.

Philᵀᴹ
fonte
Ótimo, obrigado novamente Phil. Consegui fazer bom uso dessa técnica para Oracle. Isso me faz desejar que outros bancos de dados suportem restrições adiadas.
Jake Feasel
Não se preocupe. Cair no chat e dizer oi :)
Philᵀᴹ