test_that("structs can be read", {
  skip_if_not_installed("vctrs")

  con <- dbConnect(duckdb())
  on.exit(dbDisconnect(con, shutdown = TRUE))

  res <- dbGetQuery(con, "SELECT {'x': 100, 'y': 'hello', 'z': 3.14} AS s")
  expect_equal(res, vctrs::data_frame(
    s = vctrs::data_frame(x = 100L, y = "hello", z = 3.14)
  ))

  res <- dbGetQuery(con, "SELECT 1 AS n, {'x': 100, 'y': 'hello', 'z': 3.14} AS s")
  expect_equal(res, vctrs::data_frame(
    n = 1L,
    s = vctrs::data_frame(x = 100L, y = "hello", z = 3.14)
  ))

  res <- dbGetQuery(con, "values (100, {'x': 100}), (200, {'x': 200}), (300, NULL)")
  expect_equal(res, vctrs::data_frame(
    col0 = c(100L, 200L, 300L),
    col1 = vctrs::data_frame(x = c(100L, 200L, NA))
  ))

  res <- dbGetQuery(con, "values ('a', {'x': 100, 'y': {'a': 1, 'b': 2}}), ('b', {'x': 200, y: NULL}), ('c', NULL)")
  expect_equal(res, vctrs::data_frame(
    col0 = c("a", "b", "c"),
    col1 = vctrs::data_frame(
      x = c(100L, 200L, NA),
      y = vctrs::data_frame(a = c(1L, NA, NA), b = c(2L, NA, NA))
    )
  ))

  res <- dbGetQuery(con, "select 100 AS other, [{'x': 1, 'y': 'a'}, {'x': 2, 'y': 'b'}] AS s")
  expect_equal(res, vctrs::data_frame(
    other = 100L,
    s = list(
      vctrs::data_frame(x = c(1L, 2L), y = c("a", "b"))
    )
  ))

  res <- dbGetQuery(con, "values ([{'x': 1, 'y': 'a'}, {'x': 2, 'y': 'b'}]), ([]), ([{'x': 1, 'y': 'a'}])")
  expect_equal(res, vctrs::data_frame(
    col0 = list(
      vctrs::data_frame(x = c(1L, 2L), y = c("a", "b")),
      vctrs::data_frame(x = integer(0), y = character(0)),
      vctrs::data_frame(x = 1L, y = "a")
    )
  ))
})

test_that("structs give the same results via Arrow", {
  skip_on_cran()
  skip_if_not_installed("vctrs")
  skip_if_not_installed("tibble")
  skip_if_not_installed("arrow", "13.0.0")

  con <- dbConnect(duckdb())
  on.exit(dbDisconnect(con, shutdown = TRUE))

  res <- dbGetQuery(con, "SELECT {'x': 100, 'y': 'hello', 'z': 3.14::double} AS s", arrow = TRUE)
  expect_equal(res, vctrs::data_frame(
    s = tibble::tibble(x = 100L, y = "hello", z = 3.14)
  ))

  res <- dbGetQuery(con, "SELECT 1 AS n, {'x': 100, 'y': 'hello', 'z': 3.14::double} AS s", arrow = TRUE)
  expect_equal(res, vctrs::data_frame(
    n = 1L,
    s = tibble::tibble(x = 100L, y = "hello", z = 3.14)
  ))

  res <- dbGetQuery(con, "values (100, {'x': 100}), (200, {'x': 200}), (300, NULL)", arrow = TRUE)
  expect_equal(res, vctrs::data_frame(
    col0 = c(100L, 200L, 300L),
    col1 = tibble::tibble(x = c(100L, 200L, NA))
  ))

  res <- dbGetQuery(con, "values ('a', {'x': 100, 'y': {'a': 1, 'b': 2}}), ('b', {'x': 200, y: NULL}), ('c', NULL)", arrow = TRUE)
  expect_equal(res, vctrs::data_frame(
    col0 = c("a", "b", "c"),
    col1 = tibble::tibble(
      x = c(100L, 200L, NA),
      y = tibble::tibble(a = c(1L, NA, NA), b = c(2L, NA, NA))
    )
  ))

  res <- dbGetQuery(con, "select 100 AS other, [{'x': 1, 'y': 'a'}, {'x': 2, 'y': 'b'}] AS s", arrow = TRUE)
  expect_equal(res, data.frame(
    other = 100L,
    s = vctrs::new_list_of(
      list(
        tibble::tibble(x = c(1L, 2L), y = c("a", "b"))
      ),
      ptype = tibble::tibble(x = integer(), y = character()),
      class = "arrow_list"
    )
  ))

  res <- dbGetQuery(con, "values ([{'x': 1, 'y': 'a'}, {'x': 2, 'y': 'b'}]), ([]), ([{'x': 1, 'y': 'a'}])", arrow = TRUE)
  expect_equal(res, data.frame(
    col0 = vctrs::new_list_of(
      list(
        tibble::tibble(x = c(1L, 2L), y = c("a", "b")),
        tibble::tibble(x = integer(0), y = character(0)),
        tibble::tibble(x = 1L, y = "a")
      ),
      ptype = tibble::tibble(x = integer(), y = character()),
      class = "arrow_list"
    )
  ))
})

