Disabilitare i commit espliciti in JDBC, rilevarli in SQL o mettere il database in uno stato di sola lettura


12

Contesto : sto lavorando su http://sqlfiddle.com (il mio sito) e sto cercando di prevenire una via di abuso possibile lì. Spero che chiedendo di un problema che sto affrontando attualmente, non inavvertitamente peggiori il potenziale abuso, ma cosa puoi fare? Mi fido di voi gente.

Vorrei impedire a qualsiasi utente di inviare chiamate "commit" esplicite all'interno di un determinato blocco di transazioni. Dal contesto di SQL Fiddle, il blocco delle transazioni è il codice che viene eseguito sul pannello di destra. Fondamentalmente, sto eseguendo il ciclo ed eseguendo un elenco di comandi SQL in testo normale e voglio assicurarmi che tutte le modifiche apportate vengano ripristinate alla fine del batch. Normalmente, le loro modifiche vengono ripristinate, ma a volte c'è un'istruzione esplicita 'commit' nel testo, e quindi ovviamente il mio rollback non funzionerà. Questo commit esplicito è molto probabile da parte di un utente che tenta di interrompere lo schema su SQL Fiddle, quindi altre persone che ci lavorano vedranno errori.

Principale risultato desiderato : vorrei disabilitare i commit espliciti a livello JDBC, se possibile. Questo perché devo supportare più fornitori di backend di database, e ovviamente ognuno ha le sue stranezze a basso livello.

Opzione di fallback : se non è possibile configurare JDBC per disabilitare i commit espliciti, allora sarei aperto alle soluzioni per rilevare i commit espliciti durante l'elaborazione di un batch per ciascuno dei seguenti backend: SQL Server, Oracle, MySQL e PostgreSQL.

Per SQL Server, ho pensato a questa soluzione: analizzare il piano di query XML per l'istruzione prima di eseguirla e verificare la presenza di una voce corrispondente a questo XPath:

//*[@StatementType="COMMIT TRANSACTION"]

