【Flyway】特定イベント後に実行するSQLファイルを設定する

やりたいこと

flywayによるマイグレーションでテーブルが追加された場合に、共通のトリガー設定SQLを動かすようなことをします。
生で書いてしまった場合1度しか実行されないため、これを実現するには工夫が必要です。

やり方

Callback機能を利用することで実現できます。
自分の場合マイグレーション成功時にのみ実行したかったため、afterMigrate__${識別用の名前}.sqlを作成・配置しました。

その他どのようなCallbackが存在するかは公式資料をご覧下さい。

documentation.red-gate.com

【Googleスプレッドシート】複数の範囲を1列にまとめる(結合する)

この辺り知識が薄いので、もっと良いやり方が有りましたらコメント頂けると嬉しいです。

やり方

FLATTEN関数を使えばできるようでした。

support.google.com

例えば、「E2:E40の範囲からB2より大きいものをFILTERした結果とG2:G40の範囲からB2より大きいものをFILTERした結果を結合する」場合、=FLATTEN(FILTER(E2:E40, B2 <= E2:E40), FILTER(G2:G40, B2 <= G2:G40))というように書けます。

注意点

この関数は、例えば複数列有るデータに対して適用した場合も1列に均してしまう点に注意が必要です。
例えば以下のように変換されてしまいます。

E2 F2
E3 F3

=FLATTEN(E2:F3)

E2
F2
E3
F3

フィルタ対象のデータと値になるデータが分かれている場合、=FLATTEN(...を複数回書くなり、他の方法で結合する必要が有ります。

【Gradle】プラグインバージョンを対象にグリッドテストを作成する【GitHub Actions】

TL;DR

  • gradle.kts利用の場合、引数をpluginsブロックで参照することはできないようだった
  • 代替案としては、環境変数に設定 -> System.getenvで読み出す形式にするのが一番シンプルそうだった

やり方

紹介する内容は以下からの抜粋です(余計なdiffが入ってしまっていますが、lint-and-test-main.ymlbuild.gradle.ktsだけ見て頂ければ大丈夫です)。
Kotlinのバージョンが対象です。

github.com

build.gradle.kts側の設定

環境変数から設定を取得し、取得できなければデフォルトを用いるように設定しています。

plugins {
    val kotlinVersion: String = System.getenv("KOTLIN_VERSION")?.takeIf { it.isNotEmpty() } ?: "1.7.21"
    kotlin("jvm") version kotlinVersion

    /* ... */

}

GitHub Actions側の設定

最低限の設定を抜き出した部分は以下の通りです。

    strategy:
      matrix:
        kotlin-version: [ '1.7.21', '1.8.10', '1.8.20-Beta' ]
    env:
      KOTLIN_VERSION: ${{ matrix.kotlin-version }}

補足

引数での指定について

pluginsブロックで引数(コマンドライン-PkotlinVersion=1.7.21のように入力する)を参照する方法が見つからず、とりあえず動いた環境変数での指定を採用しました。
厳密に言うと環境変数は環境によって影響を受ける可能性が有るため、あまり利用したくありませんでしたが、現在ktsでこれを回避する方法は無いようでした。
この問題は以下のイシューで議論されています。

github.com

Version Catalogを利用している場合

Version Catalogを利用している場合、以下のようにすることでできました。

github.com

やっていることの簡単な説明です。

  1. gradle/libs.versions.tomlKotlinのデフォルトバージョンと、関連して利用する依存を記述
  2. settings.gradle.ktsdependencyResolutionManagementブロックで、環境変数から必要に応じて1で指定したバージョンを上書き
  3. build.gradle.ktsではプラグインalias(libs.plugins.${pluginsブロックに指定した名前})で参照する(バグでエラー表示が出るため抑制も行う)

自分も全体は理解し切れていませんが、以下のプロジェクトを参考に作成しました。

github.com

【Gradle】Kotlinのプラグインバージョンを下げるとCaused by: java.lang.NoSuchFieldError: KOTLIN_STAT_LABEl_PROPERTYでsyncが失敗する状況への対処

TL;DR

  • 自分が遭遇した問題の原因はorg.jmailen.kotlinterプラグインKotlinバージョン間の互換性が無いことだった
  • プラグインバージョンを確認するのが良いかも

起きたこと

Kotlinプラグインのバージョンを1.7.21 -> 1.5.32に変更した所、Caused by: java.lang.NoSuchFieldError: KOTLIN_STAT_LABEl_PROPERTYsyncが失敗するようになりました。
ログ・スタックトレースは以下の通りでした。

ログ・スタックトレース

Execution failed for task ':prepareKotlinBuildScriptModel'.
> Failed to notify task execution listener.
   > KOTLIN_STAT_LABEl_PROPERTY

* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':prepareKotlinBuildScriptModel'.
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:94)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:333)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:320)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:313)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:299)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:143)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:227)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:218)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:140)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
Caused by: org.gradle.internal.event.ListenerNotificationException: Failed to notify task execution listener.
    at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:89)
    at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:346)
    at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:249)
    at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:141)
    at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at com.sun.proxy.$Proxy88.afterExecute(Unknown Source)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:91)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:333)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:320)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:313)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:299)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:143)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:227)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:218)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:140)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
