postgres_fdw pushdown

# はじめに
にゃーん
この記事は、[PostgreSQL 10全部ぬこ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/pg10-nuko-only) の20日目のエントリです。

# 今日のお題
今日のお題は、外部データラッパ(FDW)の中の一つ、postgresq_fdwに関する改善項目について。

# FDWとはなんぞ？
PostgreSQLを使っている人なら、知っているとは思うけど念のため。FDW(Foreign Data Wrapper)というのは、ざっくり言うと別の場所にある任意の情報源(別にRDBMSでなくてもいい)を、あたかも仮想的なテーブル(外部テーブル)のように見せて、その外部テーブルに対してSQLでアクセスできるようにする機能だ。
![FDW.png](https://qiita-image-store.s3.amazonaws.com/0/32626/82b33ade-537c-f310-b101-b18fc1c736a8.png)
FDWには現時点でも驚くほど多くの種類がある([Foreign data wrappers一覧参照](https://wiki.postgresql.org/wiki/Foreign_data_wrappers))。
情報源があれば、FDWを作りたがる。これも PostgreSQLかいはつしゃの サガ か・・・。

FDWはPostgreSQLが提供する機能の中でも、自分がとても気に入っているものだ。自分も以前、グラフデータベース[Neo4j](https://neo4j.com/)を情報源とするFDW([neo4j_fdw](https://github.com/nuko-yokohama/neo4j_fdw))を作成したことがある。(最近メンテしていないので、9.5以降はビルドに失敗しちゃうが・・・)

# postgres_fdw
FDWの中には、もちろんPostgreSQLに接続するFDWもある。それが、[postgres_fdw](https://www.postgresql.org/docs/10/static/postgres-fdw.html)だ。このモジュールはPostgreSQL 9.3からcontribモジュールとして提供されている。リモートサーバにあるPostgreSQLに接続する機能としては、以前からcontribモジュールのdblinkがあるが、postgres_fdwを用いると通常のテーブルと同様にSQL文でアクセスできるというメリットがある。

# FDW/postgres_fdwの改善のあゆみ
リリースノートを元に、FDW基盤とpostgres_fdwの改善点のあらましを簡単にまとめてみた。

|バージョン|FDW|postgres_fdw|
|:--|:--|:--|
|8.4|SQL/MED用の基盤を追加(DDLのみ実装)|-|
|9.0|-|-|
|9.1|参照のための基盤整理<BR>file_fdwのサポート|-|
|9.2|ALTER文の改善|-|
|9.3|外部テーブルへの書き込み機能<BR>postgres_fdwのサポート|postgres_fdwの最初のリリース|
|9.4|-|-|
|9.5|IMPORT FOREIGN SCHEMAの追加|Importing Optionsの追加|
|9.6|-|結合、ソート、UPDATE、DELETEのpushdwon<BR>Extension Optionの追加(UDFのpushdown)|
|10|-|集約関数のpushddown<BR>結合pushdownが可能なケースの追加<BR>OID列の対応|

# PostgreSQL 10のpostgres_fdwの改善項目

上の表にも書いたけど、PostgreSQL 10のpostgres_fdwの改善項目は以下の3つっぽい。

* 集約関数のpushddown
* 結合pushdownが可能なケースの追加
* OID列の対応

今日はこのうち、集約関数のpushdownの例だけを示す。
(結合pushdownが9.6だと出来ず、10だと可能なモデルが作れなかった・・・。そしてOID列の対応については、きちんと理解できず・・・)

100万件のレコードをもつ外部テーブルremote_tableのinteger列に対してACG関数をを実行する例を、```EXPLAIN ANALYZE VERBOSE```をつけて実行してみる。

* PostgreSQL 9.6

```postgres-console
local=# EXPLAIN ANALYZE VERBOSE SELECT AVG(data) FROM remote_table;
                                                              QUERY PLAN                                                               
---------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=205.06..205.07 rows=1 width=32) (actual time=1198.183..1198.183 rows=1 loops=1)
   Output: avg(data)
   ->  Foreign Scan on public.remote_table  (cost=100.00..197.75 rows=2925 width=4) (actual time=0.641..1106.070 rows=1000000 loops=1)
         Output: oid, id, data
         Remote SQL: SELECT data FROM public.remote_table
 Planning time: 0.855 ms
 Execution time: 1202.615 ms
(7 rows)
```

* PostgreSQL 10

```postgres-console
local=# EXPLAIN ANALYZE VERBOSE SELECT AVG(data) FROM remote_table;
                                              QUERY PLAN                                              
------------------------------------------------------------------------------------------------------
 Foreign Scan  (cost=2600.00..18006.03 rows=1 width=32) (actual time=135.276..135.278 rows=1 loops=1)
   Output: (avg(data))
   Relations: Aggregate on (public.remote_table)
   Remote SQL: SELECT avg(data) FROM public.remote_table
 Planning time: 0.108 ms
 Execution time: 136.650 ms
(6 rows)
```

実行計画の```Remote SQL```を見ると分かるように、PostgreSQL 10ではremote server側でAVG関数の処理を実施している。
また、PostgreSQL 9.6と比較すると、実行時間が大きく削減されているのがわかる。ただcostについては、実はPostgreSQL 10のほうが大きくなっている。これは何故なんだろう・・・？
（PostgreSQL 10のForeignScanの推定値が大きめなのかもしれないが）
 

# おわりに
postgres_fdwの進化は、今後予定されている、PostgreSQL自体のシャーディング機能にも繋がっている。次回のリリースで、postgres_fdwも含めどういった進化を遂げるのか楽しみだ。

# 参考：該当するリリースノート
本エントリに関連するPostgreSQL 10リリースノートの記載です。

E.2.3.12. Additional Modules

> * In postgres_fdw, push aggregate functions to the remote server, when possible (Jeevan Chalke, Ashutosh Bapat)
> * In postgres_fdw, push joins to the remote server in more cases (David Rowley, Ashutosh Bapat, Etsuro Fujita)
> * Properly support OID columns in postgres_fdw tables (Etsuro Fujita)