Skip to content

User defined ENUM types are NA #509

Description

@andreranza
library(DBI)
library(RPostgres)

# Connection -------------------------------------------------------------------

creds <- c(
  user = "postgres",
  psw = "postgres",
  host = "localhost"
)

con <- dbConnect(
  Postgres(),
  dbname = "postgres",
  user = creds[["user"]],
  password = creds[["psw"]],
  host = creds[["host"]]
)

# Create custom user enum type -------------------------------------------------

q_drop <- "DROP TYPE IF EXISTS \"UserRole\" CASCADE;"
dbExecute(con, q_drop)
#> NOTICE:  drop cascades to column role of table sample_users
#> [1] 0

q_create_typ <- "CREATE TYPE \"UserRole\" AS ENUM ('user', 'admin');"
dbExecute(con, q_create_typ)
#> [1] 0

# Create table -----------------------------------------------------------------

dbExecute(con, "DROP TABLE IF EXISTS sample_users")
#> [1] 0
q_create_tbl <- "
CREATE TABLE sample_users (
  role \"UserRole\" NOT NULL DEFAULT 'user'
);"
dbExecute(con, q_create_tbl)
#> [1] 0

# Check How Custom Type is represented correctly in DB -------------------------

q_list_types <- "
SELECT
    t.typname AS type_name,
    CASE
        WHEN t.typtype = 'e' THEN 'ENUM'
        WHEN t.typtype = 'c' THEN 'COMPOSITE'
        WHEN t.typtype = 'd' THEN 'DOMAIN'
        WHEN t.typtype = 'r' THEN 'RANGE'
        WHEN t.typtype = 'm' THEN 'MULTIRANGE'
        ELSE t.typtype::text
    END AS type_category,
    n.nspname AS schema_name
FROM
    pg_type t
JOIN
    pg_namespace n ON t.typnamespace = n.oid
WHERE
    -- Filter for user-defined types (not system types)
    n.nspname NOT IN ('pg_catalog', 'information_schema')
    -- Exclude array types and implicit types
    AND t.typelem = 0
    AND t.typtype NOT IN ('p')
ORDER BY
    schema_name,
    type_name;
"

dbGetQuery(con, q_list_types)
#>      type_name type_category schema_name
#> 1     UserRole          ENUM      public
#> 2 sample_users     COMPOSITE      public

# Insert data ------------------------------------------------------------------

q_ins <- "INSERT INTO sample_users (role)
VALUES ('admin'), ('user');"

dbExecute(con, q_ins)
#> [1] 2

out <- dbGetQuery(con, "SELECT * FROM sample_users;")

# Issue (1) --------------------------------------------------------------------

# pq_NA rather than pq_UserRole
constructive::construct(out)
#> list(
#>   role = c("admin", "user") |>
#>     structure(class = "pq_NA")
#> ) |>
#>   structure(row.names = c(NA, -2L), class = "data.frame")

# `match()` call returning NA in debug(RPostgres:::type_lookup)
# Apparently, the oid in the typnames data.frame from `ret` does not match with the one from con.
# UserRole it's there but with a different oid

# Issue (2) --------------------------------------------------------------------

# derived from issue 1: cannot use dplyr::rows_insert()
dbExecute(con, "DROP TABLE IF EXISTS sample_users")
#> [1] 0
q_create_tbl <- "
CREATE TABLE sample_users (
  role \"UserRole\" NOT NULL DEFAULT 'user'
);"
dbExecute(con, q_create_tbl)
#> [1] 0
target <- dplyr::tbl(con, "sample_users")

source <- tibble::tibble(
  role = c("admin", "user") |>
    structure(class = "pq_UserRole")
)

try(dplyr::rows_insert(target, source, conflict = "ignore", copy = TRUE))
#> Error in db_copy_to(dest$con, name, df, overwrite = overwrite, types = types,  : 
#>   Can't copy data to table "dbplyr_BP9t2EMwur".
#> Caused by error in `dplyr::db_write_table()`:
#> ! Can't write table table "dbplyr_BP9t2EMwur".
#> Caused by error:
#> ! Failed to fetch row : ERROR:  type "na" does not exist at character 55

Created on 2025-05-23 with reprex v2.1.1

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions