
@testset "Appender Error" begin
    db = DBInterface.connect(DuckDB.DB)
    con = DBInterface.connect(db)

    @test_throws DuckDB.QueryException DuckDB.Appender(db, "nonexistanttable")
    @test_throws DuckDB.QueryException DuckDB.Appender(con, "t")
end

@testset "Appender Usage - Schema $(schema_provided ? "Provided" : "Not Provided")" for schema_provided in (false, true)
    db = DBInterface.connect(DuckDB.DB)

    table_name = "integers"
    if schema_provided
        schema_name = "test"
        full_table_name = "$(schema_name).$(table_name)"
        DBInterface.execute(db, "CREATE SCHEMA $(schema_name)")
    else
        schema_name = nothing
        full_table_name = table_name
    end

    DBInterface.execute(db, "CREATE TABLE $(full_table_name)(i INTEGER)")

    appender = DuckDB.Appender(db, table_name, schema_name)
    DuckDB.close(appender)
    DuckDB.close(appender)

    # close!
    appender = DuckDB.Appender(db, table_name, schema_name)
    DBInterface.close!(appender)

    appender = DuckDB.Appender(db, table_name, schema_name)
    for i in 0:9
        DuckDB.append(appender, i)
        DuckDB.end_row(appender)
    end
    DuckDB.flush(appender)
    DuckDB.close(appender)

    results = DBInterface.execute(db, "SELECT * FROM $(full_table_name)")
    df = DataFrame(results)
    @test names(df) == ["i"]
    @test size(df, 1) == 10
    @test df.i == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    # close the database 
    DuckDB.close(appender)
end

