Skip to content

Commit 073ec73

Browse files
authored
refactor: Define QueryOptFlags S7 class (#1633)
1 parent 6cd8df8 commit 073ec73

File tree

17 files changed

+542
-107
lines changed

17 files changed

+542
-107
lines changed

.lintr.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
linters <- linters_with_defaults(
22
line_length_linter(100L),
33
object_name_linter = object_name_linter(
4-
styles = c("snake_case", "symbols", "SNAKE_CASE"),
5-
regexes = c("^pl__.*", "^_.*")
4+
styles = c("snake_case", "symbols", "SNAKE_CASE", "CamelCase"),
5+
regexes = c("^.*__.*", "^_.*")
66
),
77
commented_code_linter = NULL # TODO: remove commented code
88
)

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ BugReports: https://github.com/pola-rs/r-polars/issues
2020
Depends:
2121
R (>= 4.3)
2222
Imports:
23-
rlang (>= 1.1.0)
23+
rlang (>= 1.1.0),
24+
S7 (>= 0.2.0)
2425
Suggests:
2526
arrow,
2627
bit64,

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ S3method(print,polars_series)
196196
S3method(row.names,polars_data_frame)
197197
S3method(stats::median,polars_series)
198198
S3method(sum,polars_series)
199+
S3method(utils::.DollarNames,polars::QueryOptFlags)
199200
S3method(utils::.DollarNames,polars_chained_then)
200201
S3method(utils::.DollarNames,polars_data_frame)
201202
S3method(utils::.DollarNames,polars_datatype_expr)
@@ -281,6 +282,7 @@ export(polars_envvars)
281282
export(polars_info)
282283
export(polars_options)
283284
export(polars_options_reset)
285+
import(S7)
284286
import(rlang)
285287
importFrom(utils,findMatches)
286288
importFrom(utils,head)

R/functions-lazy.R

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -154,21 +154,14 @@ pl__collect_all <- function(
154154
check_dots_empty0(...)
155155
check_list_of_polars_lf(lazy_frames)
156156
engine <- arg_match0(engine, c("auto", "in-memory", "streaming"))
157+
# TODO: add support for argument `optimizations`
158+
optflags <- QueryOptFlags()
159+
check_is_S7(optflags, QueryOptFlags)
160+
validate(optflags)
157161

158162
lfs <- lapply(lazy_frames, \(x) x$`_ldf`)
159-
# TODO: add support for argument `optimizations`
160-
optflags <- list(
161-
comm_subexpr_elim = TRUE,
162-
comm_subplan_elim = TRUE,
163-
cluster_with_columns = TRUE,
164-
predicate_pushdown = TRUE,
165-
projection_pushdown = TRUE,
166-
simplify_expression = TRUE,
167-
slice_pushdown = TRUE,
168-
type_coercion = TRUE
169-
)
170163

171-
collect_all(lfs, engine = engine, optflags) |>
164+
collect_all(lfs, engine = engine, optflags = optflags) |>
172165
lapply(\(ptr) .savvy_wrap_PlRDataFrame(ptr) |> wrap())
173166
})
174167
}

