From 2f3f8b8f07a249628c03f54120a71ada8925d08a Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 18 Dec 2025 16:24:59 -0600 Subject: [PATCH 1/7] Preparatory refactoring of `add_filter()` --- R/verb-filter.R | 101 ++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 59 deletions(-) diff --git a/R/verb-filter.R b/R/verb-filter.R index b873ca3d7..cf94dc986 100644 --- a/R/verb-filter.R +++ b/R/verb-filter.R @@ -44,59 +44,42 @@ filter.tbl_lazy <- function(.data, ..., .by = NULL, .preserve = FALSE) { add_filter <- function(.data, dots) { con <- remote_con(.data) - lazy_query <- .data$lazy_query dots <- unname(dots) - dots_use_window_fun <- uses_window_fun(dots, con) - - if (filter_can_use_having(lazy_query, dots_use_window_fun)) { - return(filter_via_having(lazy_query, dots)) - } - - if (!dots_use_window_fun) { - if (filter_needs_new_query(dots, lazy_query, con)) { - lazy_select_query( - x = lazy_query, - where = dots - ) - } else { - exprs <- lazy_query$select$expr - nms <- lazy_query$select$name - projection <- purrr::map2_lgl( - exprs, - nms, - \(expr, name) is_symbol(expr) && !identical(expr, sym(name)) - ) - - if (any(projection)) { - dots <- purrr::map( - dots, - replace_sym, - nms[projection], - exprs[projection] - ) - } - - lazy_query$where <- c(lazy_query$where, dots) - lazy_query - } - } else { - # Do partial evaluation, then extract out window functions - where <- translate_window_where_all( - dots, - env_names(dbplyr_sql_translation(con)$window) - ) + # Handle window functions by adding an intermediate mutate + # by definition this has to create a subquery + if (uses_window_fun(dots, con)) { + window_funs <- env_names(dbplyr_sql_translation(con)$window) + where <- translate_window_where_all(dots, window_funs) # Add extracted window expressions as columns mutated <- mutate(.data, !!!where$comp) # And filter with the modified `where` using the new columns original_vars <- op_vars(.data) - lazy_select_query( + return(lazy_select_query( x = mutated$lazy_query, select = syms(set_names(original_vars)), where = where$expr - ) + )) + } + + lazy_query <- .data$lazy_query + if (filter_can_use_having(lazy_query)) { + names <- lazy_query$select$name + exprs <- purrr::map_if(lazy_query$select$expr, is_quosure, quo_get_expr) + dots <- purrr::map(dots, replace_sym, names, exprs) + + lazy_query$having <- c(lazy_query$having, dots) + lazy_query + } else if (filter_needs_new_query(dots, lazy_query, con)) { + lazy_select_query(x = lazy_query, where = dots) + } else { + # WHERE happens before SELECT so can't refer to aliases + dots <- rename_aliases(dots, lazy_query$select) + + lazy_query$where <- c(lazy_query$where, dots) + lazy_query } } @@ -120,12 +103,13 @@ filter_needs_new_query <- function(dots, lazy_query, con) { FALSE } -filter_can_use_having <- function(lazy_query, dots_use_window_fun) { - # From the Postgres documentation: https://www.postgresql.org/docs/current/sql-select.html#SQL-HAVING +filter_can_use_having <- function(lazy_query) { + # From the Postgres documentation: + # https://www.postgresql.org/docs/current/sql-select.html#SQL-HAVING # Each column referenced in condition must unambiguously reference a grouping # column, unless the reference appears within an aggregate function or the # ungrouped column is functionally dependent on the grouping columns. - + # # After `summarise()` every column is either # * a grouping column # * or an aggregated column @@ -134,24 +118,23 @@ filter_can_use_having <- function(lazy_query, dots_use_window_fun) { # Therefore, if `filter()` does not use a window function, then we only use # grouping or aggregated columns - if (dots_use_window_fun) { - return(FALSE) - } - - if (!is_lazy_select_query(lazy_query)) { - return(FALSE) + if (is_lazy_select_query(lazy_query)) { + lazy_query$select_operation == "summarise" + } else { + FALSE } - - lazy_query$select_operation == "summarise" } -filter_via_having <- function(lazy_query, dots) { - names <- lazy_query$select$name - exprs <- purrr::map_if(lazy_query$select$expr, is_quosure, quo_get_expr) - dots <- purrr::map(dots, replace_sym, names, exprs) +rename_aliases <- function(dots, select) { + exprs <- select$expr + nms <- select$name + projection <- purrr::map_lgl(exprs, is_symbol) + + if (!any(projection)) { + return(dots) + } - lazy_query$having <- c(lazy_query$having, dots) - lazy_query + purrr::map(dots, \(dot) replace_sym(dot, nms[projection], exprs[projection])) } check_filter <- function(...) { From 94e3206ca971e4182cdb8eb4879b7096fc3a0269 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 18 Dec 2025 16:54:26 -0600 Subject: [PATCH 2/7] Inline `filter()` after `left_join()`/`inner_join()` Part of #722 --- NEWS.md | 1 + R/db-sql.R | 12 +++++++-- R/lazy-join-query.R | 16 +++++++++++- R/query-join.R | 13 +++++++--- R/verb-filter.R | 5 ++++ tests/testthat/_snaps/verb-filter.md | 36 +++++++++++++++++++++++++++ tests/testthat/test-lazy-join-query.R | 14 +++++++++++ tests/testthat/test-verb-filter.R | 34 +++++++++++++++++++++++++ 8 files changed, 124 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index 244d73724..425f04b34 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # dbplyr (development version) +* `filter()` after a sequence of `left_join()` and `inner_joins()` no longer generates a subquery (#722). * `slice_*()` now handles missing values in line with the documentation, i.e. they are always removed (#1599). * Internal testing functions `src_test()`, `test_frame()` and `test_load()`, `test_register_src()` and `test_register_con()` have been removed. * A `filter()` followed by a `summarise()` is once again inlined correctly (#1707). diff --git a/R/db-sql.R b/R/db-sql.R index 5257cfbbd..81461b7b1 100644 --- a/R/db-sql.R +++ b/R/db-sql.R @@ -403,6 +403,7 @@ sql_query_join <- function( by = NULL, na_matches = FALSE, ..., + where = NULL, lvl = 0 ) { check_dots_used() @@ -418,6 +419,7 @@ sql_query_join.DBIConnection <- function( by = NULL, na_matches = FALSE, ..., + where = NULL, lvl = 0 ) { JOIN <- switch( @@ -440,7 +442,8 @@ sql_query_join.DBIConnection <- function( sql_clause_select(con, select), sql_clause_from(x), sql_clause(JOIN, y), - sql_clause("ON", on, sep = " AND", parens = TRUE, lvl = 1) + sql_clause("ON", on, sep = " AND", parens = TRUE, lvl = 1), + sql_clause_where(where) ) sql_format_clauses(clauses, lvl, con) } @@ -454,6 +457,7 @@ dbplyr_query_join <- function( na_matches = FALSE, ..., select = NULL, + where = NULL, lvl = 0 ) { check_2ed(con) @@ -465,6 +469,7 @@ dbplyr_query_join <- function( type = type, by = by, na_matches = na_matches, + where = where, ..., lvl = lvl ) @@ -480,6 +485,7 @@ sql_query_multi_join <- function( by_list, select, ..., + where = NULL, lvl = 0 ) { check_dots_used() @@ -524,6 +530,7 @@ sql_query_multi_join.DBIConnection <- function( by_list, select, ..., + where = NULL, lvl = 0 ) { if (vctrs::vec_duplicate_any(table_names)) { @@ -550,7 +557,8 @@ sql_query_multi_join.DBIConnection <- function( clauses <- list2( sql_clause_select(con, select), sql_clause_from(from), - !!!out + !!!out, + sql_clause_where(where) ) sql_format_clauses(clauses, lvl = lvl, con = con) } diff --git a/R/lazy-join-query.R b/R/lazy-join-query.R index 91e6b0451..71f884088 100644 --- a/R/lazy-join-query.R +++ b/R/lazy-join-query.R @@ -5,6 +5,7 @@ lazy_multi_join_query <- function( joins, table_names, vars, + where = list(), group_vars = op_grps(x), order_vars = op_sort(x), frame = op_frame(x), @@ -30,6 +31,7 @@ lazy_multi_join_query <- function( joins = joins, table_names = table_names, vars = vars, + where = where, group_vars = group_vars, order_vars = order_vars, frame = frame @@ -45,6 +47,7 @@ lazy_rf_join_query <- function( by, table_names, vars, + where = list(), group_vars = op_grps(x), order_vars = op_sort(x), frame = op_frame(x), @@ -72,6 +75,7 @@ lazy_rf_join_query <- function( by = by, table_names = table_names, vars = vars, + where = where, group_vars = group_vars, order_vars = order_vars, frame = frame @@ -188,11 +192,21 @@ sql_build.lazy_multi_join_query <- function(op, con, ..., sql_options = NULL) { } ) + # Column names in WHERE don't need to be qualified with table names/aliases + # because we know that the join has already generated aliases for ambiguous + # variables + where_sql <- translate_sql_( + op$where, + con = con, + context = list(clause = "WHERE") + ) + multi_join_query( x = sql_optimise(sql_build(op$x, con, sql_options = sql_options), con), joins = op$joins, table_names = table_names_out, - select = select_sql + select = select_sql, + where = where_sql ) } diff --git a/R/query-join.R b/R/query-join.R index bae435279..2f322a63d 100644 --- a/R/query-join.R +++ b/R/query-join.R @@ -10,7 +10,8 @@ join_query <- function( type = "inner", by = NULL, suffix = c(".x", ".y"), - na_matches = FALSE + na_matches = FALSE, + where = NULL ) { structure( list( @@ -19,19 +20,21 @@ join_query <- function( select = select, type = type, by = by, - na_matches = na_matches + na_matches = na_matches, + where = where ), class = c("join_query", "query") ) } -multi_join_query <- function(x, joins, table_names, select) { +multi_join_query <- function(x, joins, table_names, select, where = NULL) { structure( list( x = x, joins = joins, table_names = table_names, - select = select + select = select, + where = where ), class = c("multi_join_query", "query") ) @@ -90,6 +93,7 @@ sql_render.join_query <- function( by = query$by, na_matches = query$na_matches, select = query$select, + where = query$where, lvl = lvl ) } @@ -116,6 +120,7 @@ sql_render.multi_join_query <- function( table_names = query$table_names, by_list = query$by_list, select = query$select, + where = query$where, lvl = lvl ) } diff --git a/R/verb-filter.R b/R/verb-filter.R index cf94dc986..1149812b6 100644 --- a/R/verb-filter.R +++ b/R/verb-filter.R @@ -78,12 +78,17 @@ add_filter <- function(.data, dots) { # WHERE happens before SELECT so can't refer to aliases dots <- rename_aliases(dots, lazy_query$select) + # might be either a lazy_select_query or a lazy_multi_join_query lazy_query$where <- c(lazy_query$where, dots) lazy_query } } filter_needs_new_query <- function(dots, lazy_query, con) { + if (inherits(lazy_query, "lazy_multi_join_query")) { + return(FALSE) + } + if (!is_lazy_select_query(lazy_query)) { return(TRUE) } diff --git a/tests/testthat/_snaps/verb-filter.md b/tests/testthat/_snaps/verb-filter.md index e77c9c7df..9ad3b774a 100644 --- a/tests/testthat/_snaps/verb-filter.md +++ b/tests/testthat/_snaps/verb-filter.md @@ -52,6 +52,42 @@ ! `.preserve = TRUE` isn't supported on database backends. i It must be FALSE instead. +# filter() inlined after join + + Code + show_query(out) + Output + + SELECT "df_LHS".*, "z" + FROM "df" AS "df_LHS" + LEFT JOIN "df" AS "df_RHS" + ON ("df_LHS"."x" = "df_RHS"."x") + WHERE ("z" = 1.0) + +--- + + Code + show_query(out) + Output + + SELECT "df_LHS".*, "z" + FROM "df" AS "df_LHS" + LEFT JOIN "df" AS "df_RHS" + ON ("df_LHS"."x" = "df_RHS"."x") + WHERE ("y" = 1.0) AND ("z" = 2.0) + +--- + + Code + show_query(out) + Output + + SELECT "df_LHS"."x" AS "x", "df_LHS"."y" AS "y.x", "df_RHS"."y" AS "y.y" + FROM "df" AS "df_LHS" + LEFT JOIN "df" AS "df_RHS" + ON ("df_LHS"."x" = "df_RHS"."x") + WHERE ("y.y" = 1.0) + # catches `.by` with grouped-df Code diff --git a/tests/testthat/test-lazy-join-query.R b/tests/testthat/test-lazy-join-query.R index 01a2c42e1..0cda44a35 100644 --- a/tests/testthat/test-lazy-join-query.R +++ b/tests/testthat/test-lazy-join-query.R @@ -1,3 +1,17 @@ +test_that("sql_build.lazy_multi_join_query() includes where", { + lf1 <- lazy_frame(x = 1, y = 1) + lf2 <- lazy_frame(x = 1, z = 2) + + out <- lf1 |> + left_join(lf2, by = "x") |> + filter(y == 1, z == 2) + query <- out$lazy_query + + expect_s3_class(query, "lazy_multi_join_query") + built <- sql_build(query, simulate_dbi()) + expect_equal(built$where, sql('"y" = 1.0', '"z" = 2.0')) +}) + test_that("lazy_semi_join_query() checks arguments", { by0 <- list( x = "x", diff --git a/tests/testthat/test-verb-filter.R b/tests/testthat/test-verb-filter.R index a29342624..add7d1a0f 100644 --- a/tests/testthat/test-verb-filter.R +++ b/tests/testthat/test-verb-filter.R @@ -114,6 +114,40 @@ test_that("filter() inlined after mutate()", { expect_equal(lq3$where, list(quo(y == sql("1"))), ignore_formula_env = TRUE) }) +test_that("filter() inlined after join", { + lf1 <- lazy_frame(x = 1, y = 1) + lf2 <- lazy_frame(x = 1, z = 2) + + out <- lf1 |> + left_join(lf2, by = "x") |> + filter(z == 1) + expect_s3_class(out$lazy_query, "lazy_multi_join_query") + expect_snapshot(show_query(out)) + + # multiple filters are combined + out <- lf1 |> + left_join(lf2, by = "x") |> + filter(y == 1) |> + filter(z == 2) + expect_s3_class(out$lazy_query, "lazy_multi_join_query") + expect_snapshot(show_query(out)) + + # Handles aliasing from join + lf3 <- lazy_frame(x = 1, y = 2) + out <- lf1 |> + left_join(lf3, by = "x") |> + filter(y.y == 1) + expect_snapshot(show_query(out)) +}) + +test_that("inlined join works", { + # single integration test of the most complicated case + lf1 <- memdb_frame(x = 1:2, y = 1:2) + lf2 <- memdb_frame(x = 1:2, y = 3:4) + jf <- lf1 |> left_join(lf2, by = "x") |> filter(y.x == 1) + expect_equal(collect(jf), tibble(x = 1, y.x = 1, y.y = 3)) +}) + test_that("filter isn't inlined after mutate with window function #1135", { lf <- lazy_frame(x = 1L, y = 1:2) out <- lf |> From 3b40e15cf11bbe1dfea164111ea05eb4aa94e209 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 18 Dec 2025 17:55:30 -0600 Subject: [PATCH 3/7] Re-document --- man/db-sql.Rd | 13 ++++++++++++- man/sql_build.Rd | 5 ++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/man/db-sql.Rd b/man/db-sql.Rd index 562777f21..892e73913 100644 --- a/man/db-sql.Rd +++ b/man/db-sql.Rd @@ -83,10 +83,21 @@ sql_query_join( by = NULL, na_matches = FALSE, ..., + where = NULL, lvl = 0 ) -sql_query_multi_join(con, x, joins, table_names, by_list, select, ..., lvl = 0) +sql_query_multi_join( + con, + x, + joins, + table_names, + by_list, + select, + ..., + where = NULL, + lvl = 0 +) sql_query_semi_join(con, x, y, anti, by, where, vars, ..., lvl = 0) diff --git a/man/sql_build.Rd b/man/sql_build.Rd index 81de9a18c..87db23d19 100644 --- a/man/sql_build.Rd +++ b/man/sql_build.Rd @@ -25,6 +25,7 @@ lazy_multi_join_query( joins, table_names, vars, + where = list(), group_vars = op_grps(x), order_vars = op_sort(x), frame = op_frame(x), @@ -38,6 +39,7 @@ lazy_rf_join_query( by, table_names, vars, + where = list(), group_vars = op_grps(x), order_vars = op_sort(x), frame = op_frame(x), @@ -107,7 +109,8 @@ join_query( type = "inner", by = NULL, suffix = c(".x", ".y"), - na_matches = FALSE + na_matches = FALSE, + where = NULL ) select_query( From 28896b51f8efa55a7a18dee64494f487d9f66dff Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Fri, 19 Dec 2025 13:51:06 -0600 Subject: [PATCH 4/7] Update test --- tests/testthat/test-verb-filter.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-verb-filter.R b/tests/testthat/test-verb-filter.R index ee376ffda..94a6a8972 100644 --- a/tests/testthat/test-verb-filter.R +++ b/tests/testthat/test-verb-filter.R @@ -32,7 +32,7 @@ test_that("correctly inlines across all verbs", { # two table verbs lf2 <- lazy_frame(x = 1) - expect_selects(lf |> left_join(lf2, by = "x") |> filter(x == 1), 2) + expect_selects(lf |> left_join(lf2, by = "x") |> filter(x == 1), 1) expect_selects(lf |> semi_join(lf2, by = "x") |> filter(x == 1), 3) expect_selects(lf |> union(lf2) |> filter(x == 1), 3) From 2a8ab3be0768f9bdf8c9e21c8528357f3e5d87f5 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Mon, 22 Dec 2025 08:49:49 -0600 Subject: [PATCH 5/7] Revert comment changes --- R/verb-filter.R | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/R/verb-filter.R b/R/verb-filter.R index 1149812b6..129a00f5a 100644 --- a/R/verb-filter.R +++ b/R/verb-filter.R @@ -109,12 +109,11 @@ filter_needs_new_query <- function(dots, lazy_query, con) { } filter_can_use_having <- function(lazy_query) { - # From the Postgres documentation: - # https://www.postgresql.org/docs/current/sql-select.html#SQL-HAVING + # From the Postgres documentation: https://www.postgresql.org/docs/current/sql-select.html#SQL-HAVING # Each column referenced in condition must unambiguously reference a grouping # column, unless the reference appears within an aggregate function or the # ungrouped column is functionally dependent on the grouping columns. - # + # After `summarise()` every column is either # * a grouping column # * or an aggregated column From bff63e55629bd8f598f341b62882e6948b8ababf Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Mon, 22 Dec 2025 12:22:29 -0600 Subject: [PATCH 6/7] Switch logic around --- R/verb-filter.R | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/R/verb-filter.R b/R/verb-filter.R index 129a00f5a..b1f5e7b6a 100644 --- a/R/verb-filter.R +++ b/R/verb-filter.R @@ -72,40 +72,41 @@ add_filter <- function(.data, dots) { lazy_query$having <- c(lazy_query$having, dots) lazy_query - } else if (filter_needs_new_query(dots, lazy_query, con)) { - lazy_select_query(x = lazy_query, where = dots) - } else { + } else if (filter_can_inline(dots, lazy_query, con)) { # WHERE happens before SELECT so can't refer to aliases dots <- rename_aliases(dots, lazy_query$select) # might be either a lazy_select_query or a lazy_multi_join_query lazy_query$where <- c(lazy_query$where, dots) lazy_query + } else { + lazy_select_query(x = lazy_query, where = dots) } } -filter_needs_new_query <- function(dots, lazy_query, con) { +filter_can_inline <- function(dots, lazy_query, con) { if (inherits(lazy_query, "lazy_multi_join_query")) { - return(FALSE) + # can't use mutated variables, window funs, or SQL + return(TRUE) } if (!is_lazy_select_query(lazy_query)) { - return(TRUE) + return(FALSE) } if (uses_mutated_vars(dots, lazy_query$select)) { - return(TRUE) + return(FALSE) } if (uses_window_fun(lazy_query$select$expr, con)) { - return(TRUE) + return(FALSE) } if (any_expr_uses_sql(lazy_query$select$expr)) { - return(TRUE) + return(FALSE) } - FALSE + TRUE } filter_can_use_having <- function(lazy_query) { From cdaf125c7b80250020f1a9ad996639c02d1112c3 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Mon, 22 Dec 2025 14:20:47 -0600 Subject: [PATCH 7/7] Fix the problem --- R/lazy-join-query.R | 21 +++++++++++++++++---- R/verb-filter.R | 6 ++++-- tests/testthat/_snaps/backend-mssql.md | 2 ++ tests/testthat/_snaps/verb-filter.md | 26 +++++++++++++------------- tests/testthat/test-verb-filter.R | 6 +++--- 5 files changed, 39 insertions(+), 22 deletions(-) diff --git a/R/lazy-join-query.R b/R/lazy-join-query.R index 4c26ee2a7..742d2529d 100644 --- a/R/lazy-join-query.R +++ b/R/lazy-join-query.R @@ -194,11 +194,24 @@ sql_build.lazy_multi_join_query <- function(op, con, ..., sql_options = NULL) { } ) - # Column names in WHERE don't need to be qualified with table names/aliases - # because we know that the join has already generated aliases for ambiguous - # variables + join_vars <- sql_multi_join_vars( + con, + op$vars, + table_vars, + use_star = FALSE, + qualify_all_columns = sql_options$qualify_all_columns + ) + # WHERE happens after SELECT, but columns names are disambiguated using + # SELECT expressions, so need to backtransform + where <- lapply(op$where, \(expr) { + replace_sym( + expr, + names(join_vars), + lapply(unname(join_vars), \(x) sql(x[[1]])) + ) + }) where_sql <- translate_sql_( - op$where, + where, con = con, context = list(clause = "WHERE") ) diff --git a/R/verb-filter.R b/R/verb-filter.R index b1f5e7b6a..550441169 100644 --- a/R/verb-filter.R +++ b/R/verb-filter.R @@ -74,9 +74,11 @@ add_filter <- function(.data, dots) { lazy_query } else if (filter_can_inline(dots, lazy_query, con)) { # WHERE happens before SELECT so can't refer to aliases - dots <- rename_aliases(dots, lazy_query$select) - # might be either a lazy_select_query or a lazy_multi_join_query + if (is_lazy_select_query(lazy_query)) { + dots <- rename_aliases(dots, lazy_query$select) + } + lazy_query$where <- c(lazy_query$where, dots) lazy_query } else { diff --git a/tests/testthat/_snaps/backend-mssql.md b/tests/testthat/_snaps/backend-mssql.md index 9bb3b2621..e8612ba7f 100644 --- a/tests/testthat/_snaps/backend-mssql.md +++ b/tests/testthat/_snaps/backend-mssql.md @@ -759,6 +759,8 @@ EOF within quoted string Warning in `scan()`: EOF within quoted string + Warning in `scan()`: + EOF within quoted string Output SELECT [LHS]] diff --git a/tests/testthat/_snaps/verb-filter.md b/tests/testthat/_snaps/verb-filter.md index 9ad3b774a..3a4dd5d34 100644 --- a/tests/testthat/_snaps/verb-filter.md +++ b/tests/testthat/_snaps/verb-filter.md @@ -58,10 +58,10 @@ show_query(out) Output - SELECT "df_LHS".*, "z" - FROM "df" AS "df_LHS" - LEFT JOIN "df" AS "df_RHS" - ON ("df_LHS"."x" = "df_RHS"."x") + SELECT "df1".*, "z" + FROM "df1" + LEFT JOIN "df2" + ON ("df1"."x" = "df2"."x") WHERE ("z" = 1.0) --- @@ -70,10 +70,10 @@ show_query(out) Output - SELECT "df_LHS".*, "z" - FROM "df" AS "df_LHS" - LEFT JOIN "df" AS "df_RHS" - ON ("df_LHS"."x" = "df_RHS"."x") + SELECT "df1".*, "z" + FROM "df1" + LEFT JOIN "df2" + ON ("df1"."x" = "df2"."x") WHERE ("y" = 1.0) AND ("z" = 2.0) --- @@ -82,11 +82,11 @@ show_query(out) Output - SELECT "df_LHS"."x" AS "x", "df_LHS"."y" AS "y.x", "df_RHS"."y" AS "y.y" - FROM "df" AS "df_LHS" - LEFT JOIN "df" AS "df_RHS" - ON ("df_LHS"."x" = "df_RHS"."x") - WHERE ("y.y" = 1.0) + SELECT "df1"."x" AS "x", "df1"."y" AS "y.x", "df3"."y" AS "y.y" + FROM "df1" + LEFT JOIN "df3" + ON ("df1"."x" = "df3"."x") + WHERE ("df3"."y" = 1.0) # catches `.by` with grouped-df diff --git a/tests/testthat/test-verb-filter.R b/tests/testthat/test-verb-filter.R index b3a4883bd..b15336559 100644 --- a/tests/testthat/test-verb-filter.R +++ b/tests/testthat/test-verb-filter.R @@ -141,8 +141,8 @@ test_that("filter() inlined after mutate()", { }) test_that("filter() inlined after join", { - lf1 <- lazy_frame(x = 1, y = 1) - lf2 <- lazy_frame(x = 1, z = 2) + lf1 <- lazy_frame(x = 1, y = 1, .name = "df1") + lf2 <- lazy_frame(x = 1, z = 2, .name = "df2") out <- lf1 |> left_join(lf2, by = "x") |> @@ -159,7 +159,7 @@ test_that("filter() inlined after join", { expect_snapshot(show_query(out)) # Handles aliasing from join - lf3 <- lazy_frame(x = 1, y = 2) + lf3 <- lazy_frame(x = 1, y = 2, .name = "df3") out <- lf1 |> left_join(lf3, by = "x") |> filter(y.y == 1)