Penso che funzionerebbe abbastanza bene per SQL Server. Tuttavia, questo approccio non funziona con gli altri tipi di DB. L'output del piano di esecuzione XML di Oracle per commit espliciti non fa riferimento al fatto che si sta eseguendo un'istruzione commit (ma piuttosto semplicemente ripete l'output del piano di esecuzione dalle query che sta eseguendo il commit). PostgreSQL e MySQL non forniscono alcun output del piano di esecuzione (XML o altro) per commit espliciti.

Ciò mi lascia controllando l'effettiva affermazione della parola "commit". Funzionerebbe, tranne che ci possono essere tutti i tipi di variazioni possibili:

declare @sql varchar(50)
set @sql = 'com' + 'mit'
exec(@sql);

Quanto sopra è un esempio di SQL Server (che potrei aggirare), ma immagino che cose simili sarebbero possibili per Oracle, MySQL e PostgreSQL. Sbaglio su questa ipotesi? Forse non permetterebbero dichiarazioni di commit "dinamiche"? Sentiti libero di usare SQL Fiddle (preferibilmente non lo schema di esempio o qualcun altro probabilmente sta lavorando) per vedere se riesci a far accadere qualcosa di simile in Oracle, MySQL e PostgreSQL. Altrimenti, forse il semplice rilevamento delle stringhe potrebbe funzionare per quelli.

Ancora un'altra possibilità

Mi è venuta in mente un'altra opzione: se si conosce un modo per impostare uno di questi database in modalità di sola lettura, ad esempio in tale modalità non è possibile eseguire il commit di nulla, che potrebbe funzionare anche. Avrei ancora bisogno di consentire l'avvio delle transazioni e l'esecuzione del codice al loro interno, purché nulla potesse essere impegnato in quella modalità. È possibile?

Aggiornare

Quello che ho imparato di recente - questo in realtà non è un problema con PostgreSQL. Apparentemente i commit emessi all'interno di un blocco di transazione non si applicano se lo stesso blocco alla fine viene ripristinato (in Postgres). Quindi evviva Postgres!

Grazie al link di Phil al post SO, penso di poter usare l'hack DEFERRABLE INITIALLY DEFERRED per Oracle per realizzare ciò che sto cercando (verrà emesso un errore se viene emesso un commit, ma posso aggirare quello). Questo dovrebbe riguardare Oracle. (Ho pensato per un momento che le transazioni nidificate potrebbero funzionare qui, ma non sembra che Oracle supporti le transazioni nidificate? Comunque non sono riuscito a trovare nulla che funzionasse in questo modo).

Non ho ancora una soluzione per MySQL. Ho provato usando transazioni nidificate, ma non sembra funzionare. Sto seriamente pensando ad approcci più drastici per MySQL, come ad esempio non consentire altro che SELECTs sul lato destro o far cadere / ricreare il DB dopo ogni query. Né suona bene però.

Risoluzione

Quindi, ora ho implementato le soluzioni descritte per SQL Server e Oracle, e come ho già detto questo non è in realtà un problema per PostgreSQL. Per MySQL, ho fatto il passo un po 'sfortunato di limitare il pannello delle query a selezionare solo le istruzioni. DDL e DML per MySQL dovranno semplicemente essere inseriti nel pannello dello schema (lato sinistro). Spero che questo non rompa troppi vecchi violini, ma penso che sia semplicemente ciò che deve essere fatto per garantire la coerenza dei dati. Grazie!


Vale la pena ricordare che ho anche fatto molte ricerche cercando di innescare "pre-commit". Sembra che quelli non esistano, quindi purtroppo neanche questa è un'opzione.
Jake Feasel,

2
Per Oracle, questo mi sembra un buon modo subdolo di commit cattura: stackoverflow.com/a/6463800/790702 Quello che non fa menzione è che si dovrebbe essere in grado di catturare la violazione del vincolo nel codice anche per fermare la seconda situazione si verificano.
Philᵀᴹ

@Phil l'unico problema è che non ho (e non voglio davvero) il controllo sulla definizione di ciascuna delle tabelle (quelle sono definite sul pannello di sinistra). In modo che la clausola DEFERRABLE INIZIALMENTE DEFERRED non ci sia (a meno che non ci sia un modo per farlo automaticamente per tutte le tabelle, ad esempio tramite un trigger di sistema che risponde alla creazione delle istruzioni della tabella? Ugh)
Jake Feasel,

1
@NickChammas Devo disabilitare i commit in modo da poter mantenere lo schema (come definito dal pannello laterale sinistro) in uno stato fisso. Potrebbe esserci un numero qualsiasi di persone che scrivono query sullo stesso schema cercando di risolvere lo stesso problema. Per impedire loro di interferire l'uno con l'altro, devo essere sicuro di non modificare la struttura e i dati. Ciò viene realizzato tramite transazioni di rollback, ma ciò non accade se viene eseguito il commit del codice di destra.
Jake Feasel,

2
@RickJames DDL crea commit impliciti in MySQL e Oracle, ma non in PostgreSQL o SQL Server. I meccanismi che ho implementato seguendo questo post gestiscono questa preoccupazione. Per quanto riguarda l'inserimento di SQL arbitrario, questo è il punto!
Jake Feasel,

Risposte:


3

Per Oracle, questo sembra un buon modo subdolo per catturare COMMIT:

/programming//a/6463800/790702

Ciò che non menziona è che dovresti essere in grado di rilevare anche la violazione del vincolo nel tuo codice, per evitare che si verifichi la seconda situazione.


Ottimo, grazie ancora Phil. Sono stato in grado di fare buon uso di questa tecnica per Oracle. Mi fa desiderare che altri database supportino vincoli differiti.
Jake Feasel,

Nessun problema.
Entra
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.