JDBCで明示的なコミットを無効にする、SQLで検出する、またはデータベースを読み取り専用状態にする


12

背景:私はhttp://sqlfiddle.com(私のサイト)に取り組んでおり、そこで悪用される可能性のある1つの手段を防止しようとしています。私が現在取り組んでいる問題について尋ねることによって、潜在的な虐待を不注意に悪化させないことを望んでいますが、あなたは何ができますか?皆さんを信頼しています。

任意のユーザーが特定のトランザクションブロック内で明示的な「コミット」呼び出しを発行することを防止したいと思います。SQL Fiddleのコンテキストから見ると、トランザクションブロックは右側のパネルで実行されるコードです。基本的に、ループしてプレーンテキストのSQLコマンドのリストを実行し、それらの変更がすべてバッチの最後に確実にロールバックされるようにします。通常、それらの変更はロールバックされますが、テキスト内に明示的な「コミット」ステートメントがある場合があるため、もちろん私のロールバックは機能しません。この明示的なコミットは、SQL Fiddleでスキーマを壊そうとするユーザーによるものである可能性が高いため、他のユーザーがエラーを確認します。

主な望ましい結果:可能であれば、JDBCレベルで明示的なコミットを無効にします。これは、複数のデータベースバックエンドベンダーをサポートする必要があるためです。もちろん、それぞれのベンダーには低レベルの癖があります。

フォールバックオプション:明示的なコミットを無効にするようにJDBCを構成できない場合、SQL Server、Oracle、MySQL、およびPostgreSQLの各バックエンドのバッチを処理中に明示的なコミットを検出するためのソリューションを利用できます。

SQL Serverの場合、このソリューションを考えました。実行する前にステートメントのXMLクエリプランを解析し、このXPathに一致するエントリの存在を確認します。

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

これはSQL Serverでかなりうまくいくと思います。ただし、このアプローチは他のDBタイプでは機能しません。明示的なコミットに関するOracleのXML実行計画の出力は、コミットステートメントを実行しているという事実を参照していません(むしろ、コミットしているクエリから実行計画の出力を単純に繰り返します)。PostgreSQLおよびMySQLは、明示的なコミットに対して実行計画の出力(XMLまたはそれ以外)を一切提供しません。

そのため、「コミット」という単語の実際のステートメントを確認できます。これは機能しますが、可能なすべての種類のバリエーションがある場合を除きます。

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

上記はSQL Serverの例です(回避できます)が、Oracle、MySQL、PostgreSQLでも同様のことが可能だと思います。私はその仮定で間違っていますか?たぶん、彼らは「動的な」コミット文を許可しないでしょうか?Oracle、MySQL、PostgreSQLで同様のことができるかどうかを確認するために、SQL Fiddle(できればサンプルスキーマまたは他の誰かが作業している可能性のあるものではない)を自由に使用してください。そうでない場合は、単純な文字列の検出が機能する可能性があります。

さらに別の可能性

別のオプションがありました-これらのデータベースのいずれかを読み取り専用モードに設定する方法を知っている場合、そのモードでは何もコミットできませんが、それも機能します。そのモードで何もコミットできない限り、トランザクションの開始とトランザクション内でのコードの実行を許可する必要があります。それは可能ですか?

更新

私が最近学んだこと-これは実際にはPostgreSQLの問題ではありません。トランザクションブロック内で発行されたコミットは、その同じブロックが最終的に(Postgresで)ロールバックされた場合には適用されないようです。Postgresの皆さん、ありがとう!

PhilのSO投稿へのリンクのおかげで、DEFERRABLE INITIALLY DEFERREDハックを使用してOracleを達成できると思います(コミットが発行されるとエラーがスローされますが、それを回避できます)。これはOracleに対処する必要があります。(ネストされたトランザクションがここで機能するかもしれないと少しの間考えましたが、Oracleがネストされたトランザクションをサポートしているようには見えませんか?とにかく、このように機能するものは見つかりませんでした)。

MySQLのソリューションはまだありません。ネストされたトランザクションを使用してみましたが、動作しないようです。右側のSELECT以外は許可しない、または各クエリの後にDBを削除/再作成するなど、MySQLのより抜本的なアプローチを真剣に考えています。どちらも良い音ではありません。

解決

したがって、SQL ServerとOracleについて説明したソリューションを実装しましたが、前述したように、これは実際にはPostgreSQLの問題ではありません。MySQLの場合、クエリパネルをselectステートメントのみに制限するというやや不幸なステップを踏んでいます。MySQLのDDLとDMLは、スキーマパネル(左側)で入力するだけです。これがあまりにも多くの古いフィドルを壊さないことを願っていますが、それは単にデータの一貫性を確保するために行わなければならないことだと思います。ありがとう!


言及する価値がある-私はまた、「事前コミット」トリガーを調べる多くの研究をしました。それらは存在しないようですので、残念ながらそれも選択肢ではありません。
ジェイクフィーセル

2
Oracleの場合、これはCOMMITを引くのは良い卑劣な方法のように思える:stackoverflow.com/a/6463800/790702は彼が言及していないことは、あなたが第二の状況を停止するには、あまりにもあなたのコードでは、制約違反をキャッチすることができるはずということです発生します。
フィリ

@Philの唯一の問題は、私が各テーブルの定義を制御できないことです(そして、本当に望んでいません)(左のパネルで定義されています)。だから、(すべてのテーブルに対して自動的にそれを行うにはいくつかの方法がない限り-応答がテーブルステートメントを作成するために、システム・トリガーを経由して言ううわ?)DEFERRABLE INITIALLY DEFERRED句が存在しないだろうということ
ジェイクFeasel

1
@NickChammasコミットを無効にして、スキーマ(左側のパネルで定義)を固定状態に保つ必要があります。同じ問題を解決しようとする同じスキーマに対してクエリを作成する人が何人もいる可能性があります。それらが互いに干渉するのを防ぐために、構造とデータが変更されないようにする必要があります。これはロールバックされたトランザクションを介して実現されますが、右側のコードがコミットされた場合は発生しません。
ジェイクフィーセル

2
@RickJames DDLは、MySQLとOracleで暗黙的なコミットを作成しますが、PostgreSQLまたはSQL Serverでは作成しません。この投稿に続いて実装したメカニズムがその懸念を処理します。任意のSQLを入力する限り-それが全体のポイントです!
ジェイクフィーセル

回答:


3

Oracleの場合、これはCOMMITをキャッチするための巧妙な方法のようです。

/programming//a/6463800/790702

彼が言及していないのは、コード内の制約違反をキャッチして、発生する2番目の状況を止めることができるはずだということです。


どうもありがとう、フィル。このテクニックをOracleにうまく利用できました。他のデータベースが遅延制約をサポートすることを望みます。
ジェイクフィーセル

心配ない。チャットへのドロップと挨拶:)
Philᵀᴹ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.