R/lazyframe-frame.R

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -234,16 +234,8 @@ lazyframe__group_by <- function(..., .maintain_order = FALSE) {
234234
#' Individual optimizations may be disabled by setting the corresponding parameter to `FALSE`.
235235
#' @inherit pl__DataFrame return
236236
#' @inheritParams rlang::args_dots_empty
237+
#' @inheritParams QueryOptFlags
237238
#' @param type_coercion A logical, indicates type coercion optimization.
238-
#' @param predicate_pushdown A logical, indicates predicate pushdown optimization.
239-
#' @param projection_pushdown A logical, indicates projection pushdown optimization.
240-
#' @param simplify_expression A logical, indicates simplify expression optimization.
241-
#' @param slice_pushdown A logical, indicates slice pushdown optimization.
242-
#' @param comm_subplan_elim A logical, indicates trying to cache branching subplans that occur
243-
#' on self-joins or unions.
244-
#' @param comm_subexpr_elim A logical, indicates trying to cache common subexpressions.
245-
#' @param cluster_with_columns A logical, indicates to combine sequential independent calls
246-
#' to with_columns.
247239
#' @param collapse_joins `r lifecycle::badge("deprecated")`
248240
#' Use `predicate_pushdown` instead.
249241
#' @param no_optimization A logical. If `TRUE`, turn off (certain) optimizations.

R/lazyframe-opt_flags.R

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# nolint start: object_length_linter
2+
3+
# The env for storing QueryOptFlags methods
4+
polars_query_opt_flags__methods <- new.env(parent = emptyenv())
5+
6+
#' The set of the optimizations considered during query optimization
7+
#'
8+
#' `r lifecycle::badge("experimental")`
9+
#'
10+
#' @inheritParams rlang::args_dots_empty
11+
#' @param predicate_pushdown A logical, indicates predicate pushdown optimization.
12+
#' @param projection_pushdown A logical, indicates projection pushdown optimization.
13+
#' @param simplify_expression A logical, indicates simplify expression optimization.
14+
#' @param slice_pushdown A logical, indicates slice pushdown optimization.
15+
#' @param comm_subplan_elim A logical, indicates trying to cache branching subplans that occur
16+
#' on self-joins or unions.
17+
#' @param comm_subexpr_elim A logical, indicates trying to cache common subexpressions.
18+
#' @param cluster_with_columns A logical, indicates to combine sequential independent calls
19+
#' to with_columns.
20+
#' @param check_order_observe A logical, indicates not to maintain order
21+
#' if the order would not be observed.
22+
#' @param fast_projection A logical, indicates to replace simple projections
23+
#' with a faster inlined projection that skips the expression engine.
24+
#' @return A `QueryOptFlags` object.
25+
#' @name QueryOptFlags
26+
#' @examples
27+
#' opt_flags <- pl$QueryOptFlags()
28+
#' opt_flags
29+
#'
30+
#' S7::check_is_S7(opt_flags, pl$QueryOptFlags)
31+
#' @keywords internal
32+
NULL
33+
34+
QueryOptFlags <- new_class(
35+
"QueryOptFlags",
36+
properties = list(
37+
# TODO: switch to scalar properties when supported
38+
# https://github.com/RConsortium/S7/pull/433
39+
type_coercion = class_logical,
40+
type_check = class_logical,
41+
predicate_pushdown = class_logical,
42+
projection_pushdown = class_logical,
43+
simplify_expression = class_logical,
44+
slice_pushdown = class_logical,
45+
comm_subplan_elim = class_logical,
46+
comm_subexpr_elim = class_logical,
47+
cluster_with_columns = class_logical,
48+
check_order_observe = class_logical,
49+
fast_projection = class_logical,
50+
eager = class_logical,
51+
streaming = class_logical
52+
),
53+
constructor = function(
54+
...,
55+
predicate_pushdown = TRUE,
56+
projection_pushdown = TRUE,
57+
simplify_expression = TRUE,
58+
slice_pushdown = TRUE,
59+
comm_subplan_elim = TRUE,
60+
comm_subexpr_elim = TRUE,
61+
cluster_with_columns = TRUE,
62+
check_order_observe = TRUE,
63+
fast_projection = TRUE
64+
) {
65+
check_dots_empty0(...)
66+
67+
# Should be synced with impl Default for OptFlags in Rust
68+
# https://github.com/pola-rs/polars/blob/d0be4c06447f2e530c6f68d3cd73b43f2e33e2f3/crates/polars-plan/src/frame/opt_state.rs#L77-L81 # nolint: line_length_linter
69+
new_object(
70+
S7_object(),
71+
type_coercion = TRUE,
72+
type_check = TRUE,
73+
predicate_pushdown = predicate_pushdown,
74+
projection_pushdown = projection_pushdown,
75+
simplify_expression = simplify_expression,
76+
slice_pushdown = slice_pushdown,
77+
comm_subplan_elim = comm_subplan_elim,
78+
comm_subexpr_elim = comm_subexpr_elim,
79+
cluster_with_columns = cluster_with_columns,
80+
check_order_observe = check_order_observe,
81+
fast_projection = fast_projection,
82+
eager = FALSE,
83+
streaming = FALSE
84+
)
85+
},
86+
validator = function(self) {
87+
call <- caller_env(4L)
88+
89+
check_bool(self@type_coercion, arg = "type_coercion", call = call)
90+
check_bool(self@type_check, arg = "type_check", call = call)
91+
92+
check_bool(self@predicate_pushdown, arg = "predicate_pushdown", call = call)
93+
check_bool(self@projection_pushdown, arg = "projection_pushdown", call = call)
94+
check_bool(self@simplify_expression, arg = "simplify_expression", call = call)
95+
check_bool(self@slice_pushdown, arg = "slice_pushdown", call = call)
96+
check_bool(self@comm_subplan_elim, arg = "comm_subplan_elim", call = call)
97+
check_bool(self@comm_subexpr_elim, arg = "comm_subexpr_elim", call = call)
98+
check_bool(self@cluster_with_columns, arg = "cluster_with_columns", call = call)
99+
check_bool(self@check_order_observe, arg = "check_order_observe", call = call)
100+
check_bool(self@fast_projection, arg = "fast_projection", call = call)
101+
102+
check_bool(self@eager, arg = "eager", call = call)
103+
check_bool(self@streaming, arg = "streaming", call = call)
104+
}
105+
)
106+
107+
#' @rdname QueryOptFlags
108+
pl__QueryOptFlags <- QueryOptFlags
109+
110+
# TODO: moved to generated file
111+
# `local({})` is needed for `$` methods.
112+
# https://github.com/RConsortium/S7/issues/390
113+
local({
114+
method(`$`, QueryOptFlags) <- function(self, name) {
115+
method_names <- names(polars_query_opt_flags__methods)
116+
117+
if (name %in% method_names) {
118+
fn <- polars_query_opt_flags__methods[[name]]
119+
environment(fn) <- environment()
120+
fn
121+
} else {
122+
# TODO: use abstract class and better handling
123+
prop(self, name) |>
124+
wrap()
125+
}
126+
}
127+
})
128+
129+
#' @exportS3Method utils::.DollarNames
130+
`.DollarNames.polars::QueryOptFlags` <- function(x, pattern = "") {
131+
property_names <- prop_names(x)
132+
method_names <- names(polars_query_opt_flags__methods)
133+
134+
all_names <- c(property_names, method_names)
135+
filtered_names <- findMatches(pattern, all_names)
136+
137+
filtered_names[!startsWith(filtered_names, "_")]
138+
}
139+
140+
# TODO: This should be a constructor
141+
# https://github.com/RConsortium/S7/issues/522
142+
eager_opt_flags <- function() {
143+
QueryOptFlags()$no_optimizations() |>
144+
set_props_uncheck(
145+
eager = TRUE,
146+
simplify_expression = TRUE
147+
)
148+
}
149+
150+
# Because of immutability, unlike Python Polars, there are no side effects,
151+
# and a new modified OptFlags is returned
152+
QueryOptFlags__no_optimizations <- function() {
153+
set_props_uncheck(
154+
self,
155+
predicate_pushdown = FALSE,
156+
projection_pushdown = FALSE,
157+
simplify_expression = FALSE,
158+
slice_pushdown = FALSE,
159+
comm_subplan_elim = FALSE,
160+
comm_subexpr_elim = FALSE,
161+
cluster_with_columns = FALSE,
162+
check_order_observe = FALSE,
163+
fast_projection = FALSE
164+
)
165+
}
166+
167+
# nolint end

R/polars-package.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
## usethis namespace: start
55
#' @import rlang
6+
#' @import S7
67
#' @importFrom utils findMatches head tail
78
## usethis namespace: end
89
NULL

R/utils-s7.R

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Because validation step is very expensive,
2+
# this is a copy of `props<-`, the only difference is `check = FALSE`
3+
# https://github.com/RConsortium/S7/issues/574
4+
`props_uncheck<-` <- function(object, value) {
5+
for (name in names(value)) {
6+
prop(object, name, check = FALSE) <- value[[name]]
7+
}
8+
9+
object
10+
}
11+
12+
# A copy of `set_props()`, using `props_uncheck<-`
13+
# instead of `props<-` to avoid validation
14+
set_props_uncheck <- function(object, ...) {
15+
props_uncheck(object) <- list(...)
16+
object
17+
}

R/zzz.R

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ POLARS_STORE_ENVS <- list(
7171
"groupby__" = polars_groupby__methods,
7272
"rolling_groupby__" = polars_rolling_groupby__methods,
7373
"group_by_dynamic__" = polars_group_by_dynamic__methods,
74-
"sql_context__" = polars_sql_context__methods
74+
"sql_context__" = polars_sql_context__methods,
75+
QueryOptFlags__ = polars_query_opt_flags__methods
7576
)
7677

7778
lapply(names(POLARS_STORE_ENVS), function(name) {
@@ -92,6 +93,7 @@ utils::globalVariables("self")
9293
on_load(local_use_cli())
9394

9495
.onLoad <- function(libname, pkgname) {
96+
methods_register()
9597
run_on_load()
9698

9799
# Register S3 methods for optional packages

man/QueryOptFlags.Rd

Lines changed: 58 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)