PostgreSQLに接続するときに使える小技の補足

この記事はマニア向けの補足記事です。

psql service=prod

みたいな接続先の指定方法があるという話を書きました。

note.mu

ところでこれこれどう実装されているんでしょう。psqlのコードの中に、service=prodみたいなコマンドライン引数を解釈するコードがあるんだろうかと思ったけど全然そんなのは見当たらないのです。

psqlのヘルプを見てみましょう。

Usage:
 psql [OPTION]... [DBNAME [USERNAME]]

もし、psqlに適当な引数を渡すと、それはDBNAMEとして解釈されます。

$ psql hello
psql: FATAL:  database "hello" does not exist

同様に、service=prodのような文字列をコマンドライン引数として渡した場合も、psql的にはDBNAMEとして扱われます。そしてこれを解釈するのはlibpqです。libpqにはデータベースに接続するための関数がいくつかありますが、psqlでは8.3の頃はPQsetdbLogin()を使っていました(今はPQconnectdbParams()を使っていますが大体似たような話なので省略します)。

https://www.postgresql.jp/document/11/html/libpq-connect.html#LIBPQ-PQSETDBLOGIN

PGconn *PQsetdbLogin(const char *pghost,
                     const char *pgport,
                     const char *pgoptions,
                     const char *pgtty,
                     const char *dbName,
                     const char *login,
                     const char *pwd);

dbName内に=記号が含まれる場合、または有効な接続URI接頭辞を持つ場合、PQconnectdbに渡された場合とまったく同じ扱いでconninfo文字列として扱われます。 その後残りのパラメータがPQconnectdbParamsの指定のように適用されます。

つまり、PQsetdbLogin()の第五引数dbnameにservice=prodみたいな文字列をわたすことができるということです。そして8.3.0のリリースノートにはこの変更について、psqlの変更としてではなくlibpqの変更として以下のように書いてあります。

PostgreSQL: Release Notes

E.147.3.16. libpq

Interpret the dbName parameter of PQsetdbLogin() as a conninfo string if it contains an equals sign (Andrew)

  • This allows use of conninfo strings in client programs that still use PQsetdbLogin().

いかがでしたか?

今回は8.3.0からpsqlのコマンドのコマンドライン引数にconninfo文字列を渡すことができるようになった変更について調べてみました。そしてその変更はpsqlではなくlibpq側で行われたものだということがわかりました。

最後までお読みいただきありがとうございました!もし気に入っていただけたら5000兆円ください!(テンプレあまりよくわかってない)

モブプログラミング(モビング)をやってみて

ここ4ヶ月ほど、ほぼ1日中、3人で同じモニタに向かって仕事をする、いわゆるモブプログラミングやモビングと呼ばれるスタイルで仕事をしている。

note.mu

この中の一人が僕だ。

僕達は常にこの3人で仕事をしているので、その結果がモビングの効果なのかそれともこの3人だからなのかはわからないけど、これまでの経験で感じたことを書いてみたい。また、既に2人は感想を書いているのでそちらと見比べるのも面白いかもしれない。

niku.name

note.mu

アイディアが沢山出てくる

何をしたらいいのかさっぱりわからないということがほとんど無くなったし、これ以上できることは無いと思ったときも、何か他にできることが無いか、という「もう一押し」の案が出てくる。自分が何かをうっかり忘れていて、他の誰かが指摘してくれるということも多い。自分が何かを思いついたときは「めっちゃそれいいっすね!」みたいなフィードバックがすぐ返ってくるのも楽しい。

知識の共有

これを知っているのは自分だけという不安から解放された。明日休むからこれお願い、みたいに休みを取るときに引き継ぎをする必要がない。休んだ日にslackを気にする必要もない。休んでいる間に自然に自分の仕事が進んでいる。

ペアとモブ

モビングを一日中していると疲れる。それでも毎日続けられるのは3人だからだと思う。ペアよりひとり多い分Avaliabilityが高い。たまに2人で仕事をすることももあるけどそういう時はより消耗がはげしいので、こまめに休憩を挟むようにしている。

やり方について

モブプロの本によると、1つのマシンを全員で使うのがスタンダードのようだが、僕達はそれぞれの自分のMacBook ProをAirPlayでディスプレイを奪い合いながら使っている。また本によればドライバは操作役に徹するのが基本のようだが、僕達はその辺は気にせずドライバも積極的に意思表示する。

そしてもうひとつの決まりごと

これは先日Agile Japan 2018(年度) サテライト<札幌>に参加したときに教えてもらったものだ。

agilesapporo.doorkeeper.jp

takaking22.com

モブプログラミング・ベストプラクティス ソフトウェアの品質と生産性をチームで高める

モブプログラミング・ベストプラクティス ソフトウェアの品質と生産性をチームで高める

効率はどうなのか

