Skip to content

User defined ENUM types are NA #509

@andreranza

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

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