From c05a1d58127a00c8634542f1c68044326a85df68 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Sun, 28 Apr 2024 20:28:09 -0500 Subject: [PATCH] CE-882 Change doesSelectClauseRequireDistinct to work with multilocks --- .../rdbms/actions/AbstractRDBMSAction.java | 60 +++++++++++++++---- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java index 64519687..6c54f5be 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java @@ -27,6 +27,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.util.ArrayList; @@ -63,6 +64,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn; import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType; import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.security.MultiRecordSecurityLock; import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock; import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLockFilters; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; @@ -70,6 +72,7 @@ import com.kingsrook.qqq.backend.core.model.querystats.QueryStat; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils; +import com.kingsrook.qqq.backend.core.utils.memoization.Memoization; import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData; @@ -89,6 +92,9 @@ public abstract class AbstractRDBMSAction protected PreparedStatement statement; protected boolean isCancelled = false; + private static Memoization doesSelectClauseRequireDistinctMemoization = new Memoization() + .withTimeout(Duration.ofDays(365)); + /******************************************************************************* @@ -852,6 +858,7 @@ public abstract class AbstractRDBMSAction } + /******************************************************************************* ** Make it easy (e.g., for tests) to turn on logging of SQL *******************************************************************************/ @@ -938,25 +945,52 @@ public abstract class AbstractRDBMSAction /******************************************************************************* ** method that looks at security lock joins, and if a one-to-many is found where ** the specified field name is on the 'right side' of the join, then a distinct - ** needs added to select clause + ** needs added to select clause. + ** + ** Memoized because it's a lot of gyrations, and it never ever changes for a + ** running server. *******************************************************************************/ protected boolean doesSelectClauseRequireDistinct(QTableMetaData table) { - if(table != null) + if(table == null) { - for(RecordSecurityLock recordSecurityLock : RecordSecurityLockFilters.filterForReadLocks(CollectionUtils.nonNullList(table.getRecordSecurityLocks()))) + return (false); + } + + return doesSelectClauseRequireDistinctMemoization.getResult(table.getName(), (name) -> + { + MultiRecordSecurityLock multiRecordSecurityLock = RecordSecurityLockFilters.filterForReadLockTree(CollectionUtils.nonNullList(table.getRecordSecurityLocks())); + return doesMultiLockRequireDistinct(multiRecordSecurityLock, table); + }).orElse(false); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private boolean doesMultiLockRequireDistinct(MultiRecordSecurityLock multiRecordSecurityLock, QTableMetaData table) + { + for(RecordSecurityLock recordSecurityLock : multiRecordSecurityLock.getLocks()) + { + if(recordSecurityLock instanceof MultiRecordSecurityLock childMultiLock) { - for(String joinName : CollectionUtils.nonNullList(recordSecurityLock.getJoinNameChain())) + if(doesMultiLockRequireDistinct(childMultiLock, table)) { - QJoinMetaData joinMetaData = QContext.getQInstance().getJoin(joinName); - if(JoinType.ONE_TO_MANY.equals(joinMetaData.getType()) && !joinMetaData.getRightTable().equals(table.getName())) - { - return (true); - } - else if(JoinType.MANY_TO_ONE.equals(joinMetaData.getType()) && !joinMetaData.getLeftTable().equals(table.getName())) - { - return (true); - } + return (true); + } + } + + for(String joinName : CollectionUtils.nonNullList(recordSecurityLock.getJoinNameChain())) + { + QJoinMetaData joinMetaData = QContext.getQInstance().getJoin(joinName); + if(JoinType.ONE_TO_MANY.equals(joinMetaData.getType()) && !joinMetaData.getRightTable().equals(table.getName())) + { + return (true); + } + else if(JoinType.MANY_TO_ONE.equals(joinMetaData.getType()) && !joinMetaData.getLeftTable().equals(table.getName())) + { + return (true); } } }