PostgreSQLは標準に準拠しています
この動作は、SQL標準で指定されているようです。しかし、それが本当の問題であるケースを見たのはこれが初めてです。通常は、CASE
式またはPL/PgSQLBEGIN ... EXCEPTION
ブロックを使用して処理します。
MySQLのデフォルトの動作は危険で間違っています。この動作に依存する古いコードをサポートするためにのみ、そのように機能します。strictモードがアクティブな場合(絶対に常にそうする必要があります) 、新しいバージョンで修正されましたが、残念ながらまだデフォルトにはされていません。MySQLを使用する場合は、常にまたはを有効にしてください。STRICT_TRANS_TABLES
STRICT_ALL_TABLES
ANSI標準のゼロ除算は時々苦痛ですが、データ損失を引き起こす間違いからも保護します。
SQLインジェクションの警告、再設計を検討してください
ユーザーから式を実行している場合は、SQLインジェクションの問題が発生している可能性があります。セキュリティ要件によっては、それを受け入れることができる場合もありますが、すべてのユーザーを完全に信頼しないと、かなり悪い結果になります。ユーザーがだまされて、他の場所から悪意のあるコードを入力する可能性があることを忘れないでください。
式ビルダーをユーザーに公開するように再設計し、クエリビルダーを使用してユーザー式からSQLを作成することを検討してください。これははるかに複雑ですが、安全です。
それができない場合は、ユーザーが抽象構文に入力した式を解析し、実行前に検証してから、解析された式に基づいて新しいSQL式を生成できるかどうかを確認してください。そうすれば、少なくとも彼らが書くことができるものを制限することができるので、彼らは表現に厄介なものを滑り込ませません。式を書き直して、ゼロ除算のチェックなどを追加することもできます。代数式のパーサーを見つける(または書く)のは難しいことではありませんが、ユーザーにどのような種類の式を書かせるかによって異なります。
少なくとも、アプリはSELECT
、テーブルに対する権限のみを持ち、スーパーユーザーではなく、テーブルを所有していないロール(「ユーザー」)を使用している必要があります。これにより、SQLインジェクションが引き起こす害を最小限に抑えることができます。
ケースは書かれているようにこの問題を解決しません
いずれにせよ、現在、ユーザーからの式を検証および検査できないため、SQL標準CASE
ステートメントを使用してこれを解決することはできません。if( a/b > 0, a, b)
あなたは通常次のようなものを書くでしょう:
CASE
WHEN b = 0 THEN b
ELSE CASE
WHEN a/b=0 THEN a
ELSE b
END
END
これは、分母がゼロの場合を明示的に処理しますが、式を分割できる場合にのみ可能です。
醜い回避策#1
別の解決策は、置換除算演算子または関数を定義することにより、ゼロによる除算の例外を発生させる代わりに、Pgにプレースホルダーを返すようにすることです。これはゼロ除算の場合のみを解決し、他の場合は解決しません。
'NaN'
それが論理的な結果なので、私は戻りたかったのです。残念ながら、「NaN」は数値よりも大きく、それ以上であるため、より小さいまたは偽のような結果が必要です。
regress=# SELECT NUMERIC 'NaN' > 0;
?column?
----------
t
(1 row)
これは、代わりにNULLを返すという厄介なハックを使用する必要があることを意味します。
CREATE OR REPLACE FUNCTION div_null_on_zero(numeric,numeric) returns numeric AS $$
VALUES (CASE WHEN $2 = 0 THEN NULL ELSE $1/$2 END)
$$ LANGUAGE 'SQL' IMMUTABLE;
CREATE OPERATOR @/@ (
PROCEDURE = div_null_on_zero(numeric,numeric),
LEFTARG = numeric,
RIGHTARG = numeric
);
使用法:
regress=# SELECT 5 @/@ 0, 5 @/@ 0>0, CASE WHEN 5 @/@ 0 > 0 THEN 5 ELSE 0 END;
?column? | ?column? | case
----------+----------+------
| | 0
(1 row)
アプリは、入力式の「/」を、選択した@/@
演算子名または任意の演算子名に簡単に書き換えることができます。
このアプローチには非常に重大な問題が1つあります。それは、明示的な括弧のない式が期待どおりに評価されない可能性があるため、@/@
優先順位が異なることです。/
これを回避するには、新しいスキーマを作成し、そのスキーマでnull-on-errorトリックを実行する名前付きの演算子を定義してから、ユーザー式を実行する前に/
そのスキーマをに追加します。search_path
しかし、それはおそらく悪い考えです。
醜い回避策#2
分母を調べることができないので、私が考えることができるのは、すべてをDO
ブロック(Pg 9.0+)またはPL / PgSQL関数でラップし、式の評価からの例外をキャッチすることだけです。
アーウィンの答えは私よりも良い例を示しているので、これを削除しました。いずれにせよ、これはひどく危険なことです。やらないでください。アプリを修正する必要があります。