/** * diqube: Distributed Query Base. * * Copyright (C) 2015 Bastian Gloeckle * * This file is part of diqube. * * diqube is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.diqube.plan; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.diqube.context.Profiles; import org.diqube.data.column.ColumnType; import org.diqube.data.column.StandardColumnShard; import org.diqube.data.table.TableFactory; import org.diqube.data.table.TableShard; import org.diqube.data.types.lng.dict.LongDictionary; import org.diqube.execution.consumers.AbstractThreadedColumnDictIdConsumer; import org.diqube.execution.consumers.RowIdConsumer; import org.diqube.execution.steps.AbstractThreadedExecutablePlanStep; import org.diqube.execution.steps.ResolveColumnDictIdsStep; import org.diqube.execution.steps.RowIdEqualsStep; import org.diqube.executionenv.ExecutionEnvironment; import org.diqube.executionenv.ExecutionEnvironmentFactory; import org.diqube.loader.LoaderColumnInfo; import org.diqube.loader.columnshard.ColumnShardBuilderFactory; import org.diqube.loader.columnshard.ColumnShardBuilderManager; import org.diqube.queries.QueryRegistry; import org.diqube.queries.QueryUuid; import org.mockito.Mockito; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests {@link RowIdEqualsStep}. * * @author Bastian Gloeckle */ public class RowIdEqualsStepTest { private static final String COL_A = "colA"; private static final String COL_B = "colB"; private AnnotationConfigApplicationContext dataContext; private ColumnShardBuilderManager columnShardBuilderManager; private TableFactory tableFactory; private ExecutionEnvironmentFactory executionEnvironmentFactory; private Map<String, Map<Long, Object>> testResult; @BeforeMethod public void setUp() { dataContext = new AnnotationConfigApplicationContext(); dataContext.getEnvironment().setActiveProfiles(Profiles.UNIT_TEST); dataContext.scan("org.diqube"); dataContext.refresh(); ColumnShardBuilderFactory columnBuilderFactory = dataContext.getBean(ColumnShardBuilderFactory.class); LoaderColumnInfo colInfo = new LoaderColumnInfo(ColumnType.LONG); columnShardBuilderManager = columnBuilderFactory.createColumnShardBuilderManager(colInfo, 0L); tableFactory = dataContext.getBean(TableFactory.class); executionEnvironmentFactory = dataContext.getBean(ExecutionEnvironmentFactory.class); testResult = new HashMap<>(); QueryUuid.setCurrentQueryUuidAndExecutionUuid(UUID.randomUUID(), UUID.randomUUID()); } @AfterMethod public void cleanup() { QueryUuid.clearCurrent(); } @Test public void testOneColumn() throws Exception { // GIVEN // One-col table with some values Long[] colAValues = new Long[] { 1L, 10L, Long.MIN_VALUE, -599L }; columnShardBuilderManager.addValues(COL_A, colAValues, 0L); TableShard table = buildTable(columnShardBuilderManager); ExecutionEnvironment env = executionEnvironmentFactory.createQueryRemoteExecutionEnvironment(table); // RowIdEquals and ResolveValue steps List<AbstractThreadedExecutablePlanStep> steps = createIdEqualsAndResolveSteps(env, COL_A, new Long[] { Long.MIN_VALUE }, COL_A); // WHEN // executing the steps for (AbstractThreadedExecutablePlanStep step : steps) { step.run(); } // THEN Assert.assertTrue(testResult.containsKey(COL_A), "Result for col a expected"); Map<Long, Object> expected = new HashMap<>(); expected.put(2L, Long.MIN_VALUE); Assert.assertEquals(testResult.get(COL_A), expected, "Correct result for col a expected"); } @Test public void testTwoColumns() throws Exception { // GIVEN // Two-col table with some values Long[] colAValues = new Long[] { 1L, 10L, Long.MIN_VALUE, -599L }; columnShardBuilderManager.addValues(COL_A, colAValues, 0L); Long[] colBValues = new Long[] { -100L, -200L, Long.MAX_VALUE, -300L }; columnShardBuilderManager.addValues(COL_B, colBValues, 0L); TableShard table = buildTable(columnShardBuilderManager); ExecutionEnvironment env = executionEnvironmentFactory.createQueryRemoteExecutionEnvironment(table); // RowIdEquals and ResolveValue steps, search in one column, select from the other. List<AbstractThreadedExecutablePlanStep> steps = createIdEqualsAndResolveSteps(env, COL_A, new Long[] { Long.MIN_VALUE }, COL_B); // WHEN // executing the steps for (AbstractThreadedExecutablePlanStep step : steps) { step.run(); } // THEN Assert.assertTrue(testResult.containsKey(COL_B), "Result for col b expected"); Map<Long, Object> expected = new HashMap<>(); expected.put(2L, Long.MAX_VALUE); Assert.assertEquals(testResult.get(COL_B), expected, "Correct result for col b expected"); } private TableShard buildTable(ColumnShardBuilderManager columnShardBuilderManager) { List<StandardColumnShard> columns = new ArrayList<>(); for (String colName : columnShardBuilderManager.getAllColumnsWithValues()) columns.add(columnShardBuilderManager.buildAndFree(colName)); return tableFactory.createDefaultTableShard("table", columns); } private List<AbstractThreadedExecutablePlanStep> createIdEqualsAndResolveSteps(ExecutionEnvironment env, String equalsCol, Object[] equalsValues, String resolveCol) { RowIdEqualsStep rowIdEqualsStep = new RowIdEqualsStep(0, Mockito.mock(QueryRegistry.class, Mockito.RETURNS_DEEP_STUBS), env, equalsCol, new Long[] { Long.MIN_VALUE }); ResolveColumnDictIdsStep resolveValueStep = new ResolveColumnDictIdsStep(1, Mockito.mock(QueryRegistry.class, Mockito.RETURNS_DEEP_STUBS), env, resolveCol); resolveValueStep.wireOneInputConsumerToOutputOf(RowIdConsumer.class, rowIdEqualsStep); resolveValueStep.addOutputConsumer(new AbstractThreadedColumnDictIdConsumer(null) { @Override protected void allSourcesAreDone() { } @Override protected void doConsume(ExecutionEnvironment env, String colName, Map<Long, Long> rowIdToColumnDictId) { if (!testResult.containsKey(colName)) testResult.put(colName, new HashMap<Long, Object>()); Map<Long, Long> values = new HashMap<>(); // resolve column dictionary id to actual value rowIdToColumnDictId.forEach((rowId, colDictValueId) -> { LongDictionary<?> columnValueDict = env.getLongColumnShard(colName).getColumnShardDictionary(); values.put(rowId, columnValueDict.decompressValue(colDictValueId)); }); testResult.get(colName).putAll(values); } }); return Arrays.asList(new AbstractThreadedExecutablePlanStep[] { rowIdEqualsStep, resolveValueStep }); } }