tests/testthat/test-geometry.R

test_that("geometry columns can be read as blob (default)", {
  con <- local_con()

  dbExecute(con, "CREATE TABLE test_geom (id INTEGER, geom GEOMETRY)")
  dbExecute(con, "INSERT INTO test_geom VALUES (1, 'POINT (1 2)'::GEOMETRY)")
  dbExecute(con, "INSERT INTO test_geom VALUES (2, 'POINT (3 4)'::GEOMETRY)")
  dbExecute(con, "INSERT INTO test_geom VALUES (3, NULL)")

  res <- dbGetQuery(con, "SELECT * FROM test_geom ORDER BY id")

  expect_equal(nrow(res), 3)
  expect_equal(res$id, 1:3)
  expect_true(is.list(res$geom))
  expect_true(is.raw(res$geom[[1]]))
  expect_true(is.raw(res$geom[[2]]))
  expect_null(res$geom[[3]])

  # WKB: byte order (01=LE) + type (01000000=Point) + x + y
  wkb <- res$geom[[1]]
  expect_equal(wkb[1], as.raw(0x01)) # little-endian
  expect_equal(wkb[2:5], as.raw(c(0x01, 0x00, 0x00, 0x00))) # Point type
})

test_that("geometry columns can be read inline", {
  con <- local_con()

  res <- dbGetQuery(con, "SELECT 'POINT (1 2)'::GEOMETRY AS geom")

  expect_true(is.list(res$geom))
  expect_true(is.raw(res$geom[[1]]))
})

test_that("geometry = 'blob' returns raw vectors", {
  con <- local_con(geometry = "blob")

  dbExecute(con, "CREATE TABLE test_geom (id INTEGER, geom GEOMETRY)")
  dbExecute(con, "INSERT INTO test_geom VALUES (1, 'POINT (1 2)'::GEOMETRY)")

  res <- dbGetQuery(con, "SELECT * FROM test_geom")
  expect_true(is.list(res$geom))
  expect_true(is.raw(res$geom[[1]]))
})

test_that("geometry = 'wk' returns wk_wkb objects", {
  skip_if_not_installed("wk")

  con <- local_con(geometry = "wk")

  dbExecute(con, "CREATE TABLE test_geom (id INTEGER, geom GEOMETRY)")
  dbExecute(con, "INSERT INTO test_geom VALUES (1, 'POINT (1 2)'::GEOMETRY)")
  dbExecute(con, "INSERT INTO test_geom VALUES (2, 'POINT (3 4)'::GEOMETRY)")
  dbExecute(con, "INSERT INTO test_geom VALUES (3, NULL)")

  res <- dbGetQuery(con, "SELECT * FROM test_geom ORDER BY id")

  expect_equal(nrow(res), 3)
  expect_s3_class(res$geom, "wk_wkb")
  expect_equal(length(res$geom), 3)

  # Validate coordinates via wk
  coords <- wk::wk_coords(res$geom)
  expect_equal(coords$x[1], 1)
  expect_equal(coords$y[1], 2)
  expect_equal(coords$x[2], 3)
  expect_equal(coords$y[2], 4)

  # NULL becomes NA in wk
  expect_true(is.na(res$geom[3]))
})

test_that("geometry = 'wk' works end-to-end with sf", {
  skip_if_not_installed("wk")
  skip_if_not_installed("sf")

  con <- local_con(geometry = "wk")

  dbExecute(con, "CREATE TABLE test_geom (id INTEGER, geom GEOMETRY)")
  dbExecute(con, "INSERT INTO test_geom VALUES (1, 'POINT (1 2)'::GEOMETRY)")
  dbExecute(con, "INSERT INTO test_geom VALUES (2, 'POINT (3 4)'::GEOMETRY)")
  dbExecute(con, "INSERT INTO test_geom VALUES (3, NULL)")

  res <- dbGetQuery(con, "SELECT * FROM test_geom ORDER BY id")

  # Convert wk_wkb to sfc
  sfc <- sf::st_as_sfc(res$geom)

  expect_s3_class(sfc, "sfc")
  expect_equal(length(sfc), 3)

  # Validate coordinates via sf
  coords <- sf::st_coordinates(sfc[1:2])
  expect_equal(unname(coords[1, "X"]), 1)
  expect_equal(unname(coords[1, "Y"]), 2)
  expect_equal(unname(coords[2, "X"]), 3)
  expect_equal(unname(coords[2, "Y"]), 4)

  # NULL becomes empty geometry in sf
  expect_true(sf::st_is_empty(sfc[3]))
})

test_that("invalid geometry option is rejected", {
  expect_error(local_con(geometry = "invalid"), "geometry")
})

Try the duckdb package in your browser

Any scripts or data that you put into this service are public.

duckdb documentation built on March 26, 2026, 9:06 a.m.