Skip to content

fix: 🐛 use duckplyr to read Parquet as DuckDB#348

Open
Aastedet wants to merge 1 commit into
dp-next:mainfrom
Aastedet:duckplyr-read-parquet-duckdb
Open

fix: 🐛 use duckplyr to read Parquet as DuckDB#348
Aastedet wants to merge 1 commit into
dp-next:mainfrom
Aastedet:duckplyr-read-parquet-duckdb

Conversation

@Aastedet

@Aastedet Aastedet commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Description

Changes read_parquet_dataset() and read_parquet_file() to use duckplyr::read_parquet_duckdb() instead of arrow::open_dataset()/arrow::read_parquet()

This provides more robust support for duckplyr verbs and avoids errors/bugs as mentioned in #346.

Closes #346

The change to the functions is small, and only a few edits to test-read.R and test-use.R were necessary. The only substantial change to the tests is the removal of the test for incompatible schemas in test-read.R, which was necessary because duckplyr::read_parquet_duckdb handles this very differently from how arrow::open_dataset() does: if a variable's type differs between Parquet files, it is silently/automatically converted/promoted to a compatible type if at all possible and no error is thrown. I could not find a an option/flag to change this behavior.
I'd argue that, as a user this is actually the preferable behavior, since it doesn't require debugging/re-running the whole conversion, but just a single mutate() call to force the desired type.

I've kept the use of arrow::open_dataset in test-convert.R untouched for two reasons:

  • The purpose of those tests is to verify the conversion from SAS/writing of the Parquet file, not the subsequent loading.
  • Reading in the data with duckplyr::read_parquet_duckdb leads to some minor discrepancies in types and attributes in the data (described in the linked issue) that cause expect_identical() to fail vs. haven-read tables, so it's a bit more messy to run those tests with duckplyr in the pipeline.

Needs a quick/thorough review.

Checklist

  • Ran just run-all

@Aastedet

Copy link
Copy Markdown
Contributor Author

I assume that the failing GHA is a fastreg thing and not on my end, but let me know if I should look into it.

@lwjohnst86 lwjohnst86 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Just some comments ☺️ and yes, the failing GHA is for adding to the board (not sure why)

Comment thread R/read.R
Comment on lines +67 to +68
duckplyr::as_duckdb_tibble() |>
duckplyr::as_tbl()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are both these needed? And doesn't that negate the thrifty?

@Aastedet Aastedet Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ll have to look into that. It mirrors the existing osdc testing pipeline when we set up the simulated data, so I assumed it was good. I didn’t dive deep into the technical details, this was just what “worked”, so I ran with it.

The prudence choice is also quite important - I figured thrifty is going to strike the best balance between memory safety and practical usefulness for quick exploratory summary statistics etc, but it’s definitely up for discussion.

Comment on lines -150 to -173
test_that("read_parquet_dataset() errors with incompatible schemas", {
# Create a bef file where numeric columns are changed to character, so
# the schema is incompatible with the other bef files.
incompatible_data <- bef_list$data[[1]] |>
dplyr::mutate(dplyr::across(where(is.numeric), as.character))

incompatible_sas_path <- fs::path_temp(
"sas_schema_incompatible/bef2099.sas7bdat"
)
write_to_sas(incompatible_data, incompatible_sas_path)
sas_incompatible <- c(
sas_paths$output_path,
incompatible_sas_path
)

incompatible_output <- fs::path_temp("incompatible")
# Convert files.
purrr::walk(sas_incompatible, \(path) {
convert(path, incompatible_output)
})

expect_error(read_parquet_dataset(incompatible_output), "incompatible")
})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this? The point was to ensure that it correctly fails if there is an incompatible schema.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duckdb reader is inherently much more robust to type mismatches than Arrow’s. There doesn’t appear to be a way to make duckplyr::read_parquet_duckdb() fail that test, so I think it has to be removed.

@lwjohnst86 lwjohnst86 moved this from In progress to In review in Platform development Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

Limitations on the DuckDB view created by arrow::read_dataset() |> arrow::to_duckdb() in the read_* functions

2 participants