Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [PR #45](https://github.com/itsallcode/simple-jdbc/pull/45): Rename `executeStatement()` to `executeUpdate()` and return row count (**Breaking change**)
- [PR #46](https://github.com/itsallcode/simple-jdbc/pull/46): Close `Statement` / `PreparedStatement` when closing the result set.
- [PR #47](https://github.com/itsallcode/simple-jdbc/pull/47): Rename `BatchInsert` to `PreparedStatementBatch`, allow specifying a custom SQL statement (**Breaking change**)
- [PR #48](https://github.com/itsallcode/simple-jdbc/pull/48): Add support for database metadata

## [0.9.0] - 2024-12-23

Expand Down
6 changes: 3 additions & 3 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ dependencyResolutionManagement {
}
versionCatalogs {
libs {
version('mockito', '5.14.2')
version('mockito', '5.15.2')
version('junitJupiter', '5.11.2')

library('assertj', 'org.assertj:assertj-core:3.27.0')
library('assertj', 'org.assertj:assertj-core:3.27.2')
library('h2', 'com.h2database:h2:2.3.232')
library('junitPioneer', 'org.junit-pioneer:junit-pioneer:2.2.0')
library('equalsverifier', 'nl.jqno.equalsverifier:equalsverifier:3.18')
library('equalsverifier', 'nl.jqno.equalsverifier:equalsverifier:3.18.1')
library('tostringverifier', 'com.jparams:to-string-verifier:1.4.8')
library('hamcrest', 'org.hamcrest:hamcrest:3.0')
library('hamcrestResultSetMatcher', 'com.exasol:hamcrest-resultset-matcher:1.6.3')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
import java.util.stream.Stream;

import org.itsallcode.jdbc.resultset.SimpleResultSet;
import org.itsallcode.jdbc.resultset.generic.*;
import org.itsallcode.jdbc.resultset.generic.ColumnValue;
import org.itsallcode.jdbc.resultset.generic.Row;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
Expand Down Expand Up @@ -155,8 +156,7 @@ void countStarResultType() {
final SimpleResultSet<Row> result = connection
.query("select count(*) from (select 1 from dual)")) {
final Row row = result.toList().get(0);
final ColumnMetaData columnMetaData = row.columns().get(0);
assertThat(columnMetaData.type().jdbcType()).isEqualTo(JDBCType.BIGINT);
assertThat(row.columns().get(0).type().jdbcType()).isEqualTo(JDBCType.BIGINT);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
exports org.itsallcode.jdbc.resultset;
exports org.itsallcode.jdbc.resultset.generic;
exports org.itsallcode.jdbc.dialect;
exports org.itsallcode.jdbc.metadata;

requires java.logging;
requires transitive java.sql;
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/org/itsallcode/jdbc/ConnectionWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import org.itsallcode.jdbc.batch.*;
import org.itsallcode.jdbc.dialect.DbDialect;
import org.itsallcode.jdbc.metadata.DbMetaData;
import org.itsallcode.jdbc.resultset.*;
import org.itsallcode.jdbc.resultset.generic.Row;
import org.itsallcode.jdbc.statement.ConvertingPreparedStatement;
Expand Down Expand Up @@ -109,6 +110,18 @@ private Statement createStatement() {
}
}

DbMetaData getMetaData() {
return new DbMetaData(this.context, getMetaDataInternal());
}

private DatabaseMetaData getMetaDataInternal() {
try {
return connection.getMetaData();
} catch (final SQLException e) {
throw new UncheckedSQLException("Failed to get metadata", e);
}
}

void setAutoCommit(final boolean autoCommit) {
try {
connection.setAutoCommit(autoCommit);
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/itsallcode/jdbc/SimpleConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import org.itsallcode.jdbc.batch.*;
import org.itsallcode.jdbc.dialect.DbDialect;
import org.itsallcode.jdbc.metadata.DbMetaData;
import org.itsallcode.jdbc.resultset.RowMapper;
import org.itsallcode.jdbc.resultset.SimpleResultSet;
import org.itsallcode.jdbc.resultset.generic.Row;
Expand Down Expand Up @@ -117,6 +118,16 @@ public <T> RowPreparedStatementBatchBuilder<T> preparedStatementBatch(final Clas
return connection.rowPreparedStatementBatch();
}

/**
* Get database metadata.
*
* @return metadata
*/
public DbMetaData getMetaData() {
checkOperationAllowed();
return connection.getMetaData();
}

public Connection getOriginalConnection() {
checkOperationAllowed();
return connection.getOriginalConnection();
Expand Down
195 changes: 195 additions & 0 deletions src/main/java/org/itsallcode/jdbc/metadata/ColumnMetaData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package org.itsallcode.jdbc.metadata;

import java.sql.*;
import java.util.Arrays;

/**
* Description of a column.
*
* @param tableCatalog table catalog (may be {@code null})
* @param tableSchema table schema (may be {@code null})
* @param tableName table name
* @param columnName column name
* @param dataType SQL type
* @param typeName Data source dependent type name, for a UDT the type
* name is fully qualified
* @param columnSize column size.
* @param decimalDigits the number of fractional digits. {@code null} is
* returned for data types where DECIMAL_DIGITS is not
* applicable.
* @param numPrecisionRadix Radix (typically either 10 or 2)
* @param nullable is {@code NULL} allowed.
* @param remarks comment describing column (may be {@code null})
* @param columnDef default value for the column, which should be
* interpreted as a string when the value is enclosed
* in single quotes (may be {@code null})
* @param charOctetLength for char types the maximum number of bytes in the
* column
* @param ordinalPosition index of column in table (starting at 1)
* @param isNullable ISO rules are used to determine the nullability for
* a column.
* @param scopeCatalog catalog of table that is the scope of a reference
* attribute ({@code null} if DATA_TYPE isn't REF)
* @param scopeSchema schema of table that is the scope of a reference
* attribute ({@code null} if the DATA_TYPE isn't REF)
* @param scopeTable table name that this the scope of a reference
* attribute ({@code null} if the DATA_TYPE isn't REF)
* @param sourceDataType source type of a distinct type or user-generated Ref
* type, SQL type from java.sql.Types ({@code null} if
* DATA_TYPE isn't DISTINCT or user-generated REF)
* @param isAutoIncrement Indicates whether this column is auto incremented
* @param isGeneratedColumn Indicates whether this is a generated column
* @see DatabaseMetaData#getColumns(String, String, String, String)
*/
public record ColumnMetaData(
String tableCatalog,
String tableSchema,
String tableName,
String columnName,
JDBCType dataType,
String typeName,
int columnSize,
int decimalDigits,
int numPrecisionRadix,
Nullability nullable,
String remarks,
String columnDef,
int charOctetLength,
int ordinalPosition,
ISONullability isNullable,
String scopeCatalog,
String scopeSchema,
String scopeTable,
short sourceDataType,
AutoIncrement isAutoIncrement,
Generated isGeneratedColumn) {

static ColumnMetaData create(final ResultSet rs) throws SQLException {
return new ColumnMetaData(
rs.getString("TABLE_CAT"),
rs.getString("TABLE_SCHEM"),
rs.getString("TABLE_NAME"),
rs.getString("COLUMN_NAME"),
JDBCType.valueOf(rs.getInt("DATA_TYPE")),
rs.getString("TYPE_NAME"),
rs.getInt("COLUMN_SIZE"),
rs.getInt("DECIMAL_DIGITS"),
rs.getInt("NUM_PREC_RADIX"),
Nullability.valueOf(rs.getInt("NULLABLE")),
rs.getString("REMARKS"),
rs.getString("COLUMN_DEF"),
rs.getInt("CHAR_OCTET_LENGTH"),
rs.getInt("ORDINAL_POSITION"),
ISONullability.valueOfNullability(rs.getString("IS_NULLABLE")),
rs.getString("SCOPE_CATALOG"),
rs.getString("SCOPE_SCHEMA"),
rs.getString("SCOPE_TABLE"),
rs.getShort("SOURCE_DATA_TYPE"),
AutoIncrement.valueOfAutoIncrement(rs.getString("IS_AUTOINCREMENT")),
Generated.valueOfGenerated(rs.getString("IS_GENERATEDCOLUMN")));
}

/**
* Column nullability.
*/
public enum Nullability {
/** Column might not allow {@code NULL} values. */
NO_NULLS(DatabaseMetaData.columnNoNulls),
/** Column definitely allows {@code NULL} values. */
NULLABLE(DatabaseMetaData.columnNullable),
/** nullability unknown */
NULLABLE_UNKNOWN(DatabaseMetaData.columnNullableUnknown);

private final int value;

Nullability(final int value) {
this.value = value;
}

static Nullability valueOf(final int value) {
return Arrays.stream(Nullability.values())
.filter(n -> n.value == value)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
"Unknown value %d for nullability".formatted(value)));
}
}

/**
* Column ISO nullability.
*/
public enum ISONullability {
/** Column can include NULLs. */
NO_NULLS("YES"),
/** Column cannot include NULLs. */
NULLABLE("NO"),
/** Nullability for the column is unknown */
NULLABLE_UNKNOWN("");

private final String value;

ISONullability(final String value) {
this.value = value;
}

static ISONullability valueOfNullability(final String value) {
return Arrays.stream(ISONullability.values())
.filter(n -> n.value.equals(value))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
"Unknown value '%s' for ISO nullability".formatted(value)));
}
}

/**
* Indicates whether a column is auto incremented.
*/
public enum AutoIncrement {
/** Column is auto incremented. */
AUTO_INCREMENT("YES"),
/** Column is not auto incremented. */
NO_AUTO_INCREMENT("NO"),
/** It cannot be determined whether the column is auto incremented. */
UNKNOWN("");

private final String value;

AutoIncrement(final String value) {
this.value = value;
}

static AutoIncrement valueOfAutoIncrement(final String value) {
return Arrays.stream(AutoIncrement.values())
.filter(n -> n.value.equals(value))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
"Unknown value '%s' for auto increment".formatted(value)));
}
}

/**
* Indicates whether this is a generated column.
*/
public enum Generated {
/** This a generated column. */
GENERATED("YES"),
/** This not a generated column. */
NOT_GENERATED("NO"),
/** It cannot be determined whether this is a generated column. */
UNKNOWN("");

private final String value;

Generated(final String value) {
this.value = value;
}

static Generated valueOfGenerated(final String value) {
return Arrays.stream(Generated.values())
.filter(n -> n.value.equals(value))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
"Unknown value '%s' for column generated".formatted(value)));
}
}
}
Loading
Loading