diff --git a/ibis/expr/tests/test_newrels.py b/ibis/expr/tests/test_newrels.py index a9ba817d6932..7fb6701c7357 100644 --- a/ibis/expr/tests/test_newrels.py +++ b/ibis/expr/tests/test_newrels.py @@ -1764,3 +1764,68 @@ def test_projections_with_similar_expressions_have_different_names(): assert b.op().name in fields assert expr.schema() == ibis.schema({a.op().name: "string", b.op().name: "string"}) + + +def test_expr_in_join_projection(): + t1 = ibis.table({"a": "int64", "b": "string"}, name="t1") + t2 = ibis.table({"c": "int64", "b": "string"}, name="t2") + t3 = ibis.table({"a": "int64", "d": "int64", "e": "string"}, name="t3") + expr = t1.inner_join(t2, "b").select( + "a", lit1=1, lit2=2 * t1.a, lit3=_.c - 5, lit4=t2.b.length() / 2.0 + ) + + op = expr.op() + assert isinstance(op, ops.JoinChain) + assert op.schema == ibis.schema( + { + "a": "int64", + "lit1": "int8", + "lit2": "int64", + "lit3": "int64", + "lit4": "float64", + } + ) + + # simple chain selection + expr2 = expr.inner_join(t3, "a").select("a", "d", "lit1", "lit2", "lit3", "lit4") + op2 = expr2.op() + assert isinstance(op2, ops.JoinChain) + assert op2.schema == ibis.schema( + { + "a": "int64", + "d": "int64", + "lit1": "int8", + "lit2": "int64", + "lit3": "int64", + "lit4": "float64", + } + ) + + # chain with expressions from all three tables in the join + expr = ( + t1.inner_join(t2, "b") + .inner_join(t3, t2.b == t3.e) + .select( + "a", + lit1=1, + lit2=2 * t1.a, + lit3=_.c - 5, + lit4=t2.b.length() / 2.0, + lit5=_.e.cast("int") * 3, + lit6=t3.d + 1, + ) + ) + + op = expr.op() + assert isinstance(op, ops.JoinChain) + assert op.schema == ibis.schema( + { + "a": "int64", + "lit1": "int8", + "lit2": "int64", + "lit3": "int64", + "lit4": "float64", + "lit5": "int64", + "lit6": "int64", + } + ) diff --git a/ibis/expr/types/joins.py b/ibis/expr/types/joins.py index 43c1a3e36a6b..c81fbc6cfe28 100644 --- a/ibis/expr/types/joins.py +++ b/ibis/expr/types/joins.py @@ -179,7 +179,11 @@ def prepare_predicates( The comparison operation to construct if the input is a pair of expression-like objects """ - reverse = {ops.Field(chain, k): v for k, v in chain.values.items()} + reverse = { + ops.Field(chain, k): v + for k, v in chain.values.items() + if isinstance(v, ops.Field) + } deref_right = DerefMap.from_targets(right) deref_left = DerefMap.from_targets(chain.tables, extra=reverse) deref_both = DerefMap.from_targets([*chain.tables, right], extra=reverse)