PostgreSQLソースコードリーディング(exec_simple_query)
個人用のメモです。
関数名
変数
996-999
- ログを吐くとき用のクエリ文字列?
1001
- backendがクエリ処理をしているという状態に設定
1003
- クエリ処理を開始したというDTraceを設定
1005-1010
- パラメータlog_statement_statsがonならResetUsage()を実行し、統計情報をリセット
- log_statement_stats
- SQL文に関する統計情報全体をサーバログに出力する
- log_statement_stats
1012-1019
トランザクションコマンドを起動します。 query_stringによって生成されたすべてのクエリは、BEGIN/COMMIT/ABORTステートメントを見つけない限り、この同じコマンドブロックの中にあります;我々は、それらのうちの一つの後に新しいxactコマンドを強制しなければなりません、さもなければxact.cで悪いことが起こります(これは通常現在のメモリコンテキストを変更することに注意してください)。
- start_xact_command()でトランザクションを開始
1021-1027
既存の無名ステートメントを削除します。 (厳密には必要ではありませんが、unnamedステートメントとポータルを使用するようにsimple-Queryモードを定義するのが最善と思われます; これは、以前のunnamedオペレーションによって使用されたストレージを確実に回復します)。
- drop_unnamed_stmt()で既存の無名ステートメントを削除
1029-1032
パースツリーを構築するための適切なコンテキストに切り替えます。
- メモリコンテキストをMessageContextに切り替え
- MessageContext
- 各コマンド実行の度にフロントエンドの処理使われるメモリ・コンテキスト
- http://www.nminoru.jp/~nminoru/postgresql/pg-memory-management.html
- MessageContext
1034-1038(パーサ)
クエリの基本的な解析を行う (これはトランザクションが中断された状態でも安全であるべきです!)
- クエリをパースし、parsetree(RawStmtノード)のリストが返却
- クエリ文字列の中に複数のコマンドがある可能性があるのでリストが返る
1040-1048
log_statement で指定された場合、直ちにログを記録する。
- パラメータlog_statementをチェックし、log_statementの内容によってログを出力
- クエリをパースしたあとにログ出力を行うため、構文エラーを持つSQLはログに記録されない
- was_loggedをtrueに設定
- 後で確認
1050-1053
トランザクションコンテキストに切り替えて、ループに入る。
- 元のメモリコンテキストに戻す
1055-1063
歴史的な理由により、複数のSQL文が1つの "simple Query "メッセージで与えられた場合、リストの一部を別々のトランザクションにする明示的なトランザクション制御コマンドが含まれていない限り、それらを1つのトランザクションとして実行することにしています。 この動作をトランザクション機構で適切に表現するために、"暗黙の "トランザクションブロックを使用します。
- クエリ文字列の中に複数のコマンドがある場合、use_implicit_blockをtrueに設定
- 複数のコマンドを1つのトランザクションとして実行するために設定
1065-1069
生のparsetree(s)を通して実行し、それぞれを処理します。
- パースツリーの中身を処理していく
1070-1079
- 変数
1081
- トップレベルのクエリ識別子を0に更新するために呼び出し
1083-1093
ステータス表示で使用するコマンド名を取得します(PortalRun内部では、デフォルトの補完タグにもなります)。 ps_statusを設定し、宛先が必要とするSQLコマンドの開始に関する特別な処理を行います。
- parsetreeからクエリを取り出し、commandTagにコマンドタグを設定
- コマンドタグからコマンド名を取り出し、psの状態表示にコマンド名が表示されるように設定
- rocky postgres [local] idle
- から
- rocky postgres [local] SELECT
- のように変化する
- BeginCommandで、コマンド開始時に目的地を初期化する、が内部では何もやっていない
1095-1109
トランザクションが中断された場合、COMMIT/ABORT以外のすべてのコマンドを拒否してください。 なぜなら、これらのフェーズはすべてデータベースへのアクセスを試みるので、アボート状態では失敗する可能性があるからです。(なぜなら、これらの段階はすべてデータベースへのアクセスを試みますが、アボート状態では失敗する可能性があるからです(この状態では、いくつかの追加のユーティリティコマンドを許可しても安全かもしれませんが、多くは許可できません)。
- 「アボート状態」かつ「COMMIT、PREPARE、ROLLBACK、ROLLBACK TO以外のコマンド」の場合、エラーログを出力
1111-1112
トランザクションコマンドの中にいることを確認する
- トランザクションコマンドの中にいることを確認する
1114-1122
暗黙のトランザクションブロックを使用し、まだトランザクションブロックの中にいない場合、このステートメントが後続のものと一緒にグループ化されるように暗黙のブロックを開始します。 (ループの中で毎回これをしなければなりません。さもなければ、リスト内のCOMMIT/ROLLBACKは、後のステートメントをグループ化しない原因となります)。
- 1055-1063でuse_implicit_blockをtrueに設定した(クエリ文字列の中に複数のコマンドがある)場合、BeginImplicitTransactionBlock()を実行し、暗黙のトランザクションブロックを開始
1124-1125
解析中や先行するコマンドでキャンセルシグナルが出た場合、終了する
1127-1134
解析やプランニングにスナップショットが必要な場合は、スナップショットをセットアップします。
- アナライズやプランニングにスナップショットが必要な場合
- 現在のスナップショット取得し、それをアクティブスナップショットスタックにプッシュ
1136-1157
この問い合わせの分析、書き換え、計画作成にOKを出しました。
問い合わせツリーと計画ツリーを作成するための適切なコンテキストに切り替えます(これらはトランザクションコンテキストに置くことはできません。) 複数のパースツリーがある場合、それぞれに別のコンテキストを使用し、次のパースツリーに移る前にメモリを解放できるようにします。 しかし、最後の(あるいは唯一の)パースツリーには MessageContext を使うだけで、どうせ完了後すぐにリセットされる。 エラーが発生した場合、MessageContext がリセットされると per_parsetree_context は削除される。
- ここまでくればアナライザ、リライタ、プランナに処理を渡せる
- 適切なメモリコンテキストに切り替え
- parsetree_listに次の要素がある場合
- 新たなメモリコンテキストを作成し、そのメモリコンテキストに切り替え
- parsetree_listに次の要素がない(parsetree_listの最後の要素やparsetree_listに1つの要素しかない)場合
- メモリコンテキストをMessageContextに切り替え
- parsetree_listに次の要素がある場合
1159-1160 (アナライザ、リライタ)
生の解析対象(gram.y出力)と、オプションでパラメータ記号の型に関する情報($n)が与えられた場合、解析とルール書き換えを行います。
解析器と書き換え器のどちらかが1つのクエリを複数に拡張する可能性があるため、クエリノードのリストが返されます。
注意: 前述の理由により、この処理は生のパース処理とは別に行われる必要があります。
1162-1163(プランナ)
既に書き直されたクエリのリストに対する計画を生成する。
通常の最適化可能な文については、プランナーを呼び出す。 ユーティリティ文の場合は、単にラッパーPlannedStmtノードを作成します。
結果はPlannedStmtノードのリストである。
1165-1176
解析/計画時に使用されたスナップショットで実行されます。
同じスナップショットをクエリ実行に再利用することは有望に見えますが(少なくとも単純なプロトコルでは)、残念ながら、クエリで言及されたテーブルのいずれかをロックする前に取得されたスナップショットを実行に使用してしまうことになります。 これは目に見える形で異常が発生するため、控えてください。 詳しくは https://postgr.es/m/flat/5075D8DF.6050500@fuzzy.cz を参照してください。
- 1127-1134で取得したスナップショットをアクティブスナップショットスタックからポップ
1178-1179
アナライズやプランニングでキャンセルシグナルが出たら終了
1181-1187
クエリを実行するための無名ポータルを作成します。すでに存在する場合は、黙って削除する。
pg_cursorsにポータルを表示しない。
1189-1199
ここで渡すものはすべてMessageContextまたはper_parsetree_contextにあるため、ポータルに何もコピーする必要はありません。いずれにせよ、ポータルよりも長持ちします。
- PortalDefineQuery()により、portal構造体に各値を設定
1201-1204
ポータルを起動します。 ここではパラメータはありません。
- この中でエグゼキュータが呼ばれる
1206-1226
適切な出力形式を選択します。バイナリカーソルからFETCHするのでなければ、テキストです。 (ここでこれをしなければならないのはかなりグロテスクですが、他の場所でのグロテスクさを回避することができます。 ああ、後方互換性の喜び...)
- 出力形式がテキストならformat=0、バイナリならformat=1とする
- ポータルの結果を設定
1228-1233
ここで、宛先となる受信機オブジェクトを作成します。
1235-1238
トランザクションコンテキストに切り替えて実行する。
1240-1249
ポータルを完成まで実行し、ポータル(と受信機)を落とす。
- PortalRun()の中でエグゼキュートしているように見える
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)
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
これがクエリ文字列の最後のパースツリーである場合、コマンドコンプリートを報告する前にトランザクション文をクローズダウンします。 これは、コマンドコンプリートメッセージが発行される前にトランザクション終了エラーが報告されるようにするためで、コマンドコンプリートメッセージとエラーのどちらかを期待するクライアントを混乱させないようにするためで、どちらかが出てからもう一方が出るということはありません。 また、暗黙のトランザクションブロックを使用している場合、最初にそれを閉じなければなりません。
1270-1277
これがトランザクション制御文だった場合、コミットしてください。次のコマンドのために、新しいxactコマンドを開始します。
1278-1299
finish_xact_command()を呼び出さない場合は、XACT_FLAGS_NEEDIMMEDIATECOMMITが設定されていないことを確認した方がよいでしょう。 (暗黙のトランザクションブロックは、それが設定されるのを防ぐべきでした。)
トランザクションブロックを開始または終了するものを除く、すべてのクエリの後にCommandCounterIncrementを必要とします。
マルチクエリ文字列のクエリ間のステートメントタイムアウトを無効にし、タイムアウトが各クエリに個別に適用されるようにする。(次のループの繰り返しで、新しいタイムアウトが開始されます)。
1301-1307
クライアントに、このクエリが終わったことを伝える。 つまり、クライアントが送信したSQLコマンドに対して、書き換えに関係なく1つずつEndCommandレポートを発行することに注意してください。(ただし、エラーで中止されたコマンドは、EndCommandレポートを全く送信しません)。
1309-1312
これで、パースツリーごとのコンテキストが作成された場合は、それを削除することができます。
パースツリーのループを終了する
1314-1319
トランザクションステートメントが開かれている場合は、それを閉じます。 (これは、パースツリーリストが空であった場合にのみ何かを行う。そうでない場合は、最後のループ の繰り返しで、すでにそうなっています)。
1321-1325
パースツリーがなかった場合は、EmptyQueryResponse メッセージを返します。
1327-1344
適切な場合、デュレーションロギングを発する。
1346-1347
- 統計情報をサーバログに出力
1349
- 1003でクエリ処理を開始した設定したDTraceを、クエリ処理を終了したと再設定
1351-1352
- 996-999で設定した値をリセット
Discussion