Skip to content

Comments

fix: The dbplyr translation of as.numeric() and as.double() uses DOUBLE instead of NUMERIC#2031

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/fix-as-double-as-numeric-conversion
Draft

fix: The dbplyr translation of as.numeric() and as.double() uses DOUBLE instead of NUMERIC#2031
Copilot wants to merge 2 commits intomainfrom
copilot/fix-as-double-as-numeric-conversion

Conversation

Copy link
Contributor

Copilot AI commented Feb 15, 2026

as.numeric() and as.double() were being translated to SQL AS NUMERIC, which in DuckDB is DECIMAL(18,3) - a fixed-precision type with only 3 decimal places. This caused precision loss in dplyr queries.

Changes

  • Override as.numeric and as.double translations to use DOUBLE (IEEE 754 floating-point) instead of NUMERIC
  • Update test expectations and snapshots

Example

df <- data.frame(value = 1.234567)
duckdb_register(con, "df", df)

tbl(con, "df") %>% 
  mutate(x = as.numeric(value)) %>%
  show_query()

Before: CAST(value AS NUMERIC) → 1.235 (rounded to 3 decimals)
After: CAST(value AS DOUBLE) → 1.234567 (full precision)

Pattern follows dbplyr's Redshift backend.

Original prompt

This section details on the original issue you should resolve

<issue_title>as.double() / as.numeric() treated as "AS NUMERIC" by the dplyr backend</issue_title>
<issue_description>When using dplyr, as.numeric()/as.double() is converted to AS NUMERIC. In duckdb, NUMERIC is an alias to DECIMAL(18, 3).

Similar to other databases (tidyverse/dbplyr#379, tidyverse/dbplyr#408), AS DOUBLE would be a better fit to what is expected:

> packageVersion("duckdb")
[1] ‘1.4.0> packageVersion("dplyr")
[1] ‘1.1.4> packageVersion("dbplyr")
[1] ‘2.5.1> packageVersion("duckdb")
[1] ‘1.4.0>
> con <- DBI::dbConnect(duckdb::duckdb(), ":memory:")
>
> # Write a double to a table first
> DBI::dbWriteTable(con, "table", data.frame(a = c(0.0091792656587473)))
>
> # Casting the value gives surprising results
> dplyr::tbl(con, "table") |> dplyr::mutate(b = a * 1e6, c = as.double(a) * 1e6)
# Source:   SQL [?? x 3]
# Database: DuckDB 1.4.0 [lentinj@Linux 6.17.13+deb14-amd64:R 4.5.2/:memory:]
        a     b     c
    <dbl> <dbl> <dbl>
1 0.00918 9179.  9000
> dplyr::tbl(con, "table") |> dplyr::mutate(b = a * 1e6, c = as.double(a) * 1e6) |> dplyr::show_query()
<SQL>
SELECT "table".*, a * 1000000.0 AS b, CAST(a AS NUMERIC) * 1000000.0 AS c
FROM "table"

That said, there are tests explicitly for this so maybe I'm missing something very obvious?

expect_equal(translate(as.numeric(1)), sql(r"{CAST(1.0 AS NUMERIC)}"))
expect_equal(translate(as.double(1.2)), sql(r"{CAST(1.2 AS NUMERIC)}"))

Adding something along these lines into backend-dbplyr__duckdb_connection.R should help:

https://github.com/tidyverse/dbplyr/blob/ce9c71188cb9b5d676bc7c99f83f001edef9221e/R/backend-redshift.R#L68-L69

#1585 is also related, although that is purely about Inf, some of the tests there are stumbling on this too.</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix dplyr conversion of as.numeric() to AS NUMERIC in duckdb Fix as.numeric()/as.double() to use DOUBLE instead of NUMERIC Feb 15, 2026
Copilot AI requested a review from krlmlr February 15, 2026 17:09
@krlmlr krlmlr changed the title Fix as.numeric()/as.double() to use DOUBLE instead of NUMERIC fix: The dbplyr translation of as.numeric() and as.double() uses DOUBLE instead of NUMERIC Feb 15, 2026
@krlmlr
Copy link
Collaborator

krlmlr commented Feb 15, 2026

@lentinj: Does this work for you?

Copy link

@lentinj lentinj left a comment

Choose a reason for hiding this comment

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

Looks good to me! Might be worth adding a unit test for this particular case, e.g:

diff --git a/tests/testthat/test-numeric.R b/tests/testthat/test-numeric.R
new file mode 100644
index 000000000..288948adb
--- /dev/null
+++ b/tests/testthat/test-numeric.R
@@ -0,0 +1,12 @@
+test_that("as.numeric treats values as doubles", {
+  skip_if_not_installed("dplyr")
+
+  con <- local_con()
+  df <- data.frame(value = 1.23456789123)
+  duckdb_register(con, "df", df)
+  # NB: If this results in value AS NUMERIC, the result will be fixed to 3dp, i.e. 1.235
+  res <- dplyr::tbl(con, "df") |> dplyr::mutate(x = as.numeric(value)) |> dplyr::pull(x)
+  expect_equal(res, df[1, "value"])
+})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

as.double() / as.numeric() treated as "AS NUMERIC" by the dplyr backend

3 participants