relational query: Limit the max level to break the infinite loop when circles appear in relation graph

This commit is contained in:
Sergey Matvienko 2021-09-03 16:05:14 +03:00
parent 99476c78c9
commit e4f8c7e878
3 changed files with 52 additions and 5 deletions

View File

@ -223,6 +223,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
private static final String SELECT_API_USAGE_STATE = "(select aus.id, aus.created_time, aus.tenant_id, aus.entity_id, " +
"coalesce((select title from tenant where id = aus.entity_id), (select title from customer where id = aus.entity_id)) as name " +
"from api_usage_state as aus)";
static final int MAX_LEVEL_DEFAULT = 10; //This value has to be reasonable small to prevent infinite recursion as early as possible
static {
entityTableMap.put(EntityType.ASSET, "asset");
@ -704,8 +705,12 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
return whereFilter.toString();
}
private String getLvlFilter(int maxLevel) {
return maxLevel > 0 ? ("and lvl <= " + (maxLevel - 1)) : "";
String getLvlFilter(int maxLevel) {
return "and re.lvl <= " + (getMaxLevel(maxLevel) - 1);
}
int getMaxLevel(int maxLevel) {
return maxLevel > 0 ? maxLevel : MAX_LEVEL_DEFAULT;
}
private String getQueryTemplate(EntitySearchDirection direction) {

View File

@ -568,9 +568,9 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
createManyCustomRelationsBetweenTwoNodes(tenantId, "UseCase", assets, devices);
createHorizontalRingRelations(tenantId, "Ring(Loop)-Ast", assets);
//createLoopRelations(tenantId, "Loop-Tnt-Ast-Dev", tenantId, assets.get(0).getId(), devices.get(0).getId());
//createLoopRelations(tenantId, "Loop-Tnt-Ast", tenantId, assets.get(1).getId());
//createLoopRelations(tenantId, "Loop-Ast-Tnt-Ast", assets.get(2).getId(), tenantId, assets.get(3).getId());
createLoopRelations(tenantId, "Loop-Tnt-Ast-Dev", tenantId, assets.get(0).getId(), devices.get(0).getId());
createLoopRelations(tenantId, "Loop-Tnt-Ast", tenantId, assets.get(1).getId());
createLoopRelations(tenantId, "Loop-Ast-Tnt-Ast", assets.get(2).getId(), tenantId, assets.get(3).getId());
}
void createLoopRelations(TenantId tenantId, String type, EntityId... ids) {

View File

@ -0,0 +1,42 @@
package org.thingsboard.server.dao.sql.query;
import org.junit.Test;
import org.thingsboard.server.common.data.id.CustomerId;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.BDDMockito.willCallRealMethod;
import static org.mockito.Mockito.mock;
public class DefaultEntityQueryRepositoryTest {
/*
* This value has to be reasonable small to prevent infinite recursion as early as possible
* */
@Test
public void givenDefaultMaxLevel_whenStaticConstant_thenEqualsTo() {
assertThat(DefaultEntityQueryRepository.MAX_LEVEL_DEFAULT, equalTo(10));
}
@Test
public void givenMaxLevelZeroOrNegative_whenGetMaxLevel_thenReturnDefaultMaxLevel() {
DefaultEntityQueryRepository repo = mock(DefaultEntityQueryRepository.class);
willCallRealMethod().given(repo).getMaxLevel(anyInt());
assertThat(repo.getMaxLevel(0), equalTo(DefaultEntityQueryRepository.MAX_LEVEL_DEFAULT));
assertThat(repo.getMaxLevel(-1), equalTo(DefaultEntityQueryRepository.MAX_LEVEL_DEFAULT));
assertThat(repo.getMaxLevel(-2), equalTo(DefaultEntityQueryRepository.MAX_LEVEL_DEFAULT));
assertThat(repo.getMaxLevel(Integer.MIN_VALUE), equalTo(DefaultEntityQueryRepository.MAX_LEVEL_DEFAULT));
}
@Test
public void givenMaxLevelPositive_whenGetMaxLevel_thenValueTheSame() {
DefaultEntityQueryRepository repo = mock(DefaultEntityQueryRepository.class);
willCallRealMethod().given(repo).getMaxLevel(anyInt());
assertThat(repo.getMaxLevel(1), equalTo(1));
assertThat(repo.getMaxLevel(2), equalTo(2));
assertThat(repo.getMaxLevel(Integer.MAX_VALUE), equalTo(Integer.MAX_VALUE));
}
}