PostgreSQLへのJDBCアクセスをネイティブ化する

PostgreSQLへのJDBCアクセスがあるコードをGraalVMでネイティブイメージ化するとき、org.postgresql.core.v3.ConnectionFactoryImplの対応が必要だったのでメモ

たとえばこんな感じでPostgreSQLにアクセスします。

public class Main {
    public static void main(String[] args) throws SQLException {
        try (Connection conn = DriverManager.getConnection(
                "jdbc:postgresql://localhost:5432/mystat", "mystat", "pass");
             Statement stmt = conn.createStatement();
             ResultSet result = stmt.executeQuery("select * from users")) {
            while (result.next()) {
                System.out.printf("name:%s handle:%s %n",
                        result.getString("user_name"),
                        result.getString("user_handle"));
            }
        }
    }
}

JDBCドライバは42.2.5を使ってます。

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.5</version>
</dependency>

これをネイティブコンパイルすると、なんかエラーが出ました。

$ native-image -jar AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies.jar
Build on Server(pid: 153, port: 56330)*
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:153]    classlist:   2,296.43 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:153]        (cap):   3,746.53 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:153]        setup:   5,098.37 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:153]     analysis:   5,581.32 ms
error: unsupported features in 4 methods
Detailed message:
Error: com.oracle.graal.pointsto.constraints.UnresolvedElementException: Discovered unresolved type during parsing: com.sun.jna.Platform. To diagnose the issue you can use the --allow-incomplete-classpath option. The missing type is then reported at run time when it is accessed the first time.
Trace:
        at parsing org.postgresql.sspi.SSPIClient.isSSPISupported(SSPIClient.java:90)
Call path from entry point to org.postgresql.sspi.SSPIClient.isSSPISupported():
        at org.postgresql.sspi.SSPIClient.isSSPISupported(SSPIClient.java:90)

ぐぐってみると、なんかスタブが必要そう。

native-image fails to compile PostgreSQL JDBC client. · Issue #727 · oracle/graal

このようなクラスで対応できるみたいです。

https://github.com/katox/graal-pg-client/blob/master/src/main/java/stub/InternalConnectionFactoryImpl.java

コンパイルするにはSubstrateVMのライブラリが必要です。

<dependency>
    <groupId>com.oracle.substratevm</groupId>
    <artifactId>svm</artifactId>
    <version>1.0.0-rc11</version>
    <scope>provided</scope>
</dependency>

いけました!

$ native-image -jar AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies.jar
Build on Server(pid: 939, port: 58056)*
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]    classlist:   2,031.76 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]        (cap):   2,046.18 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]        setup:   3,208.73 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]   (typeflow):   5,918.46 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]    (objects):   4,188.45 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]   (features):     367.38 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]     analysis:  10,700.59 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]     universe:     441.69 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]      (parse):     690.96 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]     (inline):   1,360.90 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]    (compile):   5,880.47 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]      compile:   8,622.91 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]        image:     932.34 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]        write:     365.44 ms
[AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies:939]      [total]:  26,385.43 ms

ただ、なんかもう一度native-imageするとエラーがでます。サーバーを使わないようにしたほうがよさそう。

ネイティブコンパイルすると、やたら速いのですごく違和感がありますね。

$ time java -jar AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies.jar
name:John Smith handle:john

real    0m0.699s
user    0m0.609s
sys     0m0.500s
$ time ./AccessPostgres-1.0-SNAPSHOT-jar-with-dependencies
name:John Smith handle:john

real    0m0.033s
user    0m0.000s
sys     0m0.016s