use of org.apache.druid.query.LookupDataSource in project druid by druid-io.
the class CalciteJoinQueryTest method testInnerJoinTwoLookupsToTableUsingNumericColumnInReverse.
@Test
@Parameters(source = QueryContextForJoinProvider.class)
public void testInnerJoinTwoLookupsToTableUsingNumericColumnInReverse(Map<String, Object> queryContext) throws Exception {
// Like "testInnerJoinTwoLookupsToTableUsingNumericColumn", but the tables are specified backwards.
cannotVectorize();
testQuery("SELECT COUNT(*)\n" + "FROM lookup.lookyloo l1\n" + "INNER JOIN lookup.lookyloo l2 ON l1.k = l2.k\n" + "INNER JOIN foo on l2.k = foo.m1", queryContext, ImmutableList.of(Druids.newTimeseriesQueryBuilder().dataSource(join(join(new LookupDataSource("lookyloo"), new LookupDataSource("lookyloo"), "j0.", equalsCondition(makeColumnExpression("k"), makeColumnExpression("j0.k")), JoinType.INNER), new QueryDataSource(newScanQueryBuilder().dataSource(CalciteTests.DATASOURCE1).intervals(querySegmentSpec(Filtration.eternity())).columns("m1").context(QUERY_CONTEXT_DEFAULT).build()), "_j0.", equalsCondition(makeExpression(ColumnType.DOUBLE, "CAST(\"j0.k\", 'DOUBLE')"), DruidExpression.ofColumn(ColumnType.DOUBLE, "_j0.m1")), JoinType.INNER)).intervals(querySegmentSpec(Filtration.eternity())).granularity(Granularities.ALL).aggregators(new CountAggregatorFactory("a0")).context(QUERY_CONTEXT_DEFAULT).build()), ImmutableList.of(new Object[] { 1L }));
}
use of org.apache.druid.query.LookupDataSource in project druid by druid-io.
the class CalciteJoinQueryTest method testJoinUnionTablesOnLookup.
@Test
@Parameters(source = QueryContextForJoinProvider.class)
public void testJoinUnionTablesOnLookup(Map<String, Object> queryContext) throws Exception {
// Cannot vectorize JOIN operator.
cannotVectorize();
testQuery("SELECT lookyloo.v, COUNT(*)\n" + "FROM\n" + " (SELECT dim2 FROM foo UNION ALL SELECT dim2 FROM numfoo) u\n" + " LEFT JOIN lookup.lookyloo ON u.dim2 = lookyloo.k\n" + "WHERE lookyloo.v <> 'xa'\n" + "GROUP BY lookyloo.v", queryContext, ImmutableList.of(GroupByQuery.builder().setDataSource(join(new UnionDataSource(ImmutableList.of(new TableDataSource(CalciteTests.DATASOURCE1), new TableDataSource(CalciteTests.DATASOURCE3))), new LookupDataSource("lookyloo"), "j0.", equalsCondition(makeColumnExpression("dim2"), makeColumnExpression("j0.k")), JoinType.LEFT)).setInterval(querySegmentSpec(Filtration.eternity())).setDimFilter(not(selector("j0.v", "xa", null))).setGranularity(Granularities.ALL).setDimensions(dimensions(new DefaultDimensionSpec("j0.v", "d0"))).setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))).setContext(queryContext).build()), ImmutableList.of(new Object[] { NULL_STRING, 6L }, new Object[] { "xabc", 2L }));
}
use of org.apache.druid.query.LookupDataSource in project druid by druid-io.
the class DruidJoinRule method analyzeCondition.
/**
* If this condition is an AND of some combination of (1) literals; (2) equality conditions of the form
* {@code f(LeftRel) = RightColumn}, then return a {@link ConditionAnalysis}.
*/
private Optional<ConditionAnalysis> analyzeCondition(final RexNode condition, final RelDataType leftRowType, DruidRel<?> right) {
final List<RexNode> subConditions = decomposeAnd(condition);
final List<Pair<RexNode, RexInputRef>> equalitySubConditions = new ArrayList<>();
final List<RexLiteral> literalSubConditions = new ArrayList<>();
final int numLeftFields = leftRowType.getFieldCount();
final Set<RexInputRef> rightColumns = new HashSet<>();
for (RexNode subCondition : subConditions) {
if (RexUtil.isLiteral(subCondition, true)) {
if (subCondition.isA(SqlKind.CAST)) {
// This is CAST(literal) which is always OK.
// We know that this is CAST(literal) as it passed the check from RexUtil.isLiteral
RexCall call = (RexCall) subCondition;
// are different, then skipping the cast might change the meaning of the subcondition.
if (call.getType().getSqlTypeName().equals(call.getOperands().get(0).getType().getSqlTypeName())) {
// If the types are the same, unwrap the cast and use the underlying literal.
literalSubConditions.add((RexLiteral) call.getOperands().get(0));
} else {
// If the types are not the same, return Optional.empty() indicating the condition is not supported.
return Optional.empty();
}
} else {
// Literals are always OK.
literalSubConditions.add((RexLiteral) subCondition);
}
continue;
}
if (!subCondition.isA(SqlKind.EQUALS)) {
// If it's not EQUALS, it's not supported.
plannerContext.setPlanningError("SQL requires a join with '%s' condition that is not supported.", subCondition.getKind());
return Optional.empty();
}
final List<RexNode> operands = ((RexCall) subCondition).getOperands();
Preconditions.checkState(operands.size() == 2, "Expected 2 operands, got[%,d]", operands.size());
if (isLeftExpression(operands.get(0), numLeftFields) && isRightInputRef(operands.get(1), numLeftFields)) {
equalitySubConditions.add(Pair.of(operands.get(0), (RexInputRef) operands.get(1)));
rightColumns.add((RexInputRef) operands.get(1));
} else if (isRightInputRef(operands.get(0), numLeftFields) && isLeftExpression(operands.get(1), numLeftFields)) {
equalitySubConditions.add(Pair.of(operands.get(1), (RexInputRef) operands.get(0)));
rightColumns.add((RexInputRef) operands.get(0));
} else {
// Cannot handle this condition.
plannerContext.setPlanningError("SQL is resulting in a join that has unsupported operand types.");
return Optional.empty();
}
}
// thereby allowing join conditions on both k and v columns of the lookup
if (right != null && !DruidJoinQueryRel.computeRightRequiresSubquery(DruidJoinQueryRel.getSomeDruidChild(right)) && right instanceof DruidQueryRel) {
DruidQueryRel druidQueryRel = (DruidQueryRel) right;
if (druidQueryRel.getDruidTable().getDataSource() instanceof LookupDataSource) {
long distinctRightColumns = rightColumns.stream().map(RexSlot::getIndex).distinct().count();
if (distinctRightColumns > 1) {
// it means that the join's right side is lookup and the join condition contains both key and value columns of lookup.
// currently, the lookup datasource in the native engine doesn't support using value column in the join condition.
plannerContext.setPlanningError("SQL is resulting in a join involving lookup where value column is used in the condition.");
return Optional.empty();
}
}
}
return Optional.of(new ConditionAnalysis(numLeftFields, equalitySubConditions, literalSubConditions));
}
use of org.apache.druid.query.LookupDataSource in project druid by druid-io.
the class JoinableFactoryWrapperTest method test_createSegmentMapFn_usableClause.
@Test
public void test_createSegmentMapFn_usableClause() {
final LookupDataSource lookupDataSource = new LookupDataSource("lookyloo");
final JoinConditionAnalysis conditionAnalysis = JoinConditionAnalysis.forExpression("x == \"j.x\"", "j.", ExprMacroTable.nil());
final PreJoinableClause clause = new PreJoinableClause("j.", lookupDataSource, JoinType.LEFT, conditionAnalysis);
JoinableFactoryWrapper joinableFactoryWrapper = new JoinableFactoryWrapper(new JoinableFactory() {
@Override
public boolean isDirectlyJoinable(DataSource dataSource) {
return dataSource.equals(lookupDataSource);
}
@Override
public Optional<Joinable> build(DataSource dataSource, JoinConditionAnalysis condition) {
if (dataSource.equals(lookupDataSource) && condition.equals(conditionAnalysis)) {
return Optional.of(LookupJoinable.wrap(new MapLookupExtractor(ImmutableMap.of("k", "v"), false)));
} else {
return Optional.empty();
}
}
});
final Function<SegmentReference, SegmentReference> segmentMapFn = joinableFactoryWrapper.createSegmentMapFn(null, ImmutableList.of(clause), new AtomicLong(), new TestQuery(new TableDataSource("test"), new MultipleIntervalSegmentSpec(ImmutableList.of(Intervals.of("0/100"))), false, new HashMap()));
Assert.assertNotSame(Function.identity(), segmentMapFn);
}
use of org.apache.druid.query.LookupDataSource in project druid by druid-io.
the class JoinableFactoryWrapperTest method test_computeJoinDataSourceCacheKey_cachingUnsupported.
@Test
public void test_computeJoinDataSourceCacheKey_cachingUnsupported() {
PreJoinableClause clause1 = makeGlobalPreJoinableClause("dataSource_1", "x == \"j.x\"", "j.");
DataSource dataSource = new LookupDataSource("lookup");
PreJoinableClause clause2 = makePreJoinableClause(dataSource, "x == \"h.x\"", "h.", JoinType.LEFT);
DataSourceAnalysis analysis = EasyMock.mock(DataSourceAnalysis.class);
EasyMock.expect(analysis.getPreJoinableClauses()).andReturn(Arrays.asList(clause1, clause2)).anyTimes();
EasyMock.expect(analysis.getJoinBaseTableFilter()).andReturn(Optional.of(TrueDimFilter.instance())).anyTimes();
EasyMock.replay(analysis);
JoinableFactoryWrapper joinableFactoryWrapper = new JoinableFactoryWrapper(new JoinableFactoryWithCacheKey());
Optional<byte[]> cacheKey = joinableFactoryWrapper.computeJoinDataSourceCacheKey(analysis);
Assert.assertFalse(cacheKey.isPresent());
}
Aggregations