CatalogQueries.java
package io.github.databaseaudits.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.sql.DataSource;
import org.jspecify.annotations.Nullable;
import lombok.AllArgsConstructor;
/**
* Minimal plain-JDBC plumbing for the catalog-driven audits — run a
* parameterized query and read the rows as column-name→value maps. An injected
* collaborator wrapping the {@link DataSource}.
*/
@AllArgsConstructor
public class CatalogQueries {
private final DataSource dataSource;
/**
* Runs {@code sql} with the given positional arguments bound and returns
* every row as a column-label→value map. Lookups are case-insensitive,
* because drivers disagree on alias case (PostgreSQL lower-cases unquoted
* aliases, H2 upper-cases them) — the audits read labels in lower case.
*
* @param sql
* The SQL query to execute.
* @param args
* Positional bind arguments, in order.
* @return Every row as a case-insensitive column-label-to-value map.
* @throws IllegalStateException
* Wrapping the {@link SQLException} if
* the query cannot be run; for an audit
* that is a test error worth surfacing,
* never something to swallow.
*/
public List<Map<String, @Nullable Object>> queryForList(final String sql,
final Object... args) {
try (Connection connection = dataSource.getConnection();
PreparedStatement statement =
connection.prepareStatement(sql)) {
for (int i = 0; i < args.length; i++) {
statement.setObject(i + 1, args[i]);
}
try (ResultSet resultSet = statement.executeQuery()) {
final ResultSetMetaData metaData = resultSet.getMetaData();
final int columnCount = metaData.getColumnCount();
final List<Map<String, @Nullable Object>> rows =
new ArrayList<>();
while (resultSet.next()) {
final Map<String, @Nullable Object> row =
new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
for (int column = 1; column <= columnCount; column++) {
row.put(metaData.getColumnLabel(column),
resultSet.getObject(column));
}
rows.add(row);
}
return rows;
}
} catch (final SQLException e) {
throw new IllegalStateException(
"Catalog query failed:%n%s".formatted(sql), e);
}
}
}