わからない。モブで働くのと3人別々に働くのを単純に比較する方法は(記憶を消し去ってもう一度やる以外は)無いと思う。ただ、もし仮に一人で同じことをやっていたら、そもそも同じゴールに辿り着くことはできないと思う。

チームについて

上手くいっているのは、僕等3人がとてもモビングに向いていたからなのかもしれない。とにかく一緒に仕事をしている2人は経験豊富で、かつHRT(謙虚・尊敬・信頼)を大切にするエンジニアだ。

僕は考えを言葉にするのは上手くないけど、ふわっと「なんか違う」というと「それはこういうことですか?」「たとえばこうだったらどうですか?」などと色々な質問をつかって僕の考えを引き出してくれる。元々の僕の考えとは全然違うものを相手が受けとっていることもあるけど、それがまた新しいアイディアを生んだりもする。そうやって、他人の言葉を借りて自分のアイディアが熟成される瞬間を見るのはとても面白い。

問題に対する理解度を見誤っていないか

プログラミングの仕事は以前と比べて難しくなっているかはわからないが、取り扱う規模が大きくなっている感じはする。自分達で書くコードのだけでなく、ライブラリやフレームワーク、インフラ、ビルドツールなどさまざまな知識が必要とされる。それらを全部理解する必要は無いし、実は10%くらい理解していればそこそこの物は作れるのがいいところなんだけど、思いもよらないところではまってしまうこともある。つまり、未来を予測するのが難しくなってきている。

時々、プログラマーはこういう理解度が低くて未来が予測できない状況の中で物を作ることに慣れすぎて麻痺しているんじゃないかと思うことがある。「だいたいこんな感じでしょ」と。3人でやっていてもやっぱり間違うことはあるけれど、ひとりで不安を抱える時間は無くなった。

課題

モビングしていると結構楽しいし、辛い仕事も前向きで取り組むことができる。これはとてもいいことなんだけど、一方で辛い仕事を受け入れすぎていないか、我慢が効く分、その辛さを生み出している本質的な問題を避けていないかと思うことはある。

なので最近はスコープを広げて、チームの外とも協力しながら問題の本質にせまることを意識して仕事をしている。これを「ラインを押し上げる」と呼んでいる。常に押し上げればいいわけではなくて、例えば目の前で障害が発生している時は原因究明や再発防止よりもまず復旧作業が必要となる。状況に応じて適切なラインコントロールができるようになることがこれからの課題だと思う。

PHPの現場#27を聴いた / interfaceについて

面白かった。最近ぜんぜんPHPは書いてないんですが。

php-genba.shin1x1.com

感想書かなきゃとおもいながらダラダラしてたらもう次のエピソードが出ていたんだけど、1つ前のエピソードの感想です。

いろんな過程を経て、@shin1x1 さんの伝えたいことが熟成されてきている感じがして良い。

途中、interfaceを書くかという話で意見が分かれていた。僕も @shin1x1 さんと同じで、interfaceを使うことに特別な感情はない。

僕も昔はinterfaceを何のために書くのかわからなかったけど、今は「とりあえずinterfaceにしておいて、classでもいいかと思ったらclassにしよう」くらいの感じに思っている。これはきっと僕がOutside Inなプログラムの書きかたをするようになったからだと思う。いま自分が実装しているのはもちろんclassだけど、「そのclassが使っている何か」はinterfaceであろうがclassであろうが単体テストを書いている時点ではどちらでもいいし、そのとき再利用するかどうかは気にしない。

interfaceを何のために書くのかわかっていなかったころの僕は、interfaceというとFooInterfaceというインターフェースに対してFooImplみたいな実装があるみたいなイメージを持っていて、これって何の意味があるのって思っていた。でも、インターフェースと実装は1対1ではない。

f:id:iakio:20190226214528p:plain

phpspecで学ぶLondon School TDD

上のスライドは、Markdown#toHtmlFromReaderというメソッドの引数に渡すのはFileStorageクラスとReaderインターフェースのどちらが良いかという話(この例はphpspecのドキュメントを参考にさせてもらった)。Markdown#toHtmlFromReaderの単体テストを書いている時点では、それを動かすために最小限必要なものをモックする。その方が、意図を正しくコードに反映できると思っている。

