All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.databene.dbsanity.model.nk.IdentityCheck Maven / Gradle / Ivy

Go to download

DB Sanity is a tool for verifying a database's sanity and data integrity.

There is a newer version: 0.9.4
Show newest version
/*
 * (c) Copyright 2010 by Volker Bergmann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, is permitted under the terms of the
 * GNU General Public License (GPL).
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
 * REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
 * HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package org.databene.dbsanity.model.nk;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.util.HashSet;

import org.databene.commons.HeavyweightIterator;
import org.databene.commons.iterator.TableRowIterator;
import org.databene.commons.iterator.TableRowIteratorProxy;
import org.databene.commons.math.Intervals;
import org.databene.commons.version.VersionNumber;
import org.databene.dbsanity.model.CheckContext;
import org.databene.dbsanity.model.SanityCheck;
import org.databene.dbsanity.model.SanityCheckVerdict;
import org.databene.jdbacl.DBUtil;
import org.databene.jdbacl.identity.IdentityModel;
import org.databene.jdbacl.identity.InvalidIdentityDefinitionError;
import org.databene.jdbacl.identity.KeyMapper;
import org.databene.jdbacl.identity.mem.GlobalRowId;
import org.databene.jdbacl.identity.mem.TargetTableMapper;
import org.databene.jdbacl.model.Database;

/**
 * {@link SanityCheck} implementation that verifies an {@link IdentityModel}
 * to be complete, unique and invertible.

* Created: 02.12.2010 16:46:27 * @since 0.4 * @author Volker Bergmann */ public class IdentityCheck extends SanityCheck { protected IdentityModel identity; protected String dbId; protected int tupleCount; protected long defectCount; public IdentityCheck(String name, IdentityModel identity, Intervals affectedVersions, String dbId, File sourceFile, File tempFolder, File reportFolder, File docPage) { super(name, identity.getTableName(), affectedVersions, "identity", sourceFile, tempFolder, reportFolder, docPage); this.identity = identity; this.dbId = dbId; this.tupleCount = 0; } @Override public String getDetails() { return "Identity Check: " + identity.getDescription(); } @Override public TableRowIterator perform(CheckContext checkContext) { setVerdict(SanityCheckVerdict.EXECUTING); long start = System.nanoTime(); try { TableRowIterator iterator = new DefectIterator(checkContext); setVerdict(iterator.hasNext() ? SanityCheckVerdict.FAILED : SanityCheckVerdict.PASSED); return new Proxy(iterator); } catch (RuntimeException e) { setVerdict(SanityCheckVerdict.ERROR); throw e; } finally { int elapsed = (int) ((System.nanoTime() - start) / 1000000); this.executionTime = elapsed; fireStatusChanged(); } } @Override public long getDefectCount() { return defectCount; } @Override public void reset() { defectCount = 0; fireStatusChanged(); } class DefectIterator implements TableRowIterator { CheckContext checkContext; HeavyweightIterator nkPkIterator; TargetTableMapper tMapper; HashSet pks; HashSet reportedPksWithDuplicateNk; Object[] next; Object[] next2; public DefectIterator(CheckContext checkContext) { this.checkContext = checkContext; this.pks = new HashSet(); this.reportedPksWithDuplicateNk = new HashSet(); Connection connection = checkContext.getConnection(); KeyMapper keyMapper = checkContext.getKeyMapper(); Database database = checkContext.getDatabase(); this.tMapper = new TargetTableMapper(keyMapper, connection , "__targetDB", identity, database); this.nkPkIterator = identity.createNkPkIterator(connection, dbId, keyMapper, database); } public boolean hasNext() { if (next == null) fetchNext(); return (next != null); } public Object[] next() { Object[] result = next; next = next2; next2 = null; return result; } void fetchNext() { while (nkPkIterator.hasNext()) { Object[] candidate = nkPkIterator.next(); if (candidate.length != checkContext.getDatabase().getTable(identity.getTableName()).getPKColumnNames().length + 1) throw new InvalidIdentityDefinitionError("Invalid column count in NK-PK-Query"); tupleCount++; String nk = identity.extractNK(candidate); Object pk = identity.extractPK(candidate); GlobalRowId globalRowId = new GlobalRowId(dbId, identity.getTableName(), pk); Object previousPK = tMapper.store(pk, nk); if (previousPK != null) { // check if the NK has already been used next = new Object[] { nk, pk, "Duplicate natural key" }; reportedPksWithDuplicateNk.add(globalRowId); GlobalRowId prevGlobalRowId = new GlobalRowId(dbId, identity.getTableName(), previousPK); if (!reportedPksWithDuplicateNk.contains(prevGlobalRowId)) { next2 = new Object[] { nk, previousPK, "Duplicate natural key" }; reportedPksWithDuplicateNk.add(prevGlobalRowId); } return; } else if (!pks.add(globalRowId)) { next = new Object[] { "", pk, "Duplicate primary key" }; // TODO v0.x report PKs too return; } checkContext.getKeyMapper().store(dbId, identity, nk, pk, null); } next = null; checkTupleCount(); } private void checkTupleCount() { long rowCount = DBUtil.countRows(identity.getTableName(), checkContext.getConnection()); if (tupleCount != rowCount) throw new InvalidIdentityDefinitionError("Result set incomplete: Expected " + rowCount + " tuples but found " + tupleCount); } public String[] getColumnLabels() { return new String[] { "NK", "PK", "Error Message" }; // TODO v0.x support composite keys } public void remove() { throw new UnsupportedOperationException(getClass().getSimpleName() + " does not support remove()"); } public void close() throws IOException { nkPkIterator.close(); tMapper = null; pks = null; } } protected class Proxy extends TableRowIteratorProxy { public Proxy(TableRowIterator source) { super(source); } @Override public Object[] next() { // first query the source, then update the counter Object[] result = super.next(); // thus, if there is an exception in the source's next() method, the counter is not increased defectCount++; return result; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy