Skip to content

Commit

Permalink
Add check for not valid constraints (mfvanek#383)
Browse files Browse the repository at this point in the history
* Add check for not valid constraints

* fix :: check style

* modify :: request

* fix :: check

* fix :: coverage

* A fix for Constraint code coverage

* Improve tests and update README.md

* ForeignKey now extends Constraint

* Add IndexWithSingleColumnExtractor

---------

Co-authored-by: BLoHny <113517233+BLoHny@users.noreply.github.com>
  • Loading branch information
mfvanek and BLoHny committed May 3, 2024
1 parent c1a7408 commit c4c3f53
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 101 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ For **Java 8** compatible version take a look at release [0.7.0](https://github.
14. Columns of [serial types](https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-SERIAL) that are not primary keys ([sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/non_primary_key_columns_with_serial_types.sql)).
15. Functions without [description](https://www.postgresql.org/docs/current/sql-comment.html) ([sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/functions_without_description.sql)).
16. Indexes [with boolean](https://habr.com/ru/companies/tensor/articles/488104/) ([sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/indexes_with_boolean.sql)).
17. B-tree indexes [on array columns](https://habr.com/ru/articles/800121/) ([sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/btree_indexes_on_array_columns.sql)).
17. Tables with [not valid constraints](https://habr.com/ru/articles/800121/) ([sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/check_not_valid_constraints.sql)).
18. B-tree indexes [on array columns](https://habr.com/ru/articles/800121/) ([sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/btree_indexes_on_array_columns.sql)).

For raw sql queries see [pg-index-health-sql](https://github.com/mfvanek/pg-index-health-sql) project.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,17 @@
*/
public enum SerialType {

/**
* Constant for smallserial.
*/
SMALL_SERIAL("smallserial"),
/**
* Constant for serial.
*/
SERIAL("serial"),
/**
* Constant for bigserial.
*/
BIG_SERIAL("bigserial");

private static final Map<String, SerialType> VALUES = new HashMap<>();
Expand Down Expand Up @@ -59,6 +68,12 @@ public String toString() {
'}';
}

/**
* Gets {@code SerialType} from PostgreSQL serial column type.
*
* @param pgColumnType PostgreSQL serial column type; should be non-null.
* @return {@code SerialType}
*/
@Nonnull
public static SerialType valueFrom(@Nonnull final String pgColumnType) {
Objects.requireNonNull(pgColumnType, "pgColumnType cannot be null");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

package io.github.mfvanek.pg.model.constraint;

import io.github.mfvanek.pg.model.DbObject;
import io.github.mfvanek.pg.model.column.Column;
import io.github.mfvanek.pg.model.table.TableNameAware;
import io.github.mfvanek.pg.model.validation.Validators;
Expand All @@ -25,53 +24,23 @@
*
* @author Ivan Vakhrushev
* @see TableNameAware
* @see Constraint
*/
@Immutable
public class ForeignKey implements DbObject, TableNameAware {
public class ForeignKey extends Constraint {

private final String tableName;
private final String constraintName;
private final List<Column> columnsInConstraint;

private ForeignKey(@Nonnull final String tableName,
@Nonnull final String constraintName,
@Nonnull final List<Column> columnsInConstraint) {
this.tableName = Validators.tableNameNotBlank(tableName);
this.constraintName = Validators.notBlank(constraintName, "constraintName");
super(tableName, constraintName, ConstraintType.FOREIGN_KEY);
final List<Column> defensiveCopy = List.copyOf(Objects.requireNonNull(columnsInConstraint, "columnsInConstraint cannot be null"));
Validators.validateThatNotEmpty(defensiveCopy);
Validators.validateThatTableIsTheSame(tableName, defensiveCopy);
this.columnsInConstraint = defensiveCopy;
}

/**
* {@inheritDoc}
*/
@Nonnull
@Override
public final String getName() {
return getConstraintName();
}

/**
* {@inheritDoc}
*/
@Override
@Nonnull
public String getTableName() {
return tableName;
}

/**
* Gets the name of foreign key constraint.
*
* @return the name of foreign key
*/
@Nonnull
public String getConstraintName() {
return constraintName;
}

/**
* Gets columns of foreign key constraint.
*
Expand All @@ -83,42 +52,14 @@ public List<Column> getColumnsInConstraint() {
return columnsInConstraint;
}

/**
* {@inheritDoc}
*/
@Override
public final boolean equals(final Object other) {
if (this == other) {
return true;
}

if (!(other instanceof ForeignKey)) {
return false;
}

final ForeignKey that = (ForeignKey) other;
return Objects.equals(tableName, that.tableName) &&
Objects.equals(constraintName, that.constraintName) &&
Objects.equals(columnsInConstraint, that.columnsInConstraint);
}

/**
* {@inheritDoc}
*/
@Override
public final int hashCode() {
return Objects.hash(tableName, constraintName, columnsInConstraint);
}

/**
* {@inheritDoc}
*/
@Nonnull
@Override
public String toString() {
return ForeignKey.class.getSimpleName() + '{' +
"tableName='" + tableName + '\'' +
", constraintName='" + constraintName + '\'' +
innerToString() +
", columnsInConstraint=" + columnsInConstraint +
'}';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ void equalsAndHashCode() {
List.of(Column.ofNotNull("t", "order_id"), Column.ofNotNull("t", "limit")));
final ForeignKey withDifferentOrderOfColumns = ForeignKey.of("t", "c_t_order_id",
List.of(Column.ofNotNull("t", "limit"), Column.ofNotNull("t", "order_id")));
final ForeignKey second = ForeignKey.ofNullableColumn("t", "c_t_order_id", "no_matter_what");
final ForeignKey withDifferentColumnName = ForeignKey.ofNullableColumn("t", "c_t_order_id", "no_matter_what");

assertThat(first.equals(null)).isFalse();
//noinspection EqualsBetweenInconvertibleTypes
Expand All @@ -149,14 +149,15 @@ void equalsAndHashCode() {
.isEqualTo(first)
.hasSameHashCodeAs(first);

// column order matters
// column order doesn't matter
assertThat(withDifferentOrderOfColumns)
.isNotEqualTo(first)
.doesNotHaveSameHashCodeAs(first);
.isEqualTo(first)
.hasSameHashCodeAs(first);

assertThat(second)
.isNotEqualTo(first)
.doesNotHaveSameHashCodeAs(first);
// column name doesn't matter
assertThat(withDifferentColumnName)
.isEqualTo(first)
.hasSameHashCodeAs(first);

final ForeignKey third = ForeignKey.of("table", "c_t_order_id",
List.of(Column.ofNotNull("table", "order_id"), Column.ofNotNull("table", "limit")));
Expand All @@ -175,6 +176,7 @@ void equalsAndHashCode() {
@SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert")
void equalsHashCodeShouldAdhereContracts() {
EqualsVerifier.forClass(ForeignKey.class)
.withIgnoredFields("constraintType", "columnsInConstraint")
.verify();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import java.sql.SQLException;
import javax.annotation.Nonnull;

import static io.github.mfvanek.pg.checks.extractors.TableExtractor.TABLE_NAME;

/**
* A mapper from raw data to {@link Column} model.
*
Expand All @@ -34,7 +36,7 @@ private ColumnExtractor() {
@Nonnull
@Override
public Column extractData(@Nonnull final ResultSet resultSet) throws SQLException {
final String tableName = resultSet.getString("table_name");
final String tableName = resultSet.getString(TABLE_NAME);
final String columnName = resultSet.getString("column_name");
final boolean columnNotNull = resultSet.getBoolean("column_not_null");
if (columnNotNull) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2019-2024. Ivan Vakhrushev and others.
* https://github.com/mfvanek/pg-index-health
*
* This file is a part of "pg-index-health" - a Java library for
* analyzing and maintaining indexes health in PostgreSQL databases.
*
* Licensed under the Apache License 2.0
*/

package io.github.mfvanek.pg.checks.extractors;

import io.github.mfvanek.pg.common.maintenance.ResultSetExtractor;
import io.github.mfvanek.pg.model.column.Column;
import io.github.mfvanek.pg.model.index.IndexWithColumns;

import java.sql.ResultSet;
import java.sql.SQLException;
import javax.annotation.Nonnull;

import static io.github.mfvanek.pg.checks.extractors.TableExtractor.TABLE_NAME;

/**
* A mapper from raw data to {@link IndexWithColumns} model.
*
* @author Ivan Vahrushev
* @since 0.10.4
*/
public class IndexWithSingleColumnExtractor implements ResultSetExtractor<IndexWithColumns> {

public static final String INDEX_NAME = "index_name";
public static final String INDEX_SIZE = "index_size";

private final ResultSetExtractor<Column> columnExtractor;

private IndexWithSingleColumnExtractor() {
this.columnExtractor = ColumnExtractor.of();
}

/**
* {@inheritDoc}
*/
@Nonnull
@Override
public IndexWithColumns extractData(@Nonnull final ResultSet resultSet) throws SQLException {
final String tableName = resultSet.getString(TABLE_NAME);
final String indexName = resultSet.getString(INDEX_NAME);
final long indexSize = resultSet.getLong(INDEX_SIZE);
final Column column = columnExtractor.extractData(resultSet);
return IndexWithColumns.ofSingle(tableName, indexName, indexSize, column);
}

@Nonnull
public static ResultSetExtractor<IndexWithColumns> of() {
return new IndexWithSingleColumnExtractor();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
*/
public class TableExtractor implements ResultSetExtractor<Table> {

public static final String TABLE_NAME = "table_name";
public static final String TABLE_SIZE = "table_size";

private TableExtractor() {
}

Expand All @@ -34,8 +37,8 @@ private TableExtractor() {
@Nonnull
@Override
public Table extractData(@Nonnull final ResultSet resultSet) throws SQLException {
final String tableName = resultSet.getString("table_name");
final long tableSize = resultSet.getLong("table_size");
final String tableName = resultSet.getString(TABLE_NAME);
final long tableSize = resultSet.getLong(TABLE_SIZE);
return Table.of(tableName, tableSize);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

package io.github.mfvanek.pg.checks.host;

import io.github.mfvanek.pg.checks.extractors.IndexWithSingleColumnExtractor;
import io.github.mfvanek.pg.checks.extractors.TableExtractor;
import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnHost;
import io.github.mfvanek.pg.common.maintenance.Diagnostic;
import io.github.mfvanek.pg.common.maintenance.ResultSetExtractor;
Expand All @@ -33,12 +35,13 @@
*/
abstract class AbstractCheckOnHost<T extends DbObject> implements DatabaseCheckOnHost<T> {

protected static final String TABLE_NAME = "table_name";
protected static final String INDEX_NAME = "index_name";
protected static final String TABLE_SIZE = "table_size";
protected static final String INDEX_SIZE = "index_size";
protected static final String TABLE_NAME = TableExtractor.TABLE_NAME;
protected static final String INDEX_NAME = IndexWithSingleColumnExtractor.INDEX_NAME;
protected static final String TABLE_SIZE = TableExtractor.TABLE_SIZE;
protected static final String INDEX_SIZE = IndexWithSingleColumnExtractor.INDEX_SIZE;
protected static final String BLOAT_SIZE = "bloat_size";
protected static final String BLOAT_PERCENTAGE = "bloat_percentage";
protected static final String CONSTRAINT_NAME = "constraint_name";

/**
* An original java type representing database object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@

package io.github.mfvanek.pg.checks.host;

import io.github.mfvanek.pg.checks.extractors.ColumnExtractor;
import io.github.mfvanek.pg.checks.extractors.IndexWithSingleColumnExtractor;
import io.github.mfvanek.pg.common.maintenance.Diagnostic;
import io.github.mfvanek.pg.common.maintenance.ResultSetExtractor;
import io.github.mfvanek.pg.connection.PgConnection;
import io.github.mfvanek.pg.model.PgContext;
import io.github.mfvanek.pg.model.column.Column;
import io.github.mfvanek.pg.model.index.IndexWithColumns;

import java.util.List;
Expand All @@ -34,16 +32,15 @@ public BtreeIndexesOnArrayColumnsCheckOnHost(@Nonnull final PgConnection pgConne
super(IndexWithColumns.class, pgConnection, Diagnostic.BTREE_INDEXES_ON_ARRAY_COLUMNS);
}

/**
* Returns B-tree indexes on array columns in the specified schema.
*
* @param pgContext check's context with the specified schema
* @return list of B-tree indexes on array columns
*/
@Nonnull
@Override
public List<IndexWithColumns> check(@Nonnull final PgContext pgContext) {
final ResultSetExtractor<Column> columnExtractor = ColumnExtractor.of();
return executeQuery(pgContext, rs -> {
final String tableName = rs.getString(TABLE_NAME);
final String indexName = rs.getString(INDEX_NAME);
final long indexSize = rs.getLong(INDEX_SIZE);
final Column column = columnExtractor.extractData(rs);
return IndexWithColumns.ofSingle(tableName, indexName, indexSize, column);
});
return executeQuery(pgContext, IndexWithSingleColumnExtractor.of());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public ForeignKeysNotCoveredWithIndexCheckOnHost(@Nonnull final PgConnection pgC
public List<ForeignKey> check(@Nonnull final PgContext pgContext) {
return executeQuery(pgContext, rs -> {
final String tableName = rs.getString(TABLE_NAME);
final String constraintName = rs.getString("constraint_name");
final String constraintName = rs.getString(CONSTRAINT_NAME);
final Array columnsArray = rs.getArray("columns");
final String[] rawColumns = (String[]) columnsArray.getArray();
final List<Column> columns = ColumnsInForeignKeyParser.parseRawColumnData(tableName, rawColumns);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@

package io.github.mfvanek.pg.checks.host;

import io.github.mfvanek.pg.checks.extractors.ColumnExtractor;
import io.github.mfvanek.pg.checks.extractors.IndexWithSingleColumnExtractor;
import io.github.mfvanek.pg.common.maintenance.Diagnostic;
import io.github.mfvanek.pg.common.maintenance.ResultSetExtractor;
import io.github.mfvanek.pg.connection.PgConnection;
import io.github.mfvanek.pg.model.PgContext;
import io.github.mfvanek.pg.model.column.Column;
import io.github.mfvanek.pg.model.index.IndexWithColumns;

import java.util.List;
Expand All @@ -42,13 +40,6 @@ public IndexesWithBooleanCheckOnHost(@Nonnull final PgConnection pgConnection) {
@Nonnull
@Override
public List<IndexWithColumns> check(@Nonnull final PgContext pgContext) {
final ResultSetExtractor<Column> columnExtractor = ColumnExtractor.of();
return executeQuery(pgContext, rs -> {
final String tableName = rs.getString(TABLE_NAME);
final String indexName = rs.getString(INDEX_NAME);
final long indexSize = rs.getLong(INDEX_SIZE);
final Column column = columnExtractor.extractData(rs);
return IndexWithColumns.ofSingle(tableName, indexName, indexSize, column);
});
return executeQuery(pgContext, IndexWithSingleColumnExtractor.of());
}
}

0 comments on commit c4c3f53

Please sign in to comment.