test_that("nested lists of atomic values can be written", {
  skip_if_not(TEST_RE2)

  skip_if_not_installed("vctrs")

  con <- dbConnect(duckdb())
  on.exit(dbDisconnect(con, shutdown = TRUE))

  df <- vctrs::data_frame(a = 1:3, b = list(4:6, 2:3, 1L))
  dbWriteTable(con, "df", df)
  expect_equal(dbReadTable(con, "df"), df)

  duckdb_register(con, "df_reg", df)
  expect_equal(dbReadTable(con, "df_reg"), df)

  df2 <- vctrs::data_frame(a = 1:2, b = list(4:6, letters[2:3]))
  expect_error(dbWriteTable(con, "df2", df2), "register")
  expect_error(duckdb_register(con, "df2_reg", df2), "register")
})

test_that("nested and packed columns work in full", {
  skip_if_not(TEST_RE2)

  skip_if_not_installed("vctrs")

  con <- dbConnect(duckdb())
  on.exit(dbDisconnect(con, shutdown = TRUE))

  df <- vctrs::data_frame(
    a = vctrs::data_frame(
      x = 1:5,
      y = as.numeric(6:10),
      z = vctrs::data_frame(
        k = c(TRUE, FALSE, NA, TRUE, FALSE),
        l = letters[1:5]
      )
    ),
    b = list(
      vctrs::data_frame(
        u = structure(as.numeric(19577:19578), class = "Date"),
        v = structure(19577:19578, class = "Date"),
        w = structure(1691507820, class = c("POSIXct", "POSIXt"), tzone = "UTC") + 0:1
      )
    ),
    c = list(
      vctrs::data_frame(
        # TIME_MINUTES, TIME_MINUTES_INTEGER, TIME_HOURS, ... etc.: Loss ok
        d = structure(as.numeric(13:16), class = "difftime", units = "secs"),
        e = vctrs::data_frame(
          u = structure(17:20, class = "difftime", units = "secs"),
          v = 5:8,
          w = as.list(9:12)
        )
      )
    ),
    f = list(
      vctrs::data_frame(
        g = list(as.raw(13), as.raw(14:15), as.raw(16:18), as.raw(19:22)),
        h = vctrs::data_frame(u = 1:4, v = 5:8, w = list(vctrs::data_frame(s = 9:10)))
      )
    ),
    i = "plain old"
  )
  dbWriteTable(con, "df", df)
  expect_equal(dbReadTable(con, "df"), df)

  duckdb_register(con, "df_reg", df)
  expect_equal(dbReadTable(con, "df_reg"), df)
})
