web-dev-qa-db-ja.com

忘れられた代入演算子 "="とありふれた ":="

PL/pgSQLのドキュメントによると、変数への宣言と代入は:=で行われます。しかし、シンプルで短く、より現代的 (脚注を参照)=は期待どおりに機能しているようです:

    CREATE OR REPLACE FUNCTION foo() RETURNS int AS $$
    DECLARE
      i int;
    BEGIN
      i = 0;  
      WHILE NOT i = 25 LOOP
          i = i + 1;
          i = i * i;
      END LOOP;
      RETURN i;
    END;
    $$ LANGUAGE plpgsql;

    > SELECT foo();
    25

Pl/pgSQLは、次の行に示すように、割り当てと比較を明確に区別できることに注意してください。

      WHILE NOT i = 25 LOOP

したがって、質問は次のとおりです。

  • これについて言及および/または説明しているセクションがドキュメントに見つかりませんでしたか?
  • =の代わりに:=を使用した場合の既知の結果はありますか?

編集/脚注:

プログラミング言語の簡潔で不完全な、そしてほとんど間違った歴史 :のようなウィンクで「より現代的な」部分をとってください。

1970年-NiklausWirthは、手続き型言語であるPascalを作成しました。批評家は、Pascalがより馴染みのあるCのような「x = x + y」の代わりに「x:= x + y」構文を使用しているため、すぐにPascalを非難します。この批判は、Cがまだ発明されていないという事実にもかかわらず起こります。

1972年-デニス・リッチーは、前方と後方の両方を同時に撃つ強力な銃を発明しました。彼がCとUnixを発明したその発明による死者と恒久的な不具の数に満足していません。

41
A.H.

PL/PgSQLパーサーでは、代入演算子は次のように定義されます。

assign_operator : '='
                | COLON_EQUALS
                ;

これはレガシー機能であり、1998年に導入されてからソースコードに存在します PostgreSQL Git repo で確認できます。

バージョン9.4以降は 公式に文書化されています です。

この特異性(同じことに対して2つの演算子がある)はpgsqlユーザーリストで提起され、一部の人々はそれを削除するように要求しましたが、レガシーコードの公正なコーパスがそれに依存しているため、コアに保持されます。

これを参照してください Tom Lane(コアPg開発者)からのメッセージ

だから、あなたの質問にまっすぐに答えるために:

これについて言及および/または説明しているセクションがドキュメントに見つかりませんでしたか?

文書化されていないため、見つかりませんでした。バージョン9.4で修正されています。

:=の代わりに=を使用した場合の既知の結果はありますか?.

=を使用しても副作用はありませんが、コードを読みやすくするために、割り当てには:=を使用する必要があります。副作用として)PL/SQLとの互換性が高くなります。

更新:まれなシナリオでは副作用が発生する可能性があります(Erwinの回答を参照)


更新:Daniel、Sandyなどからの入力のおかげで回答が更新されました。

43
filiprem

Q1

これはついに Postgres9.4 で公式ドキュメントに追加されました:

PL/pgSQL変数への値の割り当ては次のように記述されます。

variable { := | = } expression;

[...] PL/SQL準拠の=の代わりに等しい(:=)を使用できます。

Q2

=の代わりに:=を使用した場合の既知の結果はありますか?

はい重大な結果の場合がありました:関数呼び出し名前付きパラメーター-これは関連していますが、まったく同じではありません。

厳密に言えば、この場合の区別は[〜#〜] sql [〜#〜]コードで行われます。しかし、それは無防備なプログラマーとの学術的な違いです。1

関数について考えてみましょう。

CREATE FUNCTION f_Oracle(is_true boolean = TRUE) -- correct use of "="
  RETURNS text AS
$func$
SELECT CASE $1
          WHEN TRUE  THEN 'That''s true.'
          WHEN FALSE THEN 'That''s false.'
          ELSE 'How should I know?'
       END
$func$  LANGUAGE sql;

余談ですが、関数定義での=の正しい使用に注意してください。これはCREATE FUNCTION構文の一部です[〜#〜] sql [〜#〜]割り当てのスタイルです。2

名前付き表記を使用した関数呼び出し:

SELECT * FROM f_Oracle(is_true := TRUE);

Postgresは:=をパラメータ割り当てとして識別し、すべてが順調です。 ただし

SELECT * FROM f_Oracle(is_true = TRUE);

=はSQL等式演算子であるため、Postgresはis_true = TRUEを呼び出し元のステートメントのコンテキストでSQL式として解釈し、次のことを試みます。結果を名前のない位置パラメータとして渡す前に評価してください。外側のスコープで識別子is_trueを探します。それが見つからない場合:

ERROR:  column "is_true" does not exist

これは幸運なケースであり、幸いにも一般的なケースです。

is_truecanが外部スコープで見つかった場合(およびデータ型に互換性がある場合)、is_true = TRUEは、によって受け入れられるboolean結果を持つ有効な式です。関数。エラーは発生しません。明らかに、これは[〜#〜] sql [〜#〜]等式演算子= ..を使用するプログラマーの意図です。

この SQL Fiddle は効果を示しています。

=:=の違いに気付いていない場合、デバッグは非常に困難です。
常に正しい演算子を使用してください。


1関数呼び出しでの名前付き表記 を使用する場合、正しい代入演算子は:=のみです。これは、PL/pgSQLだけでなく、9.4ページまでのall言語の関数に適用されます。下記参照。

2=(またはDEFAULT)を使用して、 関数パラメーターのデフォルト値 を定義できます。それは、目前の問題とはまったく関係ありません。これは、誤ったユースケースに非常に近いものです。

Postgres 9.0-9.4::=から=>への移行

名前付き関数パラメータへの割り当てのSQL標準は=>(および OracleのPL/SQLはそれを使用 。Postgres演算子は以前に予約されていなかったため、同じことはできませんでした。そのため、代わりにPL/pgSQLの代入演算子:=を使用しています。Postgres9.0のリリースでは、他の目的での=>の使用は非推奨になりました。 リリースノートごと

演算子名としての=>の非推奨(Robert Haas)

PostgreSQLの将来のバージョンでは、名前付き関数パラメーターのSQL標準表記をサポートするために、この演算子名を完全に拒否する可能性があります。今のところ、それはまだ許可されていますが、そのような演算子が定義されると警告が出されます。

=>を他の目的で使用する必要がある場合は、排除措置をとってください。将来壊れます。

Postgres 9.5:今すぐ=>を使用

このリリース以降、SQL標準演算子=>が使用されます。 :=は、下位互換性のために引き続きサポートされています。ただし、非常に古いバージョンで実行する必要のない新しいコードでは、標準の演算子を使用してください。

これは、関数呼び出し(SQLスコープ)での名前付きパラメーターの割り当てに適用され、plpgsqlコードの割り当て演算子:=には適用されません。これは、変更されません。

30

私自身の質問に対する部分的な答え:

PL/pgSQLセクション 結果ステータスの取得 は、特別な構文を使用した2つの例を示しています。

GET DIAGNOSTICS variable = item [ , ... ]; 
GET DIAGNOSTICS integer_var = ROW_COUNT;

私は両方を試しました:=および=そしてそれらは両方とも機能します。

だが GET DIAGNOSTICSは特別な構文であるため、これも通常のPL/pgSQL代入操作ではないと主張することができます。

2
A.H.

Postgresql 9のドキュメントを読む:

このページ 演算子の優先順位の表に代入演算子として「=」をリストします。

しかし不思議なことに このページ (代入演算子のドキュメント)はそれについて言及していません。

1
Mike Sokolov