use of io.confluent.ksql.engine.rewrite.ExpressionTreeRewriter.Context in project ksql by confluentinc.
the class LogicalPlanner method verifyForeignKeyJoin.
private Optional<Expression> verifyForeignKeyJoin(final JoinInfo joinInfo, final PlanNode leftNode, final PlanNode rightNode) {
final JoinType joinType = joinInfo.getType();
final Expression leftExpression = joinInfo.getLeftJoinExpression();
final Expression rightExpression = joinInfo.getRightJoinExpression();
if (joinInfo.getType().equals(JoinType.OUTER)) {
throw new KsqlException(String.format("Invalid join type:" + " full-outer join not supported for foreign-key table-table join." + " Got %s %s %s.", joinInfo.getLeftSource().getDataSource().getName().text(), joinType, joinInfo.getRightSource().getDataSource().getName().text()));
}
// because a FK-join output table has the same PK as its left input table
if (!(leftNode instanceof DataSourceNode) || !(rightNode instanceof DataSourceNode)) {
throw new KsqlException(String.format("Invalid join condition:" + " foreign-key table-table joins are not supported as part of n-way joins." + " Got %s = %s.", joinInfo.getFlippedLeftJoinExpression(), joinInfo.getFlippedRightJoinExpression()));
}
final CodeGenRunner codeGenRunner = new CodeGenRunner(leftNode.getSchema(), ksqlConfig, metaStore);
final VisitParentExpressionVisitor<Optional<Expression>, Context<Void>> unqualifiedRewritter = new VisitParentExpressionVisitor<Optional<Expression>, Context<Void>>(Optional.empty()) {
@Override
public Optional<Expression> visitQualifiedColumnReference(final QualifiedColumnReferenceExp node, final Context<Void> ctx) {
return Optional.of(new UnqualifiedColumnReferenceExp(node.getColumnName()));
}
};
final Expression leftExpressionUnqualified = ExpressionTreeRewriter.rewriteWith(unqualifiedRewritter::process, leftExpression);
final ExpressionEvaluator expressionEvaluator = codeGenRunner.buildCodeGenFromParseTree(leftExpressionUnqualified, "Left Join Expression");
final SqlType fkType = expressionEvaluator.getExpressionType();
final SqlType rightKeyType = Iterables.getOnlyElement(rightNode.getSchema().key()).type();
verifyJoinConditionTypes(fkType, rightKeyType, leftExpression, rightExpression, joinInfo.hasFlippedJoinCondition());
if (((DataSourceNode) rightNode).isWindowed()) {
throw new KsqlException("Foreign-key table-table joins are not supported on windowed tables.");
}
return Optional.of(leftExpression);
}
use of io.confluent.ksql.engine.rewrite.ExpressionTreeRewriter.Context in project ksql by confluentinc.
the class LogicalPlanner method prepareSourceForJoin.
private PlanNode prepareSourceForJoin(final DataSourceNode sourceNode, final String side, final Expression joinExpression, final boolean isForeignKeyJoin) {
final PlanNode preProjectNode;
if (isForeignKeyJoin) {
// we do not need to repartition for foreign key joins, as FK joins do not
// have co-partitioning requirements
preProjectNode = sourceNode;
} else {
// it is always safe to build the repartition node - this operation will be
// a no-op if a repartition is not required. if the source is a table, and
// a repartition is needed, then an exception will be thrown
final VisitParentExpressionVisitor<Optional<Expression>, Context<Void>> rewriter = new VisitParentExpressionVisitor<Optional<Expression>, Context<Void>>(Optional.empty()) {
@Override
public Optional<Expression> visitQualifiedColumnReference(final QualifiedColumnReferenceExp node, final Context<Void> ctx) {
return Optional.of(new UnqualifiedColumnReferenceExp(node.getColumnName()));
}
};
preProjectNode = buildInternalRepartitionNode(sourceNode, side, joinExpression, rewriter::process);
}
return buildInternalProjectNode(preProjectNode, "PrependAlias" + side, sourceNode.getAlias());
}
use of io.confluent.ksql.engine.rewrite.ExpressionTreeRewriter.Context in project ksql by confluentinc.
the class LogicalPlanner method buildForeignJoinKey.
private JoinKey buildForeignJoinKey(final Join join, final Expression foreignKeyExpression) {
final AliasedDataSource leftSource = join.getInfo().getLeftSource();
final SourceName alias = leftSource.getAlias();
final List<QualifiedColumnReferenceExp> leftSourceKeys = leftSource.getDataSource().getSchema().key().stream().map(c -> new QualifiedColumnReferenceExp(alias, c.name())).collect(Collectors.toList());
final VisitParentExpressionVisitor<Optional<Expression>, Context<Void>> aliasRewritter = new VisitParentExpressionVisitor<Optional<Expression>, Context<Void>>(Optional.empty()) {
@Override
public Optional<Expression> visitQualifiedColumnReference(final QualifiedColumnReferenceExp node, final Context<Void> ctx) {
return Optional.of(new UnqualifiedColumnReferenceExp(ColumnNames.generatedJoinColumnAlias(node.getQualifier(), node.getColumnName())));
}
};
final Expression aliasedForeignKeyExpression = ExpressionTreeRewriter.rewriteWith(aliasRewritter::process, foreignKeyExpression);
return JoinKey.foreignKey(aliasedForeignKeyExpression, leftSourceKeys);
}
use of io.confluent.ksql.engine.rewrite.ExpressionTreeRewriter.Context in project ksql by confluentinc.
the class ExpressionTreeRewriterTest method shouldRewriteStructExpression.
@Test
public void shouldRewriteStructExpression() {
// Given:
final CreateStructExpression parsed = parseExpression("STRUCT(FOO := 'foo', BAR := col4[1])");
final Expression fooVal = parsed.getFields().stream().filter(f -> f.getName().equals("FOO")).findFirst().get().getValue();
final Expression barVal = parsed.getFields().stream().filter(f -> f.getName().equals("BAR")).findFirst().get().getValue();
when(processor.apply(fooVal, context)).thenReturn(expr1);
when(processor.apply(barVal, context)).thenReturn(expr2);
// When:
final Expression rewritten = expressionRewriter.rewrite(parsed, context);
// Then:
assertThat(rewritten, equalTo(new CreateStructExpression(ImmutableList.of(new Field("FOO", expr1), new Field("BAR", expr2)))));
}
use of io.confluent.ksql.engine.rewrite.ExpressionTreeRewriter.Context in project ksql by confluentinc.
the class ExpressionTreeRewriterTest method shouldRewriteUsingPlugin.
@SuppressWarnings("unchecked")
@SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT")
private void shouldRewriteUsingPlugin(final Expression parsed) {
// Given:
when(plugin.apply(any(), any())).thenReturn(Optional.of(expr1));
// When:
final Expression rewritten = expressionRewriterWithPlugin.rewrite(parsed, context);
// Then:
assertThat(rewritten, is(expr1));
final ArgumentCaptor<Context> captor = ArgumentCaptor.forClass(Context.class);
verify(plugin).apply(same(parsed), captor.capture());
assertThat(captor.getValue().getContext(), is(context));
}
Aggregations