🐘

PostgreSQLソースコードリーディング(exec_simple_query)

2023/03/19に公開

個人用のメモです。

関数名

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L984-L986

変数

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L987-L994

996-999

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L996-L999

  • ログを吐くとき用のクエリ文字列?

1001

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1001

  • backendがクエリ処理をしているという状態に設定

1003

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1003

  • クエリ処理を開始したというDTraceを設定

1005-1010

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1005-L1010

  • パラメータlog_statement_statsがonならResetUsage()を実行し、統計情報をリセット
    • log_statement_stats
      • SQL文に関する統計情報全体をサーバログに出力する

1012-1019

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1012-L1019

トランザクションコマンドを起動します。 query_stringによって生成されたすべてのクエリは、BEGIN/COMMIT/ABORTステートメントを見つけない限り、この同じコマンドブロックの中にあります;我々は、それらのうちの一つの後に新しいxactコマンドを強制しなければなりません、さもなければxact.cで悪いことが起こります(これは通常現在のメモリコンテキストを変更することに注意してください)。

  • start_xact_command()でトランザクションを開始

1021-1027

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1021-L1027

既存の無名ステートメントを削除します。 (厳密には必要ではありませんが、unnamedステートメントとポータルを使用するようにsimple-Queryモードを定義するのが最善と思われます; これは、以前のunnamedオペレーションによって使用されたストレージを確実に回復します)。

  • drop_unnamed_stmt()で既存の無名ステートメントを削除

1029-1032

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1029-L1032

パースツリーを構築するための適切なコンテキストに切り替えます。

1034-1038(パーサ)

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1034-L1038

クエリの基本的な解析を行う (これはトランザクションが中断された状態でも安全であるべきです!)

  • クエリをパースし、parsetree(RawStmtノード)のリストが返却
    • クエリ文字列の中に複数のコマンドがある可能性があるのでリストが返る

1040-1048

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1040-L1048

log_statement で指定された場合、直ちにログを記録する。

  • パラメータlog_statementをチェックし、log_statementの内容によってログを出力
    • クエリをパースしたあとにログ出力を行うため、構文エラーを持つSQLはログに記録されない
  • was_loggedをtrueに設定
    • 後で確認

1050-1053

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1050-L1053

トランザクションコンテキストに切り替えて、ループに入る。

  • 元のメモリコンテキストに戻す

1055-1063

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1055-L1063

歴史的な理由により、複数のSQL文が1つの "simple Query "メッセージで与えられた場合、リストの一部を別々のトランザクションにする明示的なトランザクション制御コマンドが含まれていない限り、それらを1つのトランザクションとして実行することにしています。 この動作をトランザクション機構で適切に表現するために、"暗黙の "トランザクションブロックを使用します。

  • クエリ文字列の中に複数のコマンドがある場合、use_implicit_blockをtrueに設定
    • 複数のコマンドを1つのトランザクションとして実行するために設定

1065-1069

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1065-L1069

生のparsetree(s)を通して実行し、それぞれを処理します。

  • パースツリーの中身を処理していく

1070-1079

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1070-L1079

  • 変数

1081

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1081

  • トップレベルのクエリ識別子を0に更新するために呼び出し

1083-1093

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1083-L1093

ステータス表示で使用するコマンド名を取得します(PortalRun内部では、デフォルトの補完タグにもなります)。 ps_statusを設定し、宛先が必要とするSQLコマンドの開始に関する特別な処理を行います。

  • parsetreeからクエリを取り出し、commandTagにコマンドタグを設定
  • コマンドタグからコマンド名を取り出し、psの状態表示にコマンド名が表示されるように設定
    • rocky postgres [local] idle
    • から
    • rocky postgres [local] SELECT
    • のように変化する
  • BeginCommandで、コマンド開始時に目的地を初期化する、が内部では何もやっていない

1095-1109

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1095-L1109