僕がプログラマーとして最も影響を受けた読み物の1つは、平鍋さんが2005年に書いた次のブログエントリーだ*1

  • 「テストしやすい」ことが、良い設計(EoT=Ease of Testing):An Agile Way:オルタナティブ・ブログ

  • ぼくは、

    EoT(*1)の高い設計が、よいオブジェクト指向設計である。

    と主張する。設計品質の中で「テスト容易性(EoT)」を最上位と見るのだ。オブジェクト指向のさまざまな機構、用語、考え方は、すべて EoT のため、と捕らえられる。

    僕は「オブジェクト指向とは / interfaceとは / DIとは何か」ということは説明できないけど、それらを何のために使っているかは説明できる。それはテスト容易性のためであり、言いかえれば、「今書いたコードをすぐに動かしてフィードバックを得る」ためとも言える。素早くフィードバックを得るためには、動かすのは大きなコードより小さなコードの方が良い。そのために、大きなコードのほんの一部だけを切り出して動かす方法が必要となる。僕にとってオブジェクト指向やinterfaceやDIは、そのための道具だ。

    オブジェクト指向やinterfaceやDIの話をするときにしばしば再利用性の話がとりあげられる。例えばDIを使えば永続化レイヤーを別のものに差し替えられる、みたいな。でも実際差し替えることなんてそうはおきないんのではないだろうか(まあ無くはないんだけど)。

    でも、あるコードの一部分を、プロダクションコードとしても単体テストとしても動作するようにデザインするということは、それはある意味「再利用している」と言える。僕はこの意味での再利用の方が重要だと思っている。

    もっとも、僕がこのエントリーに強烈なインパクトを受けた理由は、2005年という時代が、前者の意味での再利用性が強調されがちだった時代だったからなのかもしれない。そしてその意味を本当に理解するのは、それから何年も経ってからだった。

    テーブルを作らずにクエリをちょっと試す

    あるクエリがどんな動きをするのかを手を動かしながら探索的に調べたいときに、テーブルを作らずにCTE(Common Table Expression)を使ってデータを作ればはかどるんじゃないだろうかと思った。

    たとえばこれはSQLアンチパターンにでてくるクエリだけど。

    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';

    これがどんな動きをするのかを、テーブルを作らずにCTEを使って実行してみる。まずそのまま実行すると、

    => SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
    ERROR:  relation "bugs" does not exist
    LINE 1: SELECT * FROM Bugs

    当然エラーとなる。Bugsが無いのでWITH句を使って適当に作ろう。

    => WITH Bugs AS (
        VALUES (1)
    )
    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
    ERROR:  relation "tags" does not exist
    LINE 5:   JOIN Tags AS t1 USING (bug_id)

    今度はTagsが無い。

    => WITH Bugs AS (
        VALUES (1)
    ),
    Tags AS (
        VALUES (1)
    )
    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
    ERROR:  column "bug_id" specified in USING clause does not exist in left table

    両方のテーブルにbug_idが必要だ。

    => WITH Bugs(bug_id) AS (
        VALUES (1)
    ),
    Tags(bug_id) AS (
        VALUES (1)
    )
    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
    ERROR:  column t1.tag does not exist
    LINE 10: WHERE t1.tag = 'printing' AND t2.tag = 'performance';

    Tags.tagが必要だ。

    => WITH Bugs(bug_id) AS (
        VALUES (1)
    ),
    Tags(bug_id, tag) AS (
        VALUES (1, 'printing')
    )
    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
     bug_id | tag | tag
    --------+-----+-----
    (0 rows)

    OK。エラーはなくなった。後は値を工夫すればいい。

    => WITH Bugs(bug_id, summary) AS (
        VALUES
        (1, 'bug 1'),
        (2, 'bug 2'),
        (3, 'bug 3'),
        (4, 'bug 4')
    ),
    Tags(bug_id, tag) AS (
        VALUES
        (1, 'printing'),
        (1, 'performance'),
        (2, 'printing'),
        (3, 'performance')
    )
    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
     bug_id | summary |   tag    |     tag
    --------+---------+----------+-------------
          1 | bug 1   | printing | performance
    (1 row)

    データとクエリが1つになるので試行錯誤の過程を記録しやすそうだ。

    あと、この方法はテーブルが存在する場合もWITH句を優先してくれるので、たとえば実際にはBugsが100万行くらいあるんだけど5行くらいで試したい、みたいなこともできる。

    2018年はこんな年でした

    英語学習の動画いろいろ見たけどこれが一番お気に入りです。

    www.youtube.com

    もっと前はこんな年でした

    Windows10の新フォント「UDデジタル教科書体」をブラウザで使ってみる

    ちょっと前の話だけど、Windows 10 Fall Creators Updateでフォントが追加された。

    forest.watch.impress.co.jp

    で、UDデジタル教科書体 NK-Rをブラウザのデフォルトのフォントにしてみた。 ちなみに使っているのはSurface Pro 4で解像度は2736 x 1824、スケーリングは200%。ブラウザはFirefox Developer Editionです。

    f:id:iakio:20180206190717p:plain

    長いドキュメントを読むときはなかなか良いと思う。

    f:id:iakio:20180206192633p:plain

    あと書くときも良い。なんというか、ちょっと違った気分になる。

    他のフォントと混ざったとき、最初は違和感があったけどだんだん気にならなくなってきた。

    f:id:iakio:20180206190749p:plain

    2017年はこんな年でした

    あと適当に動画貼っておきます。 www.nicovideo.jp

    もっと前はこんな年でした