Skip to content

Commit

Permalink
dbobject validation updates 4
Browse files Browse the repository at this point in the history
  • Loading branch information
seed-master committed Sep 2, 2023
1 parent ded04ae commit 4903561
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 51 deletions.
74 changes: 65 additions & 9 deletions src/main/java/org/seed/core/config/SchemaManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,22 @@

import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;

import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.persistence.Table;
import javax.sql.DataSource;

import org.hibernate.Session;
import org.hibernate.jdbc.ReturningWork;

import org.seed.C;
import org.seed.core.config.changelog.ChangeLog;
import org.seed.core.util.Assert;
import org.seed.core.util.MiscUtils;
Expand Down Expand Up @@ -97,7 +101,7 @@ private void init() {
public synchronized DatabaseInfo getDatabaseInfo() {
if (databaseInfo == null) {
try (Connection connection = dataSource.getConnection()) {
final DatabaseMetaData dbMeta = connection.getMetaData();
final var dbMeta = connection.getMetaData();
databaseInfo = new DatabaseInfo(dbMeta.getDatabaseProductName(),
dbMeta.getDatabaseProductVersion());
}
Expand All @@ -108,6 +112,32 @@ public synchronized DatabaseInfo getDatabaseInfo() {
return databaseInfo;
}

public List<String> findDependencies(Session session, String objectName, @Nullable String fieldName) {
Assert.notNull(session, C.SESSION);
Assert.notNull(objectName, "object name");

return session.doReturningWork(new ReturningWork<List<String>>() {

@Override
public List<String> execute(Connection connection) throws SQLException {
final var result = new ArrayList<String>();
final String query = buildDependencyQuery(fieldName != null);
try (var statement = connection.prepareStatement(query)) {
statement.setString(1, objectName);
if (fieldName != null) {
statement.setString(2, fieldName);
}
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
result.add(resultSet.getString(1));
}
}
}
return result;
}
});
}

synchronized SchemaConfiguration loadSchemaConfiguration(Session session) {
return session.createQuery("from SchemaConfiguration", SchemaConfiguration.class)
.uniqueResult();
Expand All @@ -127,7 +157,8 @@ synchronized boolean updateSchema() {
final long startTime = System.currentTimeMillis();
try (Connection connection = dataSource.getConnection()) {
final String customChangeSets = loadCustomChangeSets(connection);
final String changeLog = replaceLimits(systemChangeLog.replace("<#CHANGE_SETS#>", customChangeSets));
final String changeLog = replaceLimits(
systemChangeLog.replace("<#CHANGE_SETS#>", customChangeSets));
if (log.isDebugEnabled()) {
log.debug("Changelog content:\r\n{}", changeLog);
}
Expand Down Expand Up @@ -185,7 +216,8 @@ private String loadCustomChangeSets(Connection connection) throws SQLException {
final StringBuilder buf = new StringBuilder();
if (existTable(connection, CHANGELOG_TABLE)) {
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select changeset from " + CHANGELOG_TABLE + " order by id")) {
ResultSet resultSet = statement.executeQuery("select changeset from " + CHANGELOG_TABLE +
" order by id")) {
while (resultSet.next()) {
if (buf.length() > 0) {
buf.append(',');
Expand All @@ -209,7 +241,8 @@ private String loadSchemaUpdateChangeSet(SchemaVersion version) {

private static boolean existLock(Connection connection) throws SQLException {
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select count(*) from " + CHANGELOG_LOCKTABLE + " where locked = true")) {
ResultSet resultSet = statement.executeQuery("select count(*) from " + CHANGELOG_LOCKTABLE +
" where locked = true")) {
return resultSet.next() && resultSet.getInt(1) == 1;
}
}
Expand All @@ -221,12 +254,33 @@ private static void removeLock(Connection connection) throws SQLException {

private static void removeLastChangeLog(Connection connection) throws SQLException {
try (Statement statement = connection.createStatement()) {
statement.executeUpdate("delete from " + CHANGELOG_TABLE + " where id = (select max(id) from " + CHANGELOG_TABLE + ')');
statement.executeUpdate("delete from " + CHANGELOG_TABLE +
" where id = (select max(id) from " + CHANGELOG_TABLE + ')');
}
}

private static String buildDependencyQuery(boolean withAttribute) {
final var buf = new StringBuffer()
.append("select distinct dep_obj.relname from pg_depend")
.append(" join pg_rewrite on pg_depend.objid = pg_rewrite.oid")
.append(" join pg_class as dep_obj on pg_rewrite.ev_class = dep_obj.oid")
.append(" join pg_class as source_obj on pg_depend.refobjid = source_obj.oid")
.append(" join pg_attribute on pg_depend.refobjid = pg_attribute.attrelid")
.append(" and pg_depend.refobjsubid = pg_attribute.attnum")
.append(" join pg_namespace dep_ns on dep_ns.oid = dep_obj.relnamespace")
.append(" join pg_namespace source_ns on source_ns.oid = source_obj.relnamespace")
.append(" where source_ns.oid = dep_ns.oid")
.append(" and source_ns.nspname = 'public'")
.append(" and source_obj.relname = ?");
if (withAttribute) {
buf.append(" and pg_attribute.attname = ?");
}
return buf.toString();
}

private static boolean existTable(Connection connection, String tableName) throws SQLException {
try (ResultSet resultSet = connection.getMetaData().getTables(null, null, tableName, new String[] { "TABLE" })) {
try (ResultSet resultSet = connection.getMetaData().getTables(null, null, tableName,
new String[] { "TABLE" })) {
while (resultSet.next()) {
if (tableName.equalsIgnoreCase(resultSet.getString("TABLE_NAME"))) {
return true;
Expand Down Expand Up @@ -254,15 +308,17 @@ private StringResourceAccessor(String text) {
}

@Override
public InputStreamList openStreams(String relativeTo, String streamPath) throws IOException {
public InputStreamList openStreams(String relativeTo, String streamPath)
throws IOException {
Assert.state(CHANGELOG_FILENAME.equals(streamPath), "unknown path: " + streamPath);

return new InputStreamList(null, StreamUtils.getStringAsStream(text));
}

@Override
public SortedSet<String> list(String relativeTo, String path, boolean recursive,
boolean includeFiles, boolean includeDirectories) throws IOException {
boolean includeFiles, boolean includeDirectories)
throws IOException {
throw new UnsupportedOperationException();
}

Expand Down
3 changes: 1 addition & 2 deletions src/main/java/org/seed/core/data/dbobject/DBObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package org.seed.core.data.dbobject;

import org.seed.core.application.ApplicationEntity;
import org.seed.core.data.SystemEntity;

public interface DBObject extends ApplicationEntity {

Expand All @@ -34,7 +33,7 @@ public interface DBObject extends ApplicationEntity {

boolean contains(String text);

boolean contains(SystemEntity other);
boolean contains(DBObject other);

boolean isOrderHigherThan(DBObject dbObject);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import org.seed.C;
import org.seed.core.application.AbstractApplicationEntity;
import org.seed.core.application.ContentObject;
import org.seed.core.data.SystemEntity;
import org.seed.core.util.Assert;
import org.seed.core.util.CDATAXmlAdapter;

Expand Down Expand Up @@ -94,10 +93,10 @@ public String getObjectName() {
}

@Override
public boolean contains(SystemEntity entity) {
Assert.notNull(entity, C.ENTITY);
public boolean contains(DBObject dbObject) {
Assert.notNull(dbObject, C.DBOBJECT);

return DBObjectUtils.containsName(content, entity.getInternalName());
return DBObjectUtils.containsName(content, dbObject.getInternalName());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,9 @@
import java.util.List;

import org.seed.core.application.ApplicationEntityService;
import org.seed.core.data.SystemEntity;

public interface DBObjectService extends ApplicationEntityService<DBObject> {

List<DBObject> findUsage(SystemEntity entity);

List<DBObject> findViewsContains(SystemEntity entity);
List<DBObject> findUsage(DBObject object);

}
89 changes: 66 additions & 23 deletions src/main/java/org/seed/core/data/dbobject/DBObjectServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static org.seed.core.util.CollectionUtils.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.hibernate.Session;
Expand All @@ -32,11 +33,19 @@
import org.seed.core.application.module.ImportAnalysis;
import org.seed.core.application.module.Module;
import org.seed.core.application.module.TransferContext;
import org.seed.core.config.SchemaManager;
import org.seed.core.config.changelog.ChangeLog;
import org.seed.core.data.AbstractSystemObject;
import org.seed.core.data.SystemEntity;
import org.seed.core.data.ValidationException;
import org.seed.core.entity.Entity;
import org.seed.core.entity.EntityDependent;
import org.seed.core.entity.EntityField;
import org.seed.core.entity.EntityFieldGroup;
import org.seed.core.entity.EntityFunction;
import org.seed.core.entity.EntityRelation;
import org.seed.core.entity.EntityService;
import org.seed.core.entity.EntityStatus;
import org.seed.core.entity.NestedEntity;
import org.seed.core.util.Assert;

import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -45,14 +54,17 @@

@Service
public class DBObjectServiceImpl extends AbstractApplicationEntityService<DBObject>
implements DBObjectService {
implements DBObjectService, EntityDependent<DBObject> {

@Autowired
private DBObjectRepository repository;

@Autowired
private DBObjectValidator validator;

@Autowired
private SchemaManager schemaManager;

@Override
protected DBObjectRepository getRepository() {
return repository;
Expand All @@ -63,20 +75,6 @@ protected DBObjectValidator getValidator() {
return validator;
}

@Override
public List<DBObject> findUsage(SystemEntity entity) {
Assert.notNull(entity, C.ENTITY);

return findUsage(null, entity);
}

@Override
public List<DBObject> findViewsContains(SystemEntity entity) {
Assert.notNull(entity, C.ENTITY);

return findUsage(DBObjectType.VIEW, entity);
}

@Override
public void initObject(DBObject dbObject) throws ValidationException {
Assert.notNull(dbObject, C.DBOBJECT);
Expand Down Expand Up @@ -110,6 +108,52 @@ public void initObject(DBObject dbObject) throws ValidationException {
((DBObjectMetadata) dbObject).setContent(content);
}

@Override
public List<DBObject> findUsage(DBObject dbObject) {
Assert.notNull(dbObject, C.DBOBJECT);

return subList(repository.find(), object -> object.isEnabled() && object.contains(dbObject));
}

@Override
public List<DBObject> findUsage(Entity entity, Session session) {
return convertedList(schemaManager.findDependencies(session, entity.getEffectiveTableName(), null),
DBObjectServiceImpl::createDummyObject);
}

@Override
public List<DBObject> findUsage(EntityField entityField, Session session) {
return convertedList(schemaManager.findDependencies(session,
entityField.getEntity().getEffectiveTableName(),
entityField.getEffectiveColumnName()),
DBObjectServiceImpl::createDummyObject);
}

@Override
public List<DBObject> findUsage(EntityFieldGroup fieldGroup) {
return Collections.emptyList();
}

@Override
public List<DBObject> findUsage(EntityStatus entityStatus, Session session) {
return Collections.emptyList();
}

@Override
public List<DBObject> findUsage(EntityFunction entityFunction, Session session) {
return Collections.emptyList();
}

@Override
public List<DBObject> findUsage(NestedEntity nestedEntity, Session session) {
return Collections.emptyList();
}

@Override
public List<DBObject> findUsage(EntityRelation entityRelation, Session session) {
return Collections.emptyList();
}

@Override
protected void analyzeNextVersionObjects(ImportAnalysis analysis, Module currentVersionModule) {
if (analysis.getModule().getDBObjects() != null) {
Expand Down Expand Up @@ -264,18 +308,17 @@ public void saveObject(DBObject dbObject) throws ValidationException {
updateConfiguration();
}

private List<DBObject> findUsage(DBObjectType type, SystemEntity entity) {
final var objects = type != null
? repository.find(queryParam(C.TYPE, type))
: repository.find();
return subList(objects, object -> object.isEnabled() && object.contains(entity));
}

private static ChangeLog createChangeLog(DBObject currentVersionObject, DBObject nextVersionObject) {
return new DBObjectChangeLogBuilder()
.setCurrentVersionObject(currentVersionObject)
.setNextVersionObject(nextVersionObject)
.build();
}

private static DBObject createDummyObject(String name) {
final var object = new DBObjectMetadata();
object.setName(name);
return object;
}

}
20 changes: 15 additions & 5 deletions src/main/java/org/seed/core/data/dbobject/DBObjectValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.hibernate.Transaction;

import org.seed.C;
import org.seed.core.config.SchemaManager;
import org.seed.core.config.SessionProvider;
import org.seed.core.data.AbstractSystemEntityValidator;
import org.seed.core.data.DataException;
Expand All @@ -45,6 +46,9 @@ public class DBObjectValidator extends AbstractSystemEntityValidator<DBObject> {
@Autowired
private DBObjectRepository repository;

@Autowired
private SchemaManager schemaManager;

@Override
public void validateCreate(DBObject dbObject) throws ValidationException {
Assert.notNull(dbObject, C.DBOBJECT);
Expand All @@ -60,11 +64,17 @@ public void validateDelete(DBObject dbObject) throws ValidationException {
Assert.notNull(dbObject, C.DBOBJECT);
final var errors = createValidationErrors(dbObject);
final var service = getBean(DBObjectService.class);

filterAndForEach(service.findUsage(dbObject),
not(dbObject::equals),
object -> errors.addError("val.inuse.dbobjectdelete", object.getName(),
getEnumLabel(object.getType())));
try (Session session = sessionProvider.getSession()) {
schemaManager.findDependencies(session, dbObject.getInternalName(), null)
.forEach(view -> errors.addError("val.inuse.dbobjectdelete", view,
getEnumLabel(DBObjectType.VIEW)));
}
if (errors.isEmpty()) {
filterAndForEach(service.findUsage(dbObject),
not(dbObject::equals),
object -> errors.addError("val.inuse.dbobjectdelete", object.getName(),
getEnumLabel(object.getType())));
}
validate(errors);
}

Expand Down
Loading

0 comments on commit 4903561

Please sign in to comment.