JdbcUtils.java

/*
 * SPDX-FileCopyrightText: 2025 kaumei.io
 * SPDX-License-Identifier: Apache-2.0
 */
package io.kaumei.jdbc.core;

import org.jspecify.annotations.Nullable;

import java.sql.ResultSet;
import java.sql.Statement;

public final class JdbcUtils {

    private JdbcUtils() {
        // prevent instantiation
    }

    // ------------------------------------------------------------------------

    public static String marks(int count, int maxPlaceholders, String parameterName) {
        if (count < 1) {
            throw new JdbcException("Collection placeholder expansion for parameter '" + parameterName
                    + "' must contain at least 1 placeholder, but was " + count);
        } else if (count > maxPlaceholders) {
            throw new JdbcException("Collection placeholder expansion for parameter '" + parameterName
                    + "' exceeds @JdbcConfig.maxCollectionPlaceholders: " + count + " > " + maxPlaceholders);
        }
        return "?" + ",?".repeat(count - 1);
    }

    // ------------------------------------------------------------------------

    public static int checkPlaceholder(int count, int add, int maxPlaceholders) {
        if (count < 0 || add < 0 || maxPlaceholders < 0) {
            throw new IllegalArgumentException();
        }
        var newCount = Math.addExact(count, add);
        if (newCount > maxPlaceholders) {
            throw new JdbcException("SQL placeholder count exceeds @JdbcConfig.maxTotalPlaceholders: " + newCount + " > " + maxPlaceholders);
        }
        return newCount;
    }

    // ------------------------------------------------------------------------

    /**
     * Null safe close rs and then stmt.
     */
    public static void close(@Nullable Statement stmt, @Nullable ResultSet rs) {
        Exception e = null;
        try {
            if (rs != null && !rs.isClosed()) {
                rs.close();
            }
        } catch (Exception ex) {
            e = ex;
        }
        try {
            if (stmt != null && !stmt.isClosed()) {
                stmt.close();
            }
        } catch (Exception ex) {
            if (e == null) {
                e = ex;
            } else {
                e.addSuppressed(ex);
            }
        }
        if (e != null) {
            throw e instanceof RuntimeException re ? re : new JdbcException(e.getMessage(), e);
        }
    }

    /**
     * Null safe close rs and then stmt.
     */
    public static void close(Exception e, @Nullable Statement stmt, @Nullable ResultSet rs) {
        try {
            if (rs != null && !rs.isClosed()) {
                rs.close();
            }
        } catch (Exception ex) {
            e.addSuppressed(ex);
        }
        try {
            if (stmt != null && !stmt.isClosed()) {
                stmt.close();
            }
        } catch (Exception ex) {
            e.addSuppressed(ex);
        }
    }

    public static void close(Exception e, @Nullable Statement stmt) {
        try {
            if (stmt != null && !stmt.isClosed()) {
                stmt.close();
            }
        } catch (Exception ex) {
            e.addSuppressed(ex);
        }
    }

    public static void close(Exception e, @Nullable ResultSet rs) {
        try {
            if (rs != null && !rs.isClosed()) {
                rs.close();
            }
        } catch (Exception ex) {
            e.addSuppressed(ex);
        }
    }
}