Caused by: java.lang.NoSuchFieldError: KOTLIN_STAT_LABEl_PROPERTY
    at org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildEsStatListener$label$2.invoke(KotlinBuildEsStatListener.kt:32)
    at org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildEsStatListener$label$2.invoke(KotlinBuildEsStatListener.kt:32)
    at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
    at org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildEsStatListener.getLabel(KotlinBuildEsStatListener.kt:32)
    at org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildEsStatListener.reportData(KotlinBuildEsStatListener.kt:57)
    at org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildEsStatListener.afterExecute(KotlinBuildEsStatListener.kt:73)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:43)
    at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:245)
    at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:157)
    at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:61)
    at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:346)
    at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:249)
    at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:141)
    at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at com.sun.proxy.$Proxy88.afterExecute(Unknown Source)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:91)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:333)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:320)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:313)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:299)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:143)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:227)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:218)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:140)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)

原因・対処

自分のプロジェクトでは、org.jmailen.kotlinterプラグインKotlinバージョン間の互換性が無いことが原因でした。
そこで、以下の互換表に従って、org.jmailen.kotlinterのバージョンを3.9.0に下げた所、問題が解決しました。

github.com

1年のアウトプットを振り返る

今年もやります。
以下は去年分です。

wrongwrong163377.hatenablog.com

アウトプットまとめ

まず今年1年で行った主なアウトプットです。

外部OSSへのコントリビュート

全体を通して見ると、特にJetBrains/kotlinへ重要な内容で貢献できたことが良かったです。

考えてみると、色々なリポジトリへの貢献を続けて来たこともあって、「ITサービスを利用する全ての人間は自分の携わったプログラムを少なくとも間接的に動かす機会が有る」と言えそうな状態になっており、「OSS活動ってすげえなー」と改めて思います(自分の貢献はちょっとだけお手伝いした程度に過ぎませんが)。
来年も引き続き頑張っていこうと思います。

Kotlin関係

今年大きかったのは、JetBrains/kotlin(正確にはkotlin-reflect)に幾つかのPRをマージして頂けたことです。

qiita.com wrongwrong163377.hatenablog.com

KFunction.callByは数多くのライブラリ・フレームワークで利用される機能で、これの高速化はKotlinの弱点の1つであるリフレクションの遅さを緩和するものです。
value class絡みのバグ修正も、これによってkotlin-reflectに依存するライブラリが実質的にvalue classサポートを行えるようになったという内容です。
どちらも非常に重要な内容であり、メンテナの助力も頂いたとは言え、このような変更を自力で行えたのはとてもいい経験でした。

jackson-module-kotlin関係

去年1年間は多くの活動を行なったjackson-module-kotlin関係ですが、年始に少し貢献した後はメンテな不在となってしまい、一切貢献できませんでした。
ほぼ1年メンテナ不在の状況が続いてしまったこともあり、jackson-module-kotlin関係は正直プロジェクトの今後に大きな不安が有ります。