@testset "Appender API" begin
    # Open the database
    db = DBInterface.connect(DuckDB.DB)

    uuid = Base.UUID("a36a5689-48ec-4104-b147-9fed600d8250")

    # Test data for the appender api test 
    #   - `col_name`: DuckDB column name
    #   - `duck_type`: DuckDB column type 
    #   - `append_value`: Value to insert via DuckDB.append
    #   - `ref_value`: (optional) Expected value from querying the DuckDB table. If not provided, uses `append_value`
    test_data = [
        (; col_name = :bool, duck_type = "BOOLEAN", append_value = true, ref_value = true),
        (; col_name = :tint, duck_type = "TINYINT", append_value = -1, ref_value = Int8(-1)),
        (; col_name = :sint, duck_type = "SMALLINT", append_value = -2, ref_value = Int16(-2)),
        (; col_name = :int, duck_type = "INTEGER", append_value = -3, ref_value = Int32(-3)),
        (; col_name = :bint, duck_type = "BIGINT", append_value = -4, ref_value = Int64(-4)),
        (; col_name = :hint, duck_type = "HUGEINT", append_value = Int128(-5), ref_value = Int128(-5)),
        (; col_name = :utint, duck_type = "UTINYINT", append_value = 1, ref_value = UInt8(1)),
        (; col_name = :usint, duck_type = "USMALLINT", append_value = 2, ref_value = UInt16(2)),
        (; col_name = :uint, duck_type = "UINTEGER", append_value = 3, ref_value = UInt32(3)),
        (; col_name = :ubint, duck_type = "UBIGINT", append_value = 4, ref_value = UInt64(4)),
        (; col_name = :uhint, duck_type = "UHUGEINT", append_value = UInt128(5), ref_value = UInt128(5)),
        (; col_name = :dec16, duck_type = "DECIMAL(4,2)", append_value = FixedDecimal{Int16, 2}(1.01)),
        (; col_name = :dec32, duck_type = "DECIMAL(9,2)", append_value = FixedDecimal{Int32, 2}(1.02)),
        (; col_name = :dec64, duck_type = "DECIMAL(18,2)", append_value = FixedDecimal{Int64, 2}(1.03)),
        (; col_name = :dec128, duck_type = "DECIMAL(38,2)", append_value = FixedDecimal{Int128, 2}(1.04)),
        (; col_name = :float, duck_type = "FLOAT", append_value = 1.0, ref_value = Float32(1.0)),
        (; col_name = :double, duck_type = "DOUBLE", append_value = 2.0, ref_value = Float64(2.0)),
        (; col_name = :date, duck_type = "DATE", append_value = Dates.Date("1970-04-11")),
        (; col_name = :time, duck_type = "TIME", append_value = Dates.Time(0, 0, 0, 0, 200)),
        (; col_name = :timestamp, duck_type = "TIMESTAMP", append_value = Dates.DateTime("1970-01-02T01:23:45.678")),
        (; col_name = :missingval, duck_type = "INTEGER", append_value = missing),
        (; col_name = :nothingval, duck_type = "INTEGER", append_value = nothing, ref_value = missing),
        (; col_name = :largeval, duck_type = "INTEGER", append_value = Int32(2^16)),
        (; col_name = :uuid, duck_type = "UUID", append_value = uuid),
        (; col_name = :varchar, duck_type = "VARCHAR", append_value = "Foo"),
        # lists
        (; col_name = :list_bool, duck_type = "BOOLEAN[]", append_value = Vector{Bool}([true, false, true])),
        (; col_name = :list_int8, duck_type = "TINYINT[]", append_value = Vector{Int8}([1, -2, 3])),
        (; col_name = :list_int16, duck_type = "SMALLINT[]", append_value = Vector{Int16}([1, -2, 3])),
        (; col_name = :list_int32, duck_type = "INTEGER[]", append_value = Vector{Int32}([1, -2, 3])),
        (; col_name = :list_int64, duck_type = "BIGINT[]", append_value = Vector{Int64}([1, -2, 3])),
        (;
            col_name = :list_int128,
            duck_type = "HUGEINT[]",
            append_value = Vector{Int128}([Int128(1), Int128(-2), Int128(3)])
        ),
        # (; col_name = :list_uint8, duck_type = "UTINYINT[]", append_value = Vector{UInt8}([1, 2, 3])),
        (; col_name = :list_uint16, duck_type = "USMALLINT[]", append_value = Vector{UInt16}([1, 2, 3])),
        (; col_name = :list_uint32, duck_type = "UINTEGER[]", append_value = Vector{UInt32}([1, 2, 3])),
        (; col_name = :list_uint64, duck_type = "UBIGINT[]", append_value = Vector{UInt64}([1, 2, 3])),
        (;
            col_name = :list_uint128,
            duck_type = "UHUGEINT[]",
            append_value = Vector{UInt128}([UInt128(1), UInt128(2), UInt128(3)])
        ),
        (; col_name = :list_float, duck_type = "FLOAT[]", append_value = Vector{Float32}([1.0, 2.0, 3.0])),
        (; col_name = :list_double, duck_type = "DOUBLE[]", append_value = Vector{Float64}([1.0, 2.0, 3.0])),
        (; col_name = :list_string, duck_type = "VARCHAR[]", append_value = Vector{String}(["a", "bb", "ccc"])),
        (;
            col_name = :list_date,
            duck_type = "DATE[]",
            append_value = Vector{Dates.Date}([
                Dates.Date("1970-01-01"),
                Dates.Date("1970-01-02"),
                Dates.Date("1970-01-03")
            ])
        ),
        (;
            col_name = :list_time,
            duck_type = "TIME[]",
            append_value = Vector{Dates.Time}([Dates.Time(1), Dates.Time(1, 2), Dates.Time(1, 2, 3)])
        ),
        (;
            col_name = :list_timestamp,
            duck_type = "TIMESTAMP[]",
            append_value = Vector{Dates.DateTime}([
                Dates.DateTime("1970-01-01T00:00:00"),
                Dates.DateTime("1970-01-02T00:00:00"),
                Dates.DateTime("1970-01-03T00:00:00")
            ])
        )
    ]

    sql = """CREATE TABLE dtypes(
               $(join(("$(row.col_name) $(row.duck_type)" for row in test_data), ",\n"))
            )"""
    DuckDB.execute(db, sql)
    appender = DuckDB.Appender(db, "dtypes")
    for row in test_data
        DuckDB.append(appender, row.append_value)
    end
    # End the row of the appender
    DuckDB.end_row(appender)
    # Destroy the appender and flush the data
    DuckDB.flush(appender)
    DuckDB.close(appender)

    results = DBInterface.execute(db, "select * from dtypes;")
    df = DataFrame(results)
    for row in test_data
        ref_value = get(row, :ref_value, row.append_value)
        @test isequal(df[!, row.col_name], [ref_value])
    end

    # close the database 
    DBInterface.close!(db)
end
