DROP EXTENSION追跡(途中)

先日、PostgreSQL 9.1から導入されたパッケージ管理機能、EXTENSIONを使ってパッケージ化をしてみた。で、ふと思ったこと。

そういえば今まではアンインストール用のスクリプトファイルを別に用意して、そこにDROP FUNCTIONやらをいろいろ書いていたのに、今回はそれなしでいつの間にかパッケージに入っている型やら関数やら演算子などが、DROP EXTENSION背景で綺麗に削除されるようになっている。
じゃあ、DROP EXTENSIONではどうやってDROP FUNCTIONなどを発行せずに、パッケージを構成する種々のオブジェクトを削除しているだろう?
DROP EXTENSION内で直接システムカタログから削除しているのだろうか?
ということで9.1.2のソースを見てみることにした。

ソースを見てみる

DROP EXTENSIONを実装しているのは、名前から考えると
src/backend/commands/extension.c
なんだろう。で、ファイルを開いて"DROP"で検索。するとRemoveExtensions()という関数が引っかかる。関数コメントにも"Implements DROP EXTENSION."と書いてあるので、たぶんこれなんだろう。

最初のコメントは

First we identify all the extensions, then we delete them in a single performMultipleDeletions() call.
This is to avoid unwanted DROP RESTRICT errors if one of the extensions depends on another.

とある。ここで気づいたのだが、CREATE EXTENSIONやALTER EXTENSIONとは異なり、DROP EXTENSIONでは複数の拡張を指定出来るのか。PostgreSQL文書のhttp://www.postgresql.jp/document/9.1/html/sql-dropextension.htmlを見たけど確かに複数指定可能になっている。
コード上もforeachで(たぶん)指定した削除対象の拡張分ループするようになっているように見える。
で、ループ内で拡張名から拡張のIDを取得(get_extension_oid)し、そのIDの拡張のオーナーが、実行ロールと一致するか調べているっぽい。
さて、どうやらforeachループ内では削除そのものはやっていない様子。
ただ、ここで削除対象となるrelatioinのID(ExtensionRelationId)を削除対象のオブジェクトにセットしているようだ。
ループを抜けると以下のコメント。

Do the deletions.
Objects contained in the extension(s) are removed by means of their dependency links to the extensions.

ここで初めて削除をするようだ。
performMultipleDeletions()の後にfree_object_addresses()を発行している。それぞれ何をやっているのかは、この先を見てみないと分からない。free_object_addresses()は最初のコメントに出てきた関数名だ。この関数で複数の拡張間の依存性のチェックとカタログからの削除をやっているのだろうか?
これらの関数は
src/backend/catalog/dependency.c
で実装されている。ざっとソースを見た感じでは、performMultipleDeletions()で削除っぽいことをしているようだが。

performMultipleDeletions()

この関数のヘッダコメントは

performMultipleDeletions: Similar to performDeletion, but act on multiple objects at once.

The main difference from issuing multiple performDeletion calls is that the list of objects that would be implicitly dropped, for each object to be dropped, is the union of the implicit-object list for all objects.
This makes each check be more relaxed.

要するに複数のオブジェクトに対する performDeletion() と同じようなもの、ということらしい。とはいっても performDeletion() を知らないのでやっぱり見なくてはいけない。
最初の処理の前のコメントは

We save some cycles by opening pg_depend just once and passing the Relation pointer down to all the recursive deletion steps.

とな。52.18. pg_dependをRelatoinを渡しつつ再帰的にぐりぐり探しながら処理するってことか。
で、最初にheap_open()を呼ぶ。この関数がヒープ上のrelationを操作する最初の手順らしい。あちこちで使われているので覚えておかねば。heap_open(), heap_fetch(), heap_insert(), heap_update(), heap_delete(), 等々
src/backend/access/heap/heapam.c
で実装されているようだ。
で、そのあとのfor文で依存性のチェックらしきことをしている。
そのループが終わった後、reportDependentObjects()で実際に削除をしているのか。この関数の呼び出し前のコメントを見るかぎりはそう思える。

システムカタログから依存関係を確認する。

とりあえず思いつくのは52.22. pg_extensionあたり。
ただ、ここを見ても参照先は52.8. pg_authid52.32. pg_namespace52.11. pg_classくらい・・・。となると、52.22. pg_extensionのoidから何か引っ張っているかな。
とりあえず、インストールしたpg_extensionのoidを元に、pg_dependを引くと1件見つかったので、ここからclassidと同じ値を持つoidを持つpg_classを引いたりして、対象を延々探すのかな・・・?このあたりの探し方がまだきちんと追いきれていない。

自分なりの推測

  • DROP EXTENTIONでは削除対象の拡張名(複数)を受け取る。
  • pg_depend内を再帰的に追いながら依存関係のあるオブジェクトを探し出して削除する。
    • 拡張に含まれる型や関数や演算子などを一括で削除できるのはこの仕組によるものなんだろう。