正直メンテナを引き受けようかと思ったことも有りますが、仕事の状況や自由にOSS活動したかったこともあって手を上げられないままでした。
あるいは、jackson-module-kotlin全体を書き直すようなプロジェクトをやりたいとはずっと思っているので、来年は何かしらのアクションを起こせるといいなと思っています。

その他

幾つかのリポジトリにはソースコードで貢献したり、発見したバグの報告をしたりしていました。
大きな内容を実現するのもいいですが、こう言った細かい内容での貢献も続けて行きたいですね。

ブログ

2017年からの年間ブログ数50本記録が途絶えてしまいました。
割と継続していた記録が途切れてしまったのは結構残念に感じます。

理由はいくつか有りますが、初心者を脱却しつつあってブログに書く程の何かがあまり無いことと、OSSへの貢献比重を高めると新しい知識に触れる機会が少ないというのが大きい気がしています。
後はAPEXですかね……。

来年もどちらかと言えばOSS活動の方に注力したく、正直本数は戻せない気がしています。

終わりに

今年はAPEXにハマっていたせいでアウトプットが滞っていた感が有ります。
とは言えOSSへの貢献という点では大きな仕事ができたと思うので、そこは良かったです。

仕事の方は、昨年末に転職してからひたすらSpring WebFluxに向き合った(というか振り回され続けた)1年でした。

技術面では特にReactor周りの難しさが辛く、改善するユースケースもそれほど多くない感が有って、「これじゃあSpring WebFluxが流行ることは無いなあ」と感じました。
来年9月には次のLTSとなるJDK21が控えており、ここでProject Loomの仮想スレッドが正式化するようなことが有ればいよいよSpring WebFlux要らない子化するんじゃないかという不安も有ります。

本業の方も来年前半は特に忙しくなりそうで、気が抜けない日々になりそうです。
それでもこの辺りを乗り切っていけばまだまだ希望はあるので、引き続きやっていけたらと思います。

【GitHub】Kotlinの「色」が変わった話

「Swift/Kotlin愛好会 Advent Calendar 2022」の枠が空いていたので急遽書いてみました。

qiita.com


GitHubでは、画像のように、リポジトリ内の言語割合をカラフルに表示してくれます(画像はKotlinリポジトリより)。
この表示でのKotlinは紫っぽい色ですが、1年半ほど前まではオレンジっぽい色だったことをご存じでしょうか?

変更の経緯

この変更はZacSweersさんからの提案で行われました。

Zacさんはsquare/moshiのメンテナでもある超強いエンジニアです。
この件への最初の言及はこちらのツイートだったと記憶しています。

JavaKotlinは同じリポジトリに同居していることが多々有りますが、Javaは茶色で、当時のKotlinはオレンジっぽい色でした。
並べると……こう、大小ある感じで、見た目が良くありません。

ツイートより引用

ということで、Zacさんより以下のPRが出され、今の紫っぽい色に落ち着いたのでした。

github.com

後書き

変更から1年半程経過し、Kotlinを始めた当初から今の色だったという方も増えつつあるのではないかと思い、昔話としてこんな記事を書いてみました。
「Swift/Kotlin愛好会 Advent Calendar 2022」の枠はまだまだ有りますので、是非皆さんに書いて頂けると嬉しいです。

qiita.com

余談ですが、square/moshi及びZacSweers/MoshiXはコードやgradle周りが綺麗ということで個人的な推しリポジトリです。
JSONに関する一般的なライブラリですし、OSSのコードを読んで勉強してみたいという方にもおススメです。

【PostgreSQL】PL/pgSQLで全テーブルに一括で更新日時設定のトリガーをセットする

やること

テーブル別でUPDATED_ATカラムへのトリガー設定を行っている状況が有ったとします。

-- 関数作成
CREATE OR REPLACE FUNCTION trigger_set_timestamp()
  RETURNS TRIGGER AS $$ BEGIN NEW.UPDATED_AT = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql;