トランザクションが中断された場合、COMMIT/ABORT以外のすべてのコマンドを拒否してください。 なぜなら、これらのフェーズはすべてデータベースへのアクセスを試みるので、アボート状態では失敗する可能性があるからです。(なぜなら、これらの段階はすべてデータベースへのアクセスを試みますが、アボート状態では失敗する可能性があるからです(この状態では、いくつかの追加のユーティリティコマンドを許可しても安全かもしれませんが、多くは許可できません)。

  • 「アボート状態」かつ「COMMIT、PREPARE、ROLLBACK、ROLLBACK TO以外のコマンド」の場合、エラーログを出力

1111-1112

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1111-L1112

トランザクションコマンドの中にいることを確認する

  • トランザクションコマンドの中にいることを確認する

1114-1122

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1114-L1122

暗黙のトランザクションブロックを使用し、まだトランザクションブロックの中にいない場合、このステートメントが後続のものと一緒にグループ化されるように暗黙のブロックを開始します。 (ループの中で毎回これをしなければなりません。さもなければ、リスト内のCOMMIT/ROLLBACKは、後のステートメントをグループ化しない原因となります)。

  • 1055-1063でuse_implicit_blockをtrueに設定した(クエリ文字列の中に複数のコマンドがある)場合、BeginImplicitTransactionBlock()を実行し、暗黙のトランザクションブロックを開始

1124-1125

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1124-L1125

解析中や先行するコマンドでキャンセルシグナルが出た場合、終了する

1127-1134

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1127-L1134

解析やプランニングにスナップショットが必要な場合は、スナップショットをセットアップします。

  • アナライズやプランニングにスナップショットが必要な場合
    • 現在のスナップショット取得し、それをアクティブスナップショットスタックにプッシュ

1136-1157

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1136-L1157

この問い合わせの分析、書き換え、計画作成にOKを出しました。
問い合わせツリーと計画ツリーを作成するための適切なコンテキストに切り替えます(これらはトランザクションコンテキストに置くことはできません。) 複数のパースツリーがある場合、それぞれに別のコンテキストを使用し、次のパースツリーに移る前にメモリを解放できるようにします。 しかし、最後の(あるいは唯一の)パースツリーには MessageContext を使うだけで、どうせ完了後すぐにリセットされる。 エラーが発生した場合、MessageContext がリセットされると per_parsetree_context は削除される。

  • ここまでくればアナライザ、リライタ、プランナに処理を渡せる
  • 適切なメモリコンテキストに切り替え
    • parsetree_listに次の要素がある場合
      • 新たなメモリコンテキストを作成し、そのメモリコンテキストに切り替え
    • parsetree_listに次の要素がない(parsetree_listの最後の要素やparsetree_listに1つの要素しかない)場合
      • メモリコンテキストをMessageContextに切り替え

1159-1160 (アナライザ、リライタ)

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1159-L1160

生の解析対象(gram.y出力)と、オプションでパラメータ記号の型に関する情報($n)が与えられた場合、解析とルール書き換えを行います。
解析器と書き換え器のどちらかが1つのクエリを複数に拡張する可能性があるため、クエリノードのリストが返されます。
注意: 前述の理由により、この処理は生のパース処理とは別に行われる必要があります。

1162-1163(プランナ)

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1162-L1163

既に書き直されたクエリのリストに対する計画を生成する。
通常の最適化可能な文については、プランナーを呼び出す。 ユーティリティ文の場合は、単にラッパーPlannedStmtノードを作成します。
結果はPlannedStmtノードのリストである。

1165-1176

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1165-L1176

解析/計画時に使用されたスナップショットで実行されます。
同じスナップショットをクエリ実行に再利用することは有望に見えますが(少なくとも単純なプロトコルでは)、残念ながら、クエリで言及されたテーブルのいずれかをロックする前に取得されたスナップショットを実行に使用してしまうことになります。 これは目に見える形で異常が発生するため、控えてください。 詳しくは https://postgr.es/m/flat/5075D8DF.6050500@fuzzy.cz を参照してください。

  • 1127-1134で取得したスナップショットをアクティブスナップショットスタックからポップ

1178-1179

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1178-L1179

アナライズやプランニングでキャンセルシグナルが出たら終了

1181-1187

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1181-L1187

クエリを実行するための無名ポータルを作成します。すでに存在する場合は、黙って削除する。
pg_cursorsにポータルを表示しない。

1189-1199

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1189-L1199

ここで渡すものはすべてMessageContextまたはper_parsetree_contextにあるため、ポータルに何もコピーする必要はありません。いずれにせよ、ポータルよりも長持ちします。

  • PortalDefineQuery()により、portal構造体に各値を設定

1201-1204

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1201-L1204

ポータルを起動します。 ここではパラメータはありません。

  • この中でエグゼキュータが呼ばれる

1206-1226

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1206-L1226

適切な出力形式を選択します。バイナリカーソルからFETCHするのでなければ、テキストです。 (ここでこれをしなければならないのはかなりグロテスクですが、他の場所でのグロテスクさを回避することができます。 ああ、後方互換性の喜び...)

  • 出力形式がテキストならformat=0、バイナリならformat=1とする
  • ポータルの結果を設定

1228-1233

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1228-L1233

ここで、宛先となる受信機オブジェクトを作成します。

1235-1238

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1235-L1238

トランザクションコンテキストに切り替えて実行する。

1240-1249

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1240-L1253

ポータルを完成まで実行し、ポータル(と受信機)を落とす。

  • PortalRun()の中でエグゼキュートしているように見える
SELECT
ExecutePlan(EState * estate, PlanState * planstate, _Bool use_parallel_mode, CmdType operation, _Bool sendTuples, uint64 numberTuples, ScanDirection direction, DestReceiver * dest, _Bool execute_once) 
standard_ExecutorRun(QueryDesc * queryDesc, ScanDirection direction, uint64 count, _Bool execute_once) 
ExecutorRun(QueryDesc * queryDesc, ScanDirection direction, uint64 count, _Bool execute_once) 
PortalRunSelect(Portal portal, _Bool forward, long count, DestReceiver * dest) 
PortalRun(Portal portal, long count, _Bool isTopLevel, _Bool run_once, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc) 
GRANT
standard_ProcessUtility(PlannedStmt * pstmt, const char * queryString, _Bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc) 
ProcessUtility(PlannedStmt * pstmt, const char * queryString, _Bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc) 
PortalRunUtility(Portal portal, PlannedStmt * pstmt, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, QueryCompletion * qc) 
PortalRunMulti(Portal portal, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc) 
PortalRun(Portal portal, long count, _Bool isTopLevel, _Bool run_once, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc) 
exec_simple_query(const char * query_string) 

1255-1269

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1255-L1269

これがクエリ文字列の最後のパースツリーである場合、コマンドコンプリートを報告する前にトランザクション文をクローズダウンします。 これは、コマンドコンプリートメッセージが発行される前にトランザクション終了エラーが報告されるようにするためで、コマンドコンプリートメッセージとエラーのどちらかを期待するクライアントを混乱させないようにするためで、どちらかが出てからもう一方が出るということはありません。 また、暗黙のトランザクションブロックを使用している場合、最初にそれを閉じなければなりません。

1270-1277

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1270-L1277

これがトランザクション制御文だった場合、コミットしてください。次のコマンドのために、新しいxactコマンドを開始します。

1278-1299

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1278-L1299

finish_xact_command()を呼び出さない場合は、XACT_FLAGS_NEEDIMMEDIATECOMMITが設定されていないことを確認した方がよいでしょう。 (暗黙のトランザクションブロックは、それが設定されるのを防ぐべきでした。)

トランザクションブロックを開始または終了するものを除く、すべてのクエリの後にCommandCounterIncrementを必要とします。

マルチクエリ文字列のクエリ間のステートメントタイムアウトを無効にし、タイムアウトが各クエリに個別に適用されるようにする。(次のループの繰り返しで、新しいタイムアウトが開始されます)。

1301-1307

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1301-L1307

クライアントに、このクエリが終わったことを伝える。 つまり、クライアントが送信したSQLコマンドに対して、書き換えに関係なく1つずつEndCommandレポートを発行することに注意してください。(ただし、エラーで中止されたコマンドは、EndCommandレポートを全く送信しません)。

1309-1312

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1309-L1312

これで、パースツリーごとのコンテキストが作成された場合は、それを削除することができます。

パースツリーのループを終了する

1314-1319

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1314-L1319

トランザクションステートメントが開かれている場合は、それを閉じます。 (これは、パースツリーリストが空であった場合にのみ何かを行う。そうでない場合は、最後のループ の繰り返しで、すでにそうなっています)。

1321-1325

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1321-L1325

パースツリーがなかった場合は、EmptyQueryResponse メッセージを返します。

1327-1344

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1327-L1344

適切な場合、デュレーションロギングを発する。

1346-1347

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1346-L1347

  • 統計情報をサーバログに出力

1349

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1349

  • 1003でクエリ処理を開始した設定したDTraceを、クエリ処理を終了したと再設定

1351-1352

https://github.com/postgres/postgres/blob/4fc6b6eefcf98f79211bb790ee890ebcb05c178d/src/backend/tcop/postgres.c#L1351-L1352

  • 996-999で設定した値をリセット

Discussion