-- テーブル毎にトリガー設定
CREATE TABLE IF NOT EXISTS FOO(...);

CREATE TRIGGER set_timestamp
  BEFORE UPDATE ON FOO
  FOR EACH ROW
  EXECUTE PROCEDURE trigger_set_timestamp();

CREATE TABLE IF NOT EXISTS BAR(...);

CREATE TRIGGER set_timestamp
  BEFORE UPDATE ON BAR
  FOR EACH ROW
  EXECUTE PROCEDURE trigger_set_timestamp();

この書き方は可読性が悪く、設定漏れを起こしかねません。
そこで、全テーブルへの設定処理一括で行う形に修正を行います。

やったこと

PL/pgSQLを使って以下のような処理を作成しました。
管理用テーブル等を除いてUPDATED_ATカラムを持つテーブルを抽出し、それらに対してそれぞれトリガー設定を呼び出しています。

DO
$$
DECLARE
  -- postgresの管理用テーブルやflyway関連以外で、UPDATED_ATカラムを持つテーブルを抽出(その他除外したいテーブルはここに書く)
  has_updated_at_tables CURSOR FOR
    SELECT t.table_name FROM information_schema.tables t
      INNER JOIN information_schema.columns c ON c.table_name = t.table_name
        AND c.table_schema = t.table_schema
    WHERE t.table_schema = 'public'
      AND t.table_type = 'BASE TABLE'
      AND t.table_name != 'flyway_schema_history'
      AND c.column_name ILIKE 'UPDATED_AT'; -- ファイル上の定義は大文字だが、POSTGRES上は小文字扱いなため、ILIKEで検索している
  table_name VARCHAR;
BEGIN
  OPEN has_updated_at_tables;
  LOOP
    -- テーブル名を取得、取得できなくなればループ終了
    FETCH has_updated_at_tables INTO table_name;
      EXIT WHEN NOT FOUND;
    EXECUTE format(
      'CREATE TRIGGER set_timestamp
  BEFORE UPDATE ON %s
  FOR EACH ROW
  EXECUTE PROCEDURE trigger_set_timestamp()',
      table_name
    );
  END LOOP;
END
$$ LANGUAGE PLPGSQL;

これを用いると、先程のDDLは以下のようになります。

-- 関数作成
CREATE TABLE IF NOT EXISTS FOO(...);

CREATE TABLE IF NOT EXISTS BAR(...);

-- 関数作成
CREATE OR REPLACE FUNCTION trigger_set_timestamp()
  RETURNS TRIGGER AS $$ BEGIN NEW.UPDATED_AT = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql;

-- UPDATED_ATのトリガーを一括設定
DO
$$
DECLARE
  -- postgresの管理用テーブルやflyway関連以外で、UPDATED_ATカラムを持つテーブルを抽出(その他除外したいテーブルはここに書く)
  has_updated_at_tables CURSOR FOR
    SELECT t.table_name FROM information_schema.tables t
      INNER JOIN information_schema.columns c ON c.table_name = t.table_name
        AND c.table_schema = t.table_schema
    WHERE t.table_schema = 'public'
      AND t.table_type = 'BASE TABLE'
      AND t.table_name != 'flyway_schema_history'
      AND c.column_name ILIKE 'UPDATED_AT'; -- ファイル上の定義は大文字だが、POSTGRES上は小文字扱いなため、ILIKEで検索している
  table_name VARCHAR;
BEGIN
  OPEN has_updated_at_tables;
  LOOP
    -- テーブル名を取得、取得できなくなればループ終了
    FETCH has_updated_at_tables INTO table_name;
      EXIT WHEN NOT FOUND;
    EXECUTE format(
      'CREATE TRIGGER set_timestamp
  BEFORE UPDATE ON %s
  FOR EACH ROW
  EXECUTE PROCEDURE trigger_set_timestamp()',
      table_name
    );
  END LOOP;
END
$$ LANGUAGE PLPGSQL;

補足

自分は試していませんが、DEFAULT設定を上手く利用すればトリガー設定は省略できるかもしれません。

qiita.com