[
  {
    "path": ".gitignore",
    "content": ".DS_Store\nzig-out/\n.zig-cache/\nlib/libduckdb.dylib\nzig-pkg/\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"zig.testArgs\": [\"build\", \"test\", \"-Dtest-filter=${filter}\"]\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2024 Karl Seguin.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "F=\n.PHONY: t\nt:\n\tTEST_FILTER=\"${F}\" zig build -Dsystem_libduckdb=false test --summary all -freference-trace\n"
  },
  {
    "path": "build.zig",
    "content": "const std = @import(\"std\");\n\nconst LazyPath = std.Build.LazyPath;\n\npub fn build(b: *std.Build) !void {\n    const target = b.standardTargetOptions(.{});\n    const optimize = b.standardOptimizeOption(.{});\n    const system_libduckdb = b.option(bool, \"system_libduckdb\", \"link with system libduckdb\") orelse true;\n    const debug_duckdb = b.option(bool, \"debug_duckdb\", \"compile duckdb with DUCKDB_DEBUG_STACKTRACE\") orelse false;\n\n    const zuckdb = b.addModule(\"zuckdb\", .{\n        .root_source_file = b.path(\"src/zuckdb.zig\"),\n        .target = target,\n        .optimize = optimize,\n    });\n\n    const c_header: LazyPath, const lib = blk: {\n        if (system_libduckdb) {\n            const lib_path = b.path(\"lib\");\n            zuckdb.addIncludePath(lib_path);\n            zuckdb.linkSystemLibrary(\"duckdb\", .{});\n            break :blk .{ b.path(\"lib/duckdb.h\"), null };\n        } else {\n            const c_dep = b.lazyDependency(\"duckdb\", .{}) orelse return;\n            const root_module = b.createModule(.{\n                .target = target,\n                .optimize = optimize,\n                .link_libcpp = true,\n            });\n\n            const c_lib = b.addLibrary(.{\n                .name = \"duckdb\",\n                .root_module = root_module,\n            });\n\n            root_module.addIncludePath(c_dep.path(\"\"));\n            root_module.addCSourceFiles(.{\n                .files = &.{\"duckdb.cpp\"},\n                .root = c_dep.path(\"\"),\n                .flags = &.{\"-Wno-date-time\"},\n            });\n            if (debug_duckdb) {\n                root_module.addCMacro(\"DUCKDB_DEBUG_STACKTRACE\", \"\");\n            }\n            root_module.addCMacro(\"DUCKDB_STATIC_BUILD\", \"\");\n            // json tests fail because extension loading does not work\n            // on the self built version. TODO: statically link core extensions:\n            // c_lib.root_module.addCMacro(\"DUCKDB_EXTENSION_JSON_LINKED\", \"true\");\n            b.installArtifact(c_lib);\n            b.default_step.dependOn(&b.addInstallHeaderFile(\n                c_dep.path(\"duckdb.h\"),\n                \"duckdb.h\",\n            ).step);\n\n            zuckdb.linkLibrary(c_lib);\n            break :blk .{ c_dep.path(\"duckdb.h\"), c_lib };\n        }\n    };\n\n    zuckdb.addImport(\"duckdb_clib\", b.addTranslateC(.{\n        .root_source_file = c_header,\n        .target = target,\n        .optimize = optimize,\n    }).createModule());\n\n    {\n        // Setup Tests\n        const lib_test = b.addTest(.{\n            .root_module = zuckdb,\n            .test_runner = .{ .path = b.path(\"test_runner.zig\"), .mode = .simple },\n        });\n        lib_test.root_module.addIncludePath(b.path(\"lib\"));\n        lib_test.root_module.addLibraryPath(b.path(\"lib\"));\n        _ = lib;\n        // if (system_libduckdb) {\n        //     lib_test.root_module.linkSystemLibrary(\"duckdb\", .{});\n        // } else {\n        //     lib_test.root_module.linkLibrary(lib.?);\n        // }\n\n        const run_test = b.addRunArtifact(lib_test);\n        run_test.has_side_effects = true;\n\n        const test_step = b.step(\"test\", \"Run unit tests\");\n        test_step.dependOn(&run_test.step);\n    }\n}\n"
  },
  {
    "path": "build.zig.zon",
    "content": ".{\n    .name = .zuckdb,\n    .paths = .{\n        \"LICENSE\",\n        \"build.zig\",\n        \"build.zig.zon\",\n        \"lib\",\n        \"readme.md\",\n        \"src\",\n    },\n    .version = \"0.0.0\",\n    .fingerprint = 0xe57532405497940e,\n    .dependencies = .{\n        .duckdb = .{\n            .lazy = true,\n            .url = \"https://github.com/duckdb/duckdb/releases/download/v1.5.2/libduckdb-src.zip\",\n            .hash = \"N-V-__8AAP82qgEMw9wCzbQ-rD5uvO-o3X6PyzWA3zsoeIIS\",\n        },\n    },\n}\n"
  },
  {
    "path": "lib/duckdb.h",
    "content": "//===----------------------------------------------------------------------===//\n//\n//                         DuckDB\n//\n// duckdb.h\n//\n//\n//===----------------------------------------------------------------------===//\n//\n// !!!!!!!\n// WARNING: this file is autogenerated by scripts/generate_c_api.py, manual changes will be overwritten\n// !!!!!!!\n\n#pragma once\n\n#ifndef DUCKDB_C_API\n#ifdef _WIN32\n#ifdef DUCKDB_STATIC_BUILD\n#define DUCKDB_C_API\n#else\n#if defined(DUCKDB_BUILD_LIBRARY) && !defined(DUCKDB_BUILD_LOADABLE_EXTENSION)\n#define DUCKDB_C_API __declspec(dllexport)\n#else\n#define DUCKDB_C_API __declspec(dllimport)\n#endif\n#endif\n#else\n#define DUCKDB_C_API\n#endif\n#endif\n\n//! duplicate of duckdb/main/winapi.hpp\n#ifndef DUCKDB_EXTENSION_API\n#ifdef _WIN32\n#ifdef DUCKDB_STATIC_BUILD\n#define DUCKDB_EXTENSION_API\n#else\n#define DUCKDB_EXTENSION_API __declspec(dllexport)\n#endif\n#else\n#define DUCKDB_EXTENSION_API __attribute__((visibility(\"default\")))\n#endif\n#endif\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n//===--------------------------------------------------------------------===//\n// Enums\n//===--------------------------------------------------------------------===//\n\n//! WARNING: The numbers of these enums should not be changed, as changing the numbers breaks ABI compatibility.\n//! Always add enums at the END of the enum\n\n//! An enum over DuckDB's internal types.\ntypedef enum DUCKDB_TYPE {\n\tDUCKDB_TYPE_INVALID = 0,\n\t// bool\n\tDUCKDB_TYPE_BOOLEAN = 1,\n\t// int8_t\n\tDUCKDB_TYPE_TINYINT = 2,\n\t// int16_t\n\tDUCKDB_TYPE_SMALLINT = 3,\n\t// int32_t\n\tDUCKDB_TYPE_INTEGER = 4,\n\t// int64_t\n\tDUCKDB_TYPE_BIGINT = 5,\n\t// uint8_t\n\tDUCKDB_TYPE_UTINYINT = 6,\n\t// uint16_t\n\tDUCKDB_TYPE_USMALLINT = 7,\n\t// uint32_t\n\tDUCKDB_TYPE_UINTEGER = 8,\n\t// uint64_t\n\tDUCKDB_TYPE_UBIGINT = 9,\n\t// float\n\tDUCKDB_TYPE_FLOAT = 10,\n\t// double\n\tDUCKDB_TYPE_DOUBLE = 11,\n\t// duckdb_timestamp (microseconds)\n\tDUCKDB_TYPE_TIMESTAMP = 12,\n\t// duckdb_date\n\tDUCKDB_TYPE_DATE = 13,\n\t// duckdb_time\n\tDUCKDB_TYPE_TIME = 14,\n\t// duckdb_interval\n\tDUCKDB_TYPE_INTERVAL = 15,\n\t// duckdb_hugeint\n\tDUCKDB_TYPE_HUGEINT = 16,\n\t// duckdb_uhugeint\n\tDUCKDB_TYPE_UHUGEINT = 32,\n\t// const char*\n\tDUCKDB_TYPE_VARCHAR = 17,\n\t// duckdb_blob\n\tDUCKDB_TYPE_BLOB = 18,\n\t// duckdb_decimal\n\tDUCKDB_TYPE_DECIMAL = 19,\n\t// duckdb_timestamp_s (seconds)\n\tDUCKDB_TYPE_TIMESTAMP_S = 20,\n\t// duckdb_timestamp_ms (milliseconds)\n\tDUCKDB_TYPE_TIMESTAMP_MS = 21,\n\t// duckdb_timestamp_ns (nanoseconds)\n\tDUCKDB_TYPE_TIMESTAMP_NS = 22,\n\t// enum type, only useful as logical type\n\tDUCKDB_TYPE_ENUM = 23,\n\t// list type, only useful as logical type\n\tDUCKDB_TYPE_LIST = 24,\n\t// struct type, only useful as logical type\n\tDUCKDB_TYPE_STRUCT = 25,\n\t// map type, only useful as logical type\n\tDUCKDB_TYPE_MAP = 26,\n\t// duckdb_array, only useful as logical type\n\tDUCKDB_TYPE_ARRAY = 33,\n\t// duckdb_hugeint\n\tDUCKDB_TYPE_UUID = 27,\n\t// union type, only useful as logical type\n\tDUCKDB_TYPE_UNION = 28,\n\t// duckdb_bit\n\tDUCKDB_TYPE_BIT = 29,\n\t// duckdb_time_tz\n\tDUCKDB_TYPE_TIME_TZ = 30,\n\t// duckdb_timestamp (microseconds)\n\tDUCKDB_TYPE_TIMESTAMP_TZ = 31,\n\t// enum type, only useful as logical type\n\tDUCKDB_TYPE_ANY = 34,\n\t// duckdb_bignum\n\tDUCKDB_TYPE_BIGNUM = 35,\n\t// enum type, only useful as logical type\n\tDUCKDB_TYPE_SQLNULL = 36,\n\t// enum type, only useful as logical type\n\tDUCKDB_TYPE_STRING_LITERAL = 37,\n\t// enum type, only useful as logical type\n\tDUCKDB_TYPE_INTEGER_LITERAL = 38,\n\t// duckdb_time_ns (nanoseconds)\n\tDUCKDB_TYPE_TIME_NS = 39,\n\t// GEOMETRY type, WKB blob\n\tDUCKDB_TYPE_GEOMETRY = 40,\n} duckdb_type;\n\n//! An enum over the returned state of different functions.\ntypedef enum duckdb_state { DuckDBSuccess = 0, DuckDBError = 1 } duckdb_state;\n\n//! An enum over the pending state of a pending query result.\ntypedef enum duckdb_pending_state {\n\tDUCKDB_PENDING_RESULT_READY = 0,\n\tDUCKDB_PENDING_RESULT_NOT_READY = 1,\n\tDUCKDB_PENDING_ERROR = 2,\n\tDUCKDB_PENDING_NO_TASKS_AVAILABLE = 3\n} duckdb_pending_state;\n\n//! An enum over DuckDB's different result types.\ntypedef enum duckdb_result_type {\n\tDUCKDB_RESULT_TYPE_INVALID = 0,\n\tDUCKDB_RESULT_TYPE_CHANGED_ROWS = 1,\n\tDUCKDB_RESULT_TYPE_NOTHING = 2,\n\tDUCKDB_RESULT_TYPE_QUERY_RESULT = 3,\n} duckdb_result_type;\n\n//! An enum over DuckDB's different statement types.\ntypedef enum duckdb_statement_type {\n\tDUCKDB_STATEMENT_TYPE_INVALID = 0,\n\tDUCKDB_STATEMENT_TYPE_SELECT = 1,\n\tDUCKDB_STATEMENT_TYPE_INSERT = 2,\n\tDUCKDB_STATEMENT_TYPE_UPDATE = 3,\n\tDUCKDB_STATEMENT_TYPE_EXPLAIN = 4,\n\tDUCKDB_STATEMENT_TYPE_DELETE = 5,\n\tDUCKDB_STATEMENT_TYPE_PREPARE = 6,\n\tDUCKDB_STATEMENT_TYPE_CREATE = 7,\n\tDUCKDB_STATEMENT_TYPE_EXECUTE = 8,\n\tDUCKDB_STATEMENT_TYPE_ALTER = 9,\n\tDUCKDB_STATEMENT_TYPE_TRANSACTION = 10,\n\tDUCKDB_STATEMENT_TYPE_COPY = 11,\n\tDUCKDB_STATEMENT_TYPE_ANALYZE = 12,\n\tDUCKDB_STATEMENT_TYPE_VARIABLE_SET = 13,\n\tDUCKDB_STATEMENT_TYPE_CREATE_FUNC = 14,\n\tDUCKDB_STATEMENT_TYPE_DROP = 15,\n\tDUCKDB_STATEMENT_TYPE_EXPORT = 16,\n\tDUCKDB_STATEMENT_TYPE_PRAGMA = 17,\n\tDUCKDB_STATEMENT_TYPE_VACUUM = 18,\n\tDUCKDB_STATEMENT_TYPE_CALL = 19,\n\tDUCKDB_STATEMENT_TYPE_SET = 20,\n\tDUCKDB_STATEMENT_TYPE_LOAD = 21,\n\tDUCKDB_STATEMENT_TYPE_RELATION = 22,\n\tDUCKDB_STATEMENT_TYPE_EXTENSION = 23,\n\tDUCKDB_STATEMENT_TYPE_LOGICAL_PLAN = 24,\n\tDUCKDB_STATEMENT_TYPE_ATTACH = 25,\n\tDUCKDB_STATEMENT_TYPE_DETACH = 26,\n\tDUCKDB_STATEMENT_TYPE_MULTI = 27,\n} duckdb_statement_type;\n\n//! An enum over DuckDB's different error types.\ntypedef enum duckdb_error_type {\n\tDUCKDB_ERROR_INVALID = 0,\n\tDUCKDB_ERROR_OUT_OF_RANGE = 1,\n\tDUCKDB_ERROR_CONVERSION = 2,\n\tDUCKDB_ERROR_UNKNOWN_TYPE = 3,\n\tDUCKDB_ERROR_DECIMAL = 4,\n\tDUCKDB_ERROR_MISMATCH_TYPE = 5,\n\tDUCKDB_ERROR_DIVIDE_BY_ZERO = 6,\n\tDUCKDB_ERROR_OBJECT_SIZE = 7,\n\tDUCKDB_ERROR_INVALID_TYPE = 8,\n\tDUCKDB_ERROR_SERIALIZATION = 9,\n\tDUCKDB_ERROR_TRANSACTION = 10,\n\tDUCKDB_ERROR_NOT_IMPLEMENTED = 11,\n\tDUCKDB_ERROR_EXPRESSION = 12,\n\tDUCKDB_ERROR_CATALOG = 13,\n\tDUCKDB_ERROR_PARSER = 14,\n\tDUCKDB_ERROR_PLANNER = 15,\n\tDUCKDB_ERROR_SCHEDULER = 16,\n\tDUCKDB_ERROR_EXECUTOR = 17,\n\tDUCKDB_ERROR_CONSTRAINT = 18,\n\tDUCKDB_ERROR_INDEX = 19,\n\tDUCKDB_ERROR_STAT = 20,\n\tDUCKDB_ERROR_CONNECTION = 21,\n\tDUCKDB_ERROR_SYNTAX = 22,\n\tDUCKDB_ERROR_SETTINGS = 23,\n\tDUCKDB_ERROR_BINDER = 24,\n\tDUCKDB_ERROR_NETWORK = 25,\n\tDUCKDB_ERROR_OPTIMIZER = 26,\n\tDUCKDB_ERROR_NULL_POINTER = 27,\n\tDUCKDB_ERROR_IO = 28,\n\tDUCKDB_ERROR_INTERRUPT = 29,\n\tDUCKDB_ERROR_FATAL = 30,\n\tDUCKDB_ERROR_INTERNAL = 31,\n\tDUCKDB_ERROR_INVALID_INPUT = 32,\n\tDUCKDB_ERROR_OUT_OF_MEMORY = 33,\n\tDUCKDB_ERROR_PERMISSION = 34,\n\tDUCKDB_ERROR_PARAMETER_NOT_RESOLVED = 35,\n\tDUCKDB_ERROR_PARAMETER_NOT_ALLOWED = 36,\n\tDUCKDB_ERROR_DEPENDENCY = 37,\n\tDUCKDB_ERROR_HTTP = 38,\n\tDUCKDB_ERROR_MISSING_EXTENSION = 39,\n\tDUCKDB_ERROR_AUTOLOAD = 40,\n\tDUCKDB_ERROR_SEQUENCE = 41,\n\tDUCKDB_INVALID_CONFIGURATION = 42\n} duckdb_error_type;\n\n//! An enum over DuckDB's different cast modes.\ntypedef enum duckdb_cast_mode { DUCKDB_CAST_NORMAL = 0, DUCKDB_CAST_TRY = 1 } duckdb_cast_mode;\n\ntypedef enum duckdb_file_flag {\n\tDUCKDB_FILE_FLAG_INVALID = 0,\n\t// Open the file with \"read\" capabilities.\n\tDUCKDB_FILE_FLAG_READ = 1,\n\t// Open the file with \"write\" capabilities.\n\tDUCKDB_FILE_FLAG_WRITE = 2,\n\t// Create a new file, or open if it already exists.\n\tDUCKDB_FILE_FLAG_CREATE = 3,\n\t// Create a new file, or fail if it already exists.\n\tDUCKDB_FILE_FLAG_CREATE_NEW = 4,\n\t// Open the file in \"append\" mode.\n\tDUCKDB_FILE_FLAG_APPEND = 5,\n} duckdb_file_flag;\n\n//! An enum over DuckDB's configuration option scopes.\n//! This enum can be used to specify the default scope when creating a custom configuration option,\n//! but it is also be used to determine the scope in which a configuration option is set when it is\n//! changed or retrieved.\ntypedef enum duckdb_config_option_scope {\n\tDUCKDB_CONFIG_OPTION_SCOPE_INVALID = 0,\n\t// The option is set for the duration of the current transaction only.\n\t// !! CURRENTLY NOT IMPLEMENTED !!\n\tDUCKDB_CONFIG_OPTION_SCOPE_LOCAL = 1,\n\t// The option is set for the current session/connection only.\n\tDUCKDB_CONFIG_OPTION_SCOPE_SESSION = 2,\n\t// Set the option globally for all sessions/connections.\n\tDUCKDB_CONFIG_OPTION_SCOPE_GLOBAL = 3,\n} duckdb_config_option_scope;\n\n//! An enum over DuckDB's catalog entry types.\ntypedef enum duckdb_catalog_entry_type {\n\tDUCKDB_CATALOG_ENTRY_TYPE_INVALID = 0,\n\tDUCKDB_CATALOG_ENTRY_TYPE_TABLE = 1,\n\tDUCKDB_CATALOG_ENTRY_TYPE_SCHEMA = 2,\n\tDUCKDB_CATALOG_ENTRY_TYPE_VIEW = 3,\n\tDUCKDB_CATALOG_ENTRY_TYPE_INDEX = 4,\n\tDUCKDB_CATALOG_ENTRY_TYPE_PREPARED_STATEMENT = 5,\n\tDUCKDB_CATALOG_ENTRY_TYPE_SEQUENCE = 6,\n\tDUCKDB_CATALOG_ENTRY_TYPE_COLLATION = 7,\n\tDUCKDB_CATALOG_ENTRY_TYPE_TYPE = 8,\n\tDUCKDB_CATALOG_ENTRY_TYPE_DATABASE = 9,\n} duckdb_catalog_entry_type;\n\n//===--------------------------------------------------------------------===//\n// General type definitions\n//===--------------------------------------------------------------------===//\n\n//! DuckDB's index type.\ntypedef uint64_t idx_t;\n\n//! Type definition for the data pointers of selection vectors.\ntypedef uint32_t sel_t;\n\n//! The callback to destroy data, e.g.,\n//! bind data (if any), init data (if any), extra data for replacement scans (if any), etc.\ntypedef void (*duckdb_delete_callback_t)(void *data);\n\n//! The callback to copy data, e.g., bind data (if any).\ntypedef void *(*duckdb_copy_callback_t)(void *data);\n\n//! Used for threading, contains a task state.\n//! Must be destroyed with `duckdb_destroy_task_state`.\ntypedef void *duckdb_task_state;\n\n//===--------------------------------------------------------------------===//\n// Types (no explicit freeing)\n//===--------------------------------------------------------------------===//\n\n//! DATE is stored as days since 1970-01-01.\n//! Use the `duckdb_from_date` and `duckdb_to_date` functions to extract individual information.\ntypedef struct {\n\tint32_t days;\n} duckdb_date;\n\ntypedef struct {\n\tint32_t year;\n\tint8_t month;\n\tint8_t day;\n} duckdb_date_struct;\n\n//! TIME is stored as microseconds since 00:00:00.\n//! Use the `duckdb_from_time` and `duckdb_to_time` functions to extract individual information.\ntypedef struct {\n\tint64_t micros;\n} duckdb_time;\n\ntypedef struct {\n\tint8_t hour;\n\tint8_t min;\n\tint8_t sec;\n\tint32_t micros;\n} duckdb_time_struct;\n\n//! TIME_NS is stored as nanoseconds since 00:00:00.\ntypedef struct {\n\tint64_t nanos;\n} duckdb_time_ns;\n\n//! TIME_TZ is stored as 40 bits for the int64_t microseconds, and 24 bits for the int32_t offset.\n//! Use the `duckdb_from_time_tz` function to extract individual information.\ntypedef struct {\n\tuint64_t bits;\n} duckdb_time_tz;\n\ntypedef struct {\n\tduckdb_time_struct time;\n\tint32_t offset;\n} duckdb_time_tz_struct;\n\n//! TIMESTAMP is stored as microseconds since 1970-01-01.\n//! Use the `duckdb_from_timestamp` and `duckdb_to_timestamp` functions to extract individual information.\ntypedef struct {\n\tint64_t micros;\n} duckdb_timestamp;\n\ntypedef struct {\n\tduckdb_date_struct date;\n\tduckdb_time_struct time;\n} duckdb_timestamp_struct;\n\n//! TIMESTAMP_S is stored as seconds since 1970-01-01.\ntypedef struct {\n\tint64_t seconds;\n} duckdb_timestamp_s;\n\n//! TIMESTAMP_MS is stored as milliseconds since 1970-01-01.\ntypedef struct {\n\tint64_t millis;\n} duckdb_timestamp_ms;\n\n//! TIMESTAMP_NS is stored as nanoseconds since 1970-01-01.\ntypedef struct {\n\tint64_t nanos;\n} duckdb_timestamp_ns;\n\n//! INTERVAL is stored in months, days, and micros.\ntypedef struct {\n\tint32_t months;\n\tint32_t days;\n\tint64_t micros;\n} duckdb_interval;\n\n//! HUGEINT is composed of a lower and upper component.\n//! Its value is upper * 2^64 + lower.\n//! For simplified usage, use `duckdb_hugeint_to_double` and `duckdb_double_to_hugeint`.\ntypedef struct {\n\tuint64_t lower;\n\tint64_t upper;\n} duckdb_hugeint;\n\n//! UHUGEINT is composed of a lower and upper component.\n//! Its value is upper * 2^64 + lower.\n//! For simplified usage, use `duckdb_uhugeint_to_double` and `duckdb_double_to_uhugeint`.\ntypedef struct {\n\tuint64_t lower;\n\tuint64_t upper;\n} duckdb_uhugeint;\n\n//! DECIMAL is composed of a width and a scale.\n//! Their value is stored in a HUGEINT.\ntypedef struct {\n\tuint8_t width;\n\tuint8_t scale;\n\tduckdb_hugeint value;\n} duckdb_decimal;\n\n//! A type holding information about the query execution progress.\ntypedef struct {\n\tdouble percentage;\n\tuint64_t rows_processed;\n\tuint64_t total_rows_to_process;\n} duckdb_query_progress_type;\n\n//! The internal representation of a VARCHAR (string_t). If the VARCHAR does not\n//! exceed 12 characters, then we inline it. Otherwise, we inline a four-byte prefix for faster\n//! string comparisons and store a pointer to the remaining characters. This is a non-\n//! owning structure, i.e., it does not have to be freed.\ntypedef struct {\n\tunion {\n\t\tstruct {\n\t\t\tuint32_t length;\n\t\t\tchar prefix[4];\n\t\t\tchar *ptr;\n\t\t} pointer;\n\t\tstruct {\n\t\t\tuint32_t length;\n\t\t\tchar inlined[12];\n\t\t} inlined;\n\t} value;\n} duckdb_string_t;\n\n//! DuckDB's LISTs are composed of a 'parent' vector holding metadata of each list,\n//! and a child vector holding the entries of the lists.\n//! The `duckdb_list_entry` struct contains the internal representation of a LIST metadata entry.\n//! A metadata entry contains the length of the list, and its offset in the child vector.\ntypedef struct {\n\tuint64_t offset;\n\tuint64_t length;\n} duckdb_list_entry;\n\n//! A column consists of a pointer to its internal data. Don't operate on this type directly.\n//! Instead, use functions such as `duckdb_column_data`, `duckdb_nullmask_data`,\n//! `duckdb_column_type`, and `duckdb_column_name`.\ntypedef struct {\n\t// Deprecated, use `duckdb_column_data`.\n\tvoid *deprecated_data;\n\t// Deprecated, use `duckdb_nullmask_data`.\n\tbool *deprecated_nullmask;\n\t// Deprecated, use `duckdb_column_type`.\n\tduckdb_type deprecated_type;\n\t// Deprecated, use `duckdb_column_name`.\n\tchar *deprecated_name;\n\tvoid *internal_data;\n} duckdb_column;\n\n//! 1. A standalone vector that must be destroyed, or\n//! 2. A vector to a column in a data chunk that lives as long as the data chunk lives.\ntypedef struct _duckdb_vector {\n\tvoid *internal_ptr;\n} * duckdb_vector;\n\n//! A selection vector is a vector of indices, which usually refer to values in a vector.\n//! Can be used to slice vectors, changing their length and the order of their entries.\n//! Standalone selection vectors must be destroyed.\ntypedef struct _duckdb_selection_vector {\n\tvoid *internal_ptr;\n} * duckdb_selection_vector;\n\n//===--------------------------------------------------------------------===//\n// Types (explicit freeing/destroying)\n//===--------------------------------------------------------------------===//\n\n//! Strings are composed of a `char` pointer and a size.\n//! You must free `string.data` with `duckdb_free`.\ntypedef struct {\n\tchar *data;\n\tidx_t size;\n} duckdb_string;\n\n//! BLOBs are composed of a byte pointer and a size.\n//! You must free `blob.data` with `duckdb_free`.\ntypedef struct {\n\tvoid *data;\n\tidx_t size;\n} duckdb_blob;\n\n//! BITs are composed of a byte pointer and a size.\n//! BIT byte data has 0 to 7 bits of padding.\n//! The first byte contains the number of padding bits.\n//! The padding bits of the second byte are set to 1, starting from the MSB.\n//! You must free `data` with `duckdb_free`.\ntypedef struct {\n\tuint8_t *data;\n\tidx_t size;\n} duckdb_bit;\n\n//! BIGNUMs are composed of a byte pointer, a size, and an `is_negative` bool.\n//! The absolute value of the number is stored in `data` in little endian format.\n//! You must free `data` with `duckdb_free`.\ntypedef struct {\n\tuint8_t *data;\n\tidx_t size;\n\tbool is_negative;\n} duckdb_bignum;\n\n//! A query result consists of a pointer to its internal data.\n//! Must be freed with 'duckdb_destroy_result'.\ntypedef struct {\n\t// Deprecated, use `duckdb_column_count`.\n\tidx_t deprecated_column_count;\n\t// Deprecated, use `duckdb_row_count`.\n\tidx_t deprecated_row_count;\n\t// Deprecated, use `duckdb_rows_changed`.\n\tidx_t deprecated_rows_changed;\n\t// Deprecated, use `duckdb_column_*`-family of functions.\n\tduckdb_column *deprecated_columns;\n\t// Deprecated, use `duckdb_result_error`.\n\tchar *deprecated_error_message;\n\tvoid *internal_data;\n} duckdb_result;\n\n//! A database instance cache object. Must be destroyed with `duckdb_destroy_instance_cache`.\ntypedef struct _duckdb_instance_cache {\n\tvoid *internal_ptr;\n} * duckdb_instance_cache;\n\n//! A database object. Must be closed with `duckdb_close`.\ntypedef struct _duckdb_database {\n\tvoid *internal_ptr;\n} * duckdb_database;\n\n//! A connection to a duckdb database. Must be closed with `duckdb_disconnect`.\ntypedef struct _duckdb_connection {\n\tvoid *internal_ptr;\n} * duckdb_connection;\n\n//! A client context of a duckdb connection. Must be destroyed with `duckdb_destroy_context`.\ntypedef struct _duckdb_client_context {\n\tvoid *internal_ptr;\n} * duckdb_client_context;\n\n//! A prepared statement is a parameterized query that allows you to bind parameters to it.\n//! Must be destroyed with `duckdb_destroy_prepare`.\ntypedef struct _duckdb_prepared_statement {\n\tvoid *internal_ptr;\n} * duckdb_prepared_statement;\n\n//! Extracted statements. Must be destroyed with `duckdb_destroy_extracted`.\ntypedef struct _duckdb_extracted_statements {\n\tvoid *internal_ptr;\n} * duckdb_extracted_statements;\n\n//! The pending result represents an intermediate structure for a query that is not yet fully executed.\n//! Must be destroyed with `duckdb_destroy_pending`.\ntypedef struct _duckdb_pending_result {\n\tvoid *internal_ptr;\n} * duckdb_pending_result;\n\n//! The appender enables fast data loading into DuckDB.\n//! Must be destroyed with `duckdb_appender_destroy`.\ntypedef struct _duckdb_appender {\n\tvoid *internal_ptr;\n} * duckdb_appender;\n\n//! The table description allows querying information about the table.\n//! Must be destroyed with `duckdb_table_description_destroy`.\ntypedef struct _duckdb_table_description {\n\tvoid *internal_ptr;\n} * duckdb_table_description;\n\n//! The configuration can be used to provide start-up options for a database.\n//! Must be destroyed with `duckdb_destroy_config`.\ntypedef struct _duckdb_config {\n\tvoid *internal_ptr;\n} * duckdb_config;\n\n//! A custom configuration option instance. Used to register custom options that can be set on a duckdb_config.\n//! or by the user in SQL using `SET <option_name> = <value>`.\ntypedef struct _duckdb_config_option {\n\tvoid *internal_ptr;\n} * duckdb_config_option;\n\n//! A logical type.\n//! Must be destroyed with `duckdb_destroy_logical_type`.\ntypedef struct _duckdb_logical_type {\n\tvoid *internal_ptr;\n} * duckdb_logical_type;\n\n//! Holds extra information to register a custom logical type.\n//! Reserved for future use.\ntypedef struct _duckdb_create_type_info {\n\tvoid *internal_ptr;\n} * duckdb_create_type_info;\n\n//! Contains a data chunk of a duckdb_result.\n//! Must be destroyed with `duckdb_destroy_data_chunk`.\ntypedef struct _duckdb_data_chunk {\n\tvoid *internal_ptr;\n} * duckdb_data_chunk;\n\n//! A value of a logical type.\n//! Must be destroyed with `duckdb_destroy_value`.\ntypedef struct _duckdb_value {\n\tvoid *internal_ptr;\n} * duckdb_value;\n\n//! Holds a recursive tree containing profiling metrics.\n//! The tree matches the query plan, and has a top-level node.\ntypedef struct _duckdb_profiling_info {\n\tvoid *internal_ptr;\n} * duckdb_profiling_info;\n\n//! Holds error data.\n//! Must be destroyed with `duckdb_destroy_error_data`.\ntypedef struct _duckdb_error_data {\n\tvoid *internal_ptr;\n} * duckdb_error_data;\n\n//! Holds a bound expression.\n//! Must be destroyed with `duckdb_destroy_expression`.\ntypedef struct _duckdb_expression {\n\tvoid *internal_ptr;\n} * duckdb_expression;\n\n//===--------------------------------------------------------------------===//\n// C API extension information\n//===--------------------------------------------------------------------===//\n\n//! Holds the state of the C API extension initialization process.\ntypedef struct _duckdb_extension_info {\n\tvoid *internal_ptr;\n} * duckdb_extension_info;\n\n//===--------------------------------------------------------------------===//\n// Function types\n//===--------------------------------------------------------------------===//\n\n//! Additional function info.\n//! When setting this info, it is necessary to pass a destroy-callback function.\ntypedef struct _duckdb_function_info {\n\tvoid *internal_ptr;\n} * duckdb_function_info;\n\n//! The bind info of a function.\n//! When setting this info, it is necessary to pass a destroy-callback function.\ntypedef struct _duckdb_bind_info {\n\tvoid *internal_ptr;\n} * duckdb_bind_info;\n\n//! Additional function initialization info.\n//! When setting this info, it is necessary to pass a destroy-callback function.\ntypedef struct _duckdb_init_info {\n\tvoid *internal_ptr;\n} * duckdb_init_info;\n\n//===--------------------------------------------------------------------===//\n// Scalar function types\n//===--------------------------------------------------------------------===//\n\n//! A scalar function. Must be destroyed with `duckdb_destroy_scalar_function`.\ntypedef struct _duckdb_scalar_function {\n\tvoid *internal_ptr;\n} * duckdb_scalar_function;\n\n//! A scalar function set. Must be destroyed with `duckdb_destroy_scalar_function_set`.\ntypedef struct _duckdb_scalar_function_set {\n\tvoid *internal_ptr;\n} * duckdb_scalar_function_set;\n\n//! The bind function callback of the scalar function.\ntypedef void (*duckdb_scalar_function_bind_t)(duckdb_bind_info info);\n\n//! The thread-local initialization function of the scalar function.\ntypedef void (*duckdb_scalar_function_init_t)(duckdb_init_info info);\n\n//! The function to execute the scalar function on an input chunk.\ntypedef void (*duckdb_scalar_function_t)(duckdb_function_info info, duckdb_data_chunk input, duckdb_vector output);\n\n//===--------------------------------------------------------------------===//\n// Aggregate function types\n//===--------------------------------------------------------------------===//\n\n//! An aggregate function. Must be destroyed with `duckdb_destroy_aggregate_function`.\ntypedef struct _duckdb_aggregate_function {\n\tvoid *internal_ptr;\n} * duckdb_aggregate_function;\n\n//! A aggregate function set. Must be destroyed with `duckdb_destroy_aggregate_function_set`.\ntypedef struct _duckdb_aggregate_function_set {\n\tvoid *internal_ptr;\n} * duckdb_aggregate_function_set;\n\n//! The state of an aggregate function.\ntypedef struct _duckdb_aggregate_state {\n\tvoid *internal_ptr;\n} * duckdb_aggregate_state;\n\n//! A function to return the aggregate state's size.\ntypedef idx_t (*duckdb_aggregate_state_size)(duckdb_function_info info);\n\n//! A function to initialize an aggregate state.\ntypedef void (*duckdb_aggregate_init_t)(duckdb_function_info info, duckdb_aggregate_state state);\n\n//! An optional function to destroy an aggregate state.\ntypedef void (*duckdb_aggregate_destroy_t)(duckdb_aggregate_state *states, idx_t count);\n\n//! A function to update a set of aggregate states with new values.\ntypedef void (*duckdb_aggregate_update_t)(duckdb_function_info info, duckdb_data_chunk input,\n                                          duckdb_aggregate_state *states);\n\n//! A function to combine aggregate states.\ntypedef void (*duckdb_aggregate_combine_t)(duckdb_function_info info, duckdb_aggregate_state *source,\n                                           duckdb_aggregate_state *target, idx_t count);\n\n//! A function to finalize aggregate states into a result vector.\ntypedef void (*duckdb_aggregate_finalize_t)(duckdb_function_info info, duckdb_aggregate_state *source,\n                                            duckdb_vector result, idx_t count, idx_t offset);\n\n//===--------------------------------------------------------------------===//\n// Table function types\n//===--------------------------------------------------------------------===//\n\n//! A table function. Must be destroyed with `duckdb_destroy_table_function`.\ntypedef struct _duckdb_table_function {\n\tvoid *internal_ptr;\n} * duckdb_table_function;\n\n//! The bind function of the table function.\ntypedef void (*duckdb_table_function_bind_t)(duckdb_bind_info info);\n\n//! The possibly thread-local initialization function of the table function.\ntypedef void (*duckdb_table_function_init_t)(duckdb_init_info info);\n\n//! The function to generate an output chunk during table function execution.\ntypedef void (*duckdb_table_function_t)(duckdb_function_info info, duckdb_data_chunk output);\n\n//===--------------------------------------------------------------------===//\n// Copy function types\n//===--------------------------------------------------------------------===//\n\n//! A COPY function. Must be destroyed with `duckdb_destroy_copy_function`.\ntypedef struct _duckdb_copy_function {\n\tvoid *internal_ptr;\n} * duckdb_copy_function;\n\n//! Info for the bind function of a COPY function.\ntypedef struct _duckdb_copy_function_bind_info {\n\tvoid *internal_ptr;\n} * duckdb_copy_function_bind_info;\n\n//! Info for the global initialization function of a COPY function.\ntypedef struct _duckdb_copy_function_global_init_info {\n\tvoid *internal_ptr;\n} * duckdb_copy_function_global_init_info;\n\n//! Info for the sink function of a COPY function.\ntypedef struct _duckdb_copy_function_sink_info {\n\tvoid *internal_ptr;\n} * duckdb_copy_function_sink_info;\n\n//! Info for the finalize function of a COPY function.\ntypedef struct _duckdb_copy_function_finalize_info {\n\tvoid *internal_ptr;\n} * duckdb_copy_function_finalize_info;\n\n//! The bind function to use when binding a COPY ... TO function.\ntypedef void (*duckdb_copy_function_bind_t)(duckdb_copy_function_bind_info info);\n\n//! The initialization function to use when initializing a COPY ... TO function.\ntypedef void (*duckdb_copy_function_global_init_t)(duckdb_copy_function_global_init_info info);\n\n//! The function to sink an input chunk into during execution of a COPY ... TO function.\ntypedef void (*duckdb_copy_function_sink_t)(duckdb_copy_function_sink_info info, duckdb_data_chunk input);\n\n//! The function to finalize the COPY ... TO function execution.\ntypedef void (*duckdb_copy_function_finalize_t)(duckdb_copy_function_finalize_info info);\n\n//===--------------------------------------------------------------------===//\n// Cast types\n//===--------------------------------------------------------------------===//\n\n//! A cast function. Must be destroyed with `duckdb_destroy_cast_function`.\ntypedef struct _duckdb_cast_function {\n\tvoid *internal_ptr;\n} * duckdb_cast_function;\n\n//! The function to cast from an input vector to an output vector.\ntypedef bool (*duckdb_cast_function_t)(duckdb_function_info info, idx_t count, duckdb_vector input,\n                                       duckdb_vector output);\n\n//===--------------------------------------------------------------------===//\n// Replacement scan types\n//===--------------------------------------------------------------------===//\n\n//! Additional replacement scan info. When setting this info, it is necessary to pass a destroy-callback function.\ntypedef struct _duckdb_replacement_scan_info {\n\tvoid *internal_ptr;\n} * duckdb_replacement_scan_info;\n\n//! A replacement scan function.\ntypedef void (*duckdb_replacement_callback_t)(duckdb_replacement_scan_info info, const char *table_name, void *data);\n\n//===--------------------------------------------------------------------===//\n// Arrow-related types\n//===--------------------------------------------------------------------===//\n\n//! Forward declare Arrow structs\n//! It is important to notice that these structs are not defined by DuckDB but are actually Arrow external objects.\n//! They're defined by the C Data Interface Arrow spec: https://arrow.apache.org/docs/format/CDataInterface.html\n\nstruct ArrowArray;\n\nstruct ArrowSchema;\n\n//! Holds an arrow query result. Must be destroyed with `duckdb_destroy_arrow`.\ntypedef struct _duckdb_arrow {\n\tvoid *internal_ptr;\n} * duckdb_arrow;\n\n//! Holds an arrow array stream. Must be destroyed with `duckdb_destroy_arrow_stream`.\ntypedef struct _duckdb_arrow_stream {\n\tvoid *internal_ptr;\n} * duckdb_arrow_stream;\n\n//! Holds an arrow schema. Remember to release the respective ArrowSchema object.\ntypedef struct _duckdb_arrow_schema {\n\tvoid *internal_ptr;\n} * duckdb_arrow_schema;\n\n//! Holds an arrow converted schema (i.e., duckdb::ArrowTableSchema).\n//! In practice, this object holds the information necessary to do proper conversion between Arrow Types and DuckDB\n//! Types. Check duckdb/function/table/arrow/arrow_duck_schema.hpp for more details! Must be destroyed with\n//! `duckdb_destroy_arrow_converted_schema`\ntypedef struct _duckdb_arrow_converted_schema {\n\tvoid *internal_ptr;\n} * duckdb_arrow_converted_schema;\n\n//! Holds an arrow array. Remember to release the respective ArrowSchema object.\ntypedef struct _duckdb_arrow_array {\n\tvoid *internal_ptr;\n} * duckdb_arrow_array;\n\n//! The arrow options used when transforming the DuckDB schema and datachunks into Arrow schema and arrays.\n//! Used in `duckdb_to_arrow_schema` and `duckdb_data_chunk_to_arrow`\ntypedef struct _duckdb_arrow_options {\n\tvoid *internal_ptr;\n} * duckdb_arrow_options;\n\n//===--------------------------------------------------------------------===//\n// Virtual File System Access\n//===--------------------------------------------------------------------===//\n\ntypedef struct _duckdb_file_open_options {\n\tvoid *internal_ptr;\n} * duckdb_file_open_options;\n\ntypedef struct _duckdb_file_system {\n\tvoid *internal_ptr;\n} * duckdb_file_system;\n\ntypedef struct _duckdb_file_handle {\n\tvoid *internal_ptr;\n} * duckdb_file_handle;\n\n//===--------------------------------------------------------------------===//\n// Catalog Interface\n//===--------------------------------------------------------------------===//\n\n//! A handle to a database catalog.\n//! Must be destroyed with `duckdb_destroy_catalog`.\ntypedef struct _duckdb_catalog {\n\tvoid *internal_ptr;\n} * duckdb_catalog;\n\n//! A handle to a catalog entry (e.g., table, view, index, etc.).\n//! Must be destroyed with `duckdb_destroy_catalog_entry`.\ntypedef struct _duckdb_catalog_entry {\n\tvoid *internal_ptr;\n} * duckdb_catalog_entry;\n\n//===--------------------------------------------------------------------===//\n// Logging Types\n//===--------------------------------------------------------------------===//\n\n//! Holds a log storage object.\ntypedef struct _duckdb_log_storage {\n\tvoid *internal_ptr;\n} * duckdb_log_storage;\n\n//! This function is missing the logging context, which will be added later.\ntypedef void (*duckdb_logger_write_log_entry_t)(void *extra_data, duckdb_timestamp *timestamp, const char *level,\n                                                const char *log_type, const char *log_message);\n\n//===--------------------------------------------------------------------===//\n// DuckDB extension access\n//===--------------------------------------------------------------------===//\n\n//! Passed to C API extension as a parameter to the entrypoint.\nstruct duckdb_extension_access {\n\t//! Indicate that an error has occurred.\n\tvoid (*set_error)(duckdb_extension_info info, const char *error);\n\t//! Fetch the database on which to register the extension.\n\tduckdb_database *(*get_database)(duckdb_extension_info info);\n\t//! Fetch the API struct pointer.\n\tconst void *(*get_api)(duckdb_extension_info info, const char *version);\n};\n\n#ifndef DUCKDB_API_EXCLUDE_FUNCTIONS\n\n//===--------------------------------------------------------------------===//\n// Functions\n//===--------------------------------------------------------------------===//\n\n//----------------------------------------------------------------------------------------------------------------------\n// Open Connect\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to operate on the instance cache, databases, connections, as well as some metadata functions.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a new database instance cache.\nThe instance cache is necessary if a client/program (re)opens multiple databases to the same file within the same\nprocess. Must be destroyed with 'duckdb_destroy_instance_cache'.\n\n* @return The database instance cache.\n*/\nDUCKDB_C_API duckdb_instance_cache duckdb_create_instance_cache();\n\n/*!\nCreates a new database instance in the instance cache, or retrieves an existing database instance.\nMust be closed with 'duckdb_close'.\n\n* @param instance_cache The instance cache in which to create the database, or from which to take the database.\n* @param path Path to the database file on disk. Both `nullptr` and `:memory:` open or retrieve an in-memory database.\n* @param out_database The resulting cached database.\n* @param config (Optional) configuration used to create the database.\n* @param out_error If set and the function returns `DuckDBError`, this contains the error message.\nNote that the error message must be freed using `duckdb_free`.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_get_or_create_from_cache(duckdb_instance_cache instance_cache, const char *path,\n                                                          duckdb_database *out_database, duckdb_config config,\n                                                          char **out_error);\n\n/*!\nDestroys an existing database instance cache and de-allocates its memory.\n\n* @param instance_cache The instance cache to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_instance_cache(duckdb_instance_cache *instance_cache);\n\n/*!\nCreates a new database or opens an existing database file stored at the given path.\nIf no path is given a new in-memory database is created instead.\nThe database must be closed with 'duckdb_close'.\n\n* @param path Path to the database file on disk. Both `nullptr` and `:memory:` open an in-memory database.\n* @param out_database The result database object.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_open(const char *path, duckdb_database *out_database);\n\n/*!\nExtended version of duckdb_open. Creates a new database or opens an existing database file stored at the given path.\nThe database must be closed with 'duckdb_close'.\n\n* @param path Path to the database file on disk. Both `nullptr` and `:memory:` open an in-memory database.\n* @param out_database The result database object.\n* @param config (Optional) configuration used to start up the database.\n* @param out_error If set and the function returns `DuckDBError`, this contains the error message.\nNote that the error message must be freed using `duckdb_free`.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_open_ext(const char *path, duckdb_database *out_database, duckdb_config config,\n                                          char **out_error);\n\n/*!\nCloses the specified database and de-allocates all memory allocated for that database.\nThis should be called after you are done with any database allocated through `duckdb_open` or `duckdb_open_ext`.\nNote that failing to call `duckdb_close` (in case of e.g. a program crash) will not cause data corruption.\nStill, it is recommended to always correctly close a database object after you are done with it.\n\n* @param database The database object to shut down.\n*/\nDUCKDB_C_API void duckdb_close(duckdb_database *database);\n\n/*!\nOpens a connection to a database. Connections are required to query the database, and store transactional state\nassociated with the connection.\nThe instantiated connection should be closed using 'duckdb_disconnect'.\n\n* @param database The database file to connect to.\n* @param out_connection The result connection object.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_connect(duckdb_database database, duckdb_connection *out_connection);\n\n/*!\nInterrupt running query\n\n* @param connection The connection to interrupt\n*/\nDUCKDB_C_API void duckdb_interrupt(duckdb_connection connection);\n\n/*!\nGet the progress of the running query.\n\n* @param connection The connection running the query.\n* @return The query progress type containing progress information.\n*/\nDUCKDB_C_API duckdb_query_progress_type duckdb_query_progress(duckdb_connection connection);\n\n/*!\nCloses the specified connection and de-allocates all memory allocated for that connection.\n\n* @param connection The connection to close.\n*/\nDUCKDB_C_API void duckdb_disconnect(duckdb_connection *connection);\n\n/*!\nRetrieves the client context of the connection.\n\n* @param connection The connection.\n* @param out_context The client context of the connection. Must be destroyed with `duckdb_destroy_client_context`.\n*/\nDUCKDB_C_API void duckdb_connection_get_client_context(duckdb_connection connection,\n                                                       duckdb_client_context *out_context);\n\n/*!\nRetrieves the arrow options of the connection.\n\n* @param connection The connection.\n*/\nDUCKDB_C_API void duckdb_connection_get_arrow_options(duckdb_connection connection,\n                                                      duckdb_arrow_options *out_arrow_options);\n\n/*!\nReturns the connection id of the client context.\n\n* @param context The client context.\n* @return The connection id of the client context.\n*/\nDUCKDB_C_API idx_t duckdb_client_context_get_connection_id(duckdb_client_context context);\n\n/*!\nDestroys the client context and deallocates its memory.\n\n* @param context The client context to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_client_context(duckdb_client_context *context);\n\n/*!\nDestroys the arrow options and deallocates its memory.\n\n* @param arrow_options The arrow options to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_arrow_options(duckdb_arrow_options *arrow_options);\n\n/*!\nReturns the version of the linked DuckDB, with a version postfix for dev versions\n\nUsually used for developing C extensions that must return this for a compatibility check.\n*/\nDUCKDB_C_API const char *duckdb_library_version();\n\n/*!\nGet the list of (fully qualified) table names of the query.\n\n* @param connection The connection for which to get the table names.\n* @param query The query for which to get the table names.\n* @param qualified Returns fully qualified table names (catalog.schema.table), if set to true, else only the (not\nescaped) table names.\n* @return A duckdb_value of type VARCHAR[] containing the (fully qualified) table names of the query. Must be destroyed\nwith duckdb_destroy_value.\n*/\nDUCKDB_C_API duckdb_value duckdb_get_table_names(duckdb_connection connection, const char *query, bool qualified);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Configuration\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to interact with a `duckdb_config`, which is the configuration parameter for opening a database.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nInitializes an empty configuration object that can be used to provide start-up options for the DuckDB instance\nthrough `duckdb_open_ext`.\nThe duckdb_config must be destroyed using 'duckdb_destroy_config'\n\nThis will always succeed unless there is a malloc failure.\n\nNote that `duckdb_destroy_config` should always be called on the resulting config, even if the function returns\n`DuckDBError`.\n\n* @param out_config The result configuration object.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_create_config(duckdb_config *out_config);\n\n/*!\nThis returns the total amount of configuration options available for usage with `duckdb_get_config_flag`.\n\nThis should not be called in a loop as it internally loops over all the options.\n\n* @return The amount of config options available.\n*/\nDUCKDB_C_API size_t duckdb_config_count();\n\n/*!\nObtains a human-readable name and description of a specific configuration option. This can be used to e.g.\ndisplay configuration options. This will succeed unless `index` is out of range (i.e. `>= duckdb_config_count`).\n\nThe result name or description MUST NOT be freed.\n\n* @param index The index of the configuration option (between 0 and `duckdb_config_count`)\n* @param out_name A name of the configuration flag.\n* @param out_description A description of the configuration flag.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_get_config_flag(size_t index, const char **out_name, const char **out_description);\n\n/*!\nSets the specified option for the specified configuration. The configuration option is indicated by name.\nTo obtain a list of config options, see `duckdb_get_config_flag`.\n\nIn the source code, configuration options are defined in `config.cpp`.\n\nThis can fail if either the name is invalid, or if the value provided for the option is invalid.\n\n* @param config The configuration object to set the option on.\n* @param name The name of the configuration flag to set.\n* @param option The value to set the configuration flag to.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_set_config(duckdb_config config, const char *name, const char *option);\n\n/*!\nDestroys the specified configuration object and de-allocates all memory allocated for the object.\n\n* @param config The configuration object to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_config(duckdb_config *config);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Error Data\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to operate on `duckdb_error_data`, which contains, for example, the error type and message. Please use this\n// interface for all new C API functions, as it supersedes previous error handling approaches.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates duckdb_error_data.\nMust be destroyed with `duckdb_destroy_error_data`.\n\n* @param type The error type.\n* @param message The error message.\n* @return The error data.\n*/\nDUCKDB_C_API duckdb_error_data duckdb_create_error_data(duckdb_error_type type, const char *message);\n\n/*!\nDestroys the error data and deallocates its memory.\n\n* @param error_data The error data to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_error_data(duckdb_error_data *error_data);\n\n/*!\nReturns the duckdb_error_type of the error data.\n\n* @param error_data The error data.\n* @return The error type.\n*/\nDUCKDB_C_API duckdb_error_type duckdb_error_data_error_type(duckdb_error_data error_data);\n\n/*!\nReturns the error message of the error data. Must not be freed.\n\n* @param error_data The error data.\n* @return The error message.\n*/\nDUCKDB_C_API const char *duckdb_error_data_message(duckdb_error_data error_data);\n\n/*!\nReturns whether the error data contains an error or not.\n\n* @param error_data The error data.\n* @return True, if the error data contains an exception, else false.\n*/\nDUCKDB_C_API bool duckdb_error_data_has_error(duckdb_error_data error_data);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Query Execution\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to obtain a `duckdb_result` and to retrieve metadata from it.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nExecutes a SQL query within a connection and stores the full (materialized) result in the out_result pointer.\nIf the query fails to execute, DuckDBError is returned and the error message can be retrieved by calling\n`duckdb_result_error`.\n\nNote that after running `duckdb_query`, `duckdb_destroy_result` must be called on the result object even if the\nquery fails, otherwise the error stored within the result will not be freed correctly.\n\n* @param connection The connection to perform the query in.\n* @param query The SQL query to run.\n* @param out_result The query result.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_query(duckdb_connection connection, const char *query, duckdb_result *out_result);\n\n/*!\nCloses the result and de-allocates all memory allocated for that result.\n\n* @param result The result to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_result(duckdb_result *result);\n\n/*!\nReturns the column name of the specified column. The result should not need to be freed; the column names will\nautomatically be destroyed when the result is destroyed.\n\nReturns `NULL` if the column is out of range.\n\n* @param result The result object to fetch the column name from.\n* @param col The column index.\n* @return The column name of the specified column.\n*/\nDUCKDB_C_API const char *duckdb_column_name(duckdb_result *result, idx_t col);\n\n/*!\nReturns the column type of the specified column.\n\nReturns `DUCKDB_TYPE_INVALID` if the column is out of range.\n\n* @param result The result object to fetch the column type from.\n* @param col The column index.\n* @return The column type of the specified column.\n*/\nDUCKDB_C_API duckdb_type duckdb_column_type(duckdb_result *result, idx_t col);\n\n/*!\nReturns the statement type of the statement that was executed\n\n* @param result The result object to fetch the statement type from.\n* @return duckdb_statement_type value or DUCKDB_STATEMENT_TYPE_INVALID\n*/\nDUCKDB_C_API duckdb_statement_type duckdb_result_statement_type(duckdb_result result);\n\n/*!\nReturns the logical column type of the specified column.\n\nThe return type of this call should be destroyed with `duckdb_destroy_logical_type`.\n\nReturns `NULL` if the column is out of range.\n\n* @param result The result object to fetch the column type from.\n* @param col The column index.\n* @return The logical column type of the specified column.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_column_logical_type(duckdb_result *result, idx_t col);\n\n/*!\nReturns the arrow options associated with the given result. These options are definitions of how the arrow arrays/schema\nshould be produced.\n* @param result The result object to fetch arrow options from.\n* @return The arrow options associated with the given result. This must be destroyed with\n`duckdb_destroy_arrow_options`.\n*/\nDUCKDB_C_API duckdb_arrow_options duckdb_result_get_arrow_options(duckdb_result *result);\n\n/*!\nReturns the number of columns present in a the result object.\n\n* @param result The result object.\n* @return The number of columns present in the result object.\n*/\nDUCKDB_C_API idx_t duckdb_column_count(duckdb_result *result);\n\n#ifndef DUCKDB_API_NO_DEPRECATED\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nReturns the number of rows present in the result object.\n\n* @param result The result object.\n* @return The number of rows present in the result object.\n*/\nDUCKDB_C_API idx_t duckdb_row_count(duckdb_result *result);\n\n#endif\n\n/*!\nReturns the number of rows changed by the query stored in the result. This is relevant only for INSERT/UPDATE/DELETE\nqueries. For other queries the rows_changed will be 0.\n\n* @param result The result object.\n* @return The number of rows changed.\n*/\nDUCKDB_C_API idx_t duckdb_rows_changed(duckdb_result *result);\n\n#ifndef DUCKDB_API_NO_DEPRECATED\n/*!\n**DEPRECATED**: Prefer using `duckdb_result_get_chunk` instead.\n\nReturns the data of a specific column of a result in columnar format.\n\nThe function returns a dense array which contains the result data. The exact type stored in the array depends on the\ncorresponding duckdb_type (as provided by `duckdb_column_type`). For the exact type by which the data should be\naccessed, see the comments in [the types section](types) or the `DUCKDB_TYPE` enum.\n\nFor example, for a column of type `DUCKDB_TYPE_INTEGER`, rows can be accessed in the following manner:\n```c\nint32_t *data = (int32_t *) duckdb_column_data(&result, 0);\nprintf(\"Data for row %d: %d\\n\", row, data[row]);\n```\n\n* @param result The result object to fetch the column data from.\n* @param col The column index.\n* @return The column data of the specified column.\n*/\nDUCKDB_C_API void *duckdb_column_data(duckdb_result *result, idx_t col);\n\n/*!\n**DEPRECATED**: Prefer using `duckdb_result_get_chunk` instead.\n\nReturns the nullmask of a specific column of a result in columnar format. The nullmask indicates for every row\nwhether or not the corresponding row is `NULL`. If a row is `NULL`, the values present in the array provided\nby `duckdb_column_data` are undefined.\n\n```c\nint32_t *data = (int32_t *) duckdb_column_data(&result, 0);\nbool *nullmask = duckdb_nullmask_data(&result, 0);\nif (nullmask[row]) {\n    printf(\"Data for row %d: NULL\\n\", row);\n} else {\n    printf(\"Data for row %d: %d\\n\", row, data[row]);\n}\n```\n\n* @param result The result object to fetch the nullmask from.\n* @param col The column index.\n* @return The nullmask of the specified column.\n*/\nDUCKDB_C_API bool *duckdb_nullmask_data(duckdb_result *result, idx_t col);\n\n#endif\n\n/*!\nReturns the error message contained within the result. The error is only set if `duckdb_query` returns `DuckDBError`.\n\nThe result of this function must not be freed. It will be cleaned up when `duckdb_destroy_result` is called.\n\n* @param result The result object to fetch the error from.\n* @return The error of the result.\n*/\nDUCKDB_C_API const char *duckdb_result_error(duckdb_result *result);\n\n/*!\nReturns the result error type contained within the result. The error is only set if `duckdb_query` returns\n`DuckDBError`.\n\n* @param result The result object to fetch the error from.\n* @return The error type of the result.\n*/\nDUCKDB_C_API duckdb_error_type duckdb_result_error_type(duckdb_result *result);\n\n#ifndef DUCKDB_API_NO_DEPRECATED\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nFetches a data chunk from the duckdb_result. This function should be called repeatedly until the result is exhausted.\n\nThe result must be destroyed with `duckdb_destroy_data_chunk`.\n\nThis function supersedes all `duckdb_value` functions, as well as the `duckdb_column_data` and `duckdb_nullmask_data`\nfunctions. It results in significantly better performance, and should be preferred in newer code-bases.\n\nIf this function is used, none of the other result functions can be used and vice versa (i.e. this function cannot be\nmixed with the legacy result functions).\n\nUse `duckdb_result_chunk_count` to figure out how many chunks there are in the result.\n\n* @param result The result object to fetch the data chunk from.\n* @param chunk_index The chunk index to fetch from.\n* @return The resulting data chunk. Returns `NULL` if the chunk index is out of bounds.\n*/\nDUCKDB_C_API duckdb_data_chunk duckdb_result_get_chunk(duckdb_result result, idx_t chunk_index);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nChecks if the type of the internal result is StreamQueryResult.\n\n* @param result The result object to check.\n* @return Whether or not the result object is of the type StreamQueryResult\n*/\nDUCKDB_C_API bool duckdb_result_is_streaming(duckdb_result result);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nReturns the number of data chunks present in the result.\n\n* @param result The result object\n* @return Number of data chunks present in the result.\n*/\nDUCKDB_C_API idx_t duckdb_result_chunk_count(duckdb_result result);\n\n#endif\n\n/*!\nReturns the return_type of the given result, or DUCKDB_RETURN_TYPE_INVALID on error\n\n* @param result The result object\n* @return The return_type\n*/\nDUCKDB_C_API duckdb_result_type duckdb_result_return_type(duckdb_result result);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Safe Fetch Functions\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Deprecated functions to interact with a `duckdb_result`.\n//\n// DEPRECATION NOTICE:\n// This function group is deprecated and scheduled for removal.\n//\n// USE INSTEAD:\n// To access the values in a result, use `duckdb_fetch_chunk` repeatedly. For each chunk, use the `duckdb_data_chunk`\n// interface to access any columns and their values.\n//----------------------------------------------------------------------------------------------------------------------\n\n#ifndef DUCKDB_API_NO_DEPRECATED\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The boolean value at the specified location, or false if the value cannot be converted.\n*/\nDUCKDB_C_API bool duckdb_value_boolean(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The int8_t value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API int8_t duckdb_value_int8(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The int16_t value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API int16_t duckdb_value_int16(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The int32_t value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API int32_t duckdb_value_int32(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The int64_t value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API int64_t duckdb_value_int64(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The duckdb_hugeint value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API duckdb_hugeint duckdb_value_hugeint(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The duckdb_uhugeint value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API duckdb_uhugeint duckdb_value_uhugeint(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The duckdb_decimal value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API duckdb_decimal duckdb_value_decimal(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The uint8_t value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API uint8_t duckdb_value_uint8(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The uint16_t value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API uint16_t duckdb_value_uint16(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The uint32_t value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API uint32_t duckdb_value_uint32(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The uint64_t value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API uint64_t duckdb_value_uint64(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The float value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API float duckdb_value_float(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The double value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API double duckdb_value_double(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The duckdb_date value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API duckdb_date duckdb_value_date(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The duckdb_time value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API duckdb_time duckdb_value_time(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The duckdb_timestamp value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API duckdb_timestamp duckdb_value_timestamp(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The duckdb_interval value at the specified location, or 0 if the value cannot be converted.\n*/\nDUCKDB_C_API duckdb_interval duckdb_value_interval(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The text value at the specified location as a null-terminated string, or nullptr if the value cannot be\nconverted. The result must be freed with `duckdb_free`.\n*/\nDUCKDB_C_API char *duckdb_value_varchar(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The string value at the specified location. Attempts to cast the result value to string.\n*/\nDUCKDB_C_API duckdb_string duckdb_value_string(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The char* value at the specified location. ONLY works on VARCHAR columns and does not auto-cast.\nIf the column is NOT a VARCHAR column this function will return NULL.\n\nThe result must NOT be freed.\n*/\nDUCKDB_C_API char *duckdb_value_varchar_internal(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The char* value at the specified location. ONLY works on VARCHAR columns and does not auto-cast.\nIf the column is NOT a VARCHAR column this function will return NULL.\n\nThe result must NOT be freed.\n*/\nDUCKDB_C_API duckdb_string duckdb_value_string_internal(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return The duckdb_blob value at the specified location. Returns a blob with blob.data set to nullptr if the\nvalue cannot be converted. The resulting field \"blob.data\" must be freed with `duckdb_free.`\n*/\nDUCKDB_C_API duckdb_blob duckdb_value_blob(duckdb_result *result, idx_t col, idx_t row);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n* @return Returns true if the value at the specified index is NULL, and false otherwise.\n*/\nDUCKDB_C_API bool duckdb_value_is_null(duckdb_result *result, idx_t col, idx_t row);\n\n#endif\n\n//----------------------------------------------------------------------------------------------------------------------\n// Helpers\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Generic and `duckdb_string_t` helper functions.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nAllocate `size` bytes of memory using the duckdb internal malloc function. Any memory allocated in this manner\nshould be freed using `duckdb_free`.\n\n* @param size The number of bytes to allocate.\n* @return A pointer to the allocated memory region.\n*/\nDUCKDB_C_API void *duckdb_malloc(size_t size);\n\n/*!\nFree a value returned from `duckdb_malloc`, `duckdb_value_varchar`, `duckdb_value_blob`, or\n`duckdb_value_string`.\n\n* @param ptr The memory region to de-allocate.\n*/\nDUCKDB_C_API void duckdb_free(void *ptr);\n\n/*!\nThe internal vector size used by DuckDB.\nThis is the amount of tuples that will fit into a data chunk created by `duckdb_create_data_chunk`.\n\n* @return The vector size.\n*/\nDUCKDB_C_API idx_t duckdb_vector_size();\n\n/*!\nWhether or not the duckdb_string_t value is inlined.\nThis means that the data of the string does not have a separate allocation.\n\n*/\nDUCKDB_C_API bool duckdb_string_is_inlined(duckdb_string_t string);\n\n/*!\nGet the string length of a string_t\n\n* @param string The string to get the length of.\n* @return The length.\n*/\nDUCKDB_C_API uint32_t duckdb_string_t_length(duckdb_string_t string);\n\n/*!\nGet a pointer to the string data of a string_t\n\n* @param string The string to get the pointer to.\n* @return The pointer.\n*/\nDUCKDB_C_API const char *duckdb_string_t_data(duckdb_string_t *string);\n\n/*!\nChecks if a string is valid UTF-8.\n\n* @param str The string to check\n* @param len The length of the string (in bytes)\n* @return nullptr if the string is valid UTF-8. Otherwise, a duckdb_error_data containing error information. Must be\ndestroyed with `duckdb_destroy_error_data`.\n*/\nDUCKDB_C_API duckdb_error_data duckdb_valid_utf8_check(const char *str, idx_t len);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Date Time Timestamp Helpers\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to convert from and to `duckdb_[date, time, time_tz, timestamp]`.\n// `duckdb_is_finite_timestamp[_s, _ms, _ns]` helper functions.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nDecompose a `duckdb_date` object into year, month and date (stored as `duckdb_date_struct`).\n\n* @param date The date object, as obtained from a `DUCKDB_TYPE_DATE` column.\n* @return The `duckdb_date_struct` with the decomposed elements.\n*/\nDUCKDB_C_API duckdb_date_struct duckdb_from_date(duckdb_date date);\n\n/*!\nRe-compose a `duckdb_date` from year, month and date (`duckdb_date_struct`).\n\n* @param date The year, month and date stored in a `duckdb_date_struct`.\n* @return The `duckdb_date` element.\n*/\nDUCKDB_C_API duckdb_date duckdb_to_date(duckdb_date_struct date);\n\n/*!\nTest a `duckdb_date` to see if it is a finite value.\n\n* @param date The date object, as obtained from a `DUCKDB_TYPE_DATE` column.\n* @return True if the date is finite, false if it is ±infinity.\n*/\nDUCKDB_C_API bool duckdb_is_finite_date(duckdb_date date);\n\n/*!\nDecompose a `duckdb_time` object into hour, minute, second and microsecond (stored as `duckdb_time_struct`).\n\n* @param time The time object, as obtained from a `DUCKDB_TYPE_TIME` column.\n* @return The `duckdb_time_struct` with the decomposed elements.\n*/\nDUCKDB_C_API duckdb_time_struct duckdb_from_time(duckdb_time time);\n\n/*!\nCreate a `duckdb_time_tz` object from micros and a timezone offset.\n\n* @param micros The microsecond component of the time.\n* @param offset The timezone offset component of the time.\n* @return The `duckdb_time_tz` element.\n*/\nDUCKDB_C_API duckdb_time_tz duckdb_create_time_tz(int64_t micros, int32_t offset);\n\n/*!\nDecompose a TIME_TZ objects into micros and a timezone offset.\n\nUse `duckdb_from_time` to further decompose the micros into hour, minute, second and microsecond.\n\n* @param micros The time object, as obtained from a `DUCKDB_TYPE_TIME_TZ` column.\n*/\nDUCKDB_C_API duckdb_time_tz_struct duckdb_from_time_tz(duckdb_time_tz micros);\n\n/*!\nRe-compose a `duckdb_time` from hour, minute, second and microsecond (`duckdb_time_struct`).\n\n* @param time The hour, minute, second and microsecond in a `duckdb_time_struct`.\n* @return The `duckdb_time` element.\n*/\nDUCKDB_C_API duckdb_time duckdb_to_time(duckdb_time_struct time);\n\n/*!\nDecompose a `duckdb_timestamp` object into a `duckdb_timestamp_struct`.\n\n* @param ts The ts object, as obtained from a `DUCKDB_TYPE_TIMESTAMP` column.\n* @return The `duckdb_timestamp_struct` with the decomposed elements.\n*/\nDUCKDB_C_API duckdb_timestamp_struct duckdb_from_timestamp(duckdb_timestamp ts);\n\n/*!\nRe-compose a `duckdb_timestamp` from a duckdb_timestamp_struct.\n\n* @param ts The de-composed elements in a `duckdb_timestamp_struct`.\n* @return The `duckdb_timestamp` element.\n*/\nDUCKDB_C_API duckdb_timestamp duckdb_to_timestamp(duckdb_timestamp_struct ts);\n\n/*!\nTest a `duckdb_timestamp` to see if it is a finite value.\n\n* @param ts The duckdb_timestamp object, as obtained from a `DUCKDB_TYPE_TIMESTAMP` column.\n* @return True if the timestamp is finite, false if it is ±infinity.\n*/\nDUCKDB_C_API bool duckdb_is_finite_timestamp(duckdb_timestamp ts);\n\n/*!\nTest a `duckdb_timestamp_s` to see if it is a finite value.\n\n* @param ts The duckdb_timestamp_s object, as obtained from a `DUCKDB_TYPE_TIMESTAMP_S` column.\n* @return True if the timestamp is finite, false if it is ±infinity.\n*/\nDUCKDB_C_API bool duckdb_is_finite_timestamp_s(duckdb_timestamp_s ts);\n\n/*!\nTest a `duckdb_timestamp_ms` to see if it is a finite value.\n\n* @param ts The duckdb_timestamp_ms object, as obtained from a `DUCKDB_TYPE_TIMESTAMP_MS` column.\n* @return True if the timestamp is finite, false if it is ±infinity.\n*/\nDUCKDB_C_API bool duckdb_is_finite_timestamp_ms(duckdb_timestamp_ms ts);\n\n/*!\nTest a `duckdb_timestamp_ns` to see if it is a finite value.\n\n* @param ts The duckdb_timestamp_ns object, as obtained from a `DUCKDB_TYPE_TIMESTAMP_NS` column.\n* @return True if the timestamp is finite, false if it is ±infinity.\n*/\nDUCKDB_C_API bool duckdb_is_finite_timestamp_ns(duckdb_timestamp_ns ts);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Hugeint and Uhugeint Helpers\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to convert from and to `duckdb_[hugeint, uhugeint]`.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nConverts a duckdb_hugeint object (as obtained from a `DUCKDB_TYPE_HUGEINT` column) into a double.\n\n* @param val The hugeint value.\n* @return The converted `double` element.\n*/\nDUCKDB_C_API double duckdb_hugeint_to_double(duckdb_hugeint val);\n\n/*!\nConverts a double value to a duckdb_hugeint object.\n\nIf the conversion fails because the double value is too big the result will be 0.\n\n* @param val The double value.\n* @return The converted `duckdb_hugeint` element.\n*/\nDUCKDB_C_API duckdb_hugeint duckdb_double_to_hugeint(double val);\n\n/*!\nConverts a duckdb_uhugeint object (as obtained from a `DUCKDB_TYPE_UHUGEINT` column) into a double.\n\n* @param val The uhugeint value.\n* @return The converted `double` element.\n*/\nDUCKDB_C_API double duckdb_uhugeint_to_double(duckdb_uhugeint val);\n\n/*!\nConverts a double value to a duckdb_uhugeint object.\n\nIf the conversion fails because the double value is too big the result will be 0.\n\n* @param val The double value.\n* @return The converted `duckdb_uhugeint` element.\n*/\nDUCKDB_C_API duckdb_uhugeint duckdb_double_to_uhugeint(double val);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Decimal Helpers\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to convert from and to `duckdb_decimal`.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nConverts a double value to a duckdb_decimal object.\n\nIf the conversion fails because the double value is too big, or the width/scale are invalid the result will be 0.\n\n* @param val The double value.\n* @return The converted `duckdb_decimal` element.\n*/\nDUCKDB_C_API duckdb_decimal duckdb_double_to_decimal(double val, uint8_t width, uint8_t scale);\n\n/*!\nConverts a duckdb_decimal object (as obtained from a `DUCKDB_TYPE_DECIMAL` column) into a double.\n\n* @param val The decimal value.\n* @return The converted `double` element.\n*/\nDUCKDB_C_API double duckdb_decimal_to_double(duckdb_decimal val);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Prepared Statements\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// A prepared statement is a parameterized query, and you can bind parameters to it. Prepared statements are commonly\n// used to easily supply parameters to functions and avoid SQL injection attacks. They also speed up queries that are\n// executed repeatedly with different parameters. That is because the query is only parsed, bound, optimized and planned\n// once during the prepare stage, rather than once per execution, if it is possible to resolve all parameter types.\n//\n// For example:\n//   SELECT * FROM tbl WHERE id = ?\n// Or a query with multiple parameters:\n//   SELECT * FROM tbl WHERE id = $1 OR name = $2\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreate a prepared statement object from a query.\n\nNote that after calling `duckdb_prepare`, the prepared statement should always be destroyed using\n`duckdb_destroy_prepare`, even if the prepare fails.\n\nIf the prepare fails, `duckdb_prepare_error` can be called to obtain the reason why the prepare failed.\n\n* @param connection The connection object\n* @param query The SQL query to prepare\n* @param out_prepared_statement The resulting prepared statement object\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_prepare(duckdb_connection connection, const char *query,\n                                         duckdb_prepared_statement *out_prepared_statement);\n\n/*!\nCloses the prepared statement and de-allocates all memory allocated for the statement.\n\n* @param prepared_statement The prepared statement to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_prepare(duckdb_prepared_statement *prepared_statement);\n\n/*!\nReturns the error message associated with the given prepared statement.\nIf the prepared statement has no error message, this returns `nullptr` instead.\n\nThe error message should not be freed. It will be de-allocated when `duckdb_destroy_prepare` is called.\n\n* @param prepared_statement The prepared statement to obtain the error from.\n* @return The error message, or `nullptr` if there is none.\n*/\nDUCKDB_C_API const char *duckdb_prepare_error(duckdb_prepared_statement prepared_statement);\n\n/*!\nReturns the number of parameters that can be provided to the given prepared statement.\n\nReturns 0 if the query was not successfully prepared.\n\n* @param prepared_statement The prepared statement to obtain the number of parameters for.\n*/\nDUCKDB_C_API idx_t duckdb_nparams(duckdb_prepared_statement prepared_statement);\n\n/*!\nReturns the name used to identify the parameter\nThe returned string should be freed using `duckdb_free`.\n\nReturns NULL if the index is out of range for the provided prepared statement.\n\n* @param prepared_statement The prepared statement for which to get the parameter name from.\n*/\nDUCKDB_C_API const char *duckdb_parameter_name(duckdb_prepared_statement prepared_statement, idx_t index);\n\n/*!\nReturns the parameter type for the parameter at the given index.\n\nReturns `DUCKDB_TYPE_INVALID` if the parameter index is out of range or the statement was not successfully prepared.\n\n* @param prepared_statement The prepared statement.\n* @param param_idx The parameter index.\n* @return The parameter type\n*/\nDUCKDB_C_API duckdb_type duckdb_param_type(duckdb_prepared_statement prepared_statement, idx_t param_idx);\n\n/*!\nReturns the logical type for the parameter at the given index.\n\nReturns `nullptr` if the parameter index is out of range or the statement was not successfully prepared.\n\nThe return type of this call should be destroyed with `duckdb_destroy_logical_type`.\n\n* @param prepared_statement The prepared statement.\n* @param param_idx The parameter index.\n* @return The logical type of the parameter\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_param_logical_type(duckdb_prepared_statement prepared_statement,\n                                                           idx_t param_idx);\n\n/*!\nClear the params bind to the prepared statement.\n*/\nDUCKDB_C_API duckdb_state duckdb_clear_bindings(duckdb_prepared_statement prepared_statement);\n\n/*!\nReturns the statement type of the statement to be executed\n\n* @param statement The prepared statement.\n* @return duckdb_statement_type value or DUCKDB_STATEMENT_TYPE_INVALID\n*/\nDUCKDB_C_API duckdb_statement_type duckdb_prepared_statement_type(duckdb_prepared_statement statement);\n\n/*!\nReturns the number of columns present in a the result of the prepared statement. If any of the column types are invalid,\nthe result will be 1.\n\n* @param prepared_statement The prepared statement.\n* @return The number of columns present in the result of the prepared statement.\n*/\nDUCKDB_C_API idx_t duckdb_prepared_statement_column_count(duckdb_prepared_statement prepared_statement);\n\n/*!\nReturns the name of the specified column of the result of the prepared_statement.\nThe returned string should be freed using `duckdb_free`.\n\nReturns `nullptr` if the column is out of range.\n\n* @param prepared_statement The prepared statement.\n* @param col_idx The column index.\n* @return The column name of the specified column.\n*/\nDUCKDB_C_API const char *duckdb_prepared_statement_column_name(duckdb_prepared_statement prepared_statement,\n                                                               idx_t col_idx);\n\n/*!\nReturns the column type of the specified column of the result of the prepared_statement.\n\nReturns `DUCKDB_TYPE_INVALID` if the column is out of range.\nThe return type of this call should be destroyed with `duckdb_destroy_logical_type`.\n\n* @param prepared_statement The prepared statement to fetch the column type from.\n* @param col_idx The column index.\n* @return The logical type of the specified column.\n*/\nDUCKDB_C_API duckdb_logical_type\nduckdb_prepared_statement_column_logical_type(duckdb_prepared_statement prepared_statement, idx_t col_idx);\n\n/*!\nReturns the column type of the specified column of the result of the prepared_statement.\n\nReturns `DUCKDB_TYPE_INVALID` if the column is out of range.\n\n* @param prepared_statement The prepared statement to fetch the column type from.\n* @param col_idx The column index.\n* @return The type of the specified column.\n*/\nDUCKDB_C_API duckdb_type duckdb_prepared_statement_column_type(duckdb_prepared_statement prepared_statement,\n                                                               idx_t col_idx);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Bind Values to Prepared Statements\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to bind values to prepared statements. Try to use `duckdb_bind_value` and the `duckdb_create_...` interface\n// for all types.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nBinds a value to the prepared statement at the specified index.\n\nSupersedes all type-specific bind functions (e.g., `duckdb_bind_varchar`, `duckdb_bind_int64`, etc.).\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_value(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                            duckdb_value val);\n\n/*!\nRetrieve the index of the parameter for the prepared statement, identified by name\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_parameter_index(duckdb_prepared_statement prepared_statement,\n                                                      idx_t *param_idx_out, const char *name);\n\n/*!\nBinds a bool value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_boolean(duckdb_prepared_statement prepared_statement, idx_t param_idx, bool val);\n\n/*!\nBinds an int8_t value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_int8(duckdb_prepared_statement prepared_statement, idx_t param_idx, int8_t val);\n\n/*!\nBinds an int16_t value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_int16(duckdb_prepared_statement prepared_statement, idx_t param_idx, int16_t val);\n\n/*!\nBinds an int32_t value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_int32(duckdb_prepared_statement prepared_statement, idx_t param_idx, int32_t val);\n\n/*!\nBinds an int64_t value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_int64(duckdb_prepared_statement prepared_statement, idx_t param_idx, int64_t val);\n\n/*!\nBinds a duckdb_hugeint value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_hugeint(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                              duckdb_hugeint val);\n\n/*!\nBinds a duckdb_uhugeint value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_uhugeint(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                               duckdb_uhugeint val);\n\n/*!\nBinds a duckdb_decimal value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_decimal(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                              duckdb_decimal val);\n\n/*!\nBinds a uint8_t value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_uint8(duckdb_prepared_statement prepared_statement, idx_t param_idx, uint8_t val);\n\n/*!\nBinds a uint16_t value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_uint16(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                             uint16_t val);\n\n/*!\nBinds a uint32_t value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_uint32(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                             uint32_t val);\n\n/*!\nBinds a uint64_t value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_uint64(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                             uint64_t val);\n\n/*!\nBinds a float value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_float(duckdb_prepared_statement prepared_statement, idx_t param_idx, float val);\n\n/*!\nBinds a double value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_double(duckdb_prepared_statement prepared_statement, idx_t param_idx, double val);\n\n/*!\nBinds a duckdb_date value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_date(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                           duckdb_date val);\n\n/*!\nBinds a duckdb_time value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_time(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                           duckdb_time val);\n\n/*!\nBinds a duckdb_timestamp value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_timestamp(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                                duckdb_timestamp val);\n\n/*!\nBinds a duckdb_timestamp value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_timestamp_tz(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                                   duckdb_timestamp val);\n\n/*!\nBinds a duckdb_interval value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_interval(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                               duckdb_interval val);\n\n/*!\nBinds a null-terminated varchar value to the prepared statement at the specified index.\n\nSuperseded by `duckdb_bind_value`.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_varchar(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                              const char *val);\n\n/*!\nBinds a varchar value to the prepared statement at the specified index.\n\nSuperseded by `duckdb_bind_value`.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_varchar_length(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                                     const char *val, idx_t length);\n\n/*!\nBinds a blob value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_blob(duckdb_prepared_statement prepared_statement, idx_t param_idx,\n                                           const void *data, idx_t length);\n\n/*!\nBinds a NULL value to the prepared statement at the specified index.\n*/\nDUCKDB_C_API duckdb_state duckdb_bind_null(duckdb_prepared_statement prepared_statement, idx_t param_idx);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Execute Prepared Statements\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to execute a prepared statement.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nExecutes the prepared statement with the given bound parameters, and returns a materialized query result.\n\nThis method can be called multiple times for each prepared statement, and the parameters can be modified\nbetween calls to this function.\n\nNote that the result must be freed with `duckdb_destroy_result`.\n\n* @param prepared_statement The prepared statement to execute.\n* @param out_result The query result.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_execute_prepared(duckdb_prepared_statement prepared_statement,\n                                                  duckdb_result *out_result);\n\n#ifndef DUCKDB_API_NO_DEPRECATED\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nExecutes the prepared statement with the given bound parameters, and returns an optionally-streaming query result.\nTo determine if the resulting query was in fact streamed, use `duckdb_result_is_streaming`\n\nThis method can be called multiple times for each prepared statement, and the parameters can be modified\nbetween calls to this function.\n\nNote that the result must be freed with `duckdb_destroy_result`.\n\n* @param prepared_statement The prepared statement to execute.\n* @param out_result The query result.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_execute_prepared_streaming(duckdb_prepared_statement prepared_statement,\n                                                            duckdb_result *out_result);\n\n#endif\n\n//----------------------------------------------------------------------------------------------------------------------\n// Extract Statements\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// A query string can be extracted into multiple SQL statements. Each statement should be prepared and executed\n// separately.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nExtract all statements from a query.\nNote that after calling `duckdb_extract_statements`, the extracted statements should always be destroyed using\n`duckdb_destroy_extracted`, even if no statements were extracted.\n\nIf the extract fails, `duckdb_extract_statements_error` can be called to obtain the reason why the extract failed.\n\n* @param connection The connection object\n* @param query The SQL query to extract\n* @param out_extracted_statements The resulting extracted statements object\n* @return The number of extracted statements or 0 on failure.\n*/\nDUCKDB_C_API idx_t duckdb_extract_statements(duckdb_connection connection, const char *query,\n                                             duckdb_extracted_statements *out_extracted_statements);\n\n/*!\nPrepare an extracted statement.\nNote that after calling `duckdb_prepare_extracted_statement`, the prepared statement should always be destroyed using\n`duckdb_destroy_prepare`, even if the prepare fails.\n\nIf the prepare fails, `duckdb_prepare_error` can be called to obtain the reason why the prepare failed.\n\n* @param connection The connection object\n* @param extracted_statements The extracted statements object\n* @param index The index of the extracted statement to prepare\n* @param out_prepared_statement The resulting prepared statement object\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_prepare_extracted_statement(duckdb_connection connection,\n                                                             duckdb_extracted_statements extracted_statements,\n                                                             idx_t index,\n                                                             duckdb_prepared_statement *out_prepared_statement);\n\n/*!\nReturns the error message contained within the extracted statements.\nThe result of this function must not be freed. It will be cleaned up when `duckdb_destroy_extracted` is called.\n\n* @param extracted_statements The extracted statements to fetch the error from.\n* @return The error of the extracted statements.\n*/\nDUCKDB_C_API const char *duckdb_extract_statements_error(duckdb_extracted_statements extracted_statements);\n\n/*!\nDe-allocates all memory allocated for the extracted statements.\n* @param extracted_statements The extracted statements to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_extracted(duckdb_extracted_statements *extracted_statements);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Pending Result Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to interact with a pending result. First, prepare a pending result, and then execute it.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nExecutes the prepared statement with the given bound parameters, and returns a pending result.\nThe pending result represents an intermediate structure for a query that is not yet fully executed.\nThe pending result can be used to incrementally execute a query, returning control to the client between tasks.\n\nNote that after calling `duckdb_pending_prepared`, the pending result should always be destroyed using\n`duckdb_destroy_pending`, even if this function returns DuckDBError.\n\n* @param prepared_statement The prepared statement to execute.\n* @param out_result The pending query result.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_pending_prepared(duckdb_prepared_statement prepared_statement,\n                                                  duckdb_pending_result *out_result);\n\n#ifndef DUCKDB_API_NO_DEPRECATED\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nExecutes the prepared statement with the given bound parameters, and returns a pending result.\nThis pending result will create a streaming duckdb_result when executed.\nThe pending result represents an intermediate structure for a query that is not yet fully executed.\n\nNote that after calling `duckdb_pending_prepared_streaming`, the pending result should always be destroyed using\n`duckdb_destroy_pending`, even if this function returns DuckDBError.\n\n* @param prepared_statement The prepared statement to execute.\n* @param out_result The pending query result.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_pending_prepared_streaming(duckdb_prepared_statement prepared_statement,\n                                                            duckdb_pending_result *out_result);\n\n#endif\n\n/*!\nCloses the pending result and de-allocates all memory allocated for the result.\n\n* @param pending_result The pending result to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_pending(duckdb_pending_result *pending_result);\n\n/*!\nReturns the error message contained within the pending result.\n\nThe result of this function must not be freed. It will be cleaned up when `duckdb_destroy_pending` is called.\n\n* @param pending_result The pending result to fetch the error from.\n* @return The error of the pending result.\n*/\nDUCKDB_C_API const char *duckdb_pending_error(duckdb_pending_result pending_result);\n\n/*!\nExecutes a single task within the query, returning whether or not the query is ready.\n\nIf this returns DUCKDB_PENDING_RESULT_READY, the duckdb_execute_pending function can be called to obtain the result.\nIf this returns DUCKDB_PENDING_RESULT_NOT_READY, the duckdb_pending_execute_task function should be called again.\nIf this returns DUCKDB_PENDING_ERROR, an error occurred during execution.\n\nThe error message can be obtained by calling duckdb_pending_error on the pending_result.\n\n* @param pending_result The pending result to execute a task within.\n* @return The state of the pending result after the execution.\n*/\nDUCKDB_C_API duckdb_pending_state duckdb_pending_execute_task(duckdb_pending_result pending_result);\n\n/*!\nIf this returns DUCKDB_PENDING_RESULT_READY, the duckdb_execute_pending function can be called to obtain the result.\nIf this returns DUCKDB_PENDING_RESULT_NOT_READY, the duckdb_pending_execute_check_state function should be called again.\nIf this returns DUCKDB_PENDING_ERROR, an error occurred during execution.\n\nThe error message can be obtained by calling duckdb_pending_error on the pending_result.\n\n* @param pending_result The pending result.\n* @return The state of the pending result.\n*/\nDUCKDB_C_API duckdb_pending_state duckdb_pending_execute_check_state(duckdb_pending_result pending_result);\n\n/*!\nFully execute a pending query result, returning the final query result.\n\nIf duckdb_pending_execute_task has been called until DUCKDB_PENDING_RESULT_READY was returned, this will return fast.\nOtherwise, all remaining tasks must be executed first.\n\nNote that the result must be freed with `duckdb_destroy_result`.\n\n* @param pending_result The pending result to execute.\n* @param out_result The result object.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_execute_pending(duckdb_pending_result pending_result, duckdb_result *out_result);\n\n/*!\nReturns whether a duckdb_pending_state is finished executing. For example if `pending_state` is\nDUCKDB_PENDING_RESULT_READY, this function will return true.\n\n* @param pending_state The pending state on which to decide whether to finish execution.\n* @return Boolean indicating pending execution should be considered finished.\n*/\nDUCKDB_C_API bool duckdb_pending_execution_is_finished(duckdb_pending_state pending_state);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Value Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to create a `duckdb_value` for each of DuckDB's supported data types, and to access the contents of a\n// `duckdb_value`. The `duckdb_value` wrapper allows handling of primitive and arbitrarily (nested) types through the\n// same interface.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nDestroys the value and de-allocates all memory allocated for that type.\n\n* @param value The value to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_value(duckdb_value *value);\n\n/*!\nCreates a value from a null-terminated string. Returns nullptr if the string is not valid UTF-8 or other invalid input.\n\nSuperseded by `duckdb_create_varchar_length`.\n\n* @param text The null-terminated string\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_varchar(const char *text);\n\n/*!\nCreates a value from a string. Returns nullptr if the string is not valid UTF-8 or other invalid input.\n\n* @param text The text\n* @param length The length of the text\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_varchar_length(const char *text, idx_t length);\n\n/*!\nCreates a value from a boolean\n\n* @param input The boolean value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_bool(bool input);\n\n/*!\nCreates a value from an int8_t (a tinyint)\n\n* @param input The tinyint value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_int8(int8_t input);\n\n/*!\nCreates a value from a uint8_t (a utinyint)\n\n* @param input The utinyint value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_uint8(uint8_t input);\n\n/*!\nCreates a value from an int16_t (a smallint)\n\n* @param input The smallint value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_int16(int16_t input);\n\n/*!\nCreates a value from a uint16_t (a usmallint)\n\n* @param input The usmallint value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_uint16(uint16_t input);\n\n/*!\nCreates a value from an int32_t (an integer)\n\n* @param input The integer value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_int32(int32_t input);\n\n/*!\nCreates a value from a uint32_t (a uinteger)\n\n* @param input The uinteger value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_uint32(uint32_t input);\n\n/*!\nCreates a value from a uint64_t (a ubigint)\n\n* @param input The ubigint value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_uint64(uint64_t input);\n\n/*!\nCreates a value from an int64\n\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_int64(int64_t val);\n\n/*!\nCreates a value from a hugeint\n\n* @param input The hugeint value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_hugeint(duckdb_hugeint input);\n\n/*!\nCreates a value from a uhugeint\n\n* @param input The uhugeint value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_uhugeint(duckdb_uhugeint input);\n\n/*!\nCreates a BIGNUM value from a duckdb_bignum\n\n* @param input The duckdb_bignum value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_bignum(duckdb_bignum input);\n\n/*!\nCreates a DECIMAL value from a duckdb_decimal\n\n* @param input The duckdb_decimal value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_decimal(duckdb_decimal input);\n\n/*!\nCreates a value from a float\n\n* @param input The float value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_float(float input);\n\n/*!\nCreates a value from a double\n\n* @param input The double value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_double(double input);\n\n/*!\nCreates a value from a date\n\n* @param input The date value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_date(duckdb_date input);\n\n/*!\nCreates a value from a time\n\n* @param input The time value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_time(duckdb_time input);\n\n/*!\nCreates a value from a time_ns\n\n* @param input The time value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_time_ns(duckdb_time_ns input);\n\n/*!\nCreates a value from a time_tz.\nNot to be confused with `duckdb_create_time_tz`, which creates a duckdb_time_tz_t.\n\n* @param value The time_tz value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_time_tz_value(duckdb_time_tz value);\n\n/*!\nCreates a TIMESTAMP value from a duckdb_timestamp\n\n* @param input The duckdb_timestamp value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_timestamp(duckdb_timestamp input);\n\n/*!\nCreates a TIMESTAMP_TZ value from a duckdb_timestamp\n\n* @param input The duckdb_timestamp value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_timestamp_tz(duckdb_timestamp input);\n\n/*!\nCreates a TIMESTAMP_S value from a duckdb_timestamp_s\n\n* @param input The duckdb_timestamp_s value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_timestamp_s(duckdb_timestamp_s input);\n\n/*!\nCreates a TIMESTAMP_MS value from a duckdb_timestamp_ms\n\n* @param input The duckdb_timestamp_ms value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_timestamp_ms(duckdb_timestamp_ms input);\n\n/*!\nCreates a TIMESTAMP_NS value from a duckdb_timestamp_ns\n\n* @param input The duckdb_timestamp_ns value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_timestamp_ns(duckdb_timestamp_ns input);\n\n/*!\nCreates a value from an interval\n\n* @param input The interval value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_interval(duckdb_interval input);\n\n/*!\nCreates a value from a blob\n\n* @param data The blob data\n* @param length The length of the blob data\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_blob(const uint8_t *data, idx_t length);\n\n/*!\nCreates a BIT value from a duckdb_bit\n\n* @param input The duckdb_bit value\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_bit(duckdb_bit input);\n\n/*!\nCreates a UUID value from a uhugeint\n\n* @param input The duckdb_uhugeint containing the UUID\n* @return The value. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_uuid(duckdb_uhugeint input);\n\n/*!\nReturns the boolean value of the given value.\n\n* @param val A duckdb_value containing a boolean\n* @return A boolean, or false if the value cannot be converted\n*/\nDUCKDB_C_API bool duckdb_get_bool(duckdb_value val);\n\n/*!\nReturns the int8_t value of the given value.\n\n* @param val A duckdb_value containing a tinyint\n* @return A int8_t, or MinValue<int8> if the value cannot be converted\n*/\nDUCKDB_C_API int8_t duckdb_get_int8(duckdb_value val);\n\n/*!\nReturns the uint8_t value of the given value.\n\n* @param val A duckdb_value containing a utinyint\n* @return A uint8_t, or MinValue<uint8> if the value cannot be converted\n*/\nDUCKDB_C_API uint8_t duckdb_get_uint8(duckdb_value val);\n\n/*!\nReturns the int16_t value of the given value.\n\n* @param val A duckdb_value containing a smallint\n* @return A int16_t, or MinValue<int16> if the value cannot be converted\n*/\nDUCKDB_C_API int16_t duckdb_get_int16(duckdb_value val);\n\n/*!\nReturns the uint16_t value of the given value.\n\n* @param val A duckdb_value containing a usmallint\n* @return A uint16_t, or MinValue<uint16> if the value cannot be converted\n*/\nDUCKDB_C_API uint16_t duckdb_get_uint16(duckdb_value val);\n\n/*!\nReturns the int32_t value of the given value.\n\n* @param val A duckdb_value containing an integer\n* @return A int32_t, or MinValue<int32> if the value cannot be converted\n*/\nDUCKDB_C_API int32_t duckdb_get_int32(duckdb_value val);\n\n/*!\nReturns the uint32_t value of the given value.\n\n* @param val A duckdb_value containing a uinteger\n* @return A uint32_t, or MinValue<uint32> if the value cannot be converted\n*/\nDUCKDB_C_API uint32_t duckdb_get_uint32(duckdb_value val);\n\n/*!\nReturns the int64_t value of the given value.\n\n* @param val A duckdb_value containing a bigint\n* @return A int64_t, or MinValue<int64> if the value cannot be converted\n*/\nDUCKDB_C_API int64_t duckdb_get_int64(duckdb_value val);\n\n/*!\nReturns the uint64_t value of the given value.\n\n* @param val A duckdb_value containing a ubigint\n* @return A uint64_t, or MinValue<uint64> if the value cannot be converted\n*/\nDUCKDB_C_API uint64_t duckdb_get_uint64(duckdb_value val);\n\n/*!\nReturns the hugeint value of the given value.\n\n* @param val A duckdb_value containing a hugeint\n* @return A duckdb_hugeint, or MinValue<hugeint> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_hugeint duckdb_get_hugeint(duckdb_value val);\n\n/*!\nReturns the uhugeint value of the given value.\n\n* @param val A duckdb_value containing a uhugeint\n* @return A duckdb_uhugeint, or MinValue<uhugeint> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_uhugeint duckdb_get_uhugeint(duckdb_value val);\n\n/*!\nReturns the duckdb_bignum value of the given value.\nThe `data` field must be destroyed with `duckdb_free`.\n\n* @param val A duckdb_value containing a BIGNUM\n* @return A duckdb_bignum. The `data` field must be destroyed with `duckdb_free`.\n*/\nDUCKDB_C_API duckdb_bignum duckdb_get_bignum(duckdb_value val);\n\n/*!\nReturns the duckdb_decimal value of the given value.\n\n* @param val A duckdb_value containing a DECIMAL\n* @return A duckdb_decimal, or MinValue<decimal> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_decimal duckdb_get_decimal(duckdb_value val);\n\n/*!\nReturns the float value of the given value.\n\n* @param val A duckdb_value containing a float\n* @return A float, or NAN if the value cannot be converted\n*/\nDUCKDB_C_API float duckdb_get_float(duckdb_value val);\n\n/*!\nReturns the double value of the given value.\n\n* @param val A duckdb_value containing a double\n* @return A double, or NAN if the value cannot be converted\n*/\nDUCKDB_C_API double duckdb_get_double(duckdb_value val);\n\n/*!\nReturns the date value of the given value.\n\n* @param val A duckdb_value containing a date\n* @return A duckdb_date, or MinValue<date> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_date duckdb_get_date(duckdb_value val);\n\n/*!\nReturns the time value of the given value.\n\n* @param val A duckdb_value containing a time\n* @return A duckdb_time, or MinValue<time> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_time duckdb_get_time(duckdb_value val);\n\n/*!\nReturns the time_ns value of the given value.\n\n* @param val A duckdb_value containing a time_ns\n* @return A duckdb_time_ns, or MinValue<time_ns> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_time_ns duckdb_get_time_ns(duckdb_value val);\n\n/*!\nReturns the time_tz value of the given value.\n\n* @param val A duckdb_value containing a time_tz\n* @return A duckdb_time_tz, or MinValue<time_tz> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_time_tz duckdb_get_time_tz(duckdb_value val);\n\n/*!\nReturns the TIMESTAMP value of the given value.\n\n* @param val A duckdb_value containing a TIMESTAMP\n* @return A duckdb_timestamp, or MinValue<timestamp> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_timestamp duckdb_get_timestamp(duckdb_value val);\n\n/*!\nReturns the TIMESTAMP_TZ value of the given value.\n\n* @param val A duckdb_value containing a TIMESTAMP_TZ\n* @return A duckdb_timestamp, or MinValue<timestamp_tz> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_timestamp duckdb_get_timestamp_tz(duckdb_value val);\n\n/*!\nReturns the duckdb_timestamp_s value of the given value.\n\n* @param val A duckdb_value containing a TIMESTAMP_S\n* @return A duckdb_timestamp_s, or MinValue<timestamp_s> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_timestamp_s duckdb_get_timestamp_s(duckdb_value val);\n\n/*!\nReturns the duckdb_timestamp_ms value of the given value.\n\n* @param val A duckdb_value containing a TIMESTAMP_MS\n* @return A duckdb_timestamp_ms, or MinValue<timestamp_ms> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_timestamp_ms duckdb_get_timestamp_ms(duckdb_value val);\n\n/*!\nReturns the duckdb_timestamp_ns value of the given value.\n\n* @param val A duckdb_value containing a TIMESTAMP_NS\n* @return A duckdb_timestamp_ns, or MinValue<timestamp_ns> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_timestamp_ns duckdb_get_timestamp_ns(duckdb_value val);\n\n/*!\nReturns the interval value of the given value.\n\n* @param val A duckdb_value containing a interval\n* @return A duckdb_interval, or MinValue<interval> if the value cannot be converted\n*/\nDUCKDB_C_API duckdb_interval duckdb_get_interval(duckdb_value val);\n\n/*!\nReturns the type of the given value. The type is valid as long as the value is not destroyed.\nThe type itself must not be destroyed.\n\n* @param val A duckdb_value\n* @return A duckdb_logical_type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_get_value_type(duckdb_value val);\n\n/*!\nReturns the blob value of the given value.\n\n* @param val A duckdb_value containing a blob\n* @return A duckdb_blob\n*/\nDUCKDB_C_API duckdb_blob duckdb_get_blob(duckdb_value val);\n\n/*!\nReturns the duckdb_bit value of the given value.\nThe `data` field must be destroyed with `duckdb_free`.\n\n* @param val A duckdb_value containing a BIT\n* @return A duckdb_bit\n*/\nDUCKDB_C_API duckdb_bit duckdb_get_bit(duckdb_value val);\n\n/*!\nReturns a duckdb_uhugeint representing the UUID value of the given value.\n\n* @param val A duckdb_value containing a UUID\n* @return A duckdb_uhugeint representing the UUID value\n*/\nDUCKDB_C_API duckdb_uhugeint duckdb_get_uuid(duckdb_value val);\n\n/*!\nObtains a string representation of the given value.\nThe result must be destroyed with `duckdb_free`.\n\n* @param value The value\n* @return The string value. This must be destroyed with `duckdb_free`.\n*/\nDUCKDB_C_API char *duckdb_get_varchar(duckdb_value value);\n\n/*!\nCreates a struct value from a type and an array of values. Must be destroyed with `duckdb_destroy_value`.\n\n* @param type The type of the struct\n* @param values The values for the struct fields\n* @return The struct value, or nullptr, if any child type is `DUCKDB_TYPE_ANY` or `DUCKDB_TYPE_INVALID`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_struct_value(duckdb_logical_type type, duckdb_value *values);\n\n/*!\nCreates a list value from a child (element) type and an array of values of length `value_count`.\nMust be destroyed with `duckdb_destroy_value`.\n\n* @param type The type of the list\n* @param values The values for the list\n* @param value_count The number of values in the list\n* @return The list value, or nullptr, if the child type is `DUCKDB_TYPE_ANY` or `DUCKDB_TYPE_INVALID`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_list_value(duckdb_logical_type type, duckdb_value *values, idx_t value_count);\n\n/*!\nCreates an array value from a child (element) type and an array of values of length `value_count`.\nMust be destroyed with `duckdb_destroy_value`.\n\n* @param type The type of the array\n* @param values The values for the array\n* @param value_count The number of values in the array\n* @return The array value, or nullptr, if the child type is `DUCKDB_TYPE_ANY` or `DUCKDB_TYPE_INVALID`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_array_value(duckdb_logical_type type, duckdb_value *values, idx_t value_count);\n\n/*!\nCreates a map value from a map type and two arrays, one for the keys and one for the values, each of length\n`entry_count`. Must be destroyed with `duckdb_destroy_value`.\n\n* @param map_type The map type\n* @param keys The keys of the map\n* @param values The values of the map\n* @param entry_count The number of entrys (key-value pairs) in the map\n* @return The map value, or nullptr, if the parameters are invalid.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_map_value(duckdb_logical_type map_type, duckdb_value *keys,\n                                                  duckdb_value *values, idx_t entry_count);\n\n/*!\nCreates a union value from a union type, a tag index, and a value.\nMust be destroyed with `duckdb_destroy_value`.\n\n* @param union_type The union type\n* @param tag_index The index of the tag of the union\n* @param value The value of the union for that tag\n* @return The union value, or nullptr, if the parameters are invalid.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_union_value(duckdb_logical_type union_type, idx_t tag_index,\n                                                    duckdb_value value);\n\n/*!\nReturns the number of elements in a MAP value.\n\n* @param value The MAP value.\n* @return The number of elements in the map.\n*/\nDUCKDB_C_API idx_t duckdb_get_map_size(duckdb_value value);\n\n/*!\nReturns the MAP key at index as a duckdb_value.\n\n* @param value The MAP value.\n* @param index The index of the key.\n* @return The key as a duckdb_value.\n*/\nDUCKDB_C_API duckdb_value duckdb_get_map_key(duckdb_value value, idx_t index);\n\n/*!\nReturns the MAP value at index as a duckdb_value.\n\n* @param value The MAP value.\n* @param index The index of the value.\n* @return The value as a duckdb_value.\n*/\nDUCKDB_C_API duckdb_value duckdb_get_map_value(duckdb_value value, idx_t index);\n\n/*!\nReturns whether the value's type is SQLNULL or not.\n\n* @param value The value to check.\n* @return True, if the value's type is SQLNULL, otherwise false.\n*/\nDUCKDB_C_API bool duckdb_is_null_value(duckdb_value value);\n\n/*!\nCreates a value of type SQLNULL.\n\n* @return The duckdb_value representing SQLNULL. This must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_null_value();\n\n/*!\nReturns the number of elements in a LIST value.\n\n* @param value The LIST value.\n* @return The number of elements in the list.\n*/\nDUCKDB_C_API idx_t duckdb_get_list_size(duckdb_value value);\n\n/*!\nReturns the LIST child at index as a duckdb_value.\n\n* @param value The LIST value.\n* @param index The index of the child.\n* @return The child as a duckdb_value.\n*/\nDUCKDB_C_API duckdb_value duckdb_get_list_child(duckdb_value value, idx_t index);\n\n/*!\nCreates an enum value from a type and a value. Must be destroyed with `duckdb_destroy_value`.\n\n* @param type The type of the enum\n* @param value The value for the enum\n* @return The enum value, or nullptr.\n*/\nDUCKDB_C_API duckdb_value duckdb_create_enum_value(duckdb_logical_type type, uint64_t value);\n\n/*!\nReturns the enum value of the given value.\n\n* @param value A duckdb_value containing an enum\n* @return A uint64_t, or MinValue<uint64> if the value cannot be converted\n*/\nDUCKDB_C_API uint64_t duckdb_get_enum_value(duckdb_value value);\n\n/*!\nReturns the STRUCT child at index as a duckdb_value.\n\n* @param value The STRUCT value.\n* @param index The index of the child.\n* @return The child as a duckdb_value.\n*/\nDUCKDB_C_API duckdb_value duckdb_get_struct_child(duckdb_value value, idx_t index);\n\n/*!\nReturns the SQL string representation of the given value.\n\n* @param value A duckdb_value.\n* @return The SQL string representation as a null-terminated string. The result must be freed with `duckdb_free`.\n*/\nDUCKDB_C_API char *duckdb_value_to_string(duckdb_value value);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Logical Type Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to create and interact with `duckdb_logical_type`.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a `duckdb_logical_type` from a primitive type.\nThe resulting logical type must be destroyed with `duckdb_destroy_logical_type`.\n\nReturns an invalid logical type, if type is: `DUCKDB_TYPE_INVALID`, `DUCKDB_TYPE_DECIMAL`, `DUCKDB_TYPE_ENUM`,\n`DUCKDB_TYPE_LIST`, `DUCKDB_TYPE_STRUCT`, `DUCKDB_TYPE_MAP`, `DUCKDB_TYPE_ARRAY`, or `DUCKDB_TYPE_UNION`.\n\n* @param type The primitive type to create.\n* @return The logical type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_create_logical_type(duckdb_type type);\n\n/*!\nReturns the alias of a duckdb_logical_type, if set, else `nullptr`.\nThe result must be destroyed with `duckdb_free`.\n\n* @param type The logical type\n* @return The alias or `nullptr`\n*/\nDUCKDB_C_API char *duckdb_logical_type_get_alias(duckdb_logical_type type);\n\n/*!\nSets the alias of a duckdb_logical_type.\n\n* @param type The logical type\n* @param alias The alias to set\n*/\nDUCKDB_C_API void duckdb_logical_type_set_alias(duckdb_logical_type type, const char *alias);\n\n/*!\nCreates a LIST type from its child type.\nThe return type must be destroyed with `duckdb_destroy_logical_type`.\n\n* @param type The child type of the list\n* @return The logical type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_create_list_type(duckdb_logical_type type);\n\n/*!\nCreates an ARRAY type from its child type.\nThe return type must be destroyed with `duckdb_destroy_logical_type`.\n\n* @param type The child type of the array.\n* @param array_size The number of elements in the array.\n* @return The logical type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_create_array_type(duckdb_logical_type type, idx_t array_size);\n\n/*!\nCreates a MAP type from its key type and value type.\nThe return type must be destroyed with `duckdb_destroy_logical_type`.\n\n* @param key_type The map's key type.\n* @param value_type The map's value type.\n* @return The logical type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_create_map_type(duckdb_logical_type key_type, duckdb_logical_type value_type);\n\n/*!\nCreates a UNION type from the passed arrays.\nThe return type must be destroyed with `duckdb_destroy_logical_type`.\n\n* @param member_types The array of union member types.\n* @param member_names The union member names.\n* @param member_count The number of union members.\n* @return The logical type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_create_union_type(duckdb_logical_type *member_types, const char **member_names,\n                                                          idx_t member_count);\n\n/*!\nCreates a STRUCT type based on the member types and names.\nThe resulting type must be destroyed with `duckdb_destroy_logical_type`.\n\n* @param member_types The array of types of the struct members.\n* @param member_names The array of names of the struct members.\n* @param member_count The number of members of the struct.\n* @return The logical type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_create_struct_type(duckdb_logical_type *member_types, const char **member_names,\n                                                           idx_t member_count);\n\n/*!\nCreates an ENUM type from the passed member name array.\nThe resulting type should be destroyed with `duckdb_destroy_logical_type`.\n\n* @param member_names The array of names that the enum should consist of.\n* @param member_count The number of elements that were specified in the array.\n* @return The logical type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_create_enum_type(const char **member_names, idx_t member_count);\n\n/*!\nCreates a DECIMAL type with the specified width and scale.\nThe resulting type should be destroyed with `duckdb_destroy_logical_type`.\n\n* @param width The width of the decimal type\n* @param scale The scale of the decimal type\n* @return The logical type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_create_decimal_type(uint8_t width, uint8_t scale);\n\n/*!\nRetrieves the enum `duckdb_type` of a `duckdb_logical_type`.\n\n* @param type The logical type.\n* @return The `duckdb_type` id.\n*/\nDUCKDB_C_API duckdb_type duckdb_get_type_id(duckdb_logical_type type);\n\n/*!\nRetrieves the width of a decimal type.\n\n* @param type The logical type object\n* @return The width of the decimal type\n*/\nDUCKDB_C_API uint8_t duckdb_decimal_width(duckdb_logical_type type);\n\n/*!\nRetrieves the scale of a decimal type.\n\n* @param type The logical type object\n* @return The scale of the decimal type\n*/\nDUCKDB_C_API uint8_t duckdb_decimal_scale(duckdb_logical_type type);\n\n/*!\nRetrieves the internal storage type of a decimal type.\n\n* @param type The logical type object\n* @return The internal type of the decimal type\n*/\nDUCKDB_C_API duckdb_type duckdb_decimal_internal_type(duckdb_logical_type type);\n\n/*!\nRetrieves the internal storage type of an enum type.\n\n* @param type The logical type object\n* @return The internal type of the enum type\n*/\nDUCKDB_C_API duckdb_type duckdb_enum_internal_type(duckdb_logical_type type);\n\n/*!\nRetrieves the dictionary size of the enum type.\n\n* @param type The logical type object\n* @return The dictionary size of the enum type\n*/\nDUCKDB_C_API uint32_t duckdb_enum_dictionary_size(duckdb_logical_type type);\n\n/*!\nRetrieves the dictionary value at the specified position from the enum.\n\nThe result must be freed with `duckdb_free`.\n\n* @param type The logical type object\n* @param index The index in the dictionary\n* @return The string value of the enum type. Must be freed with `duckdb_free`.\n*/\nDUCKDB_C_API char *duckdb_enum_dictionary_value(duckdb_logical_type type, idx_t index);\n\n/*!\nRetrieves the child type of the given LIST type. Also accepts MAP types.\nThe result must be freed with `duckdb_destroy_logical_type`.\n\n* @param type The logical type, either LIST or MAP.\n* @return The child type of the LIST or MAP type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_list_type_child_type(duckdb_logical_type type);\n\n/*!\nRetrieves the child type of the given ARRAY type.\n\nThe result must be freed with `duckdb_destroy_logical_type`.\n\n* @param type The logical type. Must be ARRAY.\n* @return The child type of the ARRAY type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_array_type_child_type(duckdb_logical_type type);\n\n/*!\nRetrieves the array size of the given array type.\n\n* @param type The logical type object\n* @return The fixed number of elements the values of this array type can store.\n*/\nDUCKDB_C_API idx_t duckdb_array_type_array_size(duckdb_logical_type type);\n\n/*!\nRetrieves the key type of the given map type.\n\nThe result must be freed with `duckdb_destroy_logical_type`.\n\n* @param type The logical type object\n* @return The key type of the map type. Must be destroyed with `duckdb_destroy_logical_type`.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_map_type_key_type(duckdb_logical_type type);\n\n/*!\nRetrieves the value type of the given map type.\n\nThe result must be freed with `duckdb_destroy_logical_type`.\n\n* @param type The logical type object\n* @return The value type of the map type. Must be destroyed with `duckdb_destroy_logical_type`.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_map_type_value_type(duckdb_logical_type type);\n\n/*!\nReturns the number of children of a struct type.\n\n* @param type The logical type object\n* @return The number of children of a struct type.\n*/\nDUCKDB_C_API idx_t duckdb_struct_type_child_count(duckdb_logical_type type);\n\n/*!\nRetrieves the name of the struct child.\n\nThe result must be freed with `duckdb_free`.\n\n* @param type The logical type object\n* @param index The child index\n* @return The name of the struct type. Must be freed with `duckdb_free`.\n*/\nDUCKDB_C_API char *duckdb_struct_type_child_name(duckdb_logical_type type, idx_t index);\n\n/*!\nRetrieves the child type of the given struct type at the specified index.\n\nThe result must be freed with `duckdb_destroy_logical_type`.\n\n* @param type The logical type object\n* @param index The child index\n* @return The child type of the struct type. Must be destroyed with `duckdb_destroy_logical_type`.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_struct_type_child_type(duckdb_logical_type type, idx_t index);\n\n/*!\nReturns the number of members that the union type has.\n\n* @param type The logical type (union) object\n* @return The number of members of a union type.\n*/\nDUCKDB_C_API idx_t duckdb_union_type_member_count(duckdb_logical_type type);\n\n/*!\nRetrieves the name of the union member.\n\nThe result must be freed with `duckdb_free`.\n\n* @param type The logical type object\n* @param index The child index\n* @return The name of the union member. Must be freed with `duckdb_free`.\n*/\nDUCKDB_C_API char *duckdb_union_type_member_name(duckdb_logical_type type, idx_t index);\n\n/*!\nRetrieves the child type of the given union member at the specified index.\n\nThe result must be freed with `duckdb_destroy_logical_type`.\n\n* @param type The logical type object\n* @param index The child index\n* @return The child type of the union member. Must be destroyed with `duckdb_destroy_logical_type`.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_union_type_member_type(duckdb_logical_type type, idx_t index);\n\n/*!\nDestroys the logical type and de-allocates all memory allocated for that type.\n\n* @param type The logical type to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_logical_type(duckdb_logical_type *type);\n\n/*!\nRegisters a custom type within the given connection.\nThe type must have an alias\n\n* @param con The connection to use\n* @param type The custom type to register\n* @return Whether or not the registration was successful.\n*/\nDUCKDB_C_API duckdb_state duckdb_register_logical_type(duckdb_connection con, duckdb_logical_type type,\n                                                       duckdb_create_type_info info);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Data Chunk Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to interact with `duckdb_data_chunk`. Data chunks pass through the different operators of DuckDB's\n// execution engine, when, e.g., executing a scalar function. Additionally, a query result is composed of a sequence of\n// data chunks.\n//\n// A data chunk contains a number of vectors, which, in turn, contain data in a columnar format. For the query result,\n// the vectors are the result columns, and they contain the query result for each row.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates an empty data chunk with the specified column types.\nThe result must be destroyed with `duckdb_destroy_data_chunk`.\n\n* @param types An array of column types. Column types can not contain ANY and INVALID types.\n* @param column_count The number of columns.\n* @return The data chunk.\n*/\nDUCKDB_C_API duckdb_data_chunk duckdb_create_data_chunk(duckdb_logical_type *types, idx_t column_count);\n\n/*!\nDestroys the data chunk and de-allocates all memory allocated for that chunk.\n\n* @param chunk The data chunk to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_data_chunk(duckdb_data_chunk *chunk);\n\n/*!\nResets a data chunk, clearing the validity masks and setting the cardinality of the data chunk to 0.\nAfter calling this method, you must call `duckdb_vector_get_validity` and `duckdb_vector_get_data` to obtain current\ndata and validity pointers\n\n* @param chunk The data chunk to reset.\n*/\nDUCKDB_C_API void duckdb_data_chunk_reset(duckdb_data_chunk chunk);\n\n/*!\nRetrieves the number of columns in a data chunk.\n\n* @param chunk The data chunk to get the data from\n* @return The number of columns in the data chunk\n*/\nDUCKDB_C_API idx_t duckdb_data_chunk_get_column_count(duckdb_data_chunk chunk);\n\n/*!\nRetrieves the vector at the specified column index in the data chunk.\n\nThe pointer to the vector is valid for as long as the chunk is alive.\nIt does NOT need to be destroyed.\n\n* @param chunk The data chunk to get the data from\n* @return The vector\n*/\nDUCKDB_C_API duckdb_vector duckdb_data_chunk_get_vector(duckdb_data_chunk chunk, idx_t col_idx);\n\n/*!\nRetrieves the current number of tuples in a data chunk.\n\n* @param chunk The data chunk to get the data from\n* @return The number of tuples in the data chunk\n*/\nDUCKDB_C_API idx_t duckdb_data_chunk_get_size(duckdb_data_chunk chunk);\n\n/*!\nSets the current number of tuples in a data chunk.\n\n* @param chunk The data chunk to set the size in\n* @param size The number of tuples in the data chunk\n*/\nDUCKDB_C_API void duckdb_data_chunk_set_size(duckdb_data_chunk chunk, idx_t size);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Vector Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to interact with `duckdb_vector`. A vector typically (but not always) lives in a data chunk and contains a\n// subset of the rows of a column.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a flat vector. Must be destroyed with `duckdb_destroy_vector`.\n\n* @param type The logical type of the vector.\n* @param capacity The capacity of the vector.\n* @return The vector.\n*/\nDUCKDB_C_API duckdb_vector duckdb_create_vector(duckdb_logical_type type, idx_t capacity);\n\n/*!\nDestroys the vector and de-allocates its memory.\n\n* @param vector A pointer to the vector.\n*/\nDUCKDB_C_API void duckdb_destroy_vector(duckdb_vector *vector);\n\n/*!\nRetrieves the column type of the specified vector.\n\nThe result must be destroyed with `duckdb_destroy_logical_type`.\n\n* @param vector The vector get the data from\n* @return The type of the vector\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_vector_get_column_type(duckdb_vector vector);\n\n/*!\nRetrieves the data pointer of the vector.\n\nThe data pointer can be used to read or write values from the vector.\nHow to read or write values depends on the type of the vector.\n\n* @param vector The vector to get the data from\n* @return The data pointer\n*/\nDUCKDB_C_API void *duckdb_vector_get_data(duckdb_vector vector);\n\n/*!\nRetrieves the validity mask pointer of the specified vector.\n\nIf all values are valid, this function MIGHT return NULL!\n\nThe validity mask is a bitset that signifies null-ness within the data chunk.\nIt is a series of uint64_t values, where each uint64_t value contains validity for 64 tuples.\nThe bit is set to 1 if the value is valid (i.e. not NULL) or 0 if the value is invalid (i.e. NULL).\n\nValidity of a specific value can be obtained like this:\n\nidx_t entry_idx = row_idx / 64;\nidx_t idx_in_entry = row_idx % 64;\nbool is_valid = validity_mask[entry_idx] & (1 << idx_in_entry);\n\nAlternatively, the (slower) duckdb_validity_row_is_valid function can be used.\n\n* @param vector The vector to get the data from\n* @return The pointer to the validity mask, or NULL if no validity mask is present\n*/\nDUCKDB_C_API uint64_t *duckdb_vector_get_validity(duckdb_vector vector);\n\n/*!\nEnsures the validity mask is writable by allocating it.\n\nAfter this function is called, `duckdb_vector_get_validity` will ALWAYS return non-NULL.\nThis allows NULL values to be written to the vector, regardless of whether a validity mask was present before.\n\n* @param vector The vector to alter\n*/\nDUCKDB_C_API void duckdb_vector_ensure_validity_writable(duckdb_vector vector);\n\n/*!\nAssigns a string element in the vector at the specified location. For VARCHAR vectors, the input is validated as UTF-8;\nif invalid, a NULL value is assigned at that index.\n\nSuperseded by `duckdb_unsafe_vector_assign_string_element_len`, optionally combined with `duckdb_valid_utf8_check`.\n\n* @param vector The vector to alter\n* @param index The row position in the vector to assign the string to\n* @param str The null-terminated string\n*/\nDUCKDB_C_API void duckdb_vector_assign_string_element(duckdb_vector vector, idx_t index, const char *str);\n\n/*!\nAssigns a string element in the vector at the specified location. For VARCHAR vectors, the input is validated as UTF-8;\nif invalid, a NULL value is assigned at that index. For BLOB vectors, no validation is performed.\n\nSuperseded by `duckdb_unsafe_vector_assign_string_element_len`, optionally combined with `duckdb_valid_utf8_check`.\n\n* @param vector The vector to alter\n* @param index The row position in the vector to assign the string to\n* @param str The string\n* @param str_len The length of the string (in bytes)\n*/\nDUCKDB_C_API void duckdb_vector_assign_string_element_len(duckdb_vector vector, idx_t index, const char *str,\n                                                          idx_t str_len);\n\n/*!\nAssigns a string element in the vector at the specified location without UTF-8 validation. The caller is responsible for\nensuring the input is valid UTF-8. Use `duckdb_valid_utf8_check` to validate strings before calling this function if\nneeded. If the input is known to be valid UTF-8, this function can be called directly for better performance, avoiding\nthe overhead of redundant validation.\n\n* @param vector The vector to alter\n* @param index The row position in the vector to assign the string to\n* @param str The string\n* @param str_len The length of the string (in bytes)\n*/\nDUCKDB_C_API void duckdb_unsafe_vector_assign_string_element_len(duckdb_vector vector, idx_t index, const char *str,\n                                                                 idx_t str_len);\n\n/*!\nRetrieves the child vector of a list vector.\n\nThe resulting vector is valid as long as the parent vector is valid.\n\n* @param vector The vector\n* @return The child vector\n*/\nDUCKDB_C_API duckdb_vector duckdb_list_vector_get_child(duckdb_vector vector);\n\n/*!\nReturns the size of the child vector of the list.\n\n* @param vector The vector\n* @return The size of the child list\n*/\nDUCKDB_C_API idx_t duckdb_list_vector_get_size(duckdb_vector vector);\n\n/*!\nSets the size of the underlying child-vector of a list vector.\nNote that this does NOT reserve the memory in the child buffer,\nand that it is possible to set a size exceeding the capacity.\nTo set the capacity, use `duckdb_list_vector_reserve`.\n\n* @param vector The list vector.\n* @param size The size of the child list.\n* @return The duckdb state. Returns DuckDBError, if the vector is nullptr.\n*/\nDUCKDB_C_API duckdb_state duckdb_list_vector_set_size(duckdb_vector vector, idx_t size);\n\n/*!\nSets the capacity of the underlying child-vector of a list vector.\nWe increment to the next power of two, based on the required capacity.\nThus, the capacity might not match the size of the list (capacity >= size),\nwhich is set via `duckdb_list_vector_set_size`.\n\n* @param vector The list vector.\n* @param required_capacity The child buffer capacity to reserve.\n* @return The duckdb state. Returns DuckDBError, if the vector is nullptr.\n*/\nDUCKDB_C_API duckdb_state duckdb_list_vector_reserve(duckdb_vector vector, idx_t required_capacity);\n\n/*!\nRetrieves the child vector of a struct vector.\nThe resulting vector is valid as long as the parent vector is valid.\n\n* @param vector The vector\n* @param index The child index\n* @return The child vector\n*/\nDUCKDB_C_API duckdb_vector duckdb_struct_vector_get_child(duckdb_vector vector, idx_t index);\n\n/*!\nRetrieves the child vector of an array vector.\nThe resulting vector is valid as long as the parent vector is valid.\nThe resulting vector has the size of the parent vector multiplied by the array size.\n\n* @param vector The vector\n* @return The child vector\n*/\nDUCKDB_C_API duckdb_vector duckdb_array_vector_get_child(duckdb_vector vector);\n\n/*!\nSlice a vector with a selection vector.\nThe length of the selection vector must be less than or equal to the length of the vector.\nTurns the vector into a dictionary vector.\n\n* @param vector The vector to slice.\n* @param sel The selection vector.\n* @param len The length of the selection vector.\n*/\nDUCKDB_C_API void duckdb_slice_vector(duckdb_vector vector, duckdb_selection_vector sel, idx_t len);\n\n/*!\nCopy the src vector to the dst with a selection vector that identifies which indices to copy.\n\n* @param src The vector to copy from.\n* @param dst The vector to copy to.\n* @param sel The selection vector. The length of the selection vector should not be more than the length of the src\nvector\n* @param src_count The number of entries from selection vector to copy. Think of this as the effective length of the\nselection vector starting from index 0\n* @param src_offset The offset in the selection vector to copy from (important: actual number of items copied =\nsrc_count - src_offset).\n* @param dst_offset The offset in the dst vector to start copying to.\n*/\nDUCKDB_C_API void duckdb_vector_copy_sel(duckdb_vector src, duckdb_vector dst, duckdb_selection_vector sel,\n                                         idx_t src_count, idx_t src_offset, idx_t dst_offset);\n\n/*!\nCopies the value from `value` to `vector`.\n\n* @param vector The receiving vector.\n* @param value The value to copy into the vector.\n*/\nDUCKDB_C_API void duckdb_vector_reference_value(duckdb_vector vector, duckdb_value value);\n\n/*!\nChanges `to_vector` to reference `from_vector. After, the vectors share ownership of the data.\n\n* @param to_vector The receiving vector.\n* @param from_vector The vector to reference.\n*/\nDUCKDB_C_API void duckdb_vector_reference_vector(duckdb_vector to_vector, duckdb_vector from_vector);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Validity Mask Functions\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to interact with the validity mask of a vector. The validity mask is a bitmask determining whether a row in\n// a vector is `NULL`, or not.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nReturns whether or not a row is valid (i.e. not NULL) in the given validity mask.\n\n* @param validity The validity mask, as obtained through `duckdb_vector_get_validity`\n* @param row The row index\n* @return true if the row is valid, false otherwise\n*/\nDUCKDB_C_API bool duckdb_validity_row_is_valid(uint64_t *validity, idx_t row);\n\n/*!\nIn a validity mask, sets a specific row to either valid or invalid.\n\nNote that `duckdb_vector_ensure_validity_writable` should be called before calling `duckdb_vector_get_validity`,\nto ensure that there is a validity mask to write to.\n\n* @param validity The validity mask, as obtained through `duckdb_vector_get_validity`.\n* @param row The row index\n* @param valid Whether or not to set the row to valid, or invalid\n*/\nDUCKDB_C_API void duckdb_validity_set_row_validity(uint64_t *validity, idx_t row, bool valid);\n\n/*!\nIn a validity mask, sets a specific row to invalid.\n\nEquivalent to `duckdb_validity_set_row_validity` with valid set to false.\n\n* @param validity The validity mask\n* @param row The row index\n*/\nDUCKDB_C_API void duckdb_validity_set_row_invalid(uint64_t *validity, idx_t row);\n\n/*!\nIn a validity mask, sets a specific row to valid.\n\nEquivalent to `duckdb_validity_set_row_validity` with valid set to true.\n\n* @param validity The validity mask\n* @param row The row index\n*/\nDUCKDB_C_API void duckdb_validity_set_row_valid(uint64_t *validity, idx_t row);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Scalar Functions\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to create, execute, and register custom scalar functions. Scalar functions take one or more input\n// parameters, and return a single output parameter. Consider using a table function, if your scalar function does not\n// take any input parameters.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a new empty scalar function.\n\nThe return value must be destroyed with `duckdb_destroy_scalar_function`.\n\n* @return The scalar function object.\n*/\nDUCKDB_C_API duckdb_scalar_function duckdb_create_scalar_function();\n\n/*!\nDestroys the given scalar function object.\n\n* @param scalar_function The scalar function to destroy\n*/\nDUCKDB_C_API void duckdb_destroy_scalar_function(duckdb_scalar_function *scalar_function);\n\n/*!\nSets the name of the given scalar function.\n\n* @param scalar_function The scalar function\n* @param name The name of the scalar function\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_name(duckdb_scalar_function scalar_function, const char *name);\n\n/*!\nSets the parameters of the given scalar function to varargs. Does not require adding parameters with\nduckdb_scalar_function_add_parameter.\n\n* @param scalar_function The scalar function.\n* @param type The type of the arguments.\n* @return The parameter type. Cannot contain INVALID.\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_varargs(duckdb_scalar_function scalar_function, duckdb_logical_type type);\n\n/*!\nSets the scalar function's null-handling behavior to special.\n\n* @param scalar_function The scalar function.\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_special_handling(duckdb_scalar_function scalar_function);\n\n/*!\nSets the Function Stability of the scalar function to VOLATILE, indicating the function should be re-run for every row.\nThis limits optimization that can be performed for the function.\n\n* @param scalar_function The scalar function.\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_volatile(duckdb_scalar_function scalar_function);\n\n/*!\nAdds a parameter to the scalar function.\n\n* @param scalar_function The scalar function.\n* @param type The parameter type. Cannot contain INVALID.\n*/\nDUCKDB_C_API void duckdb_scalar_function_add_parameter(duckdb_scalar_function scalar_function,\n                                                       duckdb_logical_type type);\n\n/*!\nSets the return type of the scalar function.\n\n* @param scalar_function The scalar function\n* @param type Cannot contain INVALID or ANY.\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_return_type(duckdb_scalar_function scalar_function,\n                                                         duckdb_logical_type type);\n\n/*!\nAssigns extra information to the scalar function that can be fetched during binding, etc.\n\n* @param scalar_function The scalar function\n* @param extra_info The extra information\n* @param destroy The callback that will be called to destroy the extra information (if any)\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_extra_info(duckdb_scalar_function scalar_function, void *extra_info,\n                                                        duckdb_delete_callback_t destroy);\n\n/*!\nSets the (optional) bind function of the scalar function.\n\n* @param scalar_function The scalar function.\n* @param bind The bind function.\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_bind(duckdb_scalar_function scalar_function,\n                                                  duckdb_scalar_function_bind_t bind);\n\n/*!\nSets the user-provided bind data in the bind object of the scalar function.\nThe bind data object can be retrieved again during execution.\nIn most case, you also need to set the copy-callback of your bind data via duckdb_scalar_function_set_bind_data_copy.\n\n* @param info The bind info of the scalar function.\n* @param bind_data The bind data object.\n* @param destroy The callback to destroy the bind data (if any).\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_bind_data(duckdb_bind_info info, void *bind_data,\n                                                       duckdb_delete_callback_t destroy);\n\n/*!\nSets the copy-callback for the user-provided bind data in the bind object of the scalar function.\n\n* @param info The bind info of the scalar function.\n* @param copy The callback to copy the bind data (if any).\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_bind_data_copy(duckdb_bind_info info, duckdb_copy_callback_t copy);\n\n/*!\nReport that an error has occurred while calling bind on a scalar function.\n\n* @param info The bind info object.\n* @param error The error message.\n*/\nDUCKDB_C_API void duckdb_scalar_function_bind_set_error(duckdb_bind_info info, const char *error);\n\n/*!\nSets the main function of the scalar function.\n\n* @param scalar_function The scalar function\n* @param function The function\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_function(duckdb_scalar_function scalar_function,\n                                                      duckdb_scalar_function_t function);\n\n/*!\nRegister the scalar function object within the given connection.\n\nThe function requires at least a name, a function and a return type.\n\nIf the function is incomplete or a function with this name already exists DuckDBError is returned.\n\n* @param con The connection to register it in.\n* @param scalar_function The function pointer\n* @return Whether or not the registration was successful.\n*/\nDUCKDB_C_API duckdb_state duckdb_register_scalar_function(duckdb_connection con,\n                                                          duckdb_scalar_function scalar_function);\n\n/*!\nRetrieves the extra info of the function as set in `duckdb_scalar_function_set_extra_info`.\n\n* @param info The info object.\n* @return The extra info.\n*/\nDUCKDB_C_API void *duckdb_scalar_function_get_extra_info(duckdb_function_info info);\n\n/*!\nRetrieves the extra info of the function as set in the bind info.\n\n* @param info The info object.\n* @return The extra info.\n*/\nDUCKDB_C_API void *duckdb_scalar_function_bind_get_extra_info(duckdb_bind_info info);\n\n/*!\nGets the scalar function's bind data set by `duckdb_scalar_function_set_bind_data`.\nNote that the bind data is read-only.\n\n* @param info The function info.\n* @return The bind data object.\n*/\nDUCKDB_C_API void *duckdb_scalar_function_get_bind_data(duckdb_function_info info);\n\n/*!\nRetrieves the client context of the bind info of a scalar function.\n\n* @param info The bind info object of the scalar function.\n* @param out_context The client context of the bind info. Must be destroyed with `duckdb_destroy_client_context`.\n*/\nDUCKDB_C_API void duckdb_scalar_function_get_client_context(duckdb_bind_info info, duckdb_client_context *out_context);\n\n/*!\nReport that an error has occurred while executing the scalar function.\n\n* @param info The info object.\n* @param error The error message\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_error(duckdb_function_info info, const char *error);\n\n/*!\nCreates a new empty scalar function set.\n\nThe return value must be destroyed with `duckdb_destroy_scalar_function_set`.\n\n* @return The scalar function set object.\n*/\nDUCKDB_C_API duckdb_scalar_function_set duckdb_create_scalar_function_set(const char *name);\n\n/*!\nDestroys the given scalar function set object.\n\n*/\nDUCKDB_C_API void duckdb_destroy_scalar_function_set(duckdb_scalar_function_set *scalar_function_set);\n\n/*!\nAdds the scalar function as a new overload to the scalar function set.\n\nReturns DuckDBError if the function could not be added, for example if the overload already exists.\n\n* @param set The scalar function set\n* @param function The function to add\n*/\nDUCKDB_C_API duckdb_state duckdb_add_scalar_function_to_set(duckdb_scalar_function_set set,\n                                                            duckdb_scalar_function function);\n\n/*!\nRegister the scalar function set within the given connection.\n\nThe set requires at least a single valid overload.\n\nIf the set is incomplete or a function with this name already exists DuckDBError is returned.\n\n* @param con The connection to register it in.\n* @param set The function set to register\n* @return Whether or not the registration was successful.\n*/\nDUCKDB_C_API duckdb_state duckdb_register_scalar_function_set(duckdb_connection con, duckdb_scalar_function_set set);\n\n/*!\nReturns the number of input arguments of the scalar function.\n\n* @param info The bind info.\n* @return The number of input arguments.\n*/\nDUCKDB_C_API idx_t duckdb_scalar_function_bind_get_argument_count(duckdb_bind_info info);\n\n/*!\nReturns the input argument at index of the scalar function.\n\n* @param info The bind info.\n* @param index The argument index.\n* @return The input argument at index. Must be destroyed with `duckdb_destroy_expression`.\n*/\nDUCKDB_C_API duckdb_expression duckdb_scalar_function_bind_get_argument(duckdb_bind_info info, idx_t index);\n\n/*!\nRetrieves the state pointer of the function info.\n\n* @param info The function info object.\n* @return The state pointer.\n*/\nDUCKDB_C_API void *duckdb_scalar_function_get_state(duckdb_function_info info);\n\n/*!\nSets the (optional) state init function of the scalar function.\nThis is called once for each worker thread that begins executing the function\n* @param scalar_function The scalar function.\n* @param init The init function.\n*/\nDUCKDB_C_API void duckdb_scalar_function_set_init(duckdb_scalar_function scalar_function,\n                                                  duckdb_scalar_function_init_t init);\n\n/*!\nReport that an error has occurred while calling init on a scalar function.\n\n* @param info The init info object.\n* @param error The error message.\n*/\nDUCKDB_C_API void duckdb_scalar_function_init_set_error(duckdb_init_info info, const char *error);\n\n/*!\nSets the state pointer in the init info of the scalar function.\n\n* @param info The init info object.\n* @param state The state pointer.\n* @param destroy The callback to destroy the state (if any).\n*/\nDUCKDB_C_API void duckdb_scalar_function_init_set_state(duckdb_init_info info, void *state,\n                                                        duckdb_delete_callback_t destroy);\n\n/*!\nRetrieves the client context of the init info of a scalar function.\n\n* @param info The init info object of the scalar function.\n* @param out_context The client context of the init info. Must be destroyed with `duckdb_destroy_client_context`.\n*/\nDUCKDB_C_API void duckdb_scalar_function_init_get_client_context(duckdb_init_info info,\n                                                                 duckdb_client_context *out_context);\n\n/*!\nGets the scalar function's bind data set by `duckdb_scalar_function_set_bind_data`.\nNote that the bind data is read-only.\n\n* @param info The init info object.\n* @return The bind data object.\n*/\nDUCKDB_C_API void *duckdb_scalar_function_init_get_bind_data(duckdb_init_info info);\n\n/*!\nRetrieves the extra info of the function as set in the init info.\n\n* @param info The init info object.\n* @return The extra info.\n*/\nDUCKDB_C_API void *duckdb_scalar_function_init_get_extra_info(duckdb_init_info info);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Selection Vector Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to interact with `duckdb_selection_vector`. Selection vectors define a selection on top of a vector. Lets\n// say that a filter filters out all `VARCHAR`-rows containing `hello`. Then, instead of creating a full new copy of the\n// filtered-out data, it is possible to use a selection vector only selecting the rows satisfying the filter.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a new selection vector of size `size`.\nMust be destroyed with `duckdb_destroy_selection_vector`.\n\n* @param size The size of the selection vector.\n* @return The selection vector.\n*/\nDUCKDB_C_API duckdb_selection_vector duckdb_create_selection_vector(idx_t size);\n\n/*!\nDestroys the selection vector and de-allocates its memory.\n\n* @param sel The selection vector.\n*/\nDUCKDB_C_API void duckdb_destroy_selection_vector(duckdb_selection_vector sel);\n\n/*!\nAccess the data pointer of a selection vector.\n\n* @param sel The selection vector.\n* @return The data pointer.\n*/\nDUCKDB_C_API sel_t *duckdb_selection_vector_get_data_ptr(duckdb_selection_vector sel);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Aggregate Functions\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to create, execute, and register custom aggregate functions. Aggregate functions aggregate the values of a\n// column into an output value.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a new empty aggregate function.\n\nThe return value should be destroyed with `duckdb_destroy_aggregate_function`.\n\n* @return The aggregate function object.\n*/\nDUCKDB_C_API duckdb_aggregate_function duckdb_create_aggregate_function();\n\n/*!\nDestroys the given aggregate function object.\n\n*/\nDUCKDB_C_API void duckdb_destroy_aggregate_function(duckdb_aggregate_function *aggregate_function);\n\n/*!\nSets the name of the given aggregate function.\n\n* @param aggregate_function The aggregate function\n* @param name The name of the aggregate function\n*/\nDUCKDB_C_API void duckdb_aggregate_function_set_name(duckdb_aggregate_function aggregate_function, const char *name);\n\n/*!\nAdds a parameter to the aggregate function.\n\n* @param aggregate_function The aggregate function.\n* @param type The parameter type. Cannot contain INVALID.\n*/\nDUCKDB_C_API void duckdb_aggregate_function_add_parameter(duckdb_aggregate_function aggregate_function,\n                                                          duckdb_logical_type type);\n\n/*!\nSets the return type of the aggregate function.\n\n* @param aggregate_function The aggregate function.\n* @param type The return type. Cannot contain INVALID or ANY.\n*/\nDUCKDB_C_API void duckdb_aggregate_function_set_return_type(duckdb_aggregate_function aggregate_function,\n                                                            duckdb_logical_type type);\n\n/*!\nSets the main functions of the aggregate function.\n\n* @param aggregate_function The aggregate function\n* @param state_size state size\n* @param state_init state init function\n* @param update update states\n* @param combine combine states\n* @param finalize finalize states\n*/\nDUCKDB_C_API void duckdb_aggregate_function_set_functions(duckdb_aggregate_function aggregate_function,\n                                                          duckdb_aggregate_state_size state_size,\n                                                          duckdb_aggregate_init_t state_init,\n                                                          duckdb_aggregate_update_t update,\n                                                          duckdb_aggregate_combine_t combine,\n                                                          duckdb_aggregate_finalize_t finalize);\n\n/*!\nSets the state destructor callback of the aggregate function (optional)\n\n* @param aggregate_function The aggregate function\n* @param destroy state destroy callback\n*/\nDUCKDB_C_API void duckdb_aggregate_function_set_destructor(duckdb_aggregate_function aggregate_function,\n                                                           duckdb_aggregate_destroy_t destroy);\n\n/*!\nRegister the aggregate function object within the given connection.\n\nThe function requires at least a name, functions and a return type.\n\nIf the function is incomplete or a function with this name already exists DuckDBError is returned.\n\n* @param con The connection to register it in.\n* @return Whether or not the registration was successful.\n*/\nDUCKDB_C_API duckdb_state duckdb_register_aggregate_function(duckdb_connection con,\n                                                             duckdb_aggregate_function aggregate_function);\n\n/*!\nSets the NULL handling of the aggregate function to SPECIAL_HANDLING.\n\n* @param aggregate_function The aggregate function\n*/\nDUCKDB_C_API void duckdb_aggregate_function_set_special_handling(duckdb_aggregate_function aggregate_function);\n\n/*!\nAssigns extra information to the scalar function that can be fetched during binding, etc.\n\n* @param aggregate_function The aggregate function\n* @param extra_info The extra information\n* @param destroy The callback that will be called to destroy the extra information (if any)\n*/\nDUCKDB_C_API void duckdb_aggregate_function_set_extra_info(duckdb_aggregate_function aggregate_function,\n                                                           void *extra_info, duckdb_delete_callback_t destroy);\n\n/*!\nRetrieves the extra info of the function as set in `duckdb_aggregate_function_set_extra_info`.\n\n* @param info The info object\n* @return The extra info\n*/\nDUCKDB_C_API void *duckdb_aggregate_function_get_extra_info(duckdb_function_info info);\n\n/*!\nReport that an error has occurred while executing the aggregate function.\n\n* @param info The info object\n* @param error The error message\n*/\nDUCKDB_C_API void duckdb_aggregate_function_set_error(duckdb_function_info info, const char *error);\n\n/*!\nCreates a new empty aggregate function set.\n\nThe return value should be destroyed with `duckdb_destroy_aggregate_function_set`.\n\n* @return The aggregate function set object.\n*/\nDUCKDB_C_API duckdb_aggregate_function_set duckdb_create_aggregate_function_set(const char *name);\n\n/*!\nDestroys the given aggregate function set object.\n\n*/\nDUCKDB_C_API void duckdb_destroy_aggregate_function_set(duckdb_aggregate_function_set *aggregate_function_set);\n\n/*!\nAdds the aggregate function as a new overload to the aggregate function set.\n\nReturns DuckDBError if the function could not be added, for example if the overload already exists.\n\n* @param set The aggregate function set\n* @param function The function to add\n*/\nDUCKDB_C_API duckdb_state duckdb_add_aggregate_function_to_set(duckdb_aggregate_function_set set,\n                                                               duckdb_aggregate_function function);\n\n/*!\nRegister the aggregate function set within the given connection.\n\nThe set requires at least a single valid overload.\n\nIf the set is incomplete or a function with this name already exists DuckDBError is returned.\n\n* @param con The connection to register it in.\n* @param set The function set to register\n* @return Whether or not the registration was successful.\n*/\nDUCKDB_C_API duckdb_state duckdb_register_aggregate_function_set(duckdb_connection con,\n                                                                 duckdb_aggregate_function_set set);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Table Functions\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to create, execute, and register custom table functions. Table functions take one or more input parameters,\n// and return one or more output parameters.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a new empty table function.\n\nThe return value should be destroyed with `duckdb_destroy_table_function`.\n\n* @return The table function object.\n*/\nDUCKDB_C_API duckdb_table_function duckdb_create_table_function();\n\n/*!\nDestroys the given table function object.\n\n* @param table_function The table function to destroy\n*/\nDUCKDB_C_API void duckdb_destroy_table_function(duckdb_table_function *table_function);\n\n/*!\nSets the name of the given table function.\n\n* @param table_function The table function\n* @param name The name of the table function\n*/\nDUCKDB_C_API void duckdb_table_function_set_name(duckdb_table_function table_function, const char *name);\n\n/*!\nAdds a parameter to the table function.\n\n* @param table_function The table function.\n* @param type The parameter type. Cannot contain INVALID.\n*/\nDUCKDB_C_API void duckdb_table_function_add_parameter(duckdb_table_function table_function, duckdb_logical_type type);\n\n/*!\nAdds a named parameter to the table function.\n\n* @param table_function The table function.\n* @param name The parameter name.\n* @param type The parameter type. Cannot contain INVALID.\n*/\nDUCKDB_C_API void duckdb_table_function_add_named_parameter(duckdb_table_function table_function, const char *name,\n                                                            duckdb_logical_type type);\n\n/*!\nAssigns extra information to the table function that can be fetched during binding, etc.\n\n* @param table_function The table function\n* @param extra_info The extra information\n* @param destroy The callback that will be called to destroy the extra information (if any)\n*/\nDUCKDB_C_API void duckdb_table_function_set_extra_info(duckdb_table_function table_function, void *extra_info,\n                                                       duckdb_delete_callback_t destroy);\n\n/*!\nSets the bind function of the table function.\n\n* @param table_function The table function\n* @param bind The bind function\n*/\nDUCKDB_C_API void duckdb_table_function_set_bind(duckdb_table_function table_function,\n                                                 duckdb_table_function_bind_t bind);\n\n/*!\nSets the init function of the table function.\n\n* @param table_function The table function\n* @param init The init function\n*/\nDUCKDB_C_API void duckdb_table_function_set_init(duckdb_table_function table_function,\n                                                 duckdb_table_function_init_t init);\n\n/*!\nSets the thread-local init function of the table function.\n\n* @param table_function The table function\n* @param init The init function\n*/\nDUCKDB_C_API void duckdb_table_function_set_local_init(duckdb_table_function table_function,\n                                                       duckdb_table_function_init_t init);\n\n/*!\nSets the main function of the table function.\n\n* @param table_function The table function\n* @param function The function\n*/\nDUCKDB_C_API void duckdb_table_function_set_function(duckdb_table_function table_function,\n                                                     duckdb_table_function_t function);\n\n/*!\nSets whether or not the given table function supports projection pushdown.\n\nIf this is set to true, the system will provide a list of all required columns in the `init` stage through\nthe `duckdb_init_get_column_count` and `duckdb_init_get_column_index` functions.\nIf this is set to false (the default), the system will expect all columns to be projected.\n\n* @param table_function The table function\n* @param pushdown True if the table function supports projection pushdown, false otherwise.\n*/\nDUCKDB_C_API void duckdb_table_function_supports_projection_pushdown(duckdb_table_function table_function,\n                                                                     bool pushdown);\n\n/*!\nRegister the table function object within the given connection.\n\nThe function requires at least a name, a bind function, an init function and a main function.\n\nIf the function is incomplete or a function with this name already exists DuckDBError is returned.\n\n* @param con The connection to register it in.\n* @param function The function pointer\n* @return Whether or not the registration was successful.\n*/\nDUCKDB_C_API duckdb_state duckdb_register_table_function(duckdb_connection con, duckdb_table_function function);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Table Function Bind\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to implement the bind-phase of a table function. The bind-phase happens once before the execution of the\n// table function. It is useful to, e.g., set up any read-only information for the different threads during execution.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nRetrieves the extra info of the function as set in `duckdb_table_function_set_extra_info`.\n\n* @param info The info object\n* @return The extra info\n*/\nDUCKDB_C_API void *duckdb_bind_get_extra_info(duckdb_bind_info info);\n\n/*!\nRetrieves the client context of the bind info of a table function.\n\n* @param info The bind info object of the table function.\n* @param out_context The client context of the bind info. Must be destroyed with `duckdb_destroy_client_context`.\n*/\nDUCKDB_C_API void duckdb_table_function_get_client_context(duckdb_bind_info info, duckdb_client_context *out_context);\n\n/*!\nAdds a result column to the output of the table function.\n\n* @param info The table function's bind info.\n* @param name The column name.\n* @param type The logical column type.\n*/\nDUCKDB_C_API void duckdb_bind_add_result_column(duckdb_bind_info info, const char *name, duckdb_logical_type type);\n\n/*!\nRetrieves the number of regular (non-named) parameters to the function.\n\n* @param info The info object\n* @return The number of parameters\n*/\nDUCKDB_C_API idx_t duckdb_bind_get_parameter_count(duckdb_bind_info info);\n\n/*!\nRetrieves the parameter at the given index.\n\nThe result must be destroyed with `duckdb_destroy_value`.\n\n* @param info The info object\n* @param index The index of the parameter to get\n* @return The value of the parameter. Must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_bind_get_parameter(duckdb_bind_info info, idx_t index);\n\n/*!\nRetrieves a named parameter with the given name.\n\nThe result must be destroyed with `duckdb_destroy_value`.\n\n* @param info The info object\n* @param name The name of the parameter\n* @return The value of the parameter. Must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_bind_get_named_parameter(duckdb_bind_info info, const char *name);\n\n/*!\nSets the user-provided bind data in the bind object of the table function.\nThis object can be retrieved again during execution.\n\n* @param info The bind info of the table function.\n* @param bind_data The bind data object.\n* @param destroy The callback to destroy the bind data (if any).\n*/\nDUCKDB_C_API void duckdb_bind_set_bind_data(duckdb_bind_info info, void *bind_data, duckdb_delete_callback_t destroy);\n\n/*!\nSets the cardinality estimate for the table function, used for optimization.\n\n* @param info The bind data object.\n* @param is_exact Whether or not the cardinality estimate is exact, or an approximation\n*/\nDUCKDB_C_API void duckdb_bind_set_cardinality(duckdb_bind_info info, idx_t cardinality, bool is_exact);\n\n/*!\nReport that an error has occurred while calling bind on a table function.\n\n* @param info The info object\n* @param error The error message\n*/\nDUCKDB_C_API void duckdb_bind_set_error(duckdb_bind_info info, const char *error);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Table Function Init\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to implement the init-phase of a table function. The init-phase happens once for each thread and\n// initializes thread-local information prior to execution.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nRetrieves the extra info of the function as set in `duckdb_table_function_set_extra_info`.\n\n* @param info The info object\n* @return The extra info\n*/\nDUCKDB_C_API void *duckdb_init_get_extra_info(duckdb_init_info info);\n\n/*!\nGets the bind data set by `duckdb_bind_set_bind_data` during the bind.\n\nNote that the bind data should be considered as read-only.\nFor tracking state, use the init data instead.\n\n* @param info The info object\n* @return The bind data object\n*/\nDUCKDB_C_API void *duckdb_init_get_bind_data(duckdb_init_info info);\n\n/*!\nSets the user-provided init data in the init object. This object can be retrieved again during execution.\n\n* @param info The info object\n* @param init_data The init data object.\n* @param destroy The callback that will be called to destroy the init data (if any)\n*/\nDUCKDB_C_API void duckdb_init_set_init_data(duckdb_init_info info, void *init_data, duckdb_delete_callback_t destroy);\n\n/*!\nReturns the number of projected columns.\n\nThis function must be used if projection pushdown is enabled to figure out which columns to emit.\n\n* @param info The info object\n* @return The number of projected columns.\n*/\nDUCKDB_C_API idx_t duckdb_init_get_column_count(duckdb_init_info info);\n\n/*!\nReturns the column index of the projected column at the specified position.\n\nThis function must be used if projection pushdown is enabled to figure out which columns to emit.\n\n* @param info The info object\n* @param column_index The index at which to get the projected column index, from 0..duckdb_init_get_column_count(info)\n* @return The column index of the projected column.\n*/\nDUCKDB_C_API idx_t duckdb_init_get_column_index(duckdb_init_info info, idx_t column_index);\n\n/*!\nSets how many threads can process this table function in parallel (default: 1)\n\n* @param info The info object\n* @param max_threads The maximum amount of threads that can process this table function\n*/\nDUCKDB_C_API void duckdb_init_set_max_threads(duckdb_init_info info, idx_t max_threads);\n\n/*!\nReport that an error has occurred while calling init.\n\n* @param info The info object\n* @param error The error message\n*/\nDUCKDB_C_API void duckdb_init_set_error(duckdb_init_info info, const char *error);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Table Function\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to implement the execution callback of a table function. The execution callback (i.e., the main function)\n// produces a data chunk output based on a data chunk input, and has access to both the bind and init data.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nRetrieves the extra info of the function as set in `duckdb_table_function_set_extra_info`.\n\n* @param info The info object\n* @return The extra info\n*/\nDUCKDB_C_API void *duckdb_function_get_extra_info(duckdb_function_info info);\n\n/*!\nGets the table function's bind data set by `duckdb_bind_set_bind_data`.\n\nNote that the bind data is read-only.\nFor tracking state, use the init data instead.\n\n* @param info The function info object.\n* @return The bind data object.\n*/\nDUCKDB_C_API void *duckdb_function_get_bind_data(duckdb_function_info info);\n\n/*!\nGets the init data set by `duckdb_init_set_init_data` during the init.\n\n* @param info The info object\n* @return The init data object\n*/\nDUCKDB_C_API void *duckdb_function_get_init_data(duckdb_function_info info);\n\n/*!\nGets the thread-local init data set by `duckdb_init_set_init_data` during the local_init.\n\n* @param info The info object\n* @return The init data object\n*/\nDUCKDB_C_API void *duckdb_function_get_local_init_data(duckdb_function_info info);\n\n/*!\nReport that an error has occurred while executing the function.\n\n* @param info The info object\n* @param error The error message\n*/\nDUCKDB_C_API void duckdb_function_set_error(duckdb_function_info info, const char *error);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Replacement Scans\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to create, execute, and register a custom replacement scan. A replacement scan is a callback replacing a\n// scan of a table that does not exist in the catalog.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nAdd a replacement scan definition to the specified database.\n\n* @param db The database object to add the replacement scan to\n* @param replacement The replacement scan callback\n* @param extra_data Extra data that is passed back into the specified callback\n* @param delete_callback The delete callback to call on the extra data, if any\n*/\nDUCKDB_C_API void duckdb_add_replacement_scan(duckdb_database db, duckdb_replacement_callback_t replacement,\n                                              void *extra_data, duckdb_delete_callback_t delete_callback);\n\n/*!\nSets the replacement function name. If this function is called in the replacement callback,\nthe replacement scan is performed. If it is not called, the replacement callback is not performed.\n\n* @param info The info object\n* @param function_name The function name to substitute.\n*/\nDUCKDB_C_API void duckdb_replacement_scan_set_function_name(duckdb_replacement_scan_info info,\n                                                            const char *function_name);\n\n/*!\nAdds a parameter to the replacement scan function.\n\n* @param info The info object\n* @param parameter The parameter to add.\n*/\nDUCKDB_C_API void duckdb_replacement_scan_add_parameter(duckdb_replacement_scan_info info, duckdb_value parameter);\n\n/*!\nReport that an error has occurred while executing the replacement scan.\n\n* @param info The info object\n* @param error The error message\n*/\nDUCKDB_C_API void duckdb_replacement_scan_set_error(duckdb_replacement_scan_info info, const char *error);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Profiling Info\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to access the post-execution profiling information of a query. Only available, if profiling is enabled.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nReturns the root node of the profiling information. Returns nullptr, if profiling is not enabled.\n\n* @param connection A connection object.\n* @return A profiling information object.\n*/\nDUCKDB_C_API duckdb_profiling_info duckdb_get_profiling_info(duckdb_connection connection);\n\n/*!\nReturns the value of the metric of the current profiling info node. Returns nullptr, if the metric does\n not exist or is not enabled. Currently, the value holds a string, and you can retrieve the string\n by calling the corresponding function: char *duckdb_get_varchar(duckdb_value value).\n\n* @param info A profiling information object.\n* @param key The name of the requested metric.\n* @return The value of the metric. Must be freed with `duckdb_destroy_value`\n*/\nDUCKDB_C_API duckdb_value duckdb_profiling_info_get_value(duckdb_profiling_info info, const char *key);\n\n/*!\nReturns the key-value metric map of this profiling node as a MAP duckdb_value.\nThe individual elements are accessible via the duckdb_value MAP functions.\n\n* @param info A profiling information object.\n* @return The key-value metric map as a MAP duckdb_value.\n*/\nDUCKDB_C_API duckdb_value duckdb_profiling_info_get_metrics(duckdb_profiling_info info);\n\n/*!\nReturns the number of children in the current profiling info node.\n\n* @param info A profiling information object.\n* @return The number of children in the current node.\n*/\nDUCKDB_C_API idx_t duckdb_profiling_info_get_child_count(duckdb_profiling_info info);\n\n/*!\nReturns the child node at the specified index.\n\n* @param info A profiling information object.\n* @param index The index of the child node.\n* @return The child node at the specified index.\n*/\nDUCKDB_C_API duckdb_profiling_info duckdb_profiling_info_get_child(duckdb_profiling_info info, idx_t index);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Appender\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Appenders are the most efficient way of bulk-loading data into DuckDB. They are recommended for fast data loading as\n// they perform better than prepared statements or individual `INSERT INTO` statements. Appends are possible in row-wise\n// format, and by appending entire data chunks. Try to use chunk-wise appends via `duckdb_append_data_chunk` to ensure\n// support for all of DuckDBs data types. Chunk-wise appends consecutively call `duckdb_append_data_chunk` until all\n// chunks have been appended. Afterward, call `duckdb_appender_destroy` flush any outstanding data and to destroy the\n// appender instance.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates an appender object.\n\nNote that the object must be destroyed with `duckdb_appender_destroy`.\n\n* @param connection The connection context to create the appender in.\n* @param schema The schema of the table to append to, or `nullptr` for the default schema.\n* @param table The table name to append to.\n* @param out_appender The resulting appender object.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_appender_create(duckdb_connection connection, const char *schema, const char *table,\n                                                 duckdb_appender *out_appender);\n\n/*!\nCreates an appender object.\n\nNote that the object must be destroyed with `duckdb_appender_destroy`.\n\n* @param connection The connection context to create the appender in.\n* @param catalog The catalog of the table to append to, or `nullptr` for the default catalog.\n* @param schema The schema of the table to append to, or `nullptr` for the default schema.\n* @param table The table name to append to.\n* @param out_appender The resulting appender object.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_appender_create_ext(duckdb_connection connection, const char *catalog,\n                                                     const char *schema, const char *table,\n                                                     duckdb_appender *out_appender);\n\n/*!\nCreates an appender object that executes the given query with any data appended to it.\n\nNote that the object must be destroyed with `duckdb_appender_destroy`.\n\n* @param connection The connection context to create the appender in.\n* @param query The query to execute, can be an INSERT, DELETE, UPDATE or MERGE INTO statement.\n* @param column_count The number of columns to append.\n* @param types The types of the columns to append.\n* @param table_name (optionally) the table name used to refer to the appended data, defaults to \"appended_data\".\n* @param column_names (optionally) the list of column names, defaults to \"col1\", \"col2\", ...\n* @param out_appender The resulting appender object.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_appender_create_query(duckdb_connection connection, const char *query,\n                                                       idx_t column_count, duckdb_logical_type *types,\n                                                       const char *table_name, const char **column_names,\n                                                       duckdb_appender *out_appender);\n\n/*!\nReturns the number of columns that belong to the appender.\nIf there is no active column list, then this equals the table's physical columns.\n\n* @param appender The appender to get the column count from.\n* @return The number of columns in the data chunks.\n*/\nDUCKDB_C_API idx_t duckdb_appender_column_count(duckdb_appender appender);\n\n/*!\nReturns the type of the column at the specified index. This is either a type in the active column list, or the same type\nas a column in the receiving table.\n\nNote: The resulting type must be destroyed with `duckdb_destroy_logical_type`.\n\n* @param appender The appender to get the column type from.\n* @param col_idx The index of the column to get the type of.\n* @return The `duckdb_logical_type` of the column.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_appender_column_type(duckdb_appender appender, idx_t col_idx);\n\n#ifndef DUCKDB_API_NO_DEPRECATED\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\nUse duckdb_appender_error_data instead.\n\nReturns the error message associated with the appender.\nIf the appender has no error message, this returns `nullptr` instead.\n\nThe error message should not be freed. It will be de-allocated when `duckdb_appender_destroy` is called.\n\n* @param appender The appender to get the error from.\n* @return The error message, or `nullptr` if there is none.\n*/\nDUCKDB_C_API const char *duckdb_appender_error(duckdb_appender appender);\n\n#endif\n\n/*!\nReturns the error data associated with the appender.\nMust be destroyed with duckdb_destroy_error_data.\n\n* @param appender The appender to get the error data from.\n* @return The error data.\n*/\nDUCKDB_C_API duckdb_error_data duckdb_appender_error_data(duckdb_appender appender);\n\n/*!\nFlush the appender to the table, forcing the cache of the appender to be cleared. If flushing the data triggers a\nconstraint violation or any other error, then all data is invalidated, and this function returns DuckDBError.\nIt is not possible to append more values. Call duckdb_appender_error_data to obtain the error data followed by\nduckdb_appender_destroy to destroy the invalidated appender.\n\n* @param appender The appender to flush.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_appender_flush(duckdb_appender appender);\n\n/*!\nClears all buffered data from the appender without flushing it to the table. This discards any data that has been\nappended but not yet written. The appender can continue to be used after clearing.\n\n* @param appender The appender to clear.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_appender_clear(duckdb_appender appender);\n\n/*!\nCloses the appender by flushing all intermediate states and closing it for further appends. If flushing the data\ntriggers a constraint violation or any other error, then all data is invalidated, and this function returns DuckDBError.\nCall duckdb_appender_error_data to obtain the error data followed by duckdb_appender_destroy to destroy the invalidated\nappender.\n\n* @param appender The appender to flush and close.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_appender_close(duckdb_appender appender);\n\n/*!\nCloses the appender by flushing all intermediate states to the table and destroying it. By destroying it, this function\nde-allocates all memory associated with the appender. If flushing the data triggers a constraint violation,\nthen all data is invalidated, and this function returns DuckDBError. Due to the destruction of the appender, it is no\nlonger possible to obtain the specific error message with duckdb_appender_error. Therefore, call duckdb_appender_close\nbefore destroying the appender, if you need insights into the specific error.\n\n* @param appender The appender to flush, close and destroy.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_appender_destroy(duckdb_appender *appender);\n\n/*!\nAppends a column to the active column list of the appender. Immediately flushes all previous data.\n\nThe active column list specifies all columns that are expected when flushing the data. Any non-active columns are filled\nwith their default values, or NULL.\n\n* @param appender The appender to add the column to.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_appender_add_column(duckdb_appender appender, const char *name);\n\n/*!\nRemoves all columns from the active column list of the appender, resetting the appender to treat all columns as active.\nImmediately flushes all previous data.\n\n* @param appender The appender to clear the columns from.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_appender_clear_columns(duckdb_appender appender);\n\n/*!\nA nop function, provided for backwards compatibility reasons. Does nothing. Only `duckdb_appender_end_row` is required.\n*/\nDUCKDB_C_API duckdb_state duckdb_appender_begin_row(duckdb_appender appender);\n\n/*!\nFinish the current row of appends. After end_row is called, the next row can be appended.\n\n* @param appender The appender.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_appender_end_row(duckdb_appender appender);\n\n/*!\nAppend a DEFAULT value (NULL if DEFAULT not available for column) to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_default(duckdb_appender appender);\n\n/*!\nAppend a DEFAULT value, at the specified row and column, (NULL if DEFAULT not available for column) to the chunk created\nfrom the specified appender. The default value of the column must be a constant value. Non-deterministic expressions\nlike nextval('seq') or random() are not supported.\n\n* @param appender The appender to get the default value from.\n* @param chunk The data chunk to append the default value to.\n* @param col The chunk column index to append the default value to.\n* @param row The chunk row index to append the default value to.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_default_to_chunk(duckdb_appender appender, duckdb_data_chunk chunk, idx_t col,\n                                                         idx_t row);\n\n/*!\nAppend a bool value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_bool(duckdb_appender appender, bool value);\n\n/*!\nAppend an int8_t value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_int8(duckdb_appender appender, int8_t value);\n\n/*!\nAppend an int16_t value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_int16(duckdb_appender appender, int16_t value);\n\n/*!\nAppend an int32_t value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_int32(duckdb_appender appender, int32_t value);\n\n/*!\nAppend an int64_t value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_int64(duckdb_appender appender, int64_t value);\n\n/*!\nAppend a duckdb_hugeint value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_hugeint(duckdb_appender appender, duckdb_hugeint value);\n\n/*!\nAppend a uint8_t value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_uint8(duckdb_appender appender, uint8_t value);\n\n/*!\nAppend a uint16_t value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_uint16(duckdb_appender appender, uint16_t value);\n\n/*!\nAppend a uint32_t value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_uint32(duckdb_appender appender, uint32_t value);\n\n/*!\nAppend a uint64_t value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_uint64(duckdb_appender appender, uint64_t value);\n\n/*!\nAppend a duckdb_uhugeint value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_uhugeint(duckdb_appender appender, duckdb_uhugeint value);\n\n/*!\nAppend a float value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_float(duckdb_appender appender, float value);\n\n/*!\nAppend a double value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_double(duckdb_appender appender, double value);\n\n/*!\nAppend a duckdb_date value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_date(duckdb_appender appender, duckdb_date value);\n\n/*!\nAppend a duckdb_time value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_time(duckdb_appender appender, duckdb_time value);\n\n/*!\nAppend a duckdb_timestamp value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_timestamp(duckdb_appender appender, duckdb_timestamp value);\n\n/*!\nAppend a duckdb_interval value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_interval(duckdb_appender appender, duckdb_interval value);\n\n/*!\nAppend a varchar value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_varchar(duckdb_appender appender, const char *val);\n\n/*!\nAppend a varchar value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_varchar_length(duckdb_appender appender, const char *val, idx_t length);\n\n/*!\nAppend a blob value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_blob(duckdb_appender appender, const void *data, idx_t length);\n\n/*!\nAppend a NULL value to the appender (of any type).\n*/\nDUCKDB_C_API duckdb_state duckdb_append_null(duckdb_appender appender);\n\n/*!\nAppend a duckdb_value to the appender.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_value(duckdb_appender appender, duckdb_value value);\n\n/*!\nAppends a pre-filled data chunk to the specified appender.\n Attempts casting, if the data chunk types do not match the active appender types.\n\n* @param appender The appender to append to.\n* @param chunk The data chunk to append.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_append_data_chunk(duckdb_appender appender, duckdb_data_chunk chunk);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Table Description\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to create and access a `duckdb_table_description` instance.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a table description object. Note that `duckdb_table_description_destroy` should always be called on the\nresulting table_description, even if the function returns `DuckDBError`.\n\n* @param connection The connection context.\n* @param schema The schema of the table, or `nullptr` for the default schema.\n* @param table The table name.\n* @param out The resulting table description object.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_table_description_create(duckdb_connection connection, const char *schema,\n                                                          const char *table, duckdb_table_description *out);\n\n/*!\nCreates a table description object. Note that `duckdb_table_description_destroy` must be called on the resulting\ntable_description, even if the function returns `DuckDBError`.\n\n* @param connection The connection context.\n* @param catalog The catalog (database) name of the table, or `nullptr` for the default catalog.\n* @param schema The schema of the table, or `nullptr` for the default schema.\n* @param table The table name.\n* @param out The resulting table description object.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_table_description_create_ext(duckdb_connection connection, const char *catalog,\n                                                              const char *schema, const char *table,\n                                                              duckdb_table_description *out);\n\n/*!\nDestroy the TableDescription object.\n\n* @param table_description The table_description to destroy.\n*/\nDUCKDB_C_API void duckdb_table_description_destroy(duckdb_table_description *table_description);\n\n/*!\nReturns the error message associated with the given table_description.\nIf the table_description has no error message, this returns `nullptr` instead.\nThe error message should not be freed. It will be de-allocated when `duckdb_table_description_destroy` is called.\n\n* @param table_description The table_description to get the error from.\n* @return The error message, or `nullptr` if there is none.\n*/\nDUCKDB_C_API const char *duckdb_table_description_error(duckdb_table_description table_description);\n\n/*!\nCheck if the column at 'index' index of the table has a DEFAULT expression.\n\n* @param table_description The table_description to query.\n* @param index The index of the column to query.\n* @param out The out-parameter used to store the result.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_column_has_default(duckdb_table_description table_description, idx_t index, bool *out);\n\n/*!\nReturn the number of columns of the described table.\n\n* @param table_description The table_description to query.\n* @return The column count.\n*/\nDUCKDB_C_API idx_t duckdb_table_description_get_column_count(duckdb_table_description table_description);\n\n/*!\nObtain the column name at 'index'.\nThe out result must be destroyed with `duckdb_free`.\n\n* @param table_description The table_description to query.\n* @param index The index of the column to query.\n* @return The column name.\n*/\nDUCKDB_C_API char *duckdb_table_description_get_column_name(duckdb_table_description table_description, idx_t index);\n\n/*!\nObtain the column type at 'index'.\nThe return value must be destroyed with `duckdb_destroy_logical_type`.\n\n* @param table_description The table_description to query.\n* @param index The index of the column to query.\n* @return The column type.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_table_description_get_column_type(duckdb_table_description table_description,\n                                                                          idx_t index);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Arrow Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to convert from and to Arrow.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nTransforms a DuckDB Schema into an Arrow Schema\n\n* @param arrow_options The Arrow settings used to produce arrow.\n* @param types The DuckDB logical types for each column in the schema.\n* @param names The names for each column in the schema.\n* @param column_count The number of columns that exist in the schema.\n* @param out_schema The resulting arrow schema. Must be destroyed with `out_schema->release(out_schema)`.\n* @return The error data. Must be destroyed with `duckdb_destroy_error_data`.\n*/\nDUCKDB_C_API duckdb_error_data duckdb_to_arrow_schema(duckdb_arrow_options arrow_options, duckdb_logical_type *types,\n                                                      const char **names, idx_t column_count,\n                                                      struct ArrowSchema *out_schema);\n\n/*!\nTransforms a DuckDB data chunk into an Arrow array.\n\n* @param arrow_options The Arrow settings used to produce arrow.\n* @param chunk The DuckDB data chunk to convert.\n* @param out_arrow_array The output Arrow structure that will hold the converted data. Must be released with\n`out_arrow_array->release(out_arrow_array)`\n* @return The error data. Must be destroyed with `duckdb_destroy_error_data`.\n*/\nDUCKDB_C_API duckdb_error_data duckdb_data_chunk_to_arrow(duckdb_arrow_options arrow_options, duckdb_data_chunk chunk,\n                                                          struct ArrowArray *out_arrow_array);\n\n/*!\nTransforms an Arrow Schema into a DuckDB Schema.\n\n* @param connection The connection to get the transformation settings from.\n* @param schema The input Arrow schema. Must be released with `schema->release(schema)`.\n* @param out_types The Arrow converted schema with extra information about the arrow types. Must be destroyed with\n`duckdb_destroy_arrow_converted_schema`.\n* @return The error data. Must be destroyed with `duckdb_destroy_error_data`.\n*/\nDUCKDB_C_API duckdb_error_data duckdb_schema_from_arrow(duckdb_connection connection, struct ArrowSchema *schema,\n                                                        duckdb_arrow_converted_schema *out_types);\n\n/*!\nTransforms an Arrow array into a DuckDB data chunk. The data chunk will retain ownership of the underlying Arrow data.\n\n* @param connection The connection to get the transformation settings from.\n* @param arrow_array The input Arrow array. Data ownership is passed on to DuckDB's DataChunk, the underlying object\ndoes not need to be released and won't have ownership of the data.\n* @param converted_schema The Arrow converted schema with extra information about the arrow types.\n* @param out_chunk The resulting DuckDB data chunk. Must be destroyed by duckdb_destroy_data_chunk.\n* @return The error data. Must be destroyed with `duckdb_destroy_error_data`.\n*/\nDUCKDB_C_API duckdb_error_data duckdb_data_chunk_from_arrow(duckdb_connection connection,\n                                                            struct ArrowArray *arrow_array,\n                                                            duckdb_arrow_converted_schema converted_schema,\n                                                            duckdb_data_chunk *out_chunk);\n\n/*!\nDestroys the arrow converted schema and de-allocates all memory allocated for that arrow converted schema.\n\n* @param arrow_converted_schema The arrow converted schema to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_arrow_converted_schema(duckdb_arrow_converted_schema *arrow_converted_schema);\n\n#ifndef DUCKDB_API_NO_DEPRECATED\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nExecutes a SQL query within a connection and stores the full (materialized) result in an arrow structure.\nIf the query fails to execute, DuckDBError is returned and the error message can be retrieved by calling\n`duckdb_query_arrow_error`.\n\nNote that after running `duckdb_query_arrow`, `duckdb_destroy_arrow` must be called on the result object even if the\nquery fails, otherwise the error stored within the result will not be freed correctly.\n\n* @param connection The connection to perform the query in.\n* @param query The SQL query to run.\n* @param out_result The query result.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_query_arrow(duckdb_connection connection, const char *query, duckdb_arrow *out_result);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nFetch the internal arrow schema from the arrow result. Remember to call release on the respective\nArrowSchema object.\n\n* @param result The result to fetch the schema from.\n* @param out_schema The output schema.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_query_arrow_schema(duckdb_arrow result, duckdb_arrow_schema *out_schema);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nFetch the internal arrow schema from the prepared statement. Remember to call release on the respective\nArrowSchema object.\n\n* @param prepared The prepared statement to fetch the schema from.\n* @param out_schema The output schema.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_prepared_arrow_schema(duckdb_prepared_statement prepared,\n                                                       duckdb_arrow_schema *out_schema);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nConvert a data chunk into an arrow struct array. Remember to call release on the respective\nArrowArray object.\n\n* @param result The result object the data chunk have been fetched from.\n* @param chunk The data chunk to convert.\n* @param out_array The output array.\n*/\nDUCKDB_C_API void duckdb_result_arrow_array(duckdb_result result, duckdb_data_chunk chunk,\n                                            duckdb_arrow_array *out_array);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nFetch an internal arrow struct array from the arrow result. Remember to call release on the respective\nArrowArray object.\n\nThis function can be called multiple time to get next chunks, which will free the previous out_array.\nSo consume the out_array before calling this function again.\n\n* @param result The result to fetch the array from.\n* @param out_array The output array.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_query_arrow_array(duckdb_arrow result, duckdb_arrow_array *out_array);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nReturns the number of columns present in the arrow result object.\n\n* @param result The result object.\n* @return The number of columns present in the result object.\n*/\nDUCKDB_C_API idx_t duckdb_arrow_column_count(duckdb_arrow result);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nReturns the number of rows present in the arrow result object.\n\n* @param result The result object.\n* @return The number of rows present in the result object.\n*/\nDUCKDB_C_API idx_t duckdb_arrow_row_count(duckdb_arrow result);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nReturns the number of rows changed by the query stored in the arrow result. This is relevant only for\nINSERT/UPDATE/DELETE queries. For other queries the rows_changed will be 0.\n\n* @param result The result object.\n* @return The number of rows changed.\n*/\nDUCKDB_C_API idx_t duckdb_arrow_rows_changed(duckdb_arrow result);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\n Returns the error message contained within the result. The error is only set if `duckdb_query_arrow` returns\n`DuckDBError`.\n\nThe error message should not be freed. It will be de-allocated when `duckdb_destroy_arrow` is called.\n\n* @param result The result object to fetch the error from.\n* @return The error of the result.\n*/\nDUCKDB_C_API const char *duckdb_query_arrow_error(duckdb_arrow result);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nCloses the result and de-allocates all memory allocated for the arrow result.\n\n* @param result The result to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_arrow(duckdb_arrow *result);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nReleases the arrow array stream and de-allocates its memory.\n\n* @param stream_p The arrow array stream to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_arrow_stream(duckdb_arrow_stream *stream_p);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nExecutes the prepared statement with the given bound parameters, and returns an arrow query result.\nNote that after running `duckdb_execute_prepared_arrow`, `duckdb_destroy_arrow` must be called on the result object.\n\n* @param prepared_statement The prepared statement to execute.\n* @param out_result The query result.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_execute_prepared_arrow(duckdb_prepared_statement prepared_statement,\n                                                        duckdb_arrow *out_result);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nScans the Arrow stream and creates a view with the given name.\n\n* @param connection The connection on which to execute the scan.\n* @param table_name Name of the temporary view to create.\n* @param arrow Arrow stream wrapper.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_arrow_scan(duckdb_connection connection, const char *table_name,\n                                            duckdb_arrow_stream arrow);\n\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nScans the Arrow array and creates a view with the given name.\nNote that after running `duckdb_arrow_array_scan`, `duckdb_destroy_arrow_stream` must be called on the out stream.\n\n* @param connection The connection on which to execute the scan.\n* @param table_name Name of the temporary view to create.\n* @param arrow_schema Arrow schema wrapper.\n* @param arrow_array Arrow array wrapper.\n* @param out_stream Output array stream that wraps around the passed schema, for releasing/deleting once done.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_arrow_array_scan(duckdb_connection connection, const char *table_name,\n                                                  duckdb_arrow_schema arrow_schema, duckdb_arrow_array arrow_array,\n                                                  duckdb_arrow_stream *out_stream);\n\n#endif\n\n//----------------------------------------------------------------------------------------------------------------------\n// Threading Information\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to create and execute tasks.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nExecute DuckDB tasks on this thread.\n\nWill return after `max_tasks` have been executed, or if there are no more tasks present.\n\n* @param database The database object to execute tasks for\n* @param max_tasks The maximum amount of tasks to execute\n*/\nDUCKDB_C_API void duckdb_execute_tasks(duckdb_database database, idx_t max_tasks);\n\n/*!\nCreates a task state that can be used with duckdb_execute_tasks_state to execute tasks until\n`duckdb_finish_execution` is called on the state.\n\n`duckdb_destroy_state` must be called on the result.\n\n* @param database The database object to create the task state for\n* @return The task state that can be used with duckdb_execute_tasks_state.\n*/\nDUCKDB_C_API duckdb_task_state duckdb_create_task_state(duckdb_database database);\n\n/*!\nExecute DuckDB tasks on this thread.\n\nThe thread will keep on executing tasks forever, until duckdb_finish_execution is called on the state.\nMultiple threads can share the same duckdb_task_state.\n\n* @param state The task state of the executor\n*/\nDUCKDB_C_API void duckdb_execute_tasks_state(duckdb_task_state state);\n\n/*!\nExecute DuckDB tasks on this thread.\n\nThe thread will keep on executing tasks until either duckdb_finish_execution is called on the state,\nmax_tasks tasks have been executed or there are no more tasks to be executed.\n\nMultiple threads can share the same duckdb_task_state.\n\n* @param state The task state of the executor\n* @param max_tasks The maximum amount of tasks to execute\n* @return The amount of tasks that have actually been executed\n*/\nDUCKDB_C_API idx_t duckdb_execute_n_tasks_state(duckdb_task_state state, idx_t max_tasks);\n\n/*!\nFinish execution on a specific task.\n\n* @param state The task state to finish execution\n*/\nDUCKDB_C_API void duckdb_finish_execution(duckdb_task_state state);\n\n/*!\nCheck if the provided duckdb_task_state has finished execution\n\n* @param state The task state to inspect\n* @return Whether or not duckdb_finish_execution has been called on the task state\n*/\nDUCKDB_C_API bool duckdb_task_state_is_finished(duckdb_task_state state);\n\n/*!\nDestroys the task state returned from duckdb_create_task_state.\n\nNote that this should not be called while there is an active duckdb_execute_tasks_state running\non the task state.\n\n* @param state The task state to clean up\n*/\nDUCKDB_C_API void duckdb_destroy_task_state(duckdb_task_state state);\n\n/*!\nReturns true if the execution of the current query is finished.\n\n* @param con The connection on which to check\n*/\nDUCKDB_C_API bool duckdb_execution_is_finished(duckdb_connection con);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Streaming Result Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to stream a `duckdb_result`. Call `duckdb_fetch_chunk` until the result is exhausted.\n//----------------------------------------------------------------------------------------------------------------------\n\n#ifndef DUCKDB_API_NO_DEPRECATED\n/*!\n**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.\n\nFetches a data chunk from the (streaming) duckdb_result. This function should be called repeatedly until the result is\nexhausted.\n\nThe result must be destroyed with `duckdb_destroy_data_chunk`.\n\nThis function can only be used on duckdb_results created with 'duckdb_pending_prepared_streaming'\n\nIf this function is used, none of the other result functions can be used and vice versa (i.e. this function cannot be\nmixed with the legacy result functions or the materialized result functions).\n\nIt is not known beforehand how many chunks will be returned by this result.\n\n* @param result The result object to fetch the data chunk from.\n* @return The resulting data chunk. Returns `NULL` if the result has an error.\n*/\nDUCKDB_C_API duckdb_data_chunk duckdb_stream_fetch_chunk(duckdb_result result);\n\n#endif\n\n/*!\nFetches a data chunk from a duckdb_result. This function should be called repeatedly until the result is exhausted.\n\nThe result must be destroyed with `duckdb_destroy_data_chunk`.\n\nIt is not known beforehand how many chunks will be returned by this result.\n\n* @param result The result object to fetch the data chunk from.\n* @return The resulting data chunk. Returns `NULL` if the result has an error.\n*/\nDUCKDB_C_API duckdb_data_chunk duckdb_fetch_chunk(duckdb_result result);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Cast Functions\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to create, execute, and register custom cast functions.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a new cast function object.\n\n* @return The cast function object.\n*/\nDUCKDB_C_API duckdb_cast_function duckdb_create_cast_function();\n\n/*!\nSets the source type of the cast function.\n\n* @param cast_function The cast function object.\n* @param source_type The source type to set.\n*/\nDUCKDB_C_API void duckdb_cast_function_set_source_type(duckdb_cast_function cast_function,\n                                                       duckdb_logical_type source_type);\n\n/*!\nSets the target type of the cast function.\n\n* @param cast_function The cast function object.\n* @param target_type The target type to set.\n*/\nDUCKDB_C_API void duckdb_cast_function_set_target_type(duckdb_cast_function cast_function,\n                                                       duckdb_logical_type target_type);\n\n/*!\nSets the \"cost\" of implicitly casting the source type to the target type using this function.\n\n* @param cast_function The cast function object.\n* @param cost The cost to set.\n*/\nDUCKDB_C_API void duckdb_cast_function_set_implicit_cast_cost(duckdb_cast_function cast_function, int64_t cost);\n\n/*!\nSets the actual cast function to use.\n\n* @param cast_function The cast function object.\n* @param function The function to set.\n*/\nDUCKDB_C_API void duckdb_cast_function_set_function(duckdb_cast_function cast_function,\n                                                    duckdb_cast_function_t function);\n\n/*!\nAssigns extra information to the cast function that can be fetched during execution, etc.\n\n* @param extra_info The extra information\n* @param destroy The callback that will be called to destroy the extra information (if any)\n*/\nDUCKDB_C_API void duckdb_cast_function_set_extra_info(duckdb_cast_function cast_function, void *extra_info,\n                                                      duckdb_delete_callback_t destroy);\n\n/*!\nRetrieves the extra info of the function as set in `duckdb_cast_function_set_extra_info`.\n\n* @param info The info object.\n* @return The extra info.\n*/\nDUCKDB_C_API void *duckdb_cast_function_get_extra_info(duckdb_function_info info);\n\n/*!\nGet the cast execution mode from the given function info.\n\n* @param info The info object.\n* @return The cast mode.\n*/\nDUCKDB_C_API duckdb_cast_mode duckdb_cast_function_get_cast_mode(duckdb_function_info info);\n\n/*!\nReport that an error has occurred while executing the cast function.\n\n* @param info The info object.\n* @param error The error message.\n*/\nDUCKDB_C_API void duckdb_cast_function_set_error(duckdb_function_info info, const char *error);\n\n/*!\nReport that an error has occurred while executing the cast function, setting the corresponding output row to NULL.\n\n* @param info The info object.\n* @param error The error message.\n* @param row The index of the row within the output vector to set to NULL.\n* @param output The output vector.\n*/\nDUCKDB_C_API void duckdb_cast_function_set_row_error(duckdb_function_info info, const char *error, idx_t row,\n                                                     duckdb_vector output);\n\n/*!\nRegisters a cast function within the given connection.\n\n* @param con The connection to use.\n* @param cast_function The cast function to register.\n* @return Whether or not the registration was successful.\n*/\nDUCKDB_C_API duckdb_state duckdb_register_cast_function(duckdb_connection con, duckdb_cast_function cast_function);\n\n/*!\nDestroys the cast function object.\n\n* @param cast_function The cast function object.\n*/\nDUCKDB_C_API void duckdb_destroy_cast_function(duckdb_cast_function *cast_function);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Expression Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to create and access expressions. Expressions are widespread in DuckDB, especially during query planning.\n// E.g., scalar function parameters are expressions, and can be inspected during the bind-phase.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nDestroys the expression and de-allocates its memory.\n\n* @param expr A pointer to the expression.\n*/\nDUCKDB_C_API void duckdb_destroy_expression(duckdb_expression *expr);\n\n/*!\nReturns the return type of an expression.\n\n* @param expr The expression.\n* @return The return type. Must be destroyed with `duckdb_destroy_logical_type`.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_expression_return_type(duckdb_expression expr);\n\n/*!\nReturns whether the expression is foldable into a value or not.\n\n* @param expr The expression.\n* @return True, if the expression is foldable, else false.\n*/\nDUCKDB_C_API bool duckdb_expression_is_foldable(duckdb_expression expr);\n\n/*!\nFolds an expression creating a folded value.\n\n* @param context The client context.\n* @param expr The expression. Must be foldable.\n* @param out_value The folded value, if folding was successful. Must be destroyed with `duckdb_destroy_value`.\n* @return The error data. Must be destroyed with `duckdb_destroy_error_data`.\n*/\nDUCKDB_C_API duckdb_error_data duckdb_expression_fold(duckdb_client_context context, duckdb_expression expr,\n                                                      duckdb_value *out_value);\n\n//----------------------------------------------------------------------------------------------------------------------\n// File System Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to access the file system of a connection and to interact with file handles. File handle instances to files\n// allow operations such as reading, writing, and seeking in a file.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nGet a file system instance associated with the given client context.\n\n* @param context The client context.\n* @return The resulting file system instance. Must be destroyed with `duckdb_destroy_file_system`.\n*/\nDUCKDB_C_API duckdb_file_system duckdb_client_context_get_file_system(duckdb_client_context context);\n\n/*!\nDestroys the given file system instance.\n* @param file_system The file system instance to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_file_system(duckdb_file_system *file_system);\n\n/*!\nRetrieves the last error that occurred on the given file system instance.\n\n* @param file_system The file system instance.\n* @return The error data.\n*/\nDUCKDB_C_API duckdb_error_data duckdb_file_system_error_data(duckdb_file_system file_system);\n\n/*!\nOpens a file at the given path with the specified options.\n\n* @param file_system The file system instance.\n* @param path The path to the file.\n* @param options The file open options specifying how to open the file.\n* @param out_file The resulting file handle instance, or `nullptr` if the open failed. Must be destroyed with\n`duckdb_destroy_file_handle`.\n* @return Whether the operation was successful. If not, the error data can be retrieved using\n`duckdb_file_system_error_data`.\n*/\nDUCKDB_C_API duckdb_state duckdb_file_system_open(duckdb_file_system file_system, const char *path,\n                                                  duckdb_file_open_options options, duckdb_file_handle *out_file);\n\n/*!\nCreates a new file open options instance with blank settings.\n\n* @return The new file open options instance. Must be destroyed with `duckdb_destroy_file_open_options`.\n*/\nDUCKDB_C_API duckdb_file_open_options duckdb_create_file_open_options();\n\n/*!\nSets a specific flag in the file open options.\n\n* @param options The file open options instance.\n* @param flag The flag to set (e.g., read, write).\n* @param value If the flag is enabled or disabled.\n* @return `DuckDBSuccess` on success or `DuckDBError` if the flag is unrecognized or unsupported by this version of\nDuckDB.\n*/\nDUCKDB_C_API duckdb_state duckdb_file_open_options_set_flag(duckdb_file_open_options options, duckdb_file_flag flag,\n                                                            bool value);\n\n/*!\nDestroys the given file open options instance.\n* @param options The file open options instance to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_file_open_options(duckdb_file_open_options *options);\n\n/*!\nDestroys the given file handle and deallocates all associated resources.\nThis will also close the file if it is still open.\n\n* @param file_handle The file handle to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_file_handle(duckdb_file_handle *file_handle);\n\n/*!\nRetrieves the last error that occurred on the given file handle.\n\n* @param file_handle The file handle.\n* @return The error data. Must be destroyed with `duckdb_destroy_error_data`\n*/\nDUCKDB_C_API duckdb_error_data duckdb_file_handle_error_data(duckdb_file_handle file_handle);\n\n/*!\nReads data from the file into the buffer.\n\n* @param file_handle The file handle to read from.\n* @param buffer The buffer to read data into.\n* @param size The number of bytes to read.\n* @return The number of bytes actually read, or negative on error.\n*/\nDUCKDB_C_API int64_t duckdb_file_handle_read(duckdb_file_handle file_handle, void *buffer, int64_t size);\n\n/*!\nWrites data from the buffer to the file.\n\n* @param file_handle The file handle to write to.\n* @param buffer The buffer containing data to write.\n* @param size The number of bytes to write.\n* @return The number of bytes actually written, or negative on error.\n*/\nDUCKDB_C_API int64_t duckdb_file_handle_write(duckdb_file_handle file_handle, const void *buffer, int64_t size);\n\n/*!\nTells the current position in the file.\n\n* @param file_handle The file handle to tell the position of.\n* @return The current position in the file, or negative on error.\n*/\nDUCKDB_C_API int64_t duckdb_file_handle_tell(duckdb_file_handle file_handle);\n\n/*!\nGets the size of the file.\n\n* @param file_handle The file handle to get the size of.\n* @return The size of the file in bytes, or negative on error.\n*/\nDUCKDB_C_API int64_t duckdb_file_handle_size(duckdb_file_handle file_handle);\n\n/*!\nSeeks to a specific position in the file.\n\n* @param file_handle The file handle to seek in.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure. If unsuccessful, the error data can be retrieved using\n`duckdb_file_handle_error_data`.\n*/\nDUCKDB_C_API duckdb_state duckdb_file_handle_seek(duckdb_file_handle file_handle, int64_t position);\n\n/*!\nSynchronizes the file's state with the underlying storage.\n\n* @param file_handle The file handle to synchronize.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure. If unsuccessful, the error data can be retrieved using\n`duckdb_file_handle_error_data`.\n*/\nDUCKDB_C_API duckdb_state duckdb_file_handle_sync(duckdb_file_handle file_handle);\n\n/*!\nCloses the given file handle.\n\n* @param file_handle The file handle to close.\n* @return `DuckDBSuccess` on success or `DuckDBError` on failure. If unsuccessful, the error data can be retrieved using\n`duckdb_file_handle_error_data`.\n*/\nDUCKDB_C_API duckdb_state duckdb_file_handle_close(duckdb_file_handle file_handle);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Config Options Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to create, configure, and register custom configuration options.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a configuration option instance.\n\n* @return The resulting configuration option instance. Must be destroyed with `duckdb_destroy_config_option`.\n*/\nDUCKDB_C_API duckdb_config_option duckdb_create_config_option();\n\n/*!\nDestroys the given configuration option instance.\n* @param option The configuration option instance to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_config_option(duckdb_config_option *option);\n\n/*!\nSets the name of the configuration option.\n\n* @param option The configuration option instance.\n* @param name The name to set.\n*/\nDUCKDB_C_API void duckdb_config_option_set_name(duckdb_config_option option, const char *name);\n\n/*!\nSets the type of the configuration option.\n\n* @param option The configuration option instance.\n* @param type The type to set.\n*/\nDUCKDB_C_API void duckdb_config_option_set_type(duckdb_config_option option, duckdb_logical_type type);\n\n/*!\nSets the default value of the configuration option.\nIf the type of this option has already been set with `duckdb_config_option_set_type`, the value is cast to the type.\nOtherwise, the type is inferred from the value.\n\n* @param option The configuration option instance.\n* @param default_value The default value to set.\n*/\nDUCKDB_C_API void duckdb_config_option_set_default_value(duckdb_config_option option, duckdb_value default_value);\n\n/*!\nSets the default scope of the configuration option.\nIf not set, this defaults to `DUCKDB_CONFIG_OPTION_SCOPE_SESSION`.\n\n* @param option The configuration option instance.\n* @param default_scope The default scope to set.\n*/\nDUCKDB_C_API void duckdb_config_option_set_default_scope(duckdb_config_option option,\n                                                         duckdb_config_option_scope default_scope);\n\n/*!\nSets the description of the configuration option.\n\n* @param option The configuration option instance.\n* @param description The description to set.\n*/\nDUCKDB_C_API void duckdb_config_option_set_description(duckdb_config_option option, const char *description);\n\n/*!\nRegisters the given configuration option on the specified connection.\n\n* @param connection The connection to register the option on.\n* @param option The configuration option instance to register.\n* @return A duckdb_state indicating success or failure.\n*/\nDUCKDB_C_API duckdb_state duckdb_register_config_option(duckdb_connection connection, duckdb_config_option option);\n\n/*!\nRetrieves the value of a configuration option by name from the given client context.\n\n* @param context The client context.\n* @param name The name of the configuration option to retrieve.\n* @param out_scope Output parameter to optionally store the scope that the configuration option was retrieved from.\nIf this is `nullptr`, the scope is not returned.\nIf the requested option does not exist the scope is set to `DUCKDB_CONFIG_OPTION_SCOPE_INVALID`.\n* @return The value of the configuration option. Returns `nullptr` if the option does not exist.\n*/\nDUCKDB_C_API duckdb_value duckdb_client_context_get_config_option(duckdb_client_context context, const char *name,\n                                                                  duckdb_config_option_scope *out_scope);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Copy Functions\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to copy data from and to external file formats.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a new empty copy function.\n\nThe return value must be destroyed with `duckdb_destroy_copy_function`.\n\n* @return The copy function object.\n*/\nDUCKDB_C_API duckdb_copy_function duckdb_create_copy_function();\n\n/*!\nSets the name of the copy function.\n\n* @param copy_function The copy function\n* @param name The name to set\n*/\nDUCKDB_C_API void duckdb_copy_function_set_name(duckdb_copy_function copy_function, const char *name);\n\n/*!\nSets the extra info pointer of the copy function, which can be used to store arbitrary data.\n\n* @param copy_function The copy function\n* @param extra_info The extra info pointer\n* @param destructor  A destructor function to call to destroy the extra info\n*/\nDUCKDB_C_API void duckdb_copy_function_set_extra_info(duckdb_copy_function copy_function, void *extra_info,\n                                                      duckdb_delete_callback_t destructor);\n\n/*!\nRegisters the given copy function on the database connection under the specified name.\n\n* @param connection The database connection\n* @param copy_function The copy function to register\n*/\nDUCKDB_C_API duckdb_state duckdb_register_copy_function(duckdb_connection connection,\n                                                        duckdb_copy_function copy_function);\n\n/*!\nDestroys the given copy function object.\n* @param copy_function The copy function to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_copy_function(duckdb_copy_function *copy_function);\n\n/*!\nSets the bind function of the copy function, to use when binding `COPY ... TO`.\n\n* @param bind The bind function\n*/\nDUCKDB_C_API void duckdb_copy_function_set_bind(duckdb_copy_function copy_function, duckdb_copy_function_bind_t bind);\n\n/*!\nReport that an error occurred during the binding-phase of a `COPY ... TO` function.\n\n* @param info The bind info provided to the bind function\n* @param error The error message\n*/\nDUCKDB_C_API void duckdb_copy_function_bind_set_error(duckdb_copy_function_bind_info info, const char *error);\n\n/*!\nRetrieves the extra info pointer of the copy function.\n\n* @param info The bind info provided to the bind function\n* @return The extra info pointer.\n*/\nDUCKDB_C_API void *duckdb_copy_function_bind_get_extra_info(duckdb_copy_function_bind_info info);\n\n/*!\nRetrieves the client context of the current connection binding the `COPY ... TO` function.\n\nMust be destroyed with `duckdb_destroy_client_context`\n\n* @param info The bind info provided to the bind function\n* @return The client context.\n*/\nDUCKDB_C_API duckdb_client_context duckdb_copy_function_bind_get_client_context(duckdb_copy_function_bind_info info);\n\n/*!\nRetrieves the number of columns that will be provided to the `COPY ... TO` function.\n\n* @param info The bind info provided to the bind function\n* @return The number of columns.\n*/\nDUCKDB_C_API idx_t duckdb_copy_function_bind_get_column_count(duckdb_copy_function_bind_info info);\n\n/*!\nRetrieves the type of a column that will be provided to the `COPY ... TO` function.\n\n* @param info The bind info provided to the bind function\n* @param col_idx The index of the column to retrieve the type for\n* @return The type of the column. Must be destroyed with `duckdb_destroy_logical_type`.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_copy_function_bind_get_column_type(duckdb_copy_function_bind_info info,\n                                                                           idx_t col_idx);\n\n/*!\nRetrieves all values for the given options provided to the `COPY ... TO` function.\n\n* @param info The bind info provided to the bind function\n* @return A STRUCT value containing all options as fields. Must be destroyed with `duckdb_destroy_value`.\n*/\nDUCKDB_C_API duckdb_value duckdb_copy_function_bind_get_options(duckdb_copy_function_bind_info info);\n\n/*!\nSets the bind data of the copy function, to be provided to the init, sink and finalize functions.\n\n* @param info The bind info provided to the bind function\n* @param bind_data The bind data pointer\n* @param destructor  A destructor function to call to destroy the bind data\n*/\nDUCKDB_C_API void duckdb_copy_function_bind_set_bind_data(duckdb_copy_function_bind_info info, void *bind_data,\n                                                          duckdb_delete_callback_t destructor);\n\n/*!\nSets the initialization function of the copy function, called right before executing `COPY ... TO`.\n\n* @param init The init function\n*/\nDUCKDB_C_API void duckdb_copy_function_set_global_init(duckdb_copy_function copy_function,\n                                                       duckdb_copy_function_global_init_t init);\n\n/*!\nReport that an error occurred during the initialization-phase of a `COPY ... TO` function.\n\n* @param info The init info provided to the init function\n* @param error The error message\n*/\nDUCKDB_C_API void duckdb_copy_function_global_init_set_error(duckdb_copy_function_global_init_info info,\n                                                             const char *error);\n\n/*!\nRetrieves the extra info pointer of the copy function.\n\n* @param info The init info provided to the init function\n* @return The extra info pointer.\n*/\nDUCKDB_C_API void *duckdb_copy_function_global_init_get_extra_info(duckdb_copy_function_global_init_info info);\n\n/*!\nRetrieves the client context of the current connection initializing the `COPY ... TO` function.\n\nMust be destroyed with `duckdb_destroy_client_context`\n\n* @param info The init info provided to the init function\n* @return The client context.\n*/\nDUCKDB_C_API duckdb_client_context\nduckdb_copy_function_global_init_get_client_context(duckdb_copy_function_global_init_info info);\n\n/*!\nRetrieves the bind data provided during the binding-phase of a `COPY ... TO` function.\n\n* @param info The init info provided to the init function\n* @return The bind data pointer.\n*/\nDUCKDB_C_API void *duckdb_copy_function_global_init_get_bind_data(duckdb_copy_function_global_init_info info);\n\n/*!\nRetrieves the file path provided to the `COPY ... TO` function.\n\nLives for the duration of the initialization callback, must not be destroyed.\n\n* @param info The init info provided to the init function\n* @return The file path.\n*/\nDUCKDB_C_API const char *duckdb_copy_function_global_init_get_file_path(duckdb_copy_function_global_init_info info);\n\n/*!\nSets the global state of the copy function, to be provided to all subsequent local init, sink and finalize functions.\n\n* @param info The init info provided to the init function\n* @param global_state The global state pointer\n* @param destructor  A destructor function to call to destroy the global state\n*/\nDUCKDB_C_API void duckdb_copy_function_global_init_set_global_state(duckdb_copy_function_global_init_info info,\n                                                                    void *global_state,\n                                                                    duckdb_delete_callback_t destructor);\n\n/*!\nSets the sink function of the copy function, called during `COPY ... TO`.\n\n* @param function The sink function\n*/\nDUCKDB_C_API void duckdb_copy_function_set_sink(duckdb_copy_function copy_function,\n                                                duckdb_copy_function_sink_t function);\n\n/*!\nReport that an error occurred during the sink-phase of a `COPY ... TO` function.\n\n* @param info The sink info provided to the sink function\n* @param error The error message\n*/\nDUCKDB_C_API void duckdb_copy_function_sink_set_error(duckdb_copy_function_sink_info info, const char *error);\n\n/*!\nRetrieves the extra info pointer of the copy function.\n\n* @param info The sink info provided to the sink function\n* @return The extra info pointer.\n*/\nDUCKDB_C_API void *duckdb_copy_function_sink_get_extra_info(duckdb_copy_function_sink_info info);\n\n/*!\nRetrieves the client context of the current connection during the sink-phase of the `COPY ... TO` function.\n\nMust be destroyed with `duckdb_destroy_client_context`\n\n* @param info The sink info provided to the sink function\n* @return The client context.\n*/\nDUCKDB_C_API duckdb_client_context duckdb_copy_function_sink_get_client_context(duckdb_copy_function_sink_info info);\n\n/*!\nRetrieves the bind data provided during the binding-phase of a `COPY ... TO` function.\n\n* @param info The sink info provided to the sink function\n* @return The bind data pointer.\n*/\nDUCKDB_C_API void *duckdb_copy_function_sink_get_bind_data(duckdb_copy_function_sink_info info);\n\n/*!\nRetrieves the global state provided during the init-phase of a `COPY ... TO` function.\n\n* @param info The sink info provided to the sink function\n* @return The global state pointer.\n*/\nDUCKDB_C_API void *duckdb_copy_function_sink_get_global_state(duckdb_copy_function_sink_info info);\n\n/*!\nSets the finalize function of the copy function, called at the end of `COPY ... TO`.\n\n* @param finalize The finalize function\n*/\nDUCKDB_C_API void duckdb_copy_function_set_finalize(duckdb_copy_function copy_function,\n                                                    duckdb_copy_function_finalize_t finalize);\n\n/*!\nReport that an error occurred during the finalize-phase of a `COPY ... TO` function\n\n* @param info The finalize info provided to the finalize function\n* @param error The error message\n*/\nDUCKDB_C_API void duckdb_copy_function_finalize_set_error(duckdb_copy_function_finalize_info info, const char *error);\n\n/*!\nRetrieves the extra info pointer of the copy function.\n\n* @param info The finalize info provided to the finalize function\n* @return The extra info pointer.\n*/\nDUCKDB_C_API void *duckdb_copy_function_finalize_get_extra_info(duckdb_copy_function_finalize_info info);\n\n/*!\nRetrieves the client context of the current connection during the finalize-phase of the `COPY ... TO` function.\n\nMust be destroyed with `duckdb_destroy_client_context`\n\n* @param info The finalize info provided to the finalize function\n* @return The client context.\n*/\nDUCKDB_C_API duckdb_client_context\nduckdb_copy_function_finalize_get_client_context(duckdb_copy_function_finalize_info info);\n\n/*!\nRetrieves the bind data provided during the binding-phase of a `COPY ... TO` function.\n\n* @param info The finalize info provided to the finalize function\n* @return The bind data pointer.\n*/\nDUCKDB_C_API void *duckdb_copy_function_finalize_get_bind_data(duckdb_copy_function_finalize_info info);\n\n/*!\nRetrieves the global state provided during the init-phase of a `COPY ... TO` function.\n\n* @param info The finalize info provided to the finalize function\n* @return The global state pointer.\n*/\nDUCKDB_C_API void *duckdb_copy_function_finalize_get_global_state(duckdb_copy_function_finalize_info info);\n\n/*!\nSets the table function to use when executing a `COPY ... FROM (...)` statement with this copy function.\n\nThe table function must have a `duckdb_table_function_bind_t`, `duckdb_table_function_init_t` and\n`duckdb_table_function_t` set.\n\nThe table function must take a single VARCHAR parameter (the file path).\n\nOptions passed to the `COPY ... FROM (...)` statement are forwarded as named parameters to the table function.\n\nSince `COPY ... FROM` copies into an already existing table, the table function should not define its own result columns\nusing `duckdb_bind_add_result_column` when binding . Instead use `duckdb_table_function_bind_get_result_column_count`\nand related functions in the bind callback of the table function to retrieve the schema of the target table of the `COPY\n... FROM` statement.\n\n* @param copy_function The copy function\n* @param table_function The table function to use for `COPY ... FROM`\n*/\nDUCKDB_C_API void duckdb_copy_function_set_copy_from_function(duckdb_copy_function copy_function,\n                                                              duckdb_table_function table_function);\n\n/*!\nRetrieves the number of result columns of a table function.\n\nIf the table function is used in a `COPY ... FROM` statement, this can be used to retrieve the number of columns in the\ntarget table at the start of the bind callback.\n\n* @param info The bind info provided to the bind function\n* @return The number of result columns.\n*/\nDUCKDB_C_API idx_t duckdb_table_function_bind_get_result_column_count(duckdb_bind_info info);\n\n/*!\nRetrieves the name of a result column of a table function.\n\nIf the table function is used in a `COPY ... FROM` statement, this can be used to retrieve the names of the columns in\nthe target table at the start of the bind callback.\n\nThe result is valid for the duration of the bind callback or until the next call to `duckdb_bind_add_result_column`, so\nit must not be destroyed.\n\n* @param info The bind info provided to the bind function\n* @param col_idx The index of the result column to retrieve the name for\n* @return The name of the result column.\n*/\nDUCKDB_C_API const char *duckdb_table_function_bind_get_result_column_name(duckdb_bind_info info, idx_t col_idx);\n\n/*!\nRetrieves the type of a result column of a table function.\n\nIf the table function is used in a `COPY ... FROM` statement, this can be used to retrieve the types of the columns in\nthe target table at the start of the bind callback.\n\nThe result must be destroyed with `duckdb_destroy_logical_type`.\n\n* @param info The bind info provided to the bind function\n* @param col_idx The index of the result column to retrieve the type for\n* @return The type of the result column.\n*/\nDUCKDB_C_API duckdb_logical_type duckdb_table_function_bind_get_result_column_type(duckdb_bind_info info,\n                                                                                   idx_t col_idx);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Catalog Interface\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to interact with database catalogs and catalog entries.\n// You will most likely not need this API for typical usage of DuckDB as SQL is the preferred way to interact with the\n// database, but this interface can be useful for advanced extensions that need to inspect the state of the catalog from\n// inside a running query.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nRetrieve a database catalog instance by name.\nThis function can only be called from within the context of an active transaction, e.g. during execution of a registered\nfunction callback. Otherwise returns `nullptr`.\n* @param context The client context.\n* @param catalog_name The name of the catalog.\n* @return The resulting catalog instance, or `nullptr` if called from outside an active transaction or if a catalog with\nthe specified name does not exist. Must be destroyed with `duckdb_destroy_catalog`\n*/\nDUCKDB_C_API duckdb_catalog duckdb_client_context_get_catalog(duckdb_client_context context, const char *catalog_name);\n\n/*!\nRetrieve the \"type name\" of the given catalog.\nE.g. for a DuckDB database, this returns 'duckdb'.\nThe returned string is owned by the catalog and remains valid until the catalog is destroyed.\n\n* @param catalog The catalog.\n* @return The type name of the catalog.\n*/\nDUCKDB_C_API const char *duckdb_catalog_get_type_name(duckdb_catalog catalog);\n\n/*!\nRetrieve a catalog entry from the given catalog by type, schema name and entry name.\nThe returned catalog entry remains valid for the duration of the current transaction.\n\n* @param catalog The catalog.\n* @param context The client context.\n* @param entry_type The type of the catalog entry to retrieve.\n* @param schema_name The schema name of the catalog entry.\n* @param entry_name The name of the catalog entry.\n* @return The resulting catalog entry, or `nullptr` if no such entry exists. Must be destroyed with\n`duckdb_destroy_catalog_entry`. Remains valid for the duration of the current transaction.\n*/\nDUCKDB_C_API duckdb_catalog_entry duckdb_catalog_get_entry(duckdb_catalog catalog, duckdb_client_context context,\n                                                           duckdb_catalog_entry_type entry_type,\n                                                           const char *schema_name, const char *entry_name);\n\n/*!\nDestroys the given catalog instance.\n\nNote that this does not actually \"drop\" the contents of the catalog; it merely frees the C API handle.\n\n* @param catalog The catalog instance to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_catalog(duckdb_catalog *catalog);\n\n/*!\nGet the type of the given catalog entry.\n\n* @param entry The catalog entry.\n* @return The type of the catalog entry.\n*/\nDUCKDB_C_API duckdb_catalog_entry_type duckdb_catalog_entry_get_type(duckdb_catalog_entry entry);\n\n/*!\nGet the name of the given catalog entry.\n\n* @param entry The catalog entry.\n* @return The name of the catalog entry. The returned string is owned by the catalog entry and remains valid until the\ncatalog entry is destroyed.\n*/\nDUCKDB_C_API const char *duckdb_catalog_entry_get_name(duckdb_catalog_entry entry);\n\n/*!\nDestroys the given catalog entry instance.\n\nNote that this does not actually \"drop\" the catalog entry from the database catalog; it merely frees the C API handle.\n\n* @param entry The catalog entry instance to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_catalog_entry(duckdb_catalog_entry *entry);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Logging\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions exposing the log storage, which allows the configuration of a custom logger. This API is not yet ready to\n// be stabilized.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nCreates a new log storage object.\n\n* @return A log storage object. Must be destroyed with `duckdb_destroy_log_storage`.\n*/\nDUCKDB_C_API duckdb_log_storage duckdb_create_log_storage();\n\n/*!\nDestroys a log storage object.\n\n* @param log_storage The log storage object to destroy.\n*/\nDUCKDB_C_API void duckdb_destroy_log_storage(duckdb_log_storage *log_storage);\n\n/*!\nSets the callback function for writing log entries.\n\n* @param log_storage The log storage object.\n* @param function The function to call.\n*/\nDUCKDB_C_API void duckdb_log_storage_set_write_log_entry(duckdb_log_storage log_storage,\n                                                         duckdb_logger_write_log_entry_t function);\n\n/*!\nSets the extra data of the custom log storage.\n\n* @param log_storage The log storage object.\n* @param extra_data The extra data that is passed back into the callbacks.\n* @param delete_callback The delete callback to call on the extra data, if any.\n*/\nDUCKDB_C_API void duckdb_log_storage_set_extra_data(duckdb_log_storage log_storage, void *extra_data,\n                                                    duckdb_delete_callback_t delete_callback);\n\n/*!\nSets the name of the log storage.\n\n* @param log_storage The log storage object.\n* @param name The name of the log storage.\n*/\nDUCKDB_C_API void duckdb_log_storage_set_name(duckdb_log_storage log_storage, const char *name);\n\n/*!\nRegisters a custom log storage for the logger.\n\n* @param database A database object.\n* @param log_storage The log storage object.\n* @return Whether the registration was successful.\n*/\nDUCKDB_C_API duckdb_state duckdb_register_log_storage(duckdb_database database, duckdb_log_storage log_storage);\n\n//----------------------------------------------------------------------------------------------------------------------\n// Geometry Helpers\n//----------------------------------------------------------------------------------------------------------------------\n// DESCRIPTION:\n// Functions to operate on GEOMETRY types`.\n//----------------------------------------------------------------------------------------------------------------------\n\n/*!\nGets the CRS (Coordinate Reference System) of a GEOMETRY type.\nResult must be freed with `duckdb_free`.\n\n* @param type The GEOMETRY type.\n* @return The CRS of the GEOMETRY type, or NULL if the type is not a GEOMETRY type.\n*/\nDUCKDB_C_API char *duckdb_geometry_type_get_crs(duckdb_logical_type type);\n\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "readme.md",
    "content": "# Zig driver for DuckDB.\n\n## Zig Version\nThis is for Zig 0.16.0. Use the [zig-0.15](https://github.com/karlseguin/zuckdb.zig/tree/zig-0.15) branch for Zig 0.15 or the [dev](https://github.com/karlseguin/zuckdb.zig/tree/dev) which may or may not be up to date with zig dev.\n\n## Quick Example\n```zig\nconst db = try zuckdb.DB.init(io, allocator, \"/tmp/db.duck\", .{});\ndefer db.deinit();\n\nvar conn = try db.conn();\ndefer conn.deinit();\n\n// for insert/update/delete returns the # changed rows\n// returns 0 for other statements\n_ = try conn.exec(\"create table users(id int)\", .{});\n\nvar rows = try conn.query(\"select * from users\", .{});\ndefer rows.deinit();\n\nwhile (try rows.next()) |row| {\n    // get the 0th column of the current row\n    const id = row.get(i32, 0);\n    std.debug.print(\"The id is: {d}\", .{id});\n}\n```\n\nAny non-primitive value that you get from the `row` are valid only until the next call to `next` or `deinit`.\n\n## Install\nThis library is tested with DuckDB 1.5.2. You can either link to an existing libduckdb on your system, or have zuckdb download and build DuckDB for you (this will take time.)\n\n1) Add zuckdb as a dependency in your `build.zig.zon`:\n\n```bash\nzig fetch --save git+https://github.com/karlseguin/zuckdb.zig#master\n```\n\n### Link to libduckdb\n\n1) Download the libduckdb from the <a href=\"https://duckdb.org/docs/installation/index.html?version=latest&environment=cplusplus&installer=binary\">DuckDB download page</a>. \n\n2) Place the `duckdb.h` file and the `libduckdb.so` (linux) or `libduckdb.dylib` (mac) in your project's `lib` folder.\n\n3) Add this in `build.zig`:\n\n```zig\nconst zuckdb = b.dependency(\"zuckdb\", .{\n    .target = target,\n    .optimize = optimize,\n}).module(\"zuckdb\");\n\n// Your app's program\nconst exe = b.addExecutable(.{\n    .name = \"run\",\n    .target = target,\n    .optimize = optimize,\n    .root_source_file = b.path(\"src/main.zig\"),\n});\n// include the zuckdb module\nexe.root_module.addImport(\"zuckdb\", zuckdb);\n\n// link to libduckdb\nexe.linkSystemLibrary(\"duckdb\"); \n\n// tell the linker where to find libduckdb.so (linux) or libduckdb.dylib (macos)\nexe.addLibraryPath(b.path(\"lib/\"));\n```\n\n### Automatically fetch and buikd DuckDB\n\n1) Add this in `build.zig`:\n\n```zig\nconst zuckdb = b.dependency(\"zuckdb\", .{\n    .target = target,\n    .optimize = optimize,\n    .system_libduckdb = false,\n    .debug_duckdb = false, // optional, compile DuckDB with DUCKDB_DEBUG_STACKTRACE  or not\n}).module(\"zuckdb\");\n\n// Your app's program\nconst exe = b.addExecutable(.{\n    .name = \"run\",\n    .target = target,\n    .optimize = optimize,\n    .root_source_file = b.path(\"src/main.zig\"),\n});\n// include the zuckdb module\nexe.root_module.addImport(\"zuckdb\", zuckdb);\n```\n\n### Static Linking\nIt's also possible to statically link DuckDB. In order to do this, you must build DuckDB yourself, in order to [compile it using Zig C++](https://github.com/ziglang/zig/issues/9832#issuecomment-926832810) and using the [bundle-library](https://github.com/duckdb/duckdb/issues/9475) target\n\n```\ngit clone -b 1.5.2 --single-branch https://github.com/duckdb/duckdb.git\ncd duckdb\nexport CXX=\"zig c++\"\nDUCKDB_EXTENSIONS='json' make bundle-library\n```\n\nWhen this finished (it will take several minutes), you can copy `build/release/libduckdb_bundle.a` and `src/include/duckdb.h` to your project's `lib` folder. Rename `libduckdb_bundle.a` to `libduckdb.a`.\n\nFinally, Add the following to your `build.zig`:\n\n```zig\nexe.linkSystemLibrary(\"duckdb\");\nexe.linkSystemLibrary(\"stdc++\");\nexe.addLibraryPath(b.path(\"lib/\"));\n```\n\n# DB\nThe `DB` is used to initialize the database, open connections and, optionally, create a connection pool.\n\n## init\nCreates or opens the database.\n\n```zig\n// can use the special path \":memory:\" for an in-memory database\nconst db = try DB.init(allocator, \"/tmp/db.duckdb\", .{});\ndefer db.deinit();\n```\n\nThe 3rd parameter is for options. The available options, with their default, are:\n\n* `access_mode` - Sets the `access_mode` DuckDB configuration. Defaults to `.automatic`. Valid options are: `.automatic`, `.read_only` or `.read_write`.\n* `enable_external_access` - Sets the `enable_external_access` DuckDB configuration. Defaults to `true`.\n\n## initWithErr\nSame as `init`, but takes a 4th output parameter. On open failure, the output parameter will be set to the error message. This parameter must be freed if set.\n\n```zig\nvar open_err: ?[]u8 = null;\nconst db = DB.initWithErr(allocator, \"/does/not/exist\", .{}, &open_err) catch |err| {\n    if (err == error.OpenDB) {\n        defer allocator.free(open_err.?);\n        std.debug.print(\"DB open: {}\", .{open_err.?});\n    }\n    return err;\n};\n```\n\n## deinit\nCloses the database.\n\n## conn\nReturns a new [connection](#conn-1) object. \n\n```zig\nvar conn = try db.conn();\ndefer conn.deinit();\n...\n```\n\n## pool\nInitializes a [pool](#pool-1) of connections to the DB. \n\n```zig\nvar pool = try db.pool(.{.size = 2});\n\n// the pool owns the `db`, so pool.deinit will call `db.deinit`.\ndefer pool.deinit();\n\nvar conn = try pool.acquire();\ndefer pool.release(conn);\n```\n\nThe `pool` method takes an options parameter:\n* `size: usize` - The number of connections to keep in the pool. Defaults to `5`\n* `timeout: u64` - The time, in milliseconds, to wait for a connetion to be available when calling `pool.acquire()`. Defaults to `10_000`.\n* `on_connection: ?*const fn(conn: *Conn) anyerror!void` - The function to call when the pool first establishes the connection. Defaults to `null`.\n* `on_first_connection: ?*const fn(conn: *Conn) anyerror!void` - The function to call on the first connection opened by the pool. Defaults to `null`.\n\n# Conn\n\n## query\nUse `conn.query(sql, args) !Rows` to query the database and return a `zuckdb.Rows` which can be iterated. You must call `deinit` on the returned rows.\n\n```zig\nvar rows = try conn.query(\"select * from users where power > $1\", .{9000});\ndefer rows.deinit();\nwhile (try rows.next()) |row| {\n    // ...\n}\n```\n\n## exec\n`conn.exec(sql, args) !usize` is a wrapper around `query` which returns the number of affected rows for insert, updates or deletes.\n\n## row\n`conn.row(sql, args) !?OwningRow` is a wrapper around `query` which returns a single optional row. You must call `deinit` on the returned row:\n\n```zig\nvar row = (try conn.query(\"select * from users where id = $1\", .{22})) orelse return null;;\ndefer row.deinit();\n// ...\n```\n\n## begin/commit/rollback\nThe `conn.begin()`, `conn.commit()` and `conn.rollback()` calls are wrappers around `exec`, e.g.: `conn.exec(\"begin\", .{})`.\n\n## prepare\n`conn.prepare(sql, opts) !Stmt` prepares the given SQL and returns a `zuckdb.Stmt`. For one-off queries, you should prefer using `query`, `exec` or `row` which wrap `prepare` and then call `stmt.bind(values)` and finally `stmt.execute()`. Getting an explicit [Stmt](#stmt) is useful when executing the same statement multiple times with different values.\n\nValues for opts are:\n\n* `auto_release: bool` - This defaults to and should usually be kept as `false`. When `true`, the statement is automatically discarded (`deinit`) after the result of its first execution is complete. If you're going to set this to `true`, you might as well use `conn.exec`, `conn.query` or `conn.row` instead of getting an explicit statement.\n\n## err\nIf a method of `conn` returns `error.DuckDBError`, `conn.err` will be set:\n\n```zig\nvar rows = conn.query(\"....\", .{}) catch |err| {\n  if (err == error.DuckDBError) {\n    if (conn.err) |derr| {\n      std.log.err(\"DuckDB {s}\\n\", .{derr});\n    }\n  }\n  return err;\n}\n```\n\nIn the above snippet, it's possible to skip the `if (err == error.DuckDBError)`check, but in that case conn.err could be set from some previous command (conn.err is always reset when acquired from the pool).\n\n## release\n`conn.release()` will release the connection back to the pool. This does nothing if the connection did not come from the pool (i.e. `pool.acquire()`). This is the same as calling `pool.release(conn)`.\n\n# Rows\nThe `rows` returned from `conn.query` exposes the following methods:\n\n* `count()` - the number of rows in the result\n* `changed()` - the number of updated/deleted/inserted rows\n* `columnName(i: usize)` - the column name at position `i` in a result\n* `deinit()` - must be called to free resources associated with the result\n* `next() !?Row` - returns the next row\n\nThe most important method on `rows` is `next()` which is used to iterate the results. `next()` is a typical Zig iterator and returns a `?Row` which will be null when no more rows exist to be iterated.\n\n# Row\n\n## get\n`Row` exposes a `get(T, index) T` function. This function trusts you! If you ask for an <code>i32</code> the library will crash if the column is not an <code>int4</code>. Similarly, if the value can be null, you must use the optional type, e.g. <code>?i32</code>.\n\nThe supported types for `get`, are:\n* `[]u8`, \n* `[]const u8`\n* `i8`\n* `i16`\n* `i32`\n* `i64`\n* `i128`\n* `u8`\n* `u16`\n* `u32`\n* `u64`\n* `f32`\n* `f64`\n* `bool`\n* `zuckdb.Date`\n* `zuckdb.Time`\n* `zuckdb.Interval`\n* `zuckdb.UUID`\n* `zudkdb.Enum`\n\nOptional version of the above are all supported **and must be used** if it's possible the value is null.\n\nString values and enums are only valid until the next call to `next()` or `deinit`. You must dupe the values if you want them to outlive the row.\n\n## list\n`Row` exposes a `list` method which behaves similar to `get` but returns a `zuckdb.List(T)`.\n\n```zig\nconst row = (try conn.row(\"select [1, 32, 99, null, -4]::int[]\", .{})) orelse unreachable;\ndefer row.deinit();\n\nconst list = row.list(?i32, 0).?;\ntry t.expectEqual(5, list.len);\ntry t.expectEqual(1, list.get(0).?);\ntry t.expectEqual(32, list.get(1).?);\ntry t.expectEqual(99, list.get(2).?);\ntry t.expectEqual(null, list.get(3));\ntry t.expectEqual(-4, list.get(4).?);\n```\n\n`list()` always returns a nullable, i.e. `?zuckdb.List(T)`. Besides the `len` field, `get` is used on the provided list to return a value at a specific index. `row.list(T, col).get(idx)` works with any of the types supported by `row.get(col)`.\n\na `List(T)` also has a `alloc(allocator: Allocator) ![]T` method. This will allocate a `[]T` and fill it with the list values. It is the caller's responsibility to free the returned slice.\n\nAlternatively, `fill(into: []T) void` can be used used to populate `into` with items from the list. This will fill `@min(into.len, list.len)` values.\n\n# zuckdb.Enum\nThe `zuckdb.Enum` is a special type which exposes two functions: `raw() [*c]const u8` and `rowCache() ![]const u8`.\n\n`raw()` directly returns the DuckDB enum string value. If you want to turn this into a `[]const u8`, you'll need to wrap it in `std.mem.span`. The value returned by `raw()` is only valid until the next iteration.\n\n`rowCache()` takes the result of `raw()`, and dupes it, giving ownership to the Rows. Thus, the string returned by `rowCache()` outlives the current row iteration and is valid until `rows.deinit()` is called. Essentially, it is an interned string representation of the enum value (which DuckDB internally represents as an integer).\n\n# Pool\nThe `zuckdb.Pool` is a thread-safe connection pool:\n\n```zig\nconst db = try zuckdb.DB.init(allocator, \"/tmp/duckdb.zig.test\", .{});\nvar pool = db.pool(.{\n    .size = 2,\n    .on_connection = &connInit,\n    .on_first_connection = &poolInit,\n});\ndefer pool.deinit();\n\nvar conn = try pool.acquire();\ndefer conn.release();\n```\n\nThe Pool takes ownership of the DB object, thus `db.deinit` does not need to be called The `on_connection` and `on_first_connection` are optional callbacks. They both have the same signature:\n\n```zig\n?*const fn(conn: *Conn) anyerror!void\n```\n\nIf both are specific, the first initialized connection will first be passed to `on_first_connection` and then to `on_connection`.\n\n## newConn() !Conn\nBesides using `acquire()` to get a `!*Conn` from the pool, it's possible to create a new connection detached from the pool using `pool.newConn()`.  This is the same as calling `db.conn()` but, on the pool. Again, this connection will not be part of the pool and `release()` should not be called on it (but `deinit()` should).\n\n## exec/query/row\nThe `pool.exec`, `pool.query`, `pool.queryWithState`, `pool.row` and `pool.rowWithState` are convenience functions which behave like their `Conn` counterparts. \n\n`pool.exec` is the same as:\n\n```zig\nvar conn = try pool.acquire();\ndefer conn.release();\nreturn conn.exec(sql, args);\n```\n\n`pool.query`, `pool.queryWithState`, `pool.row` and `pool.rowWithState` are similar, except the connection is automatically released back to the pool when the `rows.deinit()` or `row.deinit()` is called.s\n\n# Stmt\nThe `zuckdb.Stmt` encapsulates a prepared statement. It is generated by calling `conn.prepare([]const u8, opts)`.\n\n## deinit() !void\nDeinitializes the statement.\n\n## clearBindings(stmt: \\*const Stmt) !void\nClears any previous bound values.\n\n## bind(stmt: \\*const Stmt, values: anytype) !void\nBinds the values tuple to the statement. \n\n## bindValue(stmt: \\*const Stmt, value: anytype, index: usize) !void\nBinds the specific value to the specified position.\n\n## exec(stmt: \\*const Stmt) !usize\nLike `conn.exec`, this executes the statement returning the number of affected rows. Should not be used with a query that returns results.\n\n## query(stmt: \\*const Stmt) !Rows\nLike `conn.query`, executes a result and returns the rows.\n\n\n# Query Optimizations\nIn very tight loops, performance might be improved by providing a stack-based state for the query logic to use. The `query` and `row` functions all have a `WithState` alternative, e.g.: `queryWithState`. These functions take 1 additional \"query state\" parameter:\n\n```zig\nvar state = zuckdb.StaticState(2){};\nvar rows = try conn.queryWithState(SQL, .{ARGS}, &state);\n// use rows normally\n```\n\nThe value passed to `zuckdb.StaticState` is the number of columns returned by the query. The `state` must remain valid until `rows.deinit()` is called.\n\n# Appender\nThe fastest way to insert a large amount of data is to use the appender:\n\n```zig\n// the first parameter is the schema, or null to use the default schema\nvar appender = try conn.appender(null, \"my_table\");\ndefer appender.deinit();\n\nfor (...) {\n    try appender.appendRow(.{\"over\", 9001, true});\n}\n// The appender auto-flushes, but it should be called once at the end.\ntry appender.flush();\n```\n\nThe order of the values used in `appendRow` is the order of the columns as they are defined in the table (e.g. the order that `describe $table` returns).\n\n## Appender per-column append\nThe `appender.appendRow` function depends on the fact that you have comptime knowledge of the underlying table. If you are dealing with dynamic (e.g. user-defined) schemas, that won't always be the case. Instead, use the more explicit `beginRow()`, `appendValue()` and `endRow()` methods. \n```zig\nfor (...) {\n    appender.beginRow();\n    try appender.appendValue(\"over\", 0);\n    try appender.appendValue(9001, 1);\n    try appender.appendValue(true, 2);\n    try appender.endRow();\n}\ntry appender.flush();\n```\n\nThe `appendRow()` method internally calls `beginRow(), `appendValue()` and `endRow()`.\n\n## Appender Type Support\nThe appender writes directly to the underlying storage and thus cannot leverage default column values. `appendRow` asserts that the # of values matches the number of columns. However, when using the explicit `beginRow` + `appendValue` + `endRow`, you must make sure to append a value for each column, else the behavior is undefined. \n\nEnums aren't supporting, due to [limitations in the DuckDB C API](https://github.com/duckdb/duckdb/pull/11704).\n\nDecimals are supported, but be careful! When appending a float, the value will truncated to the decimal place specified by the scale of the column (i.e. a decimal(8, 3) will have the float truncated with 3 decimal places). When appending an int, the library assumes that you have already converted the decimal to the DuckDB internal representation. While surprising, this provides callers with precise control.\n\nWhen dealing with ints, floats and decimals, appending a single value tends to be flexible. In other words, you can append an `i64` to a `tinyint` column, so long as the value fits (i.e. there's a runtime check). However, when dealing with lists (e.g. `integer[]`), the exact type is required. Thus, only a `[]u16` can be bound to a `usmallint[]` column. `decimal[]` can bind to a `[]i64`, `[]f32` or `[]f64`.\n\nList columns support null values, and thus can be bound to either a `[]const T` or a `[]const ?T`.\n\n## Appender Error\nIf any of the appender methods return an error, you can see if the optional `appender.err` has an error description. This is a `?[]const u8` field. On error, you **should not** assume that this value is set, there are error cases where DuckDB doesn't provide an error description.\n"
  },
  {
    "path": "src/appender.zig",
    "content": "const std = @import(\"std\");\nconst lib = @import(\"lib.zig\");\n\nconst c = lib.c;\nconst Date = lib.Date;\nconst Time = lib.Time;\nconst UUID = lib.UUID;\nconst Interval = lib.Interval;\nconst Vector = lib.Vector;\nconst DuckDBError = c.DuckDBError;\nconst Allocator = std.mem.Allocator;\n\npub const Appender = struct {\n    // Error message, if any\n    err: ?[]const u8,\n\n    // whether or not we own the error. The underlying duckdb appender error is\n    // owned by the the duckdb appender itself, so we don't need to manage it.\n    // but sometimes we create our own error message, in which case we have to\n    // free it.\n    own_err: bool,\n\n    // the row of the current chunk that we're writing at\n    row_index: usize,\n\n    // c.duckdb_vector_size (2048)..when row_index == 2047, we flush and create\n    // a new chunk\n    vector_size: usize,\n\n    allocator: Allocator,\n\n    // 1 vector per column. Part of the vector data is initialied upfront (the\n    // type information). Part of it is initialized for each data_chunk (the\n    // underlying duckdb vector data and the validity data).\n    vectors: []Vector,\n\n    // This is duplicate of data available from vectors, but we need it as a slice\n    // to pass to c.duckdb_create_data_chunk\n    types: []c.duckdb_logical_type,\n\n    // The collection of vectors for the appender. While we store data directly\n    // in the vector, most operations (e.g. flush) happen on the data chunk.\n    data_chunk: ?c.duckdb_data_chunk,\n    appender: *c.duckdb_appender,\n\n    pub fn init(allocator: Allocator, appender: *c.duckdb_appender) !Appender {\n        const column_count = c.duckdb_appender_column_count(appender.*);\n\n        var types = try allocator.alloc(c.duckdb_logical_type, column_count);\n        errdefer allocator.free(types);\n\n        var vectors = try allocator.alloc(Vector, column_count);\n        errdefer allocator.free(vectors);\n\n        var initialized: usize = 0;\n        errdefer for (0..initialized) |i| {\n            vectors[i].deinit();\n        };\n\n        for (0..column_count) |i| {\n            const logical_type = c.duckdb_appender_column_type(appender.*, i);\n            types[i] = logical_type;\n            vectors[i] = try Vector.init(undefined, logical_type);\n            initialized += 1;\n\n            switch (vectors[i].type) {\n                .list => {},\n                .scalar => |scalar| switch (scalar) {\n                    .simple => {},\n                    .decimal => {},\n                    .@\"enum\" => return error.CannotAppendToEnum, // https://github.com/duckdb/duckdb/pull/11704\n                },\n            }\n        }\n\n        return .{\n            .err = null,\n            .own_err = false,\n            .row_index = 0,\n            .types = types,\n            .vectors = vectors,\n            .data_chunk = null,\n            .appender = appender,\n            .allocator = allocator,\n            .vector_size = c.duckdb_vector_size(),\n        };\n    }\n\n    pub fn deinit(self: *Appender) void {\n        for (self.vectors) |*v| {\n            v.deinit();\n        }\n\n        const allocator = self.allocator;\n        allocator.free(self.types);\n        allocator.free(self.vectors);\n\n        if (self.data_chunk) |*data_chunk| {\n            _ = c.duckdb_destroy_data_chunk(data_chunk);\n        }\n\n        const appender = self.appender;\n        _ = c.duckdb_appender_destroy(appender);\n        allocator.destroy(appender);\n    }\n\n    fn newDataChunk(types: []c.duckdb_logical_type, vectors: []Vector) c.duckdb_data_chunk {\n        const data_chunk = c.duckdb_create_data_chunk(types.ptr, types.len);\n\n        for (0..types.len) |i| {\n            const v = c.duckdb_data_chunk_get_vector(data_chunk, i);\n            const vector = &vectors[i];\n            vector.loadVector(v);\n            vector.validity = null;\n        }\n        return data_chunk;\n    }\n\n    pub fn flush(self: *Appender) !void {\n        var data_chunk = self.data_chunk orelse return;\n        c.duckdb_data_chunk_set_size(data_chunk, self.row_index);\n\n        const appender = self.appender;\n        if (c.duckdb_append_data_chunk(appender.*, data_chunk) == DuckDBError) {\n            if (c.duckdb_appender_error(appender.*)) |c_err| {\n                self.setErr(std.mem.span(c_err), false);\n            }\n            return error.DuckDBError;\n        }\n\n        if (c.duckdb_appender_flush(self.appender.*) == DuckDBError) {\n            if (c.duckdb_appender_error(appender.*)) |c_err| {\n                self.setErr(std.mem.span(c_err), false);\n            }\n            return error.DuckDBError;\n        }\n\n        c.duckdb_destroy_data_chunk(&data_chunk);\n        self.data_chunk = null;\n    }\n\n    pub fn appendRow(self: *Appender, values: anytype) !void {\n        // This is to help the caller make sure they set all the values. Not setting\n        // a value is an undefined behavior.\n        std.debug.assert(values.len == self.vectors.len);\n\n        self.beginRow();\n\n        inline for (values, 0..) |value, i| {\n            try self.appendValue(value, i);\n        }\n        try self.endRow();\n    }\n\n    // The appender has two apis. The simplest is to call appendRow, passing the full\n    // row. When using appendRow, things mostly just work.\n    // It's also possible to call appendValue for each column. This API is used\n    // when the \"row\" isn't known at comptime - the app has no choice but to\n    // call appendValue for each column. In such cases, we require an explicit\n    // call to beginRow, bindValue and endRow.\n    pub fn beginRow(self: *Appender) void {\n        if (self.data_chunk == null) {\n            self.data_chunk = newDataChunk(self.types, self.vectors);\n            self.row_index = 0;\n        }\n    }\n\n    pub fn endRow(self: *Appender) !void {\n        const row_index = self.row_index + 1;\n        self.row_index = row_index;\n        if (row_index == self.vector_size) {\n            try self.flush();\n        }\n    }\n\n    pub fn appendValue(self: *Appender, value: anytype, column: usize) !void {\n        var vector = &self.vectors[column];\n        const row_index = self.row_index;\n\n        const T = @TypeOf(value);\n        const type_info = @typeInfo(T);\n        switch (type_info) {\n            .null => {\n                const validity = vector.validity orelse blk: {\n                    c.duckdb_vector_ensure_validity_writable(vector.vector);\n                    const v = c.duckdb_vector_get_validity(vector.vector);\n                    vector.validity = v;\n                    break :blk v;\n                };\n                c.duckdb_validity_set_row_invalid(validity, row_index);\n                return;\n            },\n            .optional => {\n                if (value) |v| {\n                    return self.appendValue(v, column);\n                } else {\n                    return self.appendValue(null, column);\n                }\n            },\n            .pointer => |ptr| {\n                switch (ptr.size) {\n                    .slice => return self.appendSlice(vector, @as([]const ptr.child, value), row_index),\n                    .one => switch (@typeInfo(ptr.child)) {\n                        .array => {\n                            const Slice = []const std.meta.Elem(ptr.child);\n                            return self.appendSlice(vector, @as(Slice, value), row_index);\n                        },\n                        else => appendError(T),\n                    },\n                    else => appendError(T),\n                }\n            },\n            .array => return self.appendValue(&value, column),\n            else => {},\n        }\n\n        switch (vector.data) {\n            .list => return self.appendTypeError(\"list\", T),\n            .scalar => |scalar| switch (scalar) {\n                .bool => |data| {\n                    switch (type_info) {\n                        .bool => data[row_index] = value,\n                        else => return self.appendTypeError(\"boolean\", T),\n                    }\n                },\n                .i8 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => {\n                            if (value < lib.TINYINT_MIN or value > lib.TINYINT_MAX) return self.appendIntRangeError(\"tinyint\");\n                            data[row_index] = @intCast(value);\n                        },\n                        else => return self.appendTypeError(\"tinyint\", T),\n                    }\n                },\n                .i16 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => {\n                            if (value < lib.SMALLINT_MIN or value > lib.SMALLINT_MAX) return self.appendIntRangeError(\"smallint\");\n                            data[row_index] = @intCast(value);\n                        },\n                        else => return self.appendTypeError(\"smallint\", T),\n                    }\n                },\n                .i32 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => {\n                            if (value < lib.INTEGER_MIN or value > lib.INTEGER_MAX) return self.appendIntRangeError(\"integer\");\n                            data[row_index] = @intCast(value);\n                        },\n                        else => return self.appendTypeError(\"integer\", T),\n                    }\n                },\n                .i64 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => {\n                            if (value < lib.BIGINT_MIN or value > lib.BIGINT_MAX) return self.appendIntRangeError(\"bigint\");\n                            data[row_index] = @intCast(value);\n                        },\n                        else => return self.appendTypeError(\"bigint\", T),\n                    }\n                },\n                .i128 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => {\n                            if (value < lib.HUGEINT_MIN or value > lib.HUGEINT_MAX) return self.appendIntRangeError(\"hugeint\");\n                            data[row_index] = @intCast(value);\n                        },\n                        else => return self.appendTypeError(\"hugeint\", T),\n                    }\n                },\n                .u8 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => {\n                            if (value < lib.UTINYINT_MIN or value > lib.UTINYINT_MAX) return self.appendIntRangeError(\"utinyint\");\n                            data[row_index] = @intCast(value);\n                        },\n                        else => return self.appendTypeError(\"utinyint\", T),\n                    }\n                },\n                .u16 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => {\n                            if (value < lib.USMALLINT_MIN or value > lib.USMALLINT_MAX) return self.appendIntRangeError(\"usmallint\");\n                            data[row_index] = @intCast(value);\n                        },\n                        else => return self.appendTypeError(\"usmallint\", T),\n                    }\n                },\n                .u32 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => {\n                            if (value < lib.UINTEGER_MIN or value > lib.UINTEGER_MAX) return self.appendIntRangeError(\"uinteger\");\n                            data[row_index] = @intCast(value);\n                        },\n                        else => return self.appendTypeError(\"uinteger\", T),\n                    }\n                },\n                .u64 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => {\n                            if (value < lib.UBIGINT_MIN or value > lib.UBIGINT_MAX) return self.appendIntRangeError(\"ubingint\");\n                            data[row_index] = @intCast(value);\n                        },\n                        else => return self.appendTypeError(\"ubingint\", T),\n                    }\n                },\n                .u128 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => {\n                            if (value < lib.UHUGEINT_MIN or value > lib.UHUGEINT_MAX) return self.appendIntRangeError(\"uhugeint\");\n                            data[row_index] = @intCast(value);\n                        },\n                        else => return self.appendTypeError(\"uhugeint\", T),\n                    }\n                },\n                .f32 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => data[row_index] = @floatFromInt(value),\n                        .float, .comptime_float => data[row_index] = @floatCast(value),\n                        else => return self.appendTypeError(\"real\", T),\n                    }\n                },\n                .f64 => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => data[row_index] = @floatFromInt(value),\n                        .float, .comptime_float => data[row_index] = @floatCast(value),\n                        else => return self.appendTypeError(\"double\", T),\n                    }\n                },\n                .date => |data| if (T == Date) {\n                    data[row_index] = c.duckdb_to_date(value);\n                } else {\n                    return self.appendTypeError(\"date\", T);\n                },\n                .time => |data| if (T == Time) {\n                    data[row_index] = c.duckdb_to_time(value);\n                } else {\n                    return self.appendTypeError(\"time\", T);\n                },\n                .interval => |data| if (T == Interval) {\n                    data[row_index] = value;\n                } else {\n                    return self.appendTypeError(\"interval\", T);\n                },\n                .timestamp => |data| {\n                    switch (type_info) {\n                        .int, .comptime_int => {\n                            if (value < lib.BIGINT_MIN or value > lib.BIGINT_MAX) return self.appendIntRangeError(\"i64\");\n                            data[row_index] = @intCast(value);\n                        },\n                        else => return self.appendTypeError(\"timestamp\", T),\n                    }\n                },\n                .decimal => |data| return self.setDecimal(value, data, row_index),\n                .varchar => {\n                    var buf: [std.fmt.float.min_buffer_size]u8 = undefined;\n                    const v = switch (type_info) {\n                        .int, .comptime_int => try std.fmt.bufPrint(&buf, \"{d}\", .{value}),\n                        .float, .comptime_float => try std.fmt.float.render(&buf, value, .{}),\n                        .bool => if (value == true) \"true\" else \"false\",\n                        else => {\n                            const err = try std.fmt.allocPrint(self.allocator, \"cannot bind a {any} (type {s}) to a varchar column\", .{ value, @typeName(T) });\n                            self.setErr(err, true);\n                            return error.AppendError;\n                        },\n                    };\n                    c.duckdb_vector_assign_string_element_len(vector.vector, row_index, v.ptr, v.len);\n                },\n                .uuid => |data| {\n                    switch (type_info) {\n                        .int => |int| {\n                            if (int.signedness == .signed and int.bits == 128) {\n                                data[row_index] = value;\n                                return;\n                            }\n                        },\n                        else => {},\n                    }\n                    return self.appendTypeError(\"uuid\", T);\n                },\n                else => {\n                    const err = try std.fmt.allocPrint(self.allocator, \"cannot bind a {any} (type {s}) to a column of type {s}\", .{ value, @typeName(T), @tagName(std.meta.activeTag(scalar)) });\n                    self.setErr(err, true);\n                    return error.AppendError;\n                },\n            },\n        }\n    }\n\n    pub fn clearError(self: *Appender) !void {\n        const e = self.err orelse return;\n        if (self.own_err) {\n            self.allocator.free(e);\n        }\n        self.err = null;\n        self.own_err = false;\n    }\n\n    fn appendSlice(self: *Appender, vector: *Vector, values: anytype, row_index: usize) !void {\n        const T = @TypeOf(values);\n        switch (vector.data) {\n            .list => |*list| {\n                const size = list.size;\n                const new_size = try self.setListSize(vector.vector, list, values.len, row_index);\n                switch (list.child) {\n                    .i8 => |data| return self.setListDirect(i8, \"tinyint[]\", list, values, data[size..new_size]),\n                    .i16 => |data| return self.setListDirect(i16, \"smallint[]\", list, values, data[size..new_size]),\n                    .i32 => |data| return self.setListDirect(i32, \"integer[]\", list, values, data[size..new_size]),\n                    .i64 => |data| return self.setListDirect(i64, \"bigint[]\", list, values, data[size..new_size]),\n                    .i128 => |data| return self.setListDirect(i128, \"hugeint[]\", list, values, data[size..new_size]),\n                    .u8 => |data| return self.setListDirect(u8, \"utinyint[]\", list, values, data[size..new_size]),\n                    .u16 => |data| return self.setListDirect(u16, \"usmallint[]\", list, values, data[size..new_size]),\n                    .u32 => |data| return self.setListDirect(u32, \"uinteger[]\", list, values, data[size..new_size]),\n                    .u64 => |data| return self.setListDirect(u64, \"ubigint[]\", list, values, data[size..new_size]),\n                    .u128 => |data| return self.setListDirect(u128, \"uhugeint[]\", list, values, data[size..new_size]),\n                    .f32 => |data| return self.setListDirect(f32, \"real[]\", list, values, data[size..new_size]),\n                    .f64 => |data| return self.setListDirect(f64, \"doule[]\", list, values, data[size..new_size]),\n                    .bool => |data| return self.setListDirect(bool, \"bool[]\", list, values, data[size..new_size]),\n                    .timestamp => |data| return self.setListDirect(i64, \"timestamp[]\", list, values, data[size..new_size]),\n                    .interval => |data| return self.setListDirect(Interval, \"interval[]\", list, values, data[size..new_size]),\n                    .date => |data| return self.setListTransform(Date, c.duckdb_date, \"date\", list, values, data[size..new_size], c.duckdb_to_date),\n                    .time => |data| return self.setListTransform(Time, c.duckdb_time, \"time\", list, values, data[size..new_size], c.duckdb_to_time),\n                    .blob, .varchar => if (T == []const []const u8 or T == []const []u8) {\n                        const child_vector = list.child_vector;\n                        for (values, size..) |value, i| {\n                            c.duckdb_vector_assign_string_element_len(child_vector, i, value.ptr, value.len);\n                        }\n                    } else if (T == []const ?[]const u8 or T == []const ?[]u8) {\n                        const child_vector = list.child_vector;\n                        const child_validity = list.childValidity();\n                        for (values, size..) |value, i| {\n                            if (value) |v| {\n                                c.duckdb_vector_assign_string_element_len(child_vector, i, v.ptr, v.len);\n                            } else {\n                                c.duckdb_validity_set_row_invalid(child_validity, i);\n                            }\n                        }\n                    } else {\n                        return self.appendTypeError(\"text[] / blob[]\", T);\n                    },\n                    .uuid => |data| if (T == []const []const u8 or T == []const []u8) {\n                        for (values, size..) |value, i| {\n                            data[i] = try encodeUUID(value);\n                        }\n                    } else if (T == []const ?[]const u8 or T == []const ?[]u8) {\n                        const child_validity = list.childValidity();\n                        for (values, size..) |value, i| {\n                            if (value) |v| {\n                                data[i] = try encodeUUID(v);\n                            } else {\n                                c.duckdb_validity_set_row_invalid(child_validity, i);\n                            }\n                        }\n                    } else {\n                        return self.appendTypeError(\"uuid[]\", T);\n                    },\n                    .decimal => |data| {\n                        if (T == []const i64 or T == []const f32 or T == []const f64) {\n                            for (values, size..) |value, i| {\n                                try self.setDecimal(value, data, i);\n                            }\n                        } else if (T == []const ?i64 or T == []const ?f32 or T == []const ?f64) {\n                            const child_validity = list.childValidity();\n                            for (values, size..) |value, i| {\n                                if (value) |v| {\n                                    try self.setDecimal(v, data, i);\n                                } else {\n                                    c.duckdb_validity_set_row_invalid(child_validity, i);\n                                }\n                            }\n                        } else {\n                            return self.appendTypeError(\"decimal[]\", T);\n                        }\n                    },\n                    inline else => |_, tag| return self.appendTypeError(@tagName(tag), T),\n                }\n            },\n            .scalar => |scalar| switch (scalar) {\n                .varchar, .blob => {\n                    // We have a []u8 or []const u8. This could either be a text value\n                    // or a utinyint[]. The type of the vector resolves the ambiguity.\n                    if (T == []const u8) {\n                        c.duckdb_vector_assign_string_element_len(vector.vector, row_index, values.ptr, values.len);\n                    } else {\n                        return self.appendTypeError(\"varchar/blob\", T);\n                    }\n                },\n                .uuid => |data| {\n                    // maybe we have a []u8 that represents a UUID (either in binary or hex)\n                    if (T == []const u8) {\n                        data[row_index] = try encodeUUID(values);\n                    } else {\n                        return self.appendTypeError(\"uuid\", T);\n                    }\n                },\n                inline else => |_, tag| return self.appendTypeError(@tagName(tag), T),\n            },\n        }\n    }\n\n    fn setDecimal(self: *Appender, value: anytype, vector: Vector.Decimal, row_index: usize) !void {\n        const T = @TypeOf(value);\n        switch (@typeInfo(T)) {\n            .int, .comptime_int => switch (vector.internal) {\n                .i16 => |data| {\n                    if (value < lib.SMALLINT_MIN or value > lib.SMALLINT_MAX) return self.appendIntRangeError(\"smallint\");\n                    data[row_index] = @intCast(value);\n                },\n                .i32 => |data| {\n                    if (value < lib.INTEGER_MIN or value > lib.INTEGER_MAX) return self.appendIntRangeError(\"integer\");\n                    data[row_index] = @intCast(value);\n                },\n                .i64 => |data| {\n                    if (value < lib.BIGINT_MIN or value > lib.BIGINT_MAX) return self.appendIntRangeError(\"bigint\");\n                    data[row_index] = @intCast(value);\n                },\n                .i128 => |data| {\n                    if (value < lib.HUGEINT_MIN or value > lib.HUGEINT_MAX) return self.appendIntRangeError(\"hugeint\");\n                    data[row_index] = @intCast(value);\n                },\n            },\n            .float, .comptime_float => {\n                // YES, there's a lot of duplication going on. But, I don't think the float and int codepaths can be merged\n                // without forcing int value to an i128, which seems wasteful to me.\n                const huge: i128 = switch (vector.scale) {\n                    0 => @intFromFloat(value),\n                    1 => @intFromFloat(value * 10),\n                    2 => @intFromFloat(value * 100),\n                    3 => @intFromFloat(value * 1000),\n                    4 => @intFromFloat(value * 10000),\n                    5 => @intFromFloat(value * 100000),\n                    6 => @intFromFloat(value * 1000000),\n                    7 => @intFromFloat(value * 10000000),\n                    8 => @intFromFloat(value * 100000000),\n                    9 => @intFromFloat(value * 1000000000),\n                    10 => @intFromFloat(value * 10000000000),\n                    else => |n| @intFromFloat(value * std.math.pow(f64, 10, @floatFromInt(n))),\n                };\n                switch (vector.internal) {\n                    .i16 => |data| {\n                        if (huge < lib.SMALLINT_MIN or huge > lib.SMALLINT_MAX) return self.appendIntRangeError(\"smallint\");\n                        data[row_index] = @intCast(huge);\n                    },\n                    .i32 => |data| {\n                        if (huge < lib.INTEGER_MIN or huge > lib.INTEGER_MAX) return self.appendIntRangeError(\"integer\");\n                        data[row_index] = @intCast(huge);\n                    },\n                    .i64 => |data| {\n                        if (huge < lib.BIGINT_MIN or huge > lib.BIGINT_MAX) return self.appendIntRangeError(\"bigint\");\n                        data[row_index] = @intCast(huge);\n                    },\n                    .i128 => |data| {\n                        if (huge < lib.HUGEINT_MIN or huge > lib.HUGEINT_MAX) return self.appendIntRangeError(\"hugeint\");\n                        data[row_index] = @intCast(huge);\n                    },\n                }\n            },\n            else => return self.appendTypeError(\"decimal\", T),\n        }\n    }\n\n    fn setListDirect(self: *Appender, comptime T: type, comptime column_type: []const u8, list: *Vector.List, values: anytype, data: anytype) !void {\n        if (@TypeOf(values) == []const T) {\n            @memcpy(data, values);\n            return;\n        }\n\n        if (@TypeOf(values) == []const ?T) {\n            const validity = list.childValidity();\n            const validity_start = list.size - values.len;\n\n            for (values, 0..) |value, i| {\n                if (value) |v| {\n                    data[i] = v;\n                } else {\n                    c.duckdb_validity_set_row_invalid(validity, validity_start + i);\n                }\n            }\n            return;\n        }\n\n        return self.appendTypeError(column_type, @TypeOf(values));\n    }\n\n    fn setListTransform(self: *Appender, comptime T: type, comptime D: type, comptime column_type: []const u8, list: *Vector.List, values: anytype, data: anytype, transform: *const fn (T) callconv(.c) D) !void {\n        if (@TypeOf(values) == []const T) {\n            for (values, 0..) |value, i| {\n                data[i] = transform(value);\n            }\n            return;\n        }\n\n        if (@TypeOf(values) == []const ?T) {\n            const validity = list.childValidity();\n            const validity_start = list.size - values.len;\n\n            for (values, 0..) |value, i| {\n                if (value) |v| {\n                    data[i] = transform(v);\n                } else {\n                    c.duckdb_validity_set_row_invalid(validity, validity_start + i);\n                }\n            }\n            return;\n        }\n\n        return self.appendTypeError(column_type, @TypeOf(values));\n    }\n\n    pub fn appendListMap(self: *Appender, comptime T: type, comptime D: type, column: usize, values: []const T, transform: *const fn (T) OptionalType(D)) !void {\n        var vector = &self.vectors[column];\n        switch (vector.data) {\n            .list => |*list| {\n                const size = list.size;\n                const new_size = try self.setListSize(vector.vector, list, values.len, self.row_index);\n                const validity = list.childValidity();\n                const validity_start = list.size - values.len;\n                const child = list.child;\n                switch (D) {\n                    i8 => try appendListMapInto(T, i8, child.i8[size..new_size], values, transform, validity, validity_start),\n                    i16 => try appendListMapInto(T, i16, child.i16[size..new_size], values, transform, validity, validity_start),\n                    i32 => try appendListMapInto(T, i32, child.i32[size..new_size], values, transform, validity, validity_start),\n                    i64 => switch (child) {\n                        .i64 => |cc| try appendListMapInto(T, i64, cc[size..new_size], values, transform, validity, validity_start),\n                        .timestamp => |cc| try appendListMapInto(T, i64, cc[size..new_size], values, transform, validity, validity_start),\n                        else => unreachable,\n                    },\n                    i128 => switch (child) {\n                        .uuid => |cc| try appendListMapInto(T, i128, cc[size..new_size], values, transform, validity, validity_start),\n                        .i128 => |cc| try appendListMapInto(T, i128, cc[size..new_size], values, transform, validity, validity_start),\n                        else => unreachable,\n                    },\n                    u8 => try appendListMapInto(T, u8, child.u8[size..new_size], values, transform, validity, validity_start),\n                    u16 => try appendListMapInto(T, u16, child.u16[size..new_size], values, transform, validity, validity_start),\n                    u32 => try appendListMapInto(T, u32, child.u32[size..new_size], values, transform, validity, validity_start),\n                    u64 => try appendListMapInto(T, u64, child.u64[size..new_size], values, transform, validity, validity_start),\n                    u128 => try appendListMapInto(T, u128, child.u128[size..new_size], values, transform, validity, validity_start),\n                    bool => try appendListMapInto(T, bool, child.bool[size..new_size], values, transform, validity, validity_start),\n                    f32 => try appendListMapInto(T, f32, child.f32[size..new_size], values, transform, validity, validity_start),\n                    f64 => try appendListMapInto(T, f64, child.f64[size..new_size], values, transform, validity, validity_start),\n                    []u8, []const u8 => switch (child) {\n                        .uuid => |cc| try appendUUIDListMapInto(T, cc[size..new_size], values, transform, validity, validity_start),\n                        .blob, .varchar => try appendTextListMapInto(T, list.child_vector, size, values, transform, validity),\n                        else => unreachable,\n                    },\n                    Date => try appendDateListMapInto(T, child.date[size..new_size], values, transform, validity, validity_start),\n                    Time => try appendTimeListMapInto(T, child.time[size..new_size], values, transform, validity, validity_start),\n                    // interval: [*]c.duckdb_interval,\n                    // decimal: Vector.Decimal,\n                    else => {\n                        const err = try std.fmt.allocPrint(self.allocator, \"appendListMap does not support {s} lists\", .{@typeName(D)});\n                        return self.setErr(err, true);\n                    },\n                }\n            },\n            else => {\n                const err = try std.fmt.allocPrint(self.allocator, \"column {d} is not a list\", .{column});\n                return self.setErr(err, true);\n            },\n        }\n    }\n\n    fn appendListMapInto(comptime T: type, comptime D: type, data: []D, values: anytype, transform: *const fn (T) OptionalType(D), validity: [*c]u64, validity_start: usize) !void {\n        for (values, 0..) |value, i| {\n            if (transform(value)) |v| {\n                data[i] = v;\n            } else {\n                c.duckdb_validity_set_row_invalid(validity, validity_start + i);\n            }\n        }\n    }\n\n    fn appendTextListMapInto(comptime T: type, child_vector: c.duckdb_vector, start: usize, values: anytype, transform: *const fn (T) ?[]const u8, validity: [*c]u64) !void {\n        for (values, start..) |value, i| {\n            if (transform(value)) |v| {\n                c.duckdb_vector_assign_string_element_len(child_vector, i, v.ptr, v.len);\n            } else {\n                c.duckdb_validity_set_row_invalid(validity, i);\n            }\n        }\n    }\n\n    fn appendDateListMapInto(comptime T: type, data: []c.duckdb_date, values: anytype, transform: *const fn (T) ?Date, validity: [*c]u64, validity_start: usize) !void {\n        for (values, 0..) |value, i| {\n            if (transform(value)) |v| {\n                data[i] = c.duckdb_to_date(v);\n            } else {\n                c.duckdb_validity_set_row_invalid(validity, validity_start + i);\n            }\n        }\n    }\n\n    fn appendTimeListMapInto(comptime T: type, data: []c.duckdb_time, values: anytype, transform: *const fn (T) ?Time, validity: [*c]u64, validity_start: usize) !void {\n        for (values, 0..) |value, i| {\n            if (transform(value)) |v| {\n                data[i] = c.duckdb_to_time(v);\n            } else {\n                c.duckdb_validity_set_row_invalid(validity, validity_start + i);\n            }\n        }\n    }\n\n    fn appendUUIDListMapInto(comptime T: type, data: []i128, values: anytype, transform: *const fn (T) ?[]const u8, validity: [*c]u64, validity_start: usize) !void {\n        for (values, 0..) |value, i| {\n            if (transform(value)) |v| {\n                data[i] = try encodeUUID(v);\n            } else {\n                c.duckdb_validity_set_row_invalid(validity, validity_start + i);\n            }\n        }\n    }\n\n    fn setListSize(self: *Appender, vector: c.duckdb_vector, list: *Vector.List, value_count: usize, row_index: usize) !usize {\n        const size = list.size;\n        const new_size = size + value_count;\n        list.size = new_size;\n\n        list.entries[row_index] = .{\n            .offset = size,\n            .length = value_count,\n        };\n\n        if (c.duckdb_list_vector_set_size(vector, new_size) == DuckDBError) {\n            self.setErr(\"failed to set vector size\", false);\n            return error.DuckDBError;\n        }\n        if (c.duckdb_list_vector_reserve(vector, new_size) == DuckDBError) {\n            self.setErr(\"failed to reserve vector space\", false);\n            return error.DuckDBError;\n        }\n        return new_size;\n    }\n\n    fn appendTypeError(self: *Appender, comptime data_type: []const u8, value_type: type) error{AppendError} {\n        self.setErr(\"cannot bind a \" ++ @typeName(value_type) ++ \" to a column of type \" ++ data_type, false);\n        return error.AppendError;\n    }\n\n    fn appendIntRangeError(self: *Appender, comptime data_type: []const u8) error{AppendError} {\n        self.setErr(\"value is outside of range for a column of type \" ++ data_type, false);\n        return error.AppendError;\n    }\n\n    fn setErr(self: *Appender, err: []const u8, own: bool) void {\n        // free any previous owned error we have\n        if (self.own_err) {\n            self.allocator.free(self.err.?);\n        }\n        self.err = err;\n        self.own_err = own;\n    }\n};\n\nfn OptionalType(comptime T: type) type {\n    switch (@typeInfo(T)) {\n        .optional => return T,\n        else => return ?T,\n    }\n}\n\nfn appendError(comptime T: type) void {\n    @compileError(\"cannot append value of type \" ++ @typeName(T));\n}\n\npub fn encodeUUID(value: []const u8) !i128 {\n    if (value.len == 16) {\n        const n = std.mem.readInt(i128, value[0..16], .big);\n        return n ^ (@as(i128, 1) << 127);\n    }\n    return lib.encodeUUID(value);\n}\n\nconst t = std.testing;\nconst DB = lib.DB;\ntest \"Appender: bind errors\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table x (a integer)\", .{});\n    {\n        var appender = try conn.appender(null, \"x\");\n        defer appender.deinit();\n        try t.expectError(error.AppendError, appender.appendRow(.{true}));\n        try t.expectEqualStrings(\"cannot bind a bool to a column of type integer\", appender.err.?);\n    }\n\n    {\n        var appender = try conn.appender(null, \"x\");\n        defer appender.deinit();\n        try t.expectError(error.AppendError, appender.appendRow(.{9147483647}));\n        try t.expectEqualStrings(\"value is outside of range for a column of type integer\", appender.err.?);\n    }\n}\n\ntest \"CannotAppendToDecimal\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\n        \\\\ create table x (\n        \\\\   col_tinyint tinyint,\n        \\\\   col_smallint smallint,\n        \\\\   col_integer integer,\n        \\\\   col_bigint bigint,\n        \\\\   col_hugeint hugeint,\n        \\\\   col_utinyint utinyint,\n        \\\\   col_usmallint usmallint,\n        \\\\   col_uinteger uinteger,\n        \\\\   col_ubigint ubigint,\n        \\\\   col_uhugeint uhugeint,\n        \\\\   col_bool bool,\n        \\\\   col_real real,\n        \\\\   col_double double,\n        \\\\   col_text text,\n        \\\\   col_blob blob,\n        \\\\   col_uuid uuid,\n        \\\\   col_date date,\n        \\\\   col_time time,\n        \\\\   col_interval interval,\n        \\\\   col_timestamp timestamp,\n        \\\\   col_decimal decimal(18, 6),\n        \\\\ )\n    , .{});\n\n    {\n        var appender = try conn.appender(null, \"x\");\n        defer appender.deinit();\n        try appender.appendRow(.{ -128, lib.SMALLINT_MIN, lib.INTEGER_MIN, lib.BIGINT_MIN, lib.HUGEINT_MIN, lib.UTINYINT_MAX, lib.USMALLINT_MAX, lib.UINTEGER_MAX, lib.UBIGINT_MAX, lib.UHUGEINT_MAX, true, -1.23, 1994.848288123, \"over 9000!\", &[_]u8{ 1, 2, 3, 254 }, \"34c667cd-638e-40c2-b256-0f78ccab7013\", Date{ .year = 2023, .month = 5, .day = 10 }, Time{ .hour = 21, .min = 4, .sec = 49, .micros = 123456 }, Interval{ .months = 3, .days = 7, .micros = 982810 }, 1711506018088167, 39858392.36212 });\n        try appender.flush();\n\n        try t.expectEqual(null, appender.err);\n\n        var row = (try conn.row(\"select * from x\", .{})).?;\n        defer row.deinit();\n        try t.expectEqual(-128, row.get(i8, 0));\n        try t.expectEqual(lib.SMALLINT_MIN, row.get(i16, 1));\n        try t.expectEqual(lib.INTEGER_MIN, row.get(i32, 2));\n        try t.expectEqual(lib.BIGINT_MIN, row.get(i64, 3));\n        try t.expectEqual(lib.HUGEINT_MIN, row.get(i128, 4));\n        try t.expectEqual(lib.UTINYINT_MAX, row.get(u8, 5));\n        try t.expectEqual(lib.USMALLINT_MAX, row.get(u16, 6));\n        try t.expectEqual(lib.UINTEGER_MAX, row.get(u32, 7));\n        try t.expectEqual(lib.UBIGINT_MAX, row.get(u64, 8));\n        try t.expectEqual(lib.UHUGEINT_MAX, row.get(u128, 9));\n        try t.expectEqual(true, row.get(bool, 10));\n        try t.expectEqual(-1.23, row.get(f32, 11));\n        try t.expectEqual(1994.848288123, row.get(f64, 12));\n        try t.expectEqualStrings(\"over 9000!\", row.get([]u8, 13));\n        try t.expectEqualStrings(&[_]u8{ 1, 2, 3, 254 }, row.get([]u8, 14));\n        try t.expectEqualStrings(\"34c667cd-638e-40c2-b256-0f78ccab7013\", &row.get(lib.UUID, 15));\n        try t.expectEqual(Date{ .year = 2023, .month = 5, .day = 10 }, row.get(Date, 16));\n        try t.expectEqual(Time{ .hour = 21, .min = 4, .sec = 49, .micros = 123456 }, row.get(Time, 17));\n        try t.expectEqual(Interval{ .months = 3, .days = 7, .micros = 982810 }, row.get(Interval, 18));\n        try t.expectEqual(1711506018088167, row.get(i64, 19));\n        try t.expectEqual(39858392.36212, row.get(f64, 20));\n    }\n}\n\ntest \"Appender: basic variants\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\n        \\\\ create table x (\n        \\\\   id integer,\n        \\\\   col_bool bool,\n        \\\\   col_uuid uuid\n        \\\\ )\n    , .{});\n\n    var appender = try conn.appender(null, \"x\");\n    defer appender.deinit();\n    try appender.appendRow(.{ 1, false, &[_]u8{ 0xf9, 0x3b, 0x64, 0xe0, 0x91, 0x62, 0x40, 0xf5, 0xaa, 0xb8, 0xa0, 0x1f, 0x5c, 0xe9, 0x90, 0x32 } });\n    try appender.appendRow(.{ 2, null, null });\n    try appender.appendRow(.{ 3, null, @as(i128, 96426444282114970045097725006964541666) });\n    try appender.flush();\n\n    try t.expectEqual(null, appender.err);\n\n    {\n        var row = (try conn.row(\"select * from x where id = 1\", .{})).?;\n        defer row.deinit();\n        try t.expectEqual(false, row.get(bool, 1));\n        try t.expectEqualStrings(\"f93b64e0-9162-40f5-aab8-a01f5ce99032\", &row.get(lib.UUID, 2));\n    }\n\n    {\n        var row = (try conn.row(\"select * from x where id = 2\", .{})).?;\n        defer row.deinit();\n        try t.expectEqual(null, row.get(?bool, 1));\n        try t.expectEqual(null, row.get(?lib.UUID, 2));\n    }\n\n    {\n        var row = (try conn.row(\"select col_uuid from x where id = 3\", .{})).?;\n        defer row.deinit();\n        try t.expectEqualStrings(\"c88b0ec1-fa66-40fc-8eb6-09867e9f48e2\", &row.get(lib.UUID, 0));\n    }\n}\n\ntest \"Appender: int/float/bool into varchar and json\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"SET autoinstall_known_extensions=1\", .{});\n    _ = try conn.exec(\"SET autoload_known_extensions=1\", .{});\n    _ = try conn.exec(\n        \\\\ create table x (\n        \\\\   id integer,\n        \\\\   a varchar,\n        \\\\   b json\n        \\\\ )\n    , .{});\n\n    var appender = try conn.appender(null, \"x\");\n    defer appender.deinit();\n    try appender.appendRow(.{ 1, @as(i32, -39991), @as(f64, 3.14159) });\n    try appender.appendRow(.{ 2, @as(f32, 0.991), @as(u16, 1025) });\n    try appender.appendRow(.{ 3, 1234, 5.6789 });\n    try appender.appendRow(.{ 4, -987.65, -5432 });\n    try appender.appendRow(.{ 5, true, false });\n    try appender.flush();\n\n    try t.expectEqual(null, appender.err);\n\n    {\n        var row = (try conn.row(\"select a, b from x where id = 1\", .{})).?;\n        defer row.deinit();\n        try t.expectEqualStrings(\"-39991\", row.get([]u8, 0));\n        try t.expectEqualStrings(\"3.14159e0\", row.get([]u8, 1));\n    }\n\n    {\n        var row = (try conn.row(\"select a, b from x where id = 2\", .{})).?;\n        defer row.deinit();\n        try t.expectEqualStrings(\"9.91e-1\", row.get([]u8, 0));\n        try t.expectEqualStrings(\"1025\", row.get([]u8, 1));\n    }\n\n    {\n        var row = (try conn.row(\"select a, b from x where id = 3\", .{})).?;\n        defer row.deinit();\n        try t.expectEqualStrings(\"1234\", row.get([]u8, 0));\n        try t.expectEqualStrings(\"5.6789e0\", row.get([]u8, 1));\n    }\n\n    {\n        var row = (try conn.row(\"select a, b from x where id = 4\", .{})).?;\n        defer row.deinit();\n        try t.expectEqualStrings(\"-9.8765e2\", row.get([]u8, 0));\n        try t.expectEqualStrings(\"-5432\", row.get([]u8, 1));\n    }\n\n    {\n        var row = (try conn.row(\"select a, b from x where id = 5\", .{})).?;\n        defer row.deinit();\n        try t.expectEqualStrings(\"true\", row.get([]u8, 0));\n        try t.expectEqualStrings(\"false\", row.get([]u8, 1));\n    }\n}\n\ntest \"Appender: multiple chunks\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table x (a integer, b integer)\", .{});\n\n    {\n        var appender = try conn.appender(null, \"x\");\n        defer appender.deinit();\n\n        for (0..1000) |i| {\n            appender.beginRow();\n            try appender.appendValue(i, 0);\n            if (@mod(i, 3) == 0) {\n                try appender.appendValue(null, 1);\n            } else {\n                try appender.appendValue(i * 2, 1);\n            }\n            try appender.endRow();\n        }\n        try appender.flush();\n    }\n\n    var rows = try conn.query(\"select * from x order by a\", .{});\n    defer rows.deinit();\n\n    var i: i32 = 0;\n    while (try rows.next()) |row| {\n        try t.expectEqual(i, row.get(i32, 0));\n\n        if (@mod(i, 3) == 0) {\n            try t.expectEqual(null, row.get(?i32, 1));\n        } else {\n            try t.expectEqual(i * 2, row.get(i32, 1));\n        }\n        i += 1;\n    }\n    try t.expectEqual(1000, i);\n}\n\ntest \"Appender: implicit and explicit flush\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table x (a integer)\", .{});\n\n    {\n        var appender = try conn.appender(null, \"x\");\n        defer appender.deinit();\n\n        try appender.appendRow(.{0});\n\n        appender.beginRow();\n        try appender.appendValue(1, 0);\n        try appender.endRow();\n\n        try appender.flush();\n\n        for (2..5000) |i| {\n            appender.beginRow();\n            try appender.appendValue(i, 0);\n            try appender.endRow();\n        }\n\n        try appender.appendRow(.{5000});\n\n        appender.beginRow();\n        try appender.appendValue(5001, 0);\n        try appender.endRow();\n\n        try appender.flush();\n    }\n\n    var rows = try conn.query(\"select * from x order by a\", .{});\n    defer rows.deinit();\n\n    var i: i32 = 0;\n    while (try rows.next()) |row| {\n        try t.expectEqual(i, row.get(i32, 0));\n        i += 1;\n    }\n    try t.expectEqual(5002, i);\n}\n\ntest \"Appender: hugeint\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table x (a hugeint)\", .{});\n\n    const COUNT = 1000;\n    var expected: [COUNT]i128 = undefined;\n    {\n        const rng_impl: std.Random.IoSource = .{ .io = t.io };\n        const rng = rng_impl.interface();\n\n        var appender = try conn.appender(null, \"x\");\n        defer appender.deinit();\n\n        for (0..COUNT) |i| {\n            const value = rng.int(i128);\n            expected[i] = value;\n            try appender.appendRow(.{value});\n        }\n        try appender.flush();\n    }\n\n    var rows = try conn.query(\"select * from x\", .{});\n    defer rows.deinit();\n\n    var i: i32 = 0;\n    while (try rows.next()) |row| {\n        try t.expectEqual(expected[@intCast(i)], row.get(i128, 0));\n        i += 1;\n    }\n    try t.expectEqual(COUNT, i);\n}\n\ntest \"Appender: decimal\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table appdec (id integer, d decimal(8, 4))\", .{});\n\n    {\n        var appender = try conn.appender(null, \"appdec\");\n        defer appender.deinit();\n        try appender.appendRow(.{ 1, 12345678 });\n        try appender.flush();\n\n        var row = (try conn.row(\"select d from appdec where id = 1\", .{})).?;\n        defer row.deinit();\n        try t.expectEqual(1234.5678, row.get(f64, 0));\n    }\n\n    {\n        var appender = try conn.appender(null, \"appdec\");\n        defer appender.deinit();\n        try appender.appendRow(.{ 2, 5323.224 });\n        try appender.flush();\n\n        var row = (try conn.row(\"select d from appdec where id = 2\", .{})).?;\n        defer row.deinit();\n        try t.expectEqual(5323.224, row.get(f64, 0));\n    }\n}\n\ntest \"Appender: decimal fuzz\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table appdec (d1 decimal(3, 1), d2 decimal(9, 3), d3 decimal(17, 5), d4 decimal(30, 10))\", .{});\n\n    const COUNT = 1000;\n    var expected_i16: [COUNT]f64 = undefined;\n    var expected_i32: [COUNT]f64 = undefined;\n    var expected_i64: [COUNT]f64 = undefined;\n    var expected_i128: [COUNT]f128 = undefined;\n    {\n        const rng_impl: std.Random.IoSource = .{ .io = t.io };\n        const rng = rng_impl.interface();\n\n        var appender = try conn.appender(null, \"appdec\");\n        defer appender.deinit();\n\n        for (0..COUNT) |i| {\n            const d1 = @trunc(rng.float(f64) * 100) / 10;\n            expected_i16[i] = d1;\n            const d2 = @trunc(rng.float(f64) * 100000000) / 1000;\n            expected_i32[i] = d2;\n            const d3 = @trunc(rng.float(f64) * 10000000000000000) / 100000;\n            expected_i64[i] = d3;\n            const d4 = @trunc(@as(f128, rng.float(f64)) * 100000000000000000000000000000) / 10000000000;\n            expected_i128[i] = d4;\n\n            try appender.appendRow(.{ d1, d2, d3, d4 });\n        }\n        try appender.flush();\n    }\n\n    var rows = try conn.query(\"select * from appdec\", .{});\n    defer rows.deinit();\n\n    var i: i32 = 0;\n    while (try rows.next()) |row| {\n        try t.expectApproxEqRel(expected_i16[@intCast(i)], row.get(f64, 0), 0.01);\n        try t.expectApproxEqRel(expected_i32[@intCast(i)], row.get(f64, 1), 0.001);\n        try t.expectApproxEqRel(expected_i64[@intCast(i)], row.get(f64, 2), 0.00001);\n        try t.expectApproxEqRel(expected_i128[@intCast(i)], row.get(f64, 3), 0.0000000001);\n        i += 1;\n    }\n    try t.expectEqual(COUNT, i);\n}\n\ntest \"Appender: optional values\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    errdefer std.log.err(\"conn: {?s}\", .{conn.err});\n\n    _ = try conn.exec(\"create table optionals (id integer, non_null_data integer, null_data integer)\", .{});\n\n    {\n        var appender = try conn.appender(null, \"optionals\");\n        defer appender.deinit();\n\n        var i: i32 = 0;\n        while (i < 10) : (i += 1) {\n            const non_null_data: ?i32 = i;\n            const null_data: ?i32 = null;\n\n            try appender.appendRow(.{ i, non_null_data, null_data });\n        }\n        try appender.flush();\n    }\n\n    var rows = try conn.query(\"select id, non_null_data, null_data from optionals order by id\", .{});\n    defer rows.deinit();\n\n    var i: i32 = 0;\n    while (try rows.next()) |row| {\n        try t.expectEqual(i, row.get(?i32, 0));\n        try t.expectEqual(i, row.get(?i32, 1));\n        try t.expectEqual(null, row.get(?i32, 2));\n        i += 1;\n    }\n    try t.expectEqual(10, i);\n}\n\ntest \"Appender: json\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"SET autoinstall_known_extensions=1\", .{});\n    _ = try conn.exec(\"SET autoload_known_extensions=1\", .{});\n    _ = try conn.exec(\"create table aj (id integer, data json)\", .{});\n\n    {\n        var appender = try conn.appender(null, \"aj\");\n        defer appender.deinit();\n        try appender.appendRow(.{ 1, \"{\\\"id\\\":1,\\\"x\\\":true}\" });\n        try appender.flush();\n\n        var row = (try conn.row(\"select data from aj where id = 1\", .{})).?;\n        defer row.deinit();\n        try t.expectEqualStrings(\"{\\\"id\\\":1,\\\"x\\\":true}\", row.get([]u8, 0));\n    }\n}\n\ntest \"Appender: list simple types\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\n        \\\\ create table applist (\n        \\\\  id integer,\n        \\\\  col_tinyint tinyint[],\n        \\\\  col_smallint smallint[],\n        \\\\  col_integer integer[],\n        \\\\  col_bigint bigint[],\n        \\\\  col_hugeint hugeint[],\n        \\\\  col_utinyint utinyint[],\n        \\\\  col_usmallint usmallint[],\n        \\\\  col_uinteger uinteger[],\n        \\\\  col_ubigint ubigint[],\n        \\\\  col_uhugeint uhugeint[],\n        \\\\  col_real real[],\n        \\\\  col_double double[],\n        \\\\  col_bool bool[],\n        \\\\  col_text text[],\n        \\\\  col_uuid uuid[],\n        \\\\  col_date date[],\n        \\\\  col_time time[],\n        \\\\  col_timestamp timestamp[],\n        \\\\  col_interval interval[],\n        \\\\  col_decimal decimal(18, 6)[],\n        \\\\ )\n    , .{});\n\n    {\n        var appender = try conn.appender(null, \"applist\");\n        defer appender.deinit();\n\n        try appender.appendRow(.{\n            1,\n            &[_]i8{ -128, 0, 100, 127 },\n            &[_]i16{ -32768, 0, -299, 32767 },\n            &[_]i32{ -2147483648, -4933, 0, 2147483647 },\n            &[_]i64{ -9223372036854775808, -8223372036854775800, 0, 9223372036854775807 },\n            &[_]i128{ -170141183460469231731687303715884105728, -1, 2, 170141183460469231731687303715884105727 },\n            &[_]u8{ 0, 200, 255 },\n            &[_]u16{ 0, 65535 },\n            &[_]u32{ 0, 4294967294, 4294967295 },\n            &[_]u64{ 0, 18446744073709551615 },\n            &[_]u128{ 0, 99999999999999999999998, 340282366920938463463374607431768211455 },\n            &[_]f32{ -1.0, 3.44, 0.0, 99.9991 },\n            &[_]f64{ -1.02, 9999.1303, 0.0, -8288133.11 },\n            &[_]bool{ true, false, true, true, false },\n            &[_][]const u8{ \"hello\", \"world\" },\n            [2][]const u8{ \"eadc5eb8-dd6b-4c55-9c9b-b19c76048c32\", &[_]u8{ 204, 193, 82, 169, 150, 64, 52, 71, 92, 228, 173, 248, 223, 220, 70, 252 } },\n            &[_]lib.Date{ .{ .year = 2023, .month = 5, .day = 10 }, .{ .year = 1901, .month = 2, .day = 3 } },\n            &[_]lib.Time{ .{ .hour = 10, .min = 44, .sec = 23, .micros = 123456 }, .{ .hour = 20, .min = 2, .sec = 5, .micros = 2 } },\n            &[_]i64{ 0, -1, 17135818900221234 },\n            &[_]lib.Interval{ .{ .months = 3, .days = 7, .micros = 982810 }, .{ .months = 1, .days = 2, .micros = 3 } },\n            &[_]f64{ 3.1234, -0.00002 },\n        });\n        try appender.appendRow(.{ 2, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null });\n        try appender.appendRow(.{\n            3,\n            &[_]?i8{ -1, null, 9 },\n            &[_]?i16{ -32768, null, 32767 },\n            &[_]?i32{ -200000000, null, 200000001 },\n            &[_]?i64{ -40000000000000000, null, 4000000000000001 },\n            &[_]?i128{ -999999999999999999999, null, 8888888888888888888 },\n            &[_]?u8{ 254, null },\n            &[_]?u16{ 65534, null },\n            &[_]?u32{ null, 4294967255, 0 },\n            &[_]?u64{ 1, null, null },\n            &[_]?u128{ null, null, 0, null, null },\n            &[_]?f32{ 0.0, null, 0.1 },\n            &[_]?f64{ 0.9999, null, null },\n            &[_]?bool{ false, null, true },\n            &[_]?[]const u8{ \"hello\", null, \"world\", null },\n            [_]?[]const u8{ \"eadc5eb8-dd6b-4c55-9c9b-b19c76048c3d\", null, \"FFFFFFFF-dd6b-4c55-9c9b-aaaaaaaaaaaa\", &[_]u8{ 2, 193, 82, 169, 150, 64, 52, 71, 92, 228, 173, 248, 223, 220, 70, 1 }, null },\n            &[_]?lib.Date{ .{ .year = 2023, .month = 5, .day = 10 }, null },\n            &[_]?lib.Time{ null, null, .{ .hour = 20, .min = 2, .sec = 5, .micros = 2 } },\n            &[_]?i64{ 0, null, null, 27135818900221234 },\n            &[_]?lib.Interval{ .{ .months = 3, .days = 7, .micros = 982810 }, null, null },\n            &[_]?f64{ 3.1234, null },\n        });\n        try appender.flush();\n    }\n\n    {\n        var row = (try conn.row(\"select * from applist where id = 1\", .{})).?;\n        defer row.deinit();\n\n        try assertList(&[_]i8{ -128, 0, 100, 127 }, row.list(i8, 1).?);\n        try assertList(&[_]i16{ -32768, 0, -299, 32767 }, row.list(i16, 2).?);\n        try assertList(&[_]i32{ -2147483648, -4933, 0, 2147483647 }, row.list(i32, 3).?);\n        try assertList(&[_]i64{ -9223372036854775808, -8223372036854775800, 0, 9223372036854775807 }, row.list(i64, 4).?);\n        try assertList(&[_]i128{ -170141183460469231731687303715884105728, -1, 2, 170141183460469231731687303715884105727 }, row.list(i128, 5).?);\n        try assertList(&[_]u8{ 0, 200, 255 }, row.list(u8, 6).?);\n        try assertList(&[_]u16{ 0, 65535 }, row.list(u16, 7).?);\n        try assertList(&[_]u32{ 0, 4294967294, 4294967295 }, row.list(u32, 8).?);\n        try assertList(&[_]u64{ 0, 18446744073709551615 }, row.list(u64, 9).?);\n        try assertList(&[_]u128{ 0, 99999999999999999999998, 340282366920938463463374607431768211455 }, row.list(u128, 10).?);\n        try assertList(&[_]f32{ -1.0, 3.44, 0.0, 99.9991 }, row.list(f32, 11).?);\n        try assertList(&[_]f64{ -1.02, 9999.1303, 0.0, -8288133.11 }, row.list(f64, 12).?);\n        try assertList(&[_]bool{ true, false, true, true, false }, row.list(bool, 13).?);\n\n        const list_texts = row.list([]u8, 14).?;\n        try t.expectEqualStrings(\"hello\", list_texts.get(0));\n        try t.expectEqualStrings(\"world\", list_texts.get(1));\n\n        const list_uuids = row.list(lib.UUID, 15).?;\n        try t.expectEqualStrings(\"eadc5eb8-dd6b-4c55-9c9b-b19c76048c32\", &list_uuids.get(0));\n        try t.expectEqualStrings(\"ccc152a9-9640-3447-5ce4-adf8dfdc46fc\", &list_uuids.get(1));\n\n        try assertList(&[_]lib.Date{ .{ .year = 2023, .month = 5, .day = 10 }, .{ .year = 1901, .month = 2, .day = 3 } }, row.list(lib.Date, 16).?);\n        try assertList(&[_]lib.Time{ .{ .hour = 10, .min = 44, .sec = 23, .micros = 123456 }, .{ .hour = 20, .min = 2, .sec = 5, .micros = 2 } }, row.list(lib.Time, 17).?);\n\n        // timestamp\n        try assertList(&[_]i64{ 0, -1, 17135818900221234 }, row.list(i64, 18).?);\n        try assertList(&[_]lib.Interval{ .{ .months = 3, .days = 7, .micros = 982810 }, .{ .months = 1, .days = 2, .micros = 3 } }, row.list(lib.Interval, 19).?);\n        try assertList(&[_]f64{ 3.1234, -0.00002 }, row.list(f64, 20).?);\n    }\n\n    {\n        var row = (try conn.row(\"select * from applist where id = 2\", .{})).?;\n        defer row.deinit();\n        try t.expectEqual(null, row.list(?i8, 1));\n        try t.expectEqual(null, row.list(?i16, 2));\n        try t.expectEqual(null, row.list(?i32, 3));\n        try t.expectEqual(null, row.list(?i64, 4));\n        try t.expectEqual(null, row.list(?i128, 5));\n        try t.expectEqual(null, row.list(?u8, 6));\n        try t.expectEqual(null, row.list(?u16, 7));\n        try t.expectEqual(null, row.list(?u32, 8));\n        try t.expectEqual(null, row.list(?u64, 9));\n        try t.expectEqual(null, row.list(?u128, 10));\n        try t.expectEqual(null, row.list(?f32, 11));\n        try t.expectEqual(null, row.list(?f64, 12));\n        try t.expectEqual(null, row.list(?bool, 13));\n        try t.expectEqual(null, row.list(?[]const u8, 14));\n        try t.expectEqual(null, row.list(?lib.UUID, 15));\n        try t.expectEqual(null, row.list(?lib.Date, 16));\n        try t.expectEqual(null, row.list(?lib.Time, 17));\n        try t.expectEqual(null, row.list(?i64, 18));\n        try t.expectEqual(null, row.list(?i64, 19));\n        try t.expectEqual(null, row.list(?f64, 20));\n    }\n\n    {\n        var row = (try conn.row(\"select * from applist where id = 3\", .{})).?;\n        defer row.deinit();\n\n        try assertList(&[_]?i8{ -1, null, 9 }, row.list(?i8, 1).?);\n        try assertList(&[_]?i16{ -32768, null, 32767 }, row.list(?i16, 2).?);\n        try assertList(&[_]?i32{ -200000000, null, 200000001 }, row.list(?i32, 3).?);\n        try assertList(&[_]?i64{ -40000000000000000, null, 4000000000000001 }, row.list(?i64, 4).?);\n        try assertList(&[_]?i128{ -999999999999999999999, null, 8888888888888888888 }, row.list(?i128, 5).?);\n        try assertList(&[_]?u8{ 254, null }, row.list(?u8, 6).?);\n        try assertList(&[_]?u16{ 65534, null }, row.list(?u16, 7).?);\n        try assertList(&[_]?u32{ null, 4294967255, 0 }, row.list(?u32, 8).?);\n        try assertList(&[_]?u64{ 1, null, null }, row.list(?u64, 9).?);\n        try assertList(&[_]?u128{ null, null, 0, null, null }, row.list(?u128, 10).?);\n        try assertList(&[_]?f32{ 0.0, null, 0.1 }, row.list(?f32, 11).?);\n        try assertList(&[_]?f64{ 0.9999, null, null }, row.list(?f64, 12).?);\n        try assertList(&[_]?bool{ false, null, true }, row.list(?bool, 13).?);\n\n        const list_texts = row.list(?[]u8, 14).?;\n        try t.expectEqualStrings(\"hello\", list_texts.get(0).?);\n        try t.expectEqual(null, list_texts.get(1));\n        try t.expectEqualStrings(\"world\", list_texts.get(2).?);\n        try t.expectEqual(null, list_texts.get(3));\n\n        const list_uuids = row.list(?lib.UUID, 15).?;\n        try t.expectEqualStrings(\"eadc5eb8-dd6b-4c55-9c9b-b19c76048c3d\", &(list_uuids.get(0).?));\n        try t.expectEqual(null, list_uuids.get(1));\n        try t.expectEqualStrings(\"ffffffff-dd6b-4c55-9c9b-aaaaaaaaaaaa\", &(list_uuids.get(2).?));\n        try t.expectEqualStrings(\"02c152a9-9640-3447-5ce4-adf8dfdc4601\", &(list_uuids.get(3).?));\n        try t.expectEqual(null, list_uuids.get(4));\n\n        try assertList(&[_]?lib.Date{ .{ .year = 2023, .month = 5, .day = 10 }, null }, row.list(?lib.Date, 16).?);\n        try assertList(&[_]?lib.Time{ null, null, .{ .hour = 20, .min = 2, .sec = 5, .micros = 2 } }, row.list(?lib.Time, 17).?);\n        try assertList(&[_]?i64{ 0, null, null, 27135818900221234 }, row.list(?i64, 18).?);\n        try assertList(&[_]?lib.Interval{ .{ .months = 3, .days = 7, .micros = 982810 }, null, null }, row.list(?lib.Interval, 19).?);\n        try assertList(&[_]?f64{ 3.1234, null }, row.list(?f64, 20).?);\n    }\n}\n\ntest \"Appender: list multiple\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table applist (id integer, data integer[])\", .{});\n\n    {\n        var appender = try conn.appender(null, \"applist\");\n        defer appender.deinit();\n\n        var i: i32 = 0;\n        while (i < 10) : (i += 1) {\n            try appender.appendRow(.{ i, &[_]i32{ i, i + 1, i + 2 } });\n        }\n        try appender.flush();\n    }\n\n    var rows = try conn.query(\"select data from applist order by id\", .{});\n    defer rows.deinit();\n\n    var i: i32 = 0;\n    while (try rows.next()) |row| {\n        try assertList(&[_]i32{ i, i + 1, i + 2 }, row.list(i32, 0).?);\n        i += 1;\n    }\n    try t.expectEqual(10, i);\n}\n\ntest \"Appender: appendListMap int\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table applist (id integer, data integer[])\", .{});\n\n    {\n        var appender = try conn.appender(null, \"applist\");\n        defer appender.deinit();\n\n        appender.beginRow();\n        try appender.appendValue(1, 0);\n        try appender.appendListMap([]const u8, i32, 1, &[_][]const u8{ \"123\", \"0\", \"999\" }, testAtoi);\n        try appender.endRow();\n        try appender.flush();\n\n        var row = (try conn.row(\"select data from applist where id = 1\", .{})).?;\n        defer row.deinit();\n        try assertList(&[_]?i32{ 123, null, 999 }, row.list(?i32, 0).?);\n    }\n}\n\ntest \"Appender: appendListMap text\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table applist (id integer, data varchar[])\", .{});\n\n    {\n        var appender = try conn.appender(null, \"applist\");\n        defer appender.deinit();\n\n        appender.beginRow();\n        try appender.appendValue(1, 0);\n        try appender.appendListMap(i64, []const u8, 1, &[_]i64{ -3929, 999, 0 }, testItoa);\n        try appender.endRow();\n        try appender.flush();\n\n        var row = (try conn.row(\"select data from applist where id = 1\", .{})).?;\n        defer row.deinit();\n        const list_texts = row.list(?[]u8, 0).?;\n        try t.expectEqualStrings(\"-3929\", list_texts.get(0).?);\n        try t.expectEqualStrings(\"999\", list_texts.get(1).?);\n        try t.expectEqual(null, list_texts.get(2));\n    }\n}\n\ntest \"Appender: appendListMap date\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table applist (id integer, data date[])\", .{});\n\n    {\n        var appender = try conn.appender(null, \"applist\");\n        defer appender.deinit();\n\n        appender.beginRow();\n        try appender.appendValue(1, 0);\n        try appender.appendListMap(u8, Date, 1, &[_]u8{ 1, 0, 2 }, testMapDate);\n        try appender.endRow();\n        try appender.flush();\n\n        var row = (try conn.row(\"select data from applist where id = 1\", .{})).?;\n        defer row.deinit();\n        try assertList(&[_]?Date{ .{ .year = 2005, .month = 8, .day = 10 }, null, .{ .year = -8, .month = 1, .day = 30 } }, row.list(?Date, 0).?);\n    }\n}\n\ntest \"Appender: appendListMap time\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table applist (id integer, data time[])\", .{});\n\n    {\n        var appender = try conn.appender(null, \"applist\");\n        defer appender.deinit();\n\n        appender.beginRow();\n        try appender.appendValue(1, 0);\n        try appender.appendListMap(u8, Time, 1, &[_]u8{ 1, 0, 2 }, testMapTime);\n        try appender.endRow();\n        try appender.flush();\n\n        var row = (try conn.row(\"select data from applist where id = 1\", .{})).?;\n        defer row.deinit();\n        try assertList(&[_]?Time{\n            .{ .hour = 10, .min = 56, .sec = 21, .micros = 123456 },\n            null,\n            .{ .hour = 22, .min = 8, .sec = 1, .micros = 0 },\n        }, row.list(?Time, 0).?);\n    }\n}\n\ntest \"Appender: appendListMap timestamp\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table applist (id integer, data timestamptz[])\", .{});\n\n    {\n        var appender = try conn.appender(null, \"applist\");\n        defer appender.deinit();\n\n        appender.beginRow();\n        try appender.appendValue(1, 0);\n        try appender.appendListMap(u8, i64, 1, &[_]u8{ 1, 0, 2 }, testMapTimeStamp);\n        try appender.endRow();\n        try appender.flush();\n\n        var row = (try conn.row(\"select data from applist where id = 1\", .{})).?;\n        defer row.deinit();\n        try assertList(&[_]?i64{ 1715854289547193, null, -2775838248400000 }, row.list(?i64, 0).?);\n    }\n}\n\ntest \"Appender: appendListMap UUID\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table applist (id integer, data uuid[])\", .{});\n\n    {\n        var appender = try conn.appender(null, \"applist\");\n        defer appender.deinit();\n\n        appender.beginRow();\n        try appender.appendValue(392, 0);\n        try appender.appendListMap(u8, []const u8, 1, &[_]u8{ 2, 1, 0 }, testMapUUID);\n        try appender.endRow();\n        try appender.flush();\n\n        var row = (try conn.row(\"select data from applist where id = 392\", .{})).?;\n        defer row.deinit();\n        const list = row.list(?UUID, 0).?;\n        try t.expectEqualStrings(\"e188523a-9650-41ef-8cb3-d7e3cd4833b9\", &(list.get(0).?));\n        try t.expectEqualStrings(\"61cdea17-71fd-44f2-898d-6756d6b63a97\", &(list.get(1).?));\n        try t.expectEqual(null, list.get(2));\n    }\n\n    // as i128s\n    {\n        var appender = try conn.appender(null, \"applist\");\n        defer appender.deinit();\n\n        appender.beginRow();\n        try appender.appendValue(991, 0);\n        try appender.appendListMap(u8, i128, 1, &[_]u8{ 0, 1, 2 }, testMapUUID2);\n        try appender.endRow();\n        try appender.flush();\n\n        var row = (try conn.row(\"select data from applist where id = 991\", .{})).?;\n        defer row.deinit();\n        const list = row.list(?UUID, 0).?;\n        try t.expectEqual(null, list.get(0));\n        try t.expectEqualStrings(\"db052c3d-eb85-4259-b59c-532d47f185a1\", &(list.get(1).?));\n        try t.expectEqualStrings(\"c88b0ec1-fa66-40fc-8eb6-09867e9f48e2\", &(list.get(2).?));\n    }\n}\n\ntest \"Appender: incomplete row\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table app_x (id integer)\", .{});\n\n    {\n        var appender = try conn.appender(null, \"app_x\");\n        defer appender.deinit();\n\n        appender.beginRow();\n        try appender.appendValue(1, 0);\n        try appender.endRow();\n\n        appender.beginRow();\n        try appender.appendValue(2, 0);\n        try appender.endRow();\n\n        appender.beginRow();\n        try appender.appendValue(3, 0);\n\n        // we fllush without finishing the above row. That's fine, the row should\n        // be abandoned.\n        try appender.flush();\n    }\n\n    var rows = try conn.query(\"select id from app_x order by id\", .{});\n    defer rows.deinit();\n\n    var i: i32 = 0;\n    while (try rows.next()) |row| {\n        try t.expectEqual(i + 1, row.get(i32, 0));\n        i += 1;\n    }\n    try t.expectEqual(2, i);\n}\n\nfn testAtoi(str: []const u8) ?i32 {\n    if (std.mem.eql(u8, str, \"123\")) return 123; // (•_•)\n    if (std.mem.eql(u8, str, \"0\")) return null; // ( •_•)>⌐■-■\n    if (std.mem.eql(u8, str, \"999\")) return 999; // (⌐■_■)\n    unreachable;\n}\n\nfn testItoa(value: i64) ?[]const u8 {\n    if (value == 0) return null; // (•_•)\n    if (value == 999) return \"999\"; // ( •_•)>⌐■-■\n    if (value == -3929) return \"-3929\"; // (⌐■_■)\n    unreachable;\n}\n\nfn testMapDate(value: u8) ?Date {\n    if (value == 0) return null;\n    if (value == 1) return Date{ .year = 2005, .month = 8, .day = 10 };\n    if (value == 2) return Date{ .year = -8, .month = 1, .day = 30 };\n    unreachable;\n}\n\nfn testMapTime(value: u8) ?Time {\n    if (value == 0) return null;\n    if (value == 1) return Time{ .hour = 10, .min = 56, .sec = 21, .micros = 123456 };\n    if (value == 2) return Time{ .hour = 22, .min = 8, .sec = 1, .micros = 0 };\n    unreachable;\n}\n\nfn testMapTimeStamp(value: u8) ?i64 {\n    if (value == 0) return null;\n    if (value == 1) return 1715854289547193;\n    if (value == 2) return -2775838248400000;\n    unreachable;\n}\n\nfn testMapUUID(value: u8) ?[]const u8 {\n    if (value == 0) return null;\n    if (value == 1) return \"61cdea17-71fd-44f2-898d-6756d6b63a97\";\n    if (value == 2) return \"e188523a-9650-41ef-8cb3-d7e3cd4833b9\";\n    unreachable;\n}\n\nfn testMapUUID2(value: u8) ?i128 {\n    if (value == 0) return null;\n    if (value == 1) return 120986606432550570389071696710866142625;\n    if (value == 2) return 96426444282114970045097725006964541666;\n    unreachable;\n}\n\nfn assertList(expected: anytype, actual: anytype) !void {\n    try t.expectEqual(expected.len, actual.len);\n    for (expected, 0..) |e, i| {\n        try t.expectEqual(e, actual.get(i));\n    }\n}\n"
  },
  {
    "path": "src/conn.zig",
    "content": "const std = @import(\"std\");\nconst lib = @import(\"lib.zig\");\n\nconst c = lib.c;\nconst DB = lib.DB;\nconst Row = lib.Row;\nconst Rows = lib.Rows;\nconst Pool = lib.Pool;\nconst Stmt = lib.Stmt;\nconst OwningRow = lib.OwningRow;\n\nconst DuckDBError = c.DuckDBError;\nconst Allocator = std.mem.Allocator;\n\npub const Conn = struct {\n    pool: ?*Pool = null,\n    err: ?[]const u8,\n    allocator: Allocator,\n    conn: *c.duckdb_connection,\n    query_cache: std.StringHashMapUnmanaged(CachedStmt),\n\n    pub fn open(db: *const DB) !Conn {\n        const allocator = db.allocator;\n\n        const conn = try allocator.create(c.duckdb_connection);\n        errdefer allocator.destroy(conn);\n\n        if (c.duckdb_connect(db.db.*, conn) == DuckDBError) {\n            return error.ConnectFail;\n        }\n\n        return .{\n            .err = null,\n            .conn = conn,\n            .allocator = allocator,\n            .query_cache = std.StringHashMapUnmanaged(CachedStmt){},\n        };\n    }\n\n    pub fn deinit(self: *Conn) void {\n        const allocator = self.allocator;\n\n        if (self.err) |e| {\n            allocator.free(e);\n        }\n\n        var it = self.query_cache.iterator();\n        while (it.next()) |kv| {\n            allocator.free(kv.key_ptr.*);\n            kv.value_ptr.stmt.deinit();\n        }\n        self.query_cache.deinit(allocator);\n\n        const conn = self.conn;\n        c.duckdb_disconnect(conn);\n        allocator.destroy(conn);\n    }\n\n    pub fn release(self: *Conn) void {\n        if (self.pool) |p| {\n            p.release(self);\n        }\n    }\n\n    pub fn begin(self: *Conn) !void {\n        _ = try self.exec(\"begin transaction\", .{});\n    }\n\n    pub fn commit(self: *Conn) !void {\n        _ = try self.exec(\"commit\", .{});\n    }\n\n    pub fn rollback(self: *Conn) !void {\n        _ = try self.exec(\"rollback\", .{});\n    }\n\n    pub fn exec(self: *Conn, sql: anytype, values: anytype) !usize {\n        const r = try self.getResult(sql, values);\n        if (r.stmt) |s| {\n            c.duckdb_destroy_prepare(s);\n            self.allocator.destroy(s);\n        }\n        return self.execResult(r.result);\n    }\n\n    pub fn execCache(self: *Conn, name: []const u8, version: u32, sql: anytype, values: anytype) !usize {\n        var s = try self.getCachedStmt(name, version, sql, values);\n        defer _ = c.duckdb_clear_bindings(s.stmt.*);\n        return self.execResult(try s.getResult());\n    }\n\n    fn execResult(self: *Conn, result: *c.duckdb_result) !usize {\n        defer {\n            c.duckdb_destroy_result(result);\n            self.allocator.destroy(result);\n        }\n        return c.duckdb_rows_changed(result);\n    }\n\n    pub fn query(self: *Conn, sql: anytype, values: anytype) !Rows {\n        return self.queryWithState(sql, values, null);\n    }\n\n    pub fn queryCache(self: *Conn, name: []const u8, version: u32, sql: anytype, values: anytype) !Rows {\n        return self.queryCacheWithState(name, version, sql, values, null);\n    }\n\n    pub fn queryWithState(self: *Conn, sql: anytype, values: anytype, state: anytype) !Rows {\n        const result = try self.getResult(sql, values);\n        return Rows.init(self.allocator, result.result, state, .{ .stmt = result.stmt });\n    }\n\n    pub fn queryCacheWithState(self: *Conn, name: []const u8, version: u32, sql: anytype, values: anytype, state: anytype) !Rows {\n        var stmt = try self.getCachedStmt(name, version, sql, values);\n        return stmt.query(state);\n    }\n\n    pub fn row(self: *Conn, sql: anytype, values: anytype) !?OwningRow {\n        return self.rowWithState(sql, values, null);\n    }\n\n    pub fn rowCache(self: *Conn, name: []const u8, version: u32, sql: anytype, values: anytype) !?OwningRow {\n        return self.rowCacheWithState(name, version, sql, values, null);\n    }\n\n    pub fn rowWithState(self: *Conn, sql: anytype, values: anytype, state: anytype) !?OwningRow {\n        var rows = try self.queryWithState(sql, values, state);\n        errdefer rows.deinit();\n\n        const r = (try rows.next()) orelse {\n            rows.deinit();\n            return null;\n        };\n\n        return .{\n            .row = r,\n            .rows = rows,\n        };\n    }\n\n    pub fn rowCacheWithState(self: *Conn, name: []const u8, version: u32, sql: anytype, values: anytype, state: anytype) !?OwningRow {\n        var rows = try self.queryCacheWithState(name, version, sql, values, state);\n        errdefer rows.deinit();\n\n        const r = (try rows.next()) orelse {\n            rows.deinit();\n            return null;\n        };\n\n        return .{\n            .row = r,\n            .rows = rows,\n        };\n    }\n\n    pub fn prepare(self: *Conn, sql: anytype, opts: Stmt.Opts) !Stmt {\n        const allocator = self.allocator;\n        const str = try lib.stringZ(sql, allocator);\n        defer str.deinit(allocator);\n\n        const stmt = try allocator.create(c.duckdb_prepared_statement);\n        errdefer allocator.destroy(stmt);\n        if (c.duckdb_prepare(self.conn.*, str.z, stmt) == DuckDBError) {\n            try self.duckdbError(c.duckdb_prepare_error(stmt.*));\n            return error.DuckDBError;\n        }\n\n        return Stmt.init(stmt, self, opts);\n    }\n\n    pub fn appender(self: *Conn, schema: ?[]const u8, table: []const u8) !lib.Appender {\n        const allocator = self.allocator;\n\n        var schema_z: ?lib.StringZ = null;\n\n        defer if (schema_z) |sz| sz.deinit(allocator);\n        if (schema) |s| {\n            schema_z = try lib.stringZ(s, allocator);\n        }\n\n        const table_z = try lib.stringZ(table, allocator);\n        defer table_z.deinit(allocator);\n\n        const a = try allocator.create(c.duckdb_appender);\n        errdefer allocator.destroy(a);\n        if (c.duckdb_appender_create(self.conn.*, if (schema_z) |s| s.z else null, table_z.z, a) == DuckDBError) {\n            return error.DuckDBError;\n        }\n        return lib.Appender.init(self.allocator, a);\n    }\n\n    pub fn duckdbError(self: *Conn, err: [*c]const u8) !void {\n        const allocator = self.allocator;\n        if (self.err) |e| {\n            allocator.free(e);\n        }\n        self.err = try allocator.dupe(u8, std.mem.span(err));\n    }\n\n    const Result = struct {\n        stmt: ?*c.duckdb_prepared_statement,\n        result: *c.duckdb_result,\n    };\n\n    pub fn getResult(self: *Conn, sql: anytype, values: anytype) !Result {\n        if (values.len > 0) {\n            var stmt = try self.prepare(sql, .{ .auto_release = true });\n            errdefer stmt.deinit();\n            try stmt.bind(values);\n\n            return .{\n                .stmt = stmt.stmt,\n                .result = try stmt.getResult(),\n            };\n        }\n\n        const allocator = self.allocator;\n        const str = try lib.stringZ(sql, allocator);\n        defer str.deinit(allocator);\n\n        const result = try allocator.create(c.duckdb_result);\n        errdefer allocator.destroy(result);\n\n        if (c.duckdb_query(self.conn.*, str.z, result) == DuckDBError) {\n            try self.duckdbError(c.duckdb_result_error(result));\n            return error.DuckDBError;\n        }\n\n        return .{\n            .stmt = null,\n            .result = result,\n        };\n    }\n\n    fn getCachedStmt(self: *Conn, name: []const u8, version: u32, sql: anytype, values: anytype) !Stmt {\n        var stmt: ?Stmt = null;\n        const gop = try self.query_cache.getOrPut(self.allocator, name);\n        if (gop.found_existing) {\n            const cached = gop.value_ptr;\n            if (cached.version == version) {\n                stmt = cached.stmt;\n            } else {\n                cached.stmt.deinit();\n            }\n        }\n\n        if (stmt == null) {\n            errdefer if (gop.found_existing) {\n                // if we're here, we then we must have called deinit() on the cached statement\n                // but the entry is still in the cache (because we were expecting to replace it)\n                // if we fail, we need to remove it so that subsequent calls don't get the\n                // deinitialize statement.\n                const removed = self.query_cache.fetchRemove(name);\n                self.allocator.free(removed.?.key);\n            };\n\n            stmt = try self.prepare(sql, .{ .auto_release = false });\n            if (gop.found_existing == false) {\n                gop.key_ptr.* = try self.allocator.dupe(u8, name);\n            }\n            gop.value_ptr.* = .{ .stmt = stmt.?, .version = version };\n        }\n\n        var s = stmt.?;\n        try s.bind(values);\n        return s;\n    }\n};\n\nconst CachedStmt = struct {\n    stmt: Stmt,\n    version: u32,\n};\n\nconst t = std.testing;\ntest \"conn: exec error\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    try t.expectError(error.DuckDBError, conn.exec(\"select from x\", .{}));\n    try t.expectEqualStrings(\"Parser Error: SELECT clause without selection list\", conn.err.?);\n}\n\ntest \"conn: exec success\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table t (id int)\", .{});\n    try t.expectEqual(1, try conn.exec(\"insert into t (id) values (39)\", .{}));\n\n    var rows = try conn.query(\"select * from t\", .{});\n    defer rows.deinit();\n    try t.expectEqual(39, (try rows.next()).?.get(i32, 0));\n}\n\ntest \"conn: query error\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    try t.expectError(error.DuckDBError, conn.query(\"select from x\", .{}));\n    try t.expectEqualStrings(\"Parser Error: SELECT clause without selection list\", conn.err.?);\n}\n\ntest \"conn: query select ok\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    var rows = try conn.query(\"select 39213\", .{});\n    defer rows.deinit();\n\n    const row = (try rows.next()).?;\n    try t.expectEqual(39213, row.get(i32, 0));\n    try t.expectEqual(null, try rows.next());\n}\n\ntest \"conn: query empty\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    var rows = try conn.query(\"select 1 where false\", .{});\n    defer rows.deinit();\n    try t.expectEqual(null, try rows.next());\n}\n\ntest \"conn: query mutate ok\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        const rows = try conn.query(\"create table test(id integer);\", .{});\n        defer rows.deinit();\n        try t.expectEqual(0, rows.count());\n        try t.expectEqual(0, rows.changed());\n    }\n\n    {\n        const rows = try conn.query(\"insert into test (id) values (9001);\", .{});\n        defer rows.deinit();\n\n        try t.expectEqual(1, rows.count());\n        try t.expectEqual(1, rows.changed());\n    }\n}\n\ntest \"conn: transaction\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        //rollback\n        _ = try conn.exec(\"create table t (id int)\", .{});\n        try conn.begin();\n        _ = try conn.exec(\"insert into t (id) values (1)\", .{});\n        try conn.rollback();\n\n        var rows = try conn.query(\"select * from t\", .{});\n        defer rows.deinit();\n        try t.expectEqual(null, try rows.next());\n    }\n\n    {\n        // commit\n        try conn.begin();\n        _ = try conn.exec(\"insert into t (id) values (1)\", .{});\n        try conn.commit();\n\n        var rows = try conn.query(\"select * from t\", .{});\n        defer rows.deinit();\n        try t.expectEqual(1, (try rows.next()).?.get(i32, 0));\n    }\n}\n\ntest \"conn: query with explicit state\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    var state = @import(\"zuckdb.zig\").StaticState(2){};\n    var rows = try conn.queryWithState(\"select $1::int, $2::varchar\", .{ 9392, \"teg\" }, &state);\n    defer rows.deinit();\n    const row = (try rows.next()).?;\n    try t.expectEqual(9392, row.get(i32, 0));\n    try t.expectEqualStrings(\"teg\", row.get([]u8, 1));\n}\n\ntest \"conn: sql with different string types\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    const literal = \"select $1::int, $2::varchar\";\n    try testSQLStringType(&conn, literal);\n\n    {\n        const sql = try t.allocator.dupe(u8, literal);\n        defer t.allocator.free(sql);\n        try testSQLStringType(&conn, sql);\n    }\n\n    {\n        const sql = try t.allocator.dupeZ(u8, literal);\n        defer t.allocator.free(sql);\n        try testSQLStringType(&conn, sql);\n    }\n\n    {\n        const sql = [_]u8{ 's', 'e', 'l', 'e', 'c', 't', ' ', '$', '1', ':', ':', 'i', 'n', 't', ',', ' ', '$', '2', ':', ':', 'v', 'a', 'r', 'c', 'h', 'a', 'r' };\n        try testSQLStringType(&conn, sql);\n    }\n}\n\ntest \"conn: constraint errors\" {\n    const zuckdb = @import(\"zuckdb.zig\");\n\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table t (name varchar not null)\", .{});\n    _ = try conn.exec(\"create unique index t_name on t (name)\", .{});\n    _ = try conn.exec(\"insert into t (name) values ('leto')\", .{});\n\n    {\n        // not a duplicate error\n        try t.expectError(error.DuckDBError, conn.query(\"create table t (name varchar not null)\", .{}));\n        try t.expectEqual(false, zuckdb.isDuplicate(conn.err.?));\n    }\n\n    {\n        try t.expectError(error.DuckDBError, conn.query(\"insert into t (name) values ('leto')\", .{}));\n        try t.expectEqual(true, zuckdb.isDuplicate(conn.err.?));\n    }\n}\n\ntest \"conn: prepare error\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    try t.expectError(error.DuckDBError, conn.prepare(\"select x\", .{}));\n    try t.expectEqualStrings(\"Binder Error: Referenced column \\\"x\\\" was not found because the FROM clause is missing\\n\\nLINE 1: select x\\n               ^\", conn.err.?);\n}\n\ntest \"conn: query cache\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        const row = (try conn.rowCache(\"q1\", 1, \"select $1::int\", .{2})) orelse unreachable;\n        defer row.deinit();\n        try t.expectEqual(2, row.get(i32, 0));\n    }\n\n    {\n        const row = (try conn.rowCache(\"q1\", 1, \"select $1::int\", .{33})) orelse unreachable;\n        defer row.deinit();\n        try t.expectEqual(33, row.get(i32, 0));\n    }\n\n    {\n        const row = (try conn.rowCache(\"q1\", 2, \"select $1::text\", .{\"hello\"})) orelse unreachable;\n        defer row.deinit();\n        try t.expectEqualStrings(\"hello\", row.get([]u8, 0));\n    }\n\n    {\n        const row = (try conn.rowCache(\"q2\", 1, \"select $1::bool\", .{true})) orelse unreachable;\n        defer row.deinit();\n        try t.expectEqual(true, row.get(bool, 0));\n    }\n\n    {\n        // make sure that a failure to prepare a new version doesn't leak anything\n        try t.expectError(error.DuckDBError, conn.rowCache(\"q2\", 2, \"select $1::fail\", .{true}));\n\n        const row = (try conn.rowCache(\"q2\", 3, \"select $1::utinyint\", .{255})) orelse unreachable;\n        defer row.deinit();\n        try t.expectEqual(255, row.get(u8, 0));\n    }\n}\n\ntest \"conn: exec cache\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table t1 (id integer)\", .{});\n\n    try t.expectEqual(1, try conn.execCache(\"q1\", 1, \"insert into t1 values ($1)\", .{2}));\n    try t.expectEqual(1, try conn.execCache(\"q1\", 1, \"insert into t1 values ($1)\", .{3}));\n    try t.expectEqual(1, try conn.execCache(\"q1\", 2, \"insert into t1 values (5)\", .{}));\n    try t.expectEqual(1, try conn.execCache(\"q2\", 1, \"insert into t1 values (0)\", .{}));\n\n    var rows = try conn.query(\"select * from t1 order by id\", .{});\n    defer rows.deinit();\n    try t.expectEqual(0, (try rows.next()).?.get(i32, 0));\n    try t.expectEqual(2, (try rows.next()).?.get(i32, 0));\n    try t.expectEqual(3, (try rows.next()).?.get(i32, 0));\n    try t.expectEqual(5, (try rows.next()).?.get(i32, 0));\n    try t.expectEqual(null, rows.next());\n}\n\nfn testSQLStringType(conn: *Conn, sql: anytype) !void {\n    var rows = try conn.query(sql, .{ 9392, \"teg\" });\n    defer rows.deinit();\n\n    const row = (try rows.next()).?;\n    try t.expectEqual(9392, row.get(i32, 0));\n    try t.expectEqualStrings(\"teg\", row.get([]u8, 1));\n}\n"
  },
  {
    "path": "src/db.zig",
    "content": "const std = @import(\"std\");\nconst lib = @import(\"lib.zig\");\n\nconst c = lib.c;\nconst Conn = lib.Conn;\nconst Pool = lib.Pool;\n\nconst Io = std.Io;\nconst Allocator = std.mem.Allocator;\n\nconst DuckDBError = c.DuckDBError;\nconst CONFIG_SIZEOF = c.config_sizeof;\nconst CONFIG_ALIGNOF = c.config_alignof;\nconst DB_SIZEOF = c.database_sizeof;\nconst DB_ALIGNOF = c.database_alignof;\n\npub const DB = struct {\n    io: Io,\n    allocator: Allocator,\n    db: *c.duckdb_database,\n\n    pub const Config = struct {\n        enable_external_access: bool = true,\n        access_mode: AccessMode = .automatic,\n\n        const AccessMode = enum {\n            automatic,\n            read_only,\n            read_write,\n        };\n    };\n\n    pub fn init(io: Io, allocator: Allocator, path: anytype, db_config: Config) !DB {\n        return initWithErr(io, allocator, path, db_config, null);\n    }\n\n    pub fn initWithErr(io: Io, allocator: Allocator, path: anytype, db_config: Config, err: ?*?[]u8) !DB {\n        const str = try lib.stringZ(path, allocator);\n        defer str.deinit(allocator);\n\n        const config = try allocator.create(c.duckdb_config);\n        defer allocator.destroy(config);\n\n        if (c.duckdb_create_config(config) == DuckDBError) {\n            return error.ConfigCreate;\n        }\n\n        if (db_config.access_mode != .automatic) {\n            if (c.duckdb_set_config(config.*, \"access_mode\", @tagName(db_config.access_mode)) == DuckDBError) {\n                return error.ConfigAccessMode;\n            }\n        }\n\n        if (db_config.enable_external_access == false) {\n            if (c.duckdb_set_config(config.*, \"enable_external_access\", \"false\") == DuckDBError) {\n                return error.ConfigExternalAccess;\n            }\n        }\n\n        const db = try allocator.create(c.duckdb_database);\n        errdefer allocator.destroy(db);\n\n        var out_err: [*c]u8 = undefined;\n        if (c.duckdb_open_ext(str.z, db, config.*, &out_err) == DuckDBError) {\n            defer c.duckdb_free(out_err);\n            if (err) |e| {\n                e.* = try allocator.dupe(u8, std.mem.span(out_err));\n            }\n            return error.OpenDB;\n        }\n\n        return .{ .io = io, .db = db, .allocator = allocator };\n    }\n\n    pub fn deinit(self: *const DB) void {\n        const db = self.db;\n        c.duckdb_close(db);\n        self.allocator.destroy(db);\n    }\n\n    pub fn conn(self: *const DB) !Conn {\n        return Conn.open(self);\n    }\n\n    pub fn pool(self: DB, config: Pool.Config) !*Pool {\n        return Pool.init(self, config);\n    }\n};\n\nconst t = std.testing;\ntest \"DB: error\" {\n    var err: ?[]u8 = null;\n    try t.expectError(error.OpenDB, DB.initWithErr(t.io, t.allocator, \"/tmp/does/not/exist/zuckdb\", .{}, &err));\n    try t.expectEqual(true, std.mem.find(u8, err.?, \"IO Error: Cannot open file\") != null);\n    try t.expectEqual(true, std.mem.find(u8, err.?, \"No such file or directory\") != null);\n    t.allocator.free(err.?);\n}\n"
  },
  {
    "path": "src/lib.zig",
    "content": "pub const c = @import(\"duckdb_clib\");\n\nconst row = @import(\"row.zig\");\npub const Row = row.Row;\npub const List = row.List;\npub const Enum = row.Enum;\npub const LazyList = row.LazyList;\npub const OwningRow = row.OwningRow;\n\npub const DB = @import(\"db.zig\").DB;\npub const Rows = @import(\"rows.zig\").Rows;\npub const Pool = @import(\"pool.zig\").Pool;\npub const Stmt = @import(\"stmt.zig\").Stmt;\npub const Conn = @import(\"conn.zig\").Conn;\npub const Vector = @import(\"vector.zig\").Vector;\npub const Appender = @import(\"appender.zig\").Appender;\n\npub const Date = c.duckdb_date_struct;\npub const Time = c.duckdb_time_struct;\npub const Interval = c.duckdb_interval;\npub const UUID = [36]u8;\n\npub const DataType = enum {\n    unknown,\n    boolean,\n    tinyint,\n    smallint,\n    integer,\n    bigint,\n    hugeint,\n    utinyint,\n    usmallint,\n    uinteger,\n    ubigint,\n    uhugeint,\n    real,\n    double,\n    timestamp,\n    timestamp_ms,\n    timestamp_s,\n    timestamptz,\n    date,\n    time,\n    timetz,\n    interval,\n    varchar,\n    blob,\n    decimal,\n    @\"enum\",\n    list,\n    uuid,\n    bit,\n\n    pub fn jsonStringify(self: DataType, options: std.json.StringifyOptions, out: anytype) !void {\n        return std.json.encodeJsonString(@tagName(self), options, out);\n    }\n\n    pub fn fromDuckDBType(dt: c.duckdb_type) DataType {\n        return switch (dt) {\n            c.DUCKDB_TYPE_BOOLEAN => .boolean,\n            c.DUCKDB_TYPE_TINYINT => .tinyint,\n            c.DUCKDB_TYPE_SMALLINT => .smallint,\n            c.DUCKDB_TYPE_INTEGER => .integer,\n            c.DUCKDB_TYPE_BIGINT => .bigint,\n            c.DUCKDB_TYPE_HUGEINT => .hugeint,\n            c.DUCKDB_TYPE_UTINYINT => .utinyint,\n            c.DUCKDB_TYPE_USMALLINT => .usmallint,\n            c.DUCKDB_TYPE_UINTEGER => .uinteger,\n            c.DUCKDB_TYPE_UBIGINT => .ubigint,\n            c.DUCKDB_TYPE_UHUGEINT => .uhugeint,\n            c.DUCKDB_TYPE_FLOAT => .real,\n            c.DUCKDB_TYPE_DOUBLE => .double,\n            c.DUCKDB_TYPE_TIMESTAMP => .timestamp,\n            c.DUCKDB_TYPE_TIMESTAMP_MS => .timestamp_ms,\n            c.DUCKDB_TYPE_TIMESTAMP_S => .timestamp_s,\n            c.DUCKDB_TYPE_DATE => .date,\n            c.DUCKDB_TYPE_TIME => .time,\n            c.DUCKDB_TYPE_INTERVAL => .interval,\n            c.DUCKDB_TYPE_VARCHAR => .varchar,\n            c.DUCKDB_TYPE_BLOB => .blob,\n            c.DUCKDB_TYPE_DECIMAL => .decimal,\n            c.DUCKDB_TYPE_ENUM => .@\"enum\",\n            c.DUCKDB_TYPE_LIST => .list,\n            c.DUCKDB_TYPE_UUID => .uuid,\n            c.DUCKDB_TYPE_BIT => .bit,\n            c.DUCKDB_TYPE_TIME_TZ => .timetz,\n            c.DUCKDB_TYPE_TIMESTAMP_TZ => .timestamptz,\n            else => .unknown,\n        };\n    }\n};\n\nconst std = @import(\"std\");\nconst Allocator = std.mem.Allocator;\n\npub fn hugeInt(value: i128) c.duckdb_hugeint {\n    return .{\n        .lower = @intCast(@mod(value, 18446744073709551616)),\n        .upper = @intCast(@divFloor(value, 18446744073709551616)),\n    };\n}\n\npub fn uhugeInt(value: u128) c.duckdb_uhugeint {\n    return .{\n        .lower = @intCast(@mod(value, 18446744073709551616)),\n        .upper = @intCast(@divFloor(value, 18446744073709551616)),\n    };\n}\n\n// This is here because we expose it via the public API, though it's only useful\n// in advanced cases (when writing directly to a vector, or via the appendListMap)\npub fn encodeUUID(value: []const u8) !i128 {\n    if (value.len != 36) {\n        return error.InvalidUUID;\n    }\n\n    if (value[8] != '-' or value[13] != '-' or value[18] != '-' or value[23] != '-') {\n        return error.InvalidUUID;\n    }\n\n    var bin: [16]u8 = undefined;\n    inline for (encoded_pos, 0..) |i, j| {\n        const hi = hex_to_nibble[value[i + 0]];\n        const lo = hex_to_nibble[value[i + 1]];\n        if (hi == 0xff or lo == 0xff) {\n            return error.InvalidUUID;\n        }\n        bin[j] = hi << 4 | lo;\n    }\n    const n = std.mem.readInt(i128, &bin, .big);\n    return n ^ (@as(i128, 1) << 127);\n}\n\nconst encoded_pos = [16]u8{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34 };\nconst hex_to_nibble = [_]u8{0xff} ** 48 ++ [_]u8{\n    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\n    0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n    0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff,\n    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n    0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff,\n} ++ [_]u8{0xff} ** 152;\n\npub const StringZ = struct {\n    z: [:0]const u8,\n    duped: bool,\n\n    pub fn deinit(self: StringZ, allocator: Allocator) void {\n        if (self.duped) {\n            allocator.free(self.z);\n        }\n    }\n};\n\npub fn stringZ(str: anytype, allocator: Allocator) !StringZ {\n    const T = @TypeOf(str);\n    if (comptime isNullTerminatedString(T)) {\n        return .{ .duped = false, .z = str };\n    }\n    if (comptime isStringSlice(T)) {\n        return .{ .duped = true, .z = try allocator.dupeZ(u8, str) };\n    }\n    if (comptime isStringArray(T)) {\n        return .{ .duped = true, .z = try allocator.dupeZ(u8, &str) };\n    }\n    @compileError(\"Expected a string, got: {}\" ++ @typeName(T));\n}\n\nfn isNullTerminatedString(comptime T: type) bool {\n    switch (@typeInfo(T)) {\n        .pointer => |ptr| switch (ptr.size) {\n            .one => return isNullTerminatedString(ptr.child),\n            .slice => {\n                if (ptr.child == u8) {\n                    if (std.meta.sentinel(T)) |s| return s == 0;\n                }\n                return false;\n            },\n            else => return false,\n        },\n        .array => |arr| {\n            if (arr.child == u8) {\n                if (std.meta.sentinel(T)) |s| return s == 0;\n            }\n            return false;\n        },\n        else => return false,\n    }\n}\n\nfn isStringSlice(comptime T: type) bool {\n    switch (@typeInfo(T)) {\n        .pointer => |ptr| switch (ptr.size) {\n            .slice => return ptr.child == u8 and ptr.sentinel_ptr == null,\n            else => {},\n        },\n        else => {},\n    }\n    return false;\n}\n\nfn isStringArray(comptime T: type) bool {\n    switch (@typeInfo(T)) {\n        .array => |arr| return arr.child == u8,\n        else => return false,\n    }\n}\n\npub const TINYINT_MIN = -128;\npub const TINYINT_MAX = 127;\npub const UTINYINT_MIN = 0;\npub const UTINYINT_MAX = 255;\n\npub const SMALLINT_MIN = -32768;\npub const SMALLINT_MAX = 32767;\npub const USMALLINT_MIN = 0;\npub const USMALLINT_MAX = 65535;\n\npub const INTEGER_MIN = -2147483648;\npub const INTEGER_MAX = 2147483647;\npub const UINTEGER_MIN = 0;\npub const UINTEGER_MAX = 4294967295;\n\npub const BIGINT_MIN = -9223372036854775808;\npub const BIGINT_MAX = 9223372036854775807;\npub const UBIGINT_MIN = 0;\npub const UBIGINT_MAX = 18446744073709551615;\n\npub const HUGEINT_MIN = -170141183460469231731687303715884105728;\npub const HUGEINT_MAX = 170141183460469231731687303715884105727;\npub const UHUGEINT_MIN = 0;\npub const UHUGEINT_MAX = 340282366920938463463374607431768211455;\n\nconst root = @import(\"root\");\nconst _assert = blk: {\n    if (@hasDecl(root, \"zuckdb_assert\")) {\n        break :blk root.zuckdb_assert;\n    }\n    switch (@import(\"builtin\").mode) {\n        .ReleaseFast, .ReleaseSmall => break :blk false,\n        else => break :blk true,\n    }\n};\n\npub fn assert(ok: bool) void {\n    if (comptime _assert) {\n        std.debug.assert(ok);\n    }\n}\n"
  },
  {
    "path": "src/pool.zig",
    "content": "const std = @import(\"std\");\nconst lib = @import(\"lib.zig\");\n\nconst DB = lib.DB;\nconst Conn = lib.Conn;\nconst Rows = lib.Rows;\nconst OwningRow = lib.OwningRow;\n\nconst Io = std.Io;\nconst Allocator = std.mem.Allocator;\n\npub const Pool = struct {\n    db: DB,\n    timeout: u64,\n    conns: []*Conn,\n    shutdown: bool,\n    available: usize,\n    mutex: Io.Mutex,\n    cond: Io.Condition,\n    allocator: Allocator,\n\n    pub const Config = struct {\n        size: usize = 5,\n        timeout: u32 = 10 * std.time.ms_per_s,\n        on_connection: ?*const fn (conn: *Conn) anyerror!void = null,\n        on_first_connection: ?*const fn (conn: *Conn) anyerror!void = null,\n    };\n\n    pub fn init(db: DB, config: Config) !*Pool {\n        const size = config.size;\n        const allocator = db.allocator;\n\n        const pool = try allocator.create(Pool);\n        errdefer allocator.destroy(pool);\n\n        const conns = try allocator.alloc(*Conn, size);\n        errdefer allocator.free(conns);\n\n        // if something fails while we're setting up the pool, we need to close\n        // any connection that we've initialized\n        var initialized: usize = 0;\n        errdefer {\n            for (0..initialized) |i| {\n                conns[i].deinit();\n                allocator.destroy(conns[i]);\n            }\n        }\n\n        const on_connection = config.on_connection;\n\n        for (0..size) |i| {\n            const conn = try allocator.create(Conn);\n            errdefer allocator.destroy(conn);\n\n            conn.* = try db.conn();\n            conns[i] = conn;\n            conn.pool = pool;\n            initialized += 1;\n\n            if (i == 0) {\n                if (config.on_first_connection) |f| {\n                    try f(conn);\n                }\n            }\n\n            if (on_connection) |f| {\n                try f(conn);\n            }\n        }\n\n        pool.* = .{\n            .db = db,\n            .cond = .init,\n            .mutex = .init,\n            .conns = conns,\n            .shutdown = false,\n            .available = size,\n            .allocator = allocator,\n            .timeout = @as(u64, @intCast(config.timeout)) * std.time.ns_per_ms,\n        };\n        return pool;\n    }\n\n    // blocks until all connections can be safely removed from the pool\n    pub fn deinit(self: *Pool) void {\n        const io = self.db.io;\n        const conns = self.conns;\n\n        self.mutex.lockUncancelable(io);\n        self.shutdown = true;\n        // any thread blocked in acquire() will unblock, check self.shutdown\n        // and return an error\n        self.cond.broadcast(io);\n\n        while (true) {\n            if (self.available == conns.len) {\n                break;\n            }\n            self.cond.waitUncancelable(io, &self.mutex);\n        }\n\n        // Don't need to lock this while we deallocate, as any calls to acquire\n        // will see the shutdown = true;\n        self.mutex.unlock(io);\n\n        const allocator = self.allocator;\n        for (conns) |conn| {\n            conn.deinit();\n            allocator.destroy(conn);\n        }\n        allocator.free(self.conns);\n        self.db.deinit();\n\n        allocator.destroy(self);\n    }\n\n    pub fn acquire(self: *Pool) !*Conn {\n        const io = self.db.io;\n        const conns = self.conns;\n\n        const deadline = @as(i64, @intCast(self.timeout));\n        const start = std.Io.Timestamp.now(io, .awake);\n\n        const SelectResult = union(enum) { t: Io.Cancelable!void, c: Io.Cancelable!void };\n        var select_buf: [1]SelectResult = undefined;\n\n        try self.mutex.lock(io);\n        errdefer self.mutex.unlock(io);\n\n        while (true) {\n            if (self.shutdown) {\n                return error.PoolShuttingDown;\n            }\n            const available = self.available;\n            if (available == 0) {\n                const now = std.Io.Timestamp.now(io, .awake);\n                const elapsed = start.durationTo(now).toNanoseconds();\n                if (elapsed >= deadline) {\n                    return error.Timeout;\n                }\n                const remaining_ns = deadline - elapsed;\n\n                var select: Io.Select(SelectResult) = .init(io, &select_buf);\n                defer select.cancelDiscard();\n                try select.concurrent(.t, Io.sleep, .{ io, .fromNanoseconds(remaining_ns), .awake });\n                try select.concurrent(.c, Io.Condition.wait, .{ &self.cond, io, &self.mutex });\n\n                _ = try select.await();\n                continue;\n            }\n\n            const index = available - 1;\n            const conn = conns[index];\n            self.available = index;\n            self.mutex.unlock(io);\n\n            return conn;\n        }\n    }\n\n    pub fn release(self: *Pool, conn: *Conn) void {\n        const io = self.db.io;\n        var conns = self.conns;\n        if (conn.err) |err| {\n            conn.allocator.free(err);\n            conn.err = null;\n        }\n\n        self.mutex.lockUncancelable(io);\n        const available = self.available;\n        conns[available] = conn;\n        self.available = available + 1;\n        self.mutex.unlock(io);\n\n        self.cond.signal(io);\n    }\n\n    pub fn exec(self: *Pool, sql: anytype, values: anytype) !usize {\n        var conn = try self.acquire();\n        defer self.release(conn);\n        return conn.exec(sql, values);\n    }\n\n    pub fn query(self: *Pool, sql: anytype, values: anytype) !Rows {\n        return self.queryWithState(sql, values, null);\n    }\n\n    pub fn queryWithState(self: *Pool, sql: anytype, values: anytype, state: anytype) !Rows {\n        const conn = try self.acquire();\n        errdefer self.release(conn);\n\n        const result = try conn.getResult(sql, values);\n        return Rows.init(self.allocator, result.result, state, .{\n            .conn = conn,\n            .stmt = result.stmt,\n        });\n    }\n\n    pub fn row(self: *Pool, sql: anytype, values: anytype) !?OwningRow {\n        return self.rowWithState(sql, values, null);\n    }\n\n    pub fn rowWithState(self: *Pool, sql: anytype, values: anytype, state: anytype) !?OwningRow {\n        var rows = try self.queryWithState(sql, values, state);\n        errdefer rows.deinit();\n\n        const r = (try rows.next()) orelse {\n            rows.deinit();\n            return null;\n        };\n\n        return .{\n            .row = r,\n            .rows = rows,\n        };\n    }\n\n    pub fn newConn(self: *Pool) !Conn {\n        return self.db.conn();\n    }\n};\n\nconst t = std.testing;\ntest \"Pool: thread-safety\" {\n    const db = try DB.init(t.io, t.allocator, \"/tmp/duckdb.zig.test\", .{});\n    var pool = try db.pool(.{\n        .size = 2,\n        .on_first_connection = &testPoolFirstConnection,\n    });\n    defer pool.deinit();\n\n    const t1 = try std.Thread.spawn(.{}, testPool, .{pool});\n    const t2 = try std.Thread.spawn(.{}, testPool, .{pool});\n    const t3 = try std.Thread.spawn(.{}, testPool, .{pool});\n\n    t1.join();\n    t2.join();\n    t3.join();\n\n    var c1 = try pool.acquire();\n    defer pool.release(c1);\n\n    const count = try c1.exec(\"delete from pool_test\", .{});\n    try t.expectEqual(6000, count);\n}\n\ntest \"Pool: exec/query/row\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    var pool = try db.pool(.{ .size = 1 });\n    defer pool.deinit();\n\n    _ = try pool.exec(\"create table pool_test (id integer)\", .{});\n    try t.expectEqual(3, try pool.exec(\"insert into pool_test (id) values ($1), ($2), ($3)\", .{ 1, 20, 300 }));\n\n    {\n        var rows = try pool.query(\"select * from pool_test where id != $1 order by id\", .{20});\n        defer rows.deinit();\n\n        try t.expectEqual(1, (try rows.next()).?.get(i32, 0));\n        try t.expectEqual(300, (try rows.next()).?.get(i32, 0));\n        try t.expectEqual(null, rows.next());\n    }\n\n    {\n        var row = (try pool.row(\"select * from pool_test where id = $1\", .{300})) orelse unreachable;\n        defer row.deinit();\n        try t.expectEqual(300, row.get(i32, 0));\n    }\n\n    {\n        const row = try pool.row(\"select * from pool_test where id = $1\", .{400});\n        try t.expectEqual(null, row);\n    }\n\n    try t.expectEqual(3, try pool.exec(\"delete from pool_test\", .{}));\n}\n\nfn testPool(p: *Pool) void {\n    for (0..2000) |i| {\n        var conn = p.acquire() catch unreachable;\n        _ = conn.exec(\"insert into pool_test (id) values ($1)\", .{i}) catch unreachable;\n        conn.release();\n    }\n}\n\nfn testPoolFirstConnection(conn: *Conn) !void {\n    _ = try conn.exec(\"drop table if exists pool_test\", .{});\n    _ = try conn.exec(\"create table pool_test (id uint16 not null)\", .{});\n}\n"
  },
  {
    "path": "src/row.zig",
    "content": "const std = @import(\"std\");\nconst typed = @import(\"typed\");\nconst lib = @import(\"lib.zig\");\n\nconst c = lib.c;\nconst DB = lib.DB;\nconst Rows = lib.Rows;\nconst Vector = lib.Vector;\n\nconst UUID = lib.UUID;\nconst Time = lib.Time;\nconst Date = lib.Date;\nconst Interval = lib.Interval;\nconst DataType = lib.DataType;\n\nconst Allocator = std.mem.Allocator;\n\npub const Row = struct {\n    index: usize,\n    vectors: []Vector,\n\n    pub fn get(self: Row, comptime T: type, col: usize) T {\n        const index = self.index;\n        const vector = self.vectors[col];\n\n        const TT = switch (@typeInfo(T)) {\n            .optional => |opt| blk: {\n                if (_isNull(vector.validity.?, index)) return null;\n                break :blk opt.child;\n            },\n            else => blk: {\n                lib.assert(_isNull(vector.validity.?, index) == false);\n                break :blk T;\n            },\n        };\n\n        switch (vector.data) {\n            .scalar => |scalar| return getScalar(TT, scalar, index, col),\n            else => unreachable,\n        }\n    }\n\n    pub fn list(self: Row, comptime T: type, col: usize) ?List(T) {\n        const index = self.index;\n        const vector = self.vectors[col];\n        if (_isNull(vector.validity.?, index)) return null;\n\n        const vc = vector.data.list;\n        const entry = vc.entries[index];\n        return List(T).init(col, vc.child, vc.validity, entry.offset, entry.length);\n    }\n\n    pub fn lazyList(self: Row, col: usize) ?LazyList {\n        const index = self.index;\n        const vector = self.vectors[col];\n        if (_isNull(vector.validity.?, index)) return null;\n\n        const vc = vector.data.list;\n        const entry = vc.entries[index];\n        return LazyList.init(col, vc.child, vc.validity, entry.offset, entry.length);\n    }\n\n    pub fn listItemType(self: Row, col: usize) DataType {\n        // (⌐■_■)\n        return lib.DataType.fromDuckDBType(self.vectors[col].data.list.type);\n    }\n\n    pub fn isNull(self: Row, col: usize) bool {\n        return _isNull(self.vectors[col].validity.?, self.index);\n    }\n};\n\n// Returned by conn.row, wraps a row and rows, the latter so that\n// it can be deinit'd\npub const OwningRow = struct {\n    row: Row,\n    rows: Rows,\n\n    pub fn get(self: OwningRow, comptime T: type, col: usize) T {\n        return self.row.get(T, col);\n    }\n\n    pub fn list(self: OwningRow, comptime T: type, col: usize) ?List(T) {\n        return self.row.list(T, col);\n    }\n\n    pub fn deinit(self: OwningRow) void {\n        self.rows.deinit();\n    }\n};\n\npub const Enum = struct {\n    idx: usize,\n    _col: usize,\n    _logical_type: c.duckdb_logical_type,\n    _cache: *std.AutoHashMap(u64, []const u8),\n\n    pub fn rowCache(self: Enum) ![]const u8 {\n        const gop = try self._cache.getOrPut(self.idx);\n        if (gop.found_existing) {\n            return gop.value_ptr.*;\n        }\n\n        const string_value = c.duckdb_enum_dictionary_value(self._logical_type, self.idx);\n\n        // Using self._cache.allocator is pretty bad. I _know_ this is the rows\n        // ArenaAllocator, so it's right, but it is  ugly and error prone should\n        // anything ever change\n        const value = try self._cache.allocator.dupe(u8, std.mem.span(string_value));\n        c.duckdb_free(string_value);\n        gop.value_ptr.* = value;\n        return value;\n    }\n\n    pub fn raw(self: Enum) [*c]const u8 {\n        return c.duckdb_enum_dictionary_value(self._logical_type, self.idx);\n    }\n};\n\npub fn List(comptime T: type) type {\n    return struct {\n        len: usize,\n        col: usize,\n        _validity: [*c]u64,\n        _offset: usize,\n        _scalar: Vector.Scalar,\n\n        const Self = @This();\n\n        fn init(col: usize, scalar: Vector.Scalar, validity: [*c]u64, offset: usize, length: usize) Self {\n            return .{\n                .col = col,\n                .len = length,\n                ._offset = offset,\n                ._scalar = scalar,\n                ._validity = validity,\n            };\n        }\n\n        pub fn get(self: *const Self, i: usize) T {\n            const index = i + self._offset;\n\n            const TT = switch (@typeInfo(T)) {\n                .optional => |opt| blk: {\n                    if (_isNull(self._validity, index)) return null;\n                    break :blk opt.child;\n                },\n                else => blk: {\n                    lib.assert(_isNull(self._validity, index) == false);\n                    break :blk T;\n                },\n            };\n\n            return getScalar(TT, self._scalar, index, self.col);\n        }\n\n        pub fn alloc(self: *const Self, allocator: Allocator) ![]T {\n            const arr = try allocator.alloc(T, self.len);\n            self.fill(arr);\n            return arr;\n        }\n\n        pub fn fill(self: *const Self, into: []T) void {\n            const limit = @min(into.len, self.len);\n            for (0..limit) |i| {\n                into[i] = self.get(i);\n            }\n        }\n    };\n}\n\n// A list who's type isn't known at compile-time\npub const LazyList = struct {\n    len: usize,\n    col: usize,\n    _validity: [*c]u64,\n    _offset: usize,\n    _scalar: Vector.Scalar,\n\n    fn init(col: usize, scalar: Vector.Scalar, validity: [*c]u64, offset: usize, length: usize) LazyList {\n        return .{\n            .col = col,\n            .len = length,\n            ._offset = offset,\n            ._scalar = scalar,\n            ._validity = validity,\n        };\n    }\n\n    pub fn get(self: *const LazyList, comptime T: type, i: usize) T {\n        const index = i + self._offset;\n\n        const TT = switch (@typeInfo(T)) {\n            .optional => |opt| blk: {\n                if (_isNull(self._validity, index)) return null;\n                break :blk opt.child;\n            },\n            else => blk: {\n                lib.assert(_isNull(self._validity, index) == false);\n                break :blk T;\n            },\n        };\n        return getScalar(TT, self._scalar, index, self.col);\n    }\n\n    pub fn isNull(self: *const LazyList, i: usize) bool {\n        return _isNull(self._validity, i);\n    }\n};\n\ninline fn _isNull(validity: [*c]u64, index: usize) bool {\n    if (validity == 0) {\n        return false;\n    }\n    const entry_index = index / 64;\n    const entry_mask = index % 64;\n    return validity[entry_index] & std.math.shl(u64, 1, entry_mask) == 0;\n}\n\nfn getScalar(comptime T: type, scalar: Vector.Scalar, index: usize, col: usize) T {\n    switch (T) {\n        []u8, []const u8 => return getBlob(scalar, index),\n        i8 => return scalar.i8[index],\n        i16 => return scalar.i16[index],\n        i32 => return scalar.i32[index],\n        i64 => switch (scalar) {\n            .i64 => |vc| return vc[index],\n            .timestamp => |vc| return vc[index],\n            else => unreachable,\n        },\n        i128 => return scalar.i128[index],\n        u128 => return scalar.u128[index],\n        u8 => return scalar.u8[index],\n        u16 => return scalar.u16[index],\n        u32 => return scalar.u32[index],\n        u64 => return scalar.u64[index],\n        f32 => return scalar.f32[index],\n        f64 => switch (scalar) {\n            .f64 => |vc| return vc[index],\n            .decimal => |vc| {\n                const value = switch (vc.internal) {\n                    inline else => |internal| lib.hugeInt(internal[index]),\n                };\n                return c.duckdb_decimal_to_double(c.duckdb_decimal{\n                    .width = vc.width,\n                    .scale = vc.scale,\n                    .value = value,\n                });\n            },\n            else => unreachable,\n        },\n        bool => return scalar.bool[index],\n        Date => return c.duckdb_from_date(scalar.date[index]),\n        Time => return c.duckdb_from_time(scalar.time[index]),\n        Interval => return scalar.interval[index],\n        Enum => {\n            const e = scalar.@\"enum\";\n            return Enum{\n                .idx = switch (e.internal) {\n                    inline else => |internal| internal[index],\n                },\n                ._col = col,\n                ._cache = e.cache,\n                ._logical_type = e.logical_type,\n            };\n        },\n        UUID => return getUUID(scalar, index),\n        else => @compileError(\"Cannot get value of type \" ++ @typeName(T)),\n    }\n}\n\nfn getBlob(scalar: Vector.Scalar, index: usize) []u8 {\n    switch (scalar) {\n        .blob, .varchar => |vc| {\n            // This sucks. This is an untagged union. But both versions (inlined and pointer)\n            // have the same leading 8 bytes, including the length which is the first 4 bytes.\n            // There is a c.duckdb_string_is_inlined that we could use instead of hard-coding\n            // the 12, but that requires dereferencing value, which I'd like to avoid.\n            // For one reason, when inlined, it's easy to accidently pass the address of the local copy\n            const value = &vc[index];\n            const len = value.value.inlined.length;\n            if (len <= 12) {\n                return value.value.inlined.inlined[0..len];\n            }\n            const pointer = value.value.pointer;\n            return pointer.ptr[0..len];\n        },\n        else => unreachable,\n    }\n}\n\n// largely taken from duckdb's uuid type\nfn getUUID(scalar: Vector.Scalar, index: usize) UUID {\n    const hex = \"0123456789abcdef\";\n    const n = scalar.uuid[index];\n\n    const h = lib.hugeInt(n);\n\n    const u = h.upper ^ (@as(i64, 1) << 63);\n    const l = h.lower;\n\n    var buf: [36]u8 = undefined;\n\n    const b1: u8 = @intCast((u >> 56) & 0xFF);\n    buf[0] = hex[b1 >> 4];\n    buf[1] = hex[b1 & 0x0f];\n\n    const b2: u8 = @intCast((u >> 48) & 0xFF);\n    buf[2] = hex[b2 >> 4];\n    buf[3] = hex[b2 & 0x0f];\n\n    const b3: u8 = @intCast((u >> 40) & 0xFF);\n    buf[4] = hex[b3 >> 4];\n    buf[5] = hex[b3 & 0x0f];\n\n    const b4: u8 = @intCast((u >> 32) & 0xFF);\n    buf[6] = hex[b4 >> 4];\n    buf[7] = hex[b4 & 0x0f];\n\n    buf[8] = '-';\n\n    const b5: u8 = @intCast((u >> 24) & 0xFF);\n    buf[9] = hex[b5 >> 4];\n    buf[10] = hex[b5 & 0x0f];\n\n    const b6: u8 = @intCast((u >> 16) & 0xFF);\n    buf[11] = hex[b6 >> 4];\n    buf[12] = hex[b6 & 0x0f];\n\n    buf[13] = '-';\n\n    const b7: u8 = @intCast((u >> 8) & 0xFF);\n    buf[14] = hex[b7 >> 4];\n    buf[15] = hex[b7 & 0x0f];\n\n    const b8: u8 = @intCast(u & 0xFF);\n    buf[16] = hex[b8 >> 4];\n    buf[17] = hex[b8 & 0x0f];\n\n    buf[18] = '-';\n\n    const b9: u8 = @intCast((l >> 56) & 0xFF);\n    buf[19] = hex[b9 >> 4];\n    buf[20] = hex[b9 & 0x0f];\n\n    const b10: u8 = @intCast((l >> 48) & 0xFF);\n    buf[21] = hex[b10 >> 4];\n    buf[22] = hex[b10 & 0x0f];\n\n    buf[23] = '-';\n\n    const b11: u8 = @intCast((l >> 40) & 0xFF);\n    buf[24] = hex[b11 >> 4];\n    buf[25] = hex[b11 & 0x0f];\n\n    const b12: u8 = @intCast((l >> 32) & 0xFF);\n    buf[26] = hex[b12 >> 4];\n    buf[27] = hex[b12 & 0x0f];\n\n    const b13: u8 = @intCast((l >> 24) & 0xFF);\n    buf[28] = hex[b13 >> 4];\n    buf[29] = hex[b13 & 0x0f];\n\n    const b14: u8 = @intCast((l >> 16) & 0xFF);\n    buf[30] = hex[b14 >> 4];\n    buf[31] = hex[b14 & 0x0f];\n\n    const b15: u8 = @intCast((l >> 8) & 0xFF);\n    buf[32] = hex[b15 >> 4];\n    buf[33] = hex[b15 & 0x0f];\n\n    const b16: u8 = @intCast(l & 0xFF);\n    buf[34] = hex[b16 >> 4];\n    buf[35] = hex[b16 & 0x0f];\n\n    return buf;\n}\n\nconst t = std.testing;\n// Test this specifically since there's special handling based on the length\n// of the column (inlined vs pointer)\ntest \"read varchar\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        var rows = try conn.query(\n            \\\\\n            \\\\ select '1' union all\n            \\\\ select '12345' union all\n            \\\\ select '123456789A' union all\n            \\\\ select '123456789AB' union all\n            \\\\ select '123456789ABC' union all\n            \\\\ select '123456789ABCD' union all\n            \\\\ select '123456789ABCDE' union all\n            \\\\ select '123456789ABCDEF' union all\n            \\\\ select null\n        , .{});\n        defer rows.deinit();\n\n        {\n            const row = (try rows.next()).?;\n            try t.expectEqualStrings(\"1\", row.get([]u8, 0));\n            try t.expectEqualStrings(\"1\", row.get(?[]u8, 0).?);\n            try t.expectEqualStrings(\"1\", row.get([]const u8, 0));\n            try t.expectEqualStrings(\"1\", row.get(?[]const u8, 0).?);\n            try t.expectEqual(false, row.isNull(0));\n        }\n\n        try t.expectEqualStrings(\"12345\", (try rows.next()).?.get([]const u8, 0));\n        try t.expectEqualStrings(\"123456789A\", (try rows.next()).?.get([]const u8, 0));\n        try t.expectEqualStrings(\"123456789AB\", (try rows.next()).?.get([]const u8, 0));\n        try t.expectEqualStrings(\"123456789ABC\", (try rows.next()).?.get([]const u8, 0));\n        try t.expectEqualStrings(\"123456789ABCD\", (try rows.next()).?.get([]const u8, 0));\n        try t.expectEqualStrings(\"123456789ABCDE\", (try rows.next()).?.get([]const u8, 0));\n\n        {\n            const row = (try rows.next()).?;\n            try t.expectEqualStrings(\"123456789ABCDEF\", row.get([]u8, 0));\n            try t.expectEqualStrings(\"123456789ABCDEF\", row.get(?[]u8, 0).?);\n            try t.expectEqualStrings(\"123456789ABCDEF\", row.get([]const u8, 0));\n            try t.expectEqualStrings(\"123456789ABCDEF\", row.get(?[]const u8, 0).?);\n        }\n\n        {\n            const row = (try rows.next()).?;\n            try t.expectEqual(null, row.get(?[]u8, 0));\n            try t.expectEqual(null, row.get(?[]const u8, 0));\n            try t.expectEqual(true, row.isNull(0));\n        }\n\n        try t.expectEqual(null, try rows.next());\n    }\n}\n\n// Test this specifically since there's special handling based on the length\n// of the column (inlined vs pointer)\ntest \"read blob\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        var rows = try conn.query(\n            \\\\\n            \\\\ select '\\xAA'::blob union all\n            \\\\ select '\\xAA\\xAA\\xAA\\xAA\\xAB'::blob union all\n            \\\\ select '\\xAA\\xAA\\xAA\\xAA\\xAB\\xAA\\xAA\\xAA\\xAA\\xAB\\xAA\\xAA\\xAA\\xAA\\xAB'::blob union all\n            \\\\ select null\n        , .{});\n        defer rows.deinit();\n\n        try t.expectEqualSlices(u8, @as([]const u8, &.{170}), (try rows.next()).?.get([]const u8, 0));\n        try t.expectEqualSlices(u8, @as([]const u8, &.{ 170, 170, 170, 170, 171 }), (try rows.next()).?.get([]const u8, 0));\n        try t.expectEqualSlices(u8, @as([]const u8, &.{ 170, 170, 170, 170, 171, 170, 170, 170, 170, 171, 170, 170, 170, 170, 171 }), (try rows.next()).?.get([]const u8, 0));\n        try t.expectEqual(null, (try rows.next()).?.get(?[]const u8, 0));\n        try t.expectEqual(null, try rows.next());\n    }\n}\n\ntest \"read ints\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        var rows = try conn.query(\n            \\\\\n            \\\\ select 0::tinyint, 0::smallint, 0::integer, 0::bigint, 0::hugeint, 0::utinyint, 0::usmallint, 0::uinteger, 0::ubigint\n            \\\\ union all\n            \\\\ select 127::tinyint, 32767::smallint, 2147483647::integer, 9223372036854775807::bigint, 170141183460469231731687303715884105727::hugeint, 255::utinyint, 65535::usmallint, 4294967295::uinteger, 18446744073709551615::ubigint\n            \\\\ union all\n            \\\\ select -127::tinyint, -32767::smallint, -2147483647::integer, -9223372036854775807::bigint, -170141183460469231731687303715884105727::hugeint, 0::utinyint, 0::usmallint, 0::uinteger, 0::ubigint\n            \\\\ union all\n            \\\\ select null, null, null, null, null, null, null, null, null\n        , .{});\n        defer rows.deinit();\n\n        var row = (try rows.next()) orelse unreachable;\n        try t.expectEqual(0, row.get(i8, 0));\n        try t.expectEqual(0, row.get(i16, 1));\n        try t.expectEqual(0, row.get(i32, 2));\n        try t.expectEqual(0, row.get(i64, 3));\n        try t.expectEqual(0, row.get(i128, 4));\n        try t.expectEqual(0, row.get(u8, 5));\n        try t.expectEqual(0, row.get(u16, 6));\n        try t.expectEqual(0, row.get(u32, 7));\n        try t.expectEqual(0, row.get(u64, 8));\n\n        row = (try rows.next()) orelse unreachable;\n        try t.expectEqual(127, row.get(i8, 0));\n        try t.expectEqual(32767, row.get(i16, 1));\n        try t.expectEqual(2147483647, row.get(i32, 2));\n        try t.expectEqual(9223372036854775807, row.get(i64, 3));\n        try t.expectEqual(170141183460469231731687303715884105727, row.get(i128, 4));\n        try t.expectEqual(255, row.get(u8, 5));\n        try t.expectEqual(65535, row.get(u16, 6));\n        try t.expectEqual(4294967295, row.get(u32, 7));\n        try t.expectEqual(18446744073709551615, row.get(u64, 8));\n\n        row = (try rows.next()) orelse unreachable;\n        try t.expectEqual(-127, row.get(i8, 0));\n        try t.expectEqual(-32767, row.get(i16, 1));\n        try t.expectEqual(-2147483647, row.get(i32, 2));\n        try t.expectEqual(-9223372036854775807, row.get(i64, 3));\n        try t.expectEqual(-170141183460469231731687303715884105727, row.get(i128, 4));\n\n        row = (try rows.next()) orelse unreachable;\n        try t.expectEqual(null, row.get(?i8, 0));\n        try t.expectEqual(null, row.get(?i16, 1));\n        try t.expectEqual(null, row.get(?i32, 2));\n        try t.expectEqual(null, row.get(?i64, 3));\n        try t.expectEqual(null, row.get(?i128, 4));\n        try t.expectEqual(null, row.get(?u8, 5));\n        try t.expectEqual(null, row.get(?u16, 6));\n        try t.expectEqual(null, row.get(?u32, 7));\n        try t.expectEqual(null, row.get(?u64, 8));\n\n        try t.expectEqual(null, try rows.next());\n    }\n}\n\ntest \"read bool\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        var rows = try conn.query(\"select 0::bool, 1::bool, null::bool\", .{});\n        defer rows.deinit();\n\n        var row = (try rows.next()) orelse unreachable;\n        try t.expectEqual(false, row.get(bool, 0));\n        try t.expectEqual(true, row.get(bool, 1));\n        try t.expectEqual(null, row.get(?bool, 2));\n\n        try t.expectEqual(null, try rows.next());\n    }\n}\n\ntest \"read float\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        var rows = try conn.query(\"select 32.329::real, -0.29291::double, null::real, null::double\", .{});\n        defer rows.deinit();\n\n        var row = (try rows.next()) orelse unreachable;\n        try t.expectEqual(32.329, row.get(f32, 0));\n        try t.expectEqual(-0.29291, row.get(f64, 1));\n        try t.expectEqual(null, row.get(?f32, 2));\n        try t.expectEqual(null, row.get(?f64, 3));\n\n        try t.expectEqual(null, try rows.next());\n    }\n}\n\ntest \"read decimal\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        // decimals (representation is different based on the width)\n        var rows = try conn.query(\"select 1.23::decimal(3,2), 1.24::decimal(8, 4), 1.25::decimal(12, 5), 1.26::decimal(18, 3), 1.27::decimal(35, 4)\", .{});\n        defer rows.deinit();\n\n        const row = (try rows.next()).?;\n        try t.expectEqual(1.23, row.get(f64, 0));\n        try t.expectEqual(1.24, row.get(f64, 1));\n        try t.expectEqual(1.25, row.get(f64, 2));\n        try t.expectEqual(1.26, row.get(f64, 3));\n        try t.expectEqual(1.27, row.get(f64, 4));\n    }\n}\n\ntest \"read date & time\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        var rows = try conn.query(\"select date '1992-09-20', time '14:21:13.332', timestamp '1993-10-21 11:30:02'\", .{});\n        defer rows.deinit();\n\n        var row = (try rows.next()) orelse unreachable;\n        try t.expectEqual(Date{ .year = 1992, .month = 9, .day = 20 }, row.get(Date, 0));\n        try t.expectEqual(Time{ .hour = 14, .min = 21, .sec = 13, .micros = 332000 }, row.get(Time, 1));\n        try t.expectEqual(751203002000000, row.get(i64, 2));\n    }\n}\n\ntest \"read list\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n    _ = try conn.exec(\"SET autoinstall_known_extensions=1\", .{});\n    _ = try conn.exec(\"SET autoload_known_extensions=1\", .{});\n    _ = try conn.exec(\"create type my_type as enum ('type_a', 'type_b')\", .{});\n\n    {\n        var rows = try conn.query(\"select [1, 32, 99, null, -4]::int[]\", .{});\n        defer rows.deinit();\n\n        var row = (try rows.next()) orelse unreachable;\n\n        const list = row.list(?i32, 0).?;\n        try t.expectEqual(5, list.len);\n        try t.expectEqual(1, list.get(0).?);\n        try t.expectEqual(32, list.get(1).?);\n        try t.expectEqual(99, list.get(2).?);\n        try t.expectEqual(null, list.get(3));\n        try t.expectEqual(-4, list.get(4).?);\n\n        const arr = try list.alloc(t.allocator);\n        defer t.allocator.free(arr);\n        try t.expectEqualSlices(?i32, &.{ 1, 32, 99, null, -4 }, arr);\n    }\n\n    {\n        var rows = try conn.query(\"select ['tag1', null, 'tag2']::varchar[]\", .{});\n        defer rows.deinit();\n\n        var row = (try rows.next()) orelse unreachable;\n        const list = row.list(?[]u8, 0).?;\n        try t.expectEqual(3, list.len);\n        try t.expectEqualStrings(\"tag1\", list.get(0).?);\n        try t.expectEqual(null, list.get(1));\n        try t.expectEqualStrings(\"tag2\", list.get(2).?);\n    }\n\n    {\n        var rows = try conn.query(\"select ['tag1', null, 'tag2']::varchar[]\", .{});\n        defer rows.deinit();\n\n        var row = (try rows.next()) orelse unreachable;\n        const list = row.lazyList(0).?;\n        try t.expectEqual(3, list.len);\n        try t.expectEqualStrings(\"tag1\", list.get([]const u8, 0));\n        try t.expectEqual(null, list.get(?[]const u8, 1));\n        try t.expectEqualStrings(\"tag2\", list.get(?[]const u8, 2).?);\n    }\n\n    {\n        var rows = try conn.query(\"select ['tag1', null, 'tag2']::varchar[]\", .{});\n        defer rows.deinit();\n\n        var row = (try rows.next()) orelse unreachable;\n        const list = row.list(?[]const u8, 0).?;\n        try t.expectEqual(3, list.len);\n        try t.expectEqualStrings(\"tag1\", list.get(0).?);\n        try t.expectEqual(null, list.get(1));\n        try t.expectEqualStrings(\"tag2\", list.get(2).?);\n    }\n\n    {\n        var rows = try conn.query(\"select ['type_a', null, 'type_b', 'type_a']::my_type[]\", .{});\n        defer rows.deinit();\n\n        var row = (try rows.next()) orelse unreachable;\n        const list = row.list(?Enum, 0).?;\n        try t.expectEqual(.@\"enum\", row.listItemType(0));\n        try t.expectEqual(4, list.len);\n        try t.expectEqualStrings(\"type_a\", try list.get(0).?.rowCache());\n        try t.expectEqual(null, list.get(1));\n        try t.expectEqualStrings(\"type_b\", try list.get(2).?.rowCache());\n        try t.expectEqualStrings(\"type_a\", try list.get(3).?.rowCache());\n    }\n}\n\n// There's some internal caching with this, so we need to test mulitple rows\ntest \"read enum\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create type my_type as enum ('type_a', 'type_b')\", .{});\n    _ = try conn.exec(\"create type tea_type as enum ('keemun', 'silver_needle')\", .{});\n\n    var rows = try conn.query(\n        \\\\ select 'type_a'::my_type, 'type_b'::my_type, null::my_type, 'type_a'::my_type, 'keemun'::tea_type, 'silver_needle'::tea_type, null::tea_type, 'silver_needle'::tea_type\n        \\\\ union all\n        \\\\ select 'type_b'::my_type, null::my_type, 'type_a'::my_type, 'type_b'::my_type, 'keemun'::tea_type, 'silver_needle'::tea_type, null::tea_type, 'silver_needle'::tea_type\n    , .{});\n    defer rows.deinit();\n\n    var row = (try rows.next()) orelse unreachable;\n    try t.expectEqualStrings(\"type_a\", std.mem.span(row.get(Enum, 0).raw()));\n    try t.expectEqualStrings(\"type_a\", try row.get(Enum, 0).rowCache());\n    try t.expectEqualStrings(\"type_a\", try row.get(Enum, 0).rowCache());\n    try t.expectEqualStrings(\"type_b\", try row.get(Enum, 1).rowCache());\n    try t.expectEqualStrings(\"type_b\", try row.get(?Enum, 1).?.rowCache());\n    try t.expectEqual(null, row.get(?Enum, 2));\n    try t.expectEqualStrings(\"type_a\", try row.get(Enum, 3).rowCache());\n    try t.expectEqualStrings(\"keemun\", try row.get(Enum, 4).rowCache());\n    try t.expectEqualStrings(\"silver_needle\", std.mem.span(row.get(Enum, 5).raw()));\n    try t.expectEqual(null, row.get(?Enum, 6));\n    try t.expectEqualStrings(\"silver_needle\", try row.get(Enum, 7).rowCache());\n\n    row = (try rows.next()) orelse unreachable;\n    try t.expectEqualStrings(\"type_b\", try row.get(Enum, 0).rowCache());\n    try t.expectEqual(null, row.get(?Enum, 1));\n    try t.expectEqualStrings(\"type_a\", try row.get(Enum, 2).rowCache());\n    try t.expectEqualStrings(\"type_b\", try row.get(Enum, 3).rowCache());\n    try t.expectEqualStrings(\"keemun\", try row.get(?Enum, 4).?.rowCache());\n    try t.expectEqualStrings(\"silver_needle\", try row.get(Enum, 5).rowCache());\n    try t.expectEqual(null, row.get(?Enum, 6));\n    try t.expectEqualStrings(\"silver_needle\", try row.get(Enum, 7).rowCache());\n}\n\ntest \"owning row\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        // error case\n        try t.expectError(error.DuckDBError, conn.row(\"select x\", .{}));\n        try t.expectEqualStrings(\"Binder Error: Referenced column \\\"x\\\" was not found because the FROM clause is missing\\n\\nLINE 1: select x\\n               ^\", conn.err.?);\n    }\n\n    {\n        // null\n        const row = try conn.row(\"select 1 where false\", .{});\n        try t.expectEqual(null, row);\n    }\n\n    {\n        const row = (try conn.row(\"select $1::bigint\", .{-991823891832})) orelse unreachable;\n        defer row.deinit();\n        try t.expectEqual(-991823891832, row.get(i64, 0));\n    }\n\n    {\n        _ = try conn.exec(\"SET autoinstall_known_extensions=1\", .{});\n        _ = try conn.exec(\"SET autoload_known_extensions=1\", .{});\n        const row = (try conn.row(\"select [1, 32, 99, null, -4]::int[]\", .{})) orelse unreachable;\n        defer row.deinit();\n\n        const list = row.list(?i32, 0).?;\n        try t.expectEqual(5, list.len);\n        try t.expectEqual(1, list.get(0).?);\n        try t.expectEqual(32, list.get(1).?);\n        try t.expectEqual(99, list.get(2).?);\n        try t.expectEqual(null, list.get(3));\n        try t.expectEqual(-4, list.get(4).?);\n    }\n}\n"
  },
  {
    "path": "src/rows.zig",
    "content": "const std = @import(\"std\");\nconst lib = @import(\"lib.zig\");\n\nconst c = lib.c;\nconst Row = lib.Row;\nconst Conn = lib.Conn;\nconst Vector = lib.Vector;\nconst DataType = lib.DataType;\n\nconst DuckDBError = c.DuckDBError;\nconst Allocator = std.mem.Allocator;\n\npub const Rows = struct {\n    // Depending on how it's executed, Rows might own a prepared statement and/or\n    // a connection (or neither). By \"own\", we mean that when rows.deinit() is\n    // called, it must free/release whatever it owns.\n    own: Own,\n\n    // The underlying duckdb result\n    result: *c.duckdb_result,\n\n    // The number of chunks in this result\n    chunk_count: usize = 0,\n\n    // The next chunk to laod\n    chunk_index: usize = 0,\n\n    // The number of columns in this rersult\n    column_count: usize = 0,\n\n    // The current chunk object\n    chunk: ?c.duckdb_data_chunk = null,\n\n    // The number of rows in the current chunk\n    row_count: usize = 0,\n\n    // The row index, within the current chunk\n    row_index: usize = 0,\n\n    // Vector data + validity  + type info for the current chunk\n    vectors: []Vector = undefined,\n\n    arena: *std.heap.ArenaAllocator,\n\n    const Own = struct {\n        conn: ?*Conn = null,\n        stmt: ?*c.duckdb_prepared_statement = null,\n    };\n\n    pub fn init(allocator: Allocator, result: *c.duckdb_result, state: anytype, own: Own) !Rows {\n        errdefer {\n            if (own.stmt) |s| {\n                c.duckdb_destroy_prepare(s);\n                allocator.destroy(s);\n            }\n            if (own.conn) |conn| {\n                conn.release();\n            }\n        }\n\n        const r = result.*;\n        const chunk_count = c.duckdb_result_chunk_count(r);\n        const column_count = c.duckdb_column_count(result);\n\n        var arena = try allocator.create(std.heap.ArenaAllocator);\n        errdefer allocator.destroy(arena);\n\n        arena.* = std.heap.ArenaAllocator.init(allocator);\n        errdefer arena.deinit();\n\n        const aa = arena.allocator();\n        var vectors: []Vector = undefined;\n        if (@TypeOf(state) == @TypeOf(null)) {\n            vectors = try aa.alloc(Vector, column_count);\n        } else {\n            vectors = try state.getVectors(column_count);\n        }\n\n        for (0..column_count) |i| {\n            vectors[i] = try Vector.init(aa, c.duckdb_column_logical_type(result, i));\n        }\n\n        return .{\n            .own = own,\n            .arena = arena,\n            .result = result,\n            .vectors = vectors,\n            .chunk_count = chunk_count,\n            .column_count = column_count,\n        };\n    }\n\n    pub fn deinit(self: *const Rows) void {\n        for (self.vectors) |*v| {\n            v.deinit();\n        }\n\n        const allocator = self.arena.child_allocator;\n        {\n            const result = self.result;\n            c.duckdb_destroy_result(result);\n            allocator.destroy(result);\n        }\n\n        const own = self.own;\n        if (own.stmt) |stmt| {\n            c.duckdb_destroy_prepare(stmt);\n            allocator.destroy(stmt);\n        }\n\n        if (own.conn) |conn| {\n            conn.release();\n        }\n\n        self.arena.deinit();\n        allocator.destroy(self.arena);\n    }\n\n    pub fn changed(self: *const Rows) usize {\n        return c.duckdb_rows_changed(self.result);\n    }\n\n    pub fn count(self: *const Rows) usize {\n        return c.duckdb_row_count(self.result);\n    }\n\n    pub fn columnName(self: *const Rows, i: usize) [*c]const u8 {\n        return c.duckdb_column_name(self.result, i);\n    }\n\n    pub fn columnType(self: *const Rows, i: usize) DataType {\n        switch (self.vectors[i].type) {\n            .list => return .list,\n            .scalar => |s| switch (s) {\n                .@\"enum\" => return .@\"enum\",\n                .decimal => return .decimal,\n                .simple => |duckdb_type| return DataType.fromDuckDBType(duckdb_type),\n            },\n        }\n    }\n\n    pub fn next(self: *Rows) !?Row {\n        var row_index = self.row_index;\n        if (row_index == self.row_count) {\n            if (try self.loadNextChunk() == false) {\n                return null;\n            }\n            row_index = 0;\n        }\n\n        self.row_index = row_index + 1;\n\n        return .{\n            .index = row_index,\n            .vectors = self.vectors,\n        };\n    }\n\n    fn loadNextChunk(self: *Rows) !bool {\n        const result = self.result.*;\n        const chunk_count = self.chunk_count;\n        const column_count = self.column_count;\n\n        if (self.chunk) |*chunk| {\n            c.duckdb_destroy_data_chunk(chunk);\n            self.chunk = null;\n        }\n\n        var chunk_index = self.chunk_index;\n\n        while (true) {\n            if (chunk_index == chunk_count) return false;\n\n            var chunk = c.duckdb_result_get_chunk(result, chunk_index);\n            chunk_index += 1;\n\n            const row_count = c.duckdb_data_chunk_get_size(chunk);\n            if (row_count == 0) {\n                c.duckdb_destroy_data_chunk(&chunk);\n                continue;\n            }\n\n            var vectors = self.vectors;\n\n            for (0..column_count) |col| {\n                const vector = &vectors[col];\n                const real_vector = c.duckdb_data_chunk_get_vector(chunk, col);\n                vector.loadVector(real_vector);\n                vector.validity = c.duckdb_vector_get_validity(real_vector);\n            }\n\n            self.chunk = chunk;\n            self.row_count = row_count;\n            self.chunk_index = chunk_index;\n\n            return true;\n        }\n        unreachable;\n    }\n};\n\nconst t = std.testing;\nconst DB = lib.DB;\ntest \"rows: introspect\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table test(id integer, name varchar);\", .{});\n\n    const rows = try conn.query(\"select id, name from test\", .{});\n    defer rows.deinit();\n\n    try t.expectEqual(2, rows.column_count);\n    try t.expectEqualStrings(\"id\", std.mem.span(rows.columnName(0)));\n    try t.expectEqualStrings(\"name\", std.mem.span(rows.columnName(1)));\n\n    try t.expectEqual(.integer, rows.columnType(0));\n    try t.expectEqual(.varchar, rows.columnType(1));\n}\n\ntest \"rows: introspect empty\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create table test(id integer, name varchar);\", .{});\n\n    const rows = try conn.query(\"select id, name from test where false\", .{});\n    defer rows.deinit();\n\n    try t.expectEqual(2, rows.column_count);\n    try t.expectEqualStrings(\"id\", std.mem.span(rows.columnName(0)));\n    try t.expectEqualStrings(\"name\", std.mem.span(rows.columnName(1)));\n\n    try t.expectEqual(.integer, rows.columnType(0));\n    try t.expectEqual(.varchar, rows.columnType(1));\n}\n"
  },
  {
    "path": "src/stmt.zig",
    "content": "const std = @import(\"std\");\nconst lib = @import(\"lib.zig\");\n\nconst M = @This();\n\nconst c = lib.c;\nconst Conn = lib.Conn;\nconst Rows = lib.Rows;\n\nconst DuckDBError = c.DuckDBError;\nconst Allocator = std.mem.Allocator;\n\nconst UUID = lib.UUID;\nconst Time = lib.Time;\nconst Date = lib.Date;\nconst Interval = lib.Interval;\nconst DataType = lib.DataType;\n\npub const Stmt = struct {\n    conn: *Conn,\n    auto_release: bool,\n    stmt: *c.duckdb_prepared_statement,\n\n    pub const StatementType = enum {\n        invalid,\n        select,\n        insert,\n        update,\n        explain,\n        delete,\n        prepare,\n        create,\n        execute,\n        alter,\n        transaction,\n        copy,\n        analyze,\n        variable_set,\n        create_func,\n        drop,\n        @\"export\",\n        pragma,\n        vacuum,\n        call,\n        set,\n        load,\n        relation,\n        extension,\n        logical_plan,\n        attach,\n        detach,\n        multi,\n        unknown,\n    };\n\n    pub const Opts = struct {\n        auto_release: bool = false,\n    };\n\n    pub fn init(stmt: *c.duckdb_prepared_statement, conn: *Conn, opts: Opts) Stmt {\n        return .{\n            .conn = conn,\n            .stmt = stmt,\n            .auto_release = opts.auto_release,\n        };\n    }\n\n    pub fn deinit(self: Stmt) void {\n        const stmt = self.stmt;\n        c.duckdb_destroy_prepare(stmt);\n        self.conn.allocator.destroy(stmt);\n    }\n\n    pub fn clearBindings(self: *const Stmt) !void {\n        if (c.duckdb_clear_bindings(self.stmt.*) == DuckDBError) {\n            return error.DuckDBError;\n        }\n    }\n\n    pub fn statementType(self: *const Stmt) StatementType {\n        return switch (c.duckdb_prepared_statement_type(self.stmt.*)) {\n            c.DUCKDB_STATEMENT_TYPE_INVALID => .invalid,\n            c.DUCKDB_STATEMENT_TYPE_SELECT => .select,\n            c.DUCKDB_STATEMENT_TYPE_INSERT => .insert,\n            c.DUCKDB_STATEMENT_TYPE_UPDATE => .update,\n            c.DUCKDB_STATEMENT_TYPE_EXPLAIN => .explain,\n            c.DUCKDB_STATEMENT_TYPE_DELETE => .delete,\n            c.DUCKDB_STATEMENT_TYPE_PREPARE => .prepare,\n            c.DUCKDB_STATEMENT_TYPE_CREATE => .create,\n            c.DUCKDB_STATEMENT_TYPE_EXECUTE => .execute,\n            c.DUCKDB_STATEMENT_TYPE_ALTER => .alter,\n            c.DUCKDB_STATEMENT_TYPE_TRANSACTION => .transaction,\n            c.DUCKDB_STATEMENT_TYPE_COPY => .copy,\n            c.DUCKDB_STATEMENT_TYPE_ANALYZE => .analyze,\n            c.DUCKDB_STATEMENT_TYPE_VARIABLE_SET => .variable_set,\n            c.DUCKDB_STATEMENT_TYPE_CREATE_FUNC => .create_func,\n            c.DUCKDB_STATEMENT_TYPE_DROP => .drop,\n            c.DUCKDB_STATEMENT_TYPE_EXPORT => .@\"export\",\n            c.DUCKDB_STATEMENT_TYPE_PRAGMA => .pragma,\n            c.DUCKDB_STATEMENT_TYPE_VACUUM => .vacuum,\n            c.DUCKDB_STATEMENT_TYPE_CALL => .call,\n            c.DUCKDB_STATEMENT_TYPE_SET => .set,\n            c.DUCKDB_STATEMENT_TYPE_LOAD => .load,\n            c.DUCKDB_STATEMENT_TYPE_RELATION => .relation,\n            c.DUCKDB_STATEMENT_TYPE_EXTENSION => .extension,\n            c.DUCKDB_STATEMENT_TYPE_LOGICAL_PLAN => .logical_plan,\n            c.DUCKDB_STATEMENT_TYPE_ATTACH => .attach,\n            c.DUCKDB_STATEMENT_TYPE_DETACH => .detach,\n            c.DUCKDB_STATEMENT_TYPE_MULTI => .multi,\n            else => .unknown,\n        };\n    }\n\n    pub fn bind(self: *const Stmt, values: anytype) !void {\n        const stmt = self.stmt.*;\n        inline for (values, 0..) |value, i| {\n            _ = try M.bindValue(@TypeOf(value), stmt, value, i + 1);\n        }\n    }\n\n    pub fn bindValue(self: *const Stmt, value: anytype, i: usize) !void {\n        _ = try M.bindValue(@TypeOf(value), self.stmt.*, value, i + 1);\n    }\n\n    pub fn exec(self: *const Stmt) !usize {\n        const result = try self.getResult();\n        defer {\n            c.duckdb_destroy_result(result);\n            self.conn.allocator.destroy(result);\n        }\n        return c.duckdb_rows_changed(result);\n    }\n\n    pub fn query(self: *const Stmt, state: anytype) !Rows {\n        const result = try self.getResult();\n        return Rows.init(self.conn.allocator, result, state, .{\n            .stmt = if (self.auto_release) self.stmt else null,\n        });\n    }\n\n    pub fn getResult(self: *const Stmt) !*c.duckdb_result {\n        const conn = self.conn;\n        const allocator = conn.allocator;\n\n        const result = try allocator.create(c.duckdb_result);\n        errdefer allocator.destroy(result);\n\n        if (c.duckdb_execute_prepared(self.stmt.*, result) == DuckDBError) {\n            try self.conn.duckdbError(c.duckdb_result_error(result));\n            return error.DuckDBError;\n        }\n        return result;\n    }\n\n    pub fn numberOfParameters(self: Stmt) usize {\n        return c.duckdb_nparams(self.stmt.*);\n    }\n\n    pub fn dataTypeC(self: Stmt, i: usize) c.duckdb_type {\n        return c.duckdb_param_type(self.stmt.*, i + 1);\n    }\n\n    pub fn dataType(self: Stmt, i: usize) lib.DataType {\n        return lib.DataType.fromDuckDBType(self.dataTypeC(i));\n    }\n};\n\nfn bindValue(comptime T: type, stmt: c.duckdb_prepared_statement, value: anytype, bind_index: usize) !c_uint {\n    var rc: c_uint = 0;\n    switch (@typeInfo(T)) {\n        .null => rc = c.duckdb_bind_null(stmt, bind_index),\n        .comptime_int => rc = bindI64(stmt, bind_index, @intCast(value)),\n        .comptime_float => rc = c.duckdb_bind_double(stmt, bind_index, @floatCast(value)),\n        .int => |int| {\n            if (int.signedness == .signed) {\n                switch (int.bits) {\n                    1...8 => rc = c.duckdb_bind_int8(stmt, bind_index, @intCast(value)),\n                    9...16 => rc = c.duckdb_bind_int16(stmt, bind_index, @intCast(value)),\n                    17...32 => rc = c.duckdb_bind_int32(stmt, bind_index, @intCast(value)),\n                    33...63 => rc = c.duckdb_bind_int64(stmt, bind_index, @intCast(value)),\n                    64 => rc = bindI64(stmt, bind_index, value),\n                    65...128 => rc = c.duckdb_bind_hugeint(stmt, bind_index, lib.hugeInt(@intCast(value))),\n                    else => bindError(T),\n                }\n            } else {\n                switch (int.bits) {\n                    1...8 => rc = c.duckdb_bind_uint8(stmt, bind_index, @intCast(value)),\n                    9...16 => rc = c.duckdb_bind_uint16(stmt, bind_index, @intCast(value)),\n                    17...32 => rc = c.duckdb_bind_uint32(stmt, bind_index, @intCast(value)),\n                    33...64 => rc = c.duckdb_bind_uint64(stmt, bind_index, @intCast(value)),\n                    65...128 => rc = c.duckdb_bind_uhugeint(stmt, bind_index, lib.uhugeInt(@intCast(value))),\n                    else => bindError(T),\n                }\n            }\n        },\n        .float => |float| {\n            switch (float.bits) {\n                1...32 => rc = c.duckdb_bind_float(stmt, bind_index, @floatCast(value)),\n                33...64 => rc = c.duckdb_bind_double(stmt, bind_index, @floatCast(value)),\n                else => bindError(T),\n            }\n        },\n        .bool => rc = c.duckdb_bind_boolean(stmt, bind_index, value),\n        .pointer => |ptr| {\n            switch (ptr.size) {\n                .slice => {\n                    if (ptr.is_const) {\n                        rc = bindSlice(stmt, bind_index, @as([]const ptr.child, value));\n                    } else {\n                        rc = bindSlice(stmt, bind_index, @as([]ptr.child, value));\n                    }\n                },\n                .one => switch (@typeInfo(ptr.child)) {\n                    .array => {\n                        const Slice = []const std.meta.Elem(ptr.child);\n                        rc = bindSlice(stmt, bind_index, @as(Slice, value));\n                    },\n                    else => bindError(T),\n                },\n                else => bindError(T),\n            }\n        },\n        .array => rc = try bindValue(@TypeOf(&value), stmt, &value, bind_index),\n        .optional => |opt| {\n            if (value) |v| {\n                rc = try bindValue(opt.child, stmt, v, bind_index);\n            } else {\n                rc = c.duckdb_bind_null(stmt, bind_index);\n            }\n        },\n        .@\"struct\" => {\n            if (T == Date) {\n                rc = c.duckdb_bind_date(stmt, bind_index, c.duckdb_to_date(value));\n            } else if (T == Time) {\n                rc = c.duckdb_bind_time(stmt, bind_index, c.duckdb_to_time(value));\n            } else if (T == Interval) {\n                rc = c.duckdb_bind_interval(stmt, bind_index, value);\n            } else {\n                bindError(T);\n            }\n        },\n        else => bindError(T),\n    }\n\n    if (rc == DuckDBError) {\n        return error.Bind;\n    }\n    return rc;\n}\n\nfn bindI64(stmt: c.duckdb_prepared_statement, bind_index: usize, value: i64) c_uint {\n    switch (c.duckdb_param_type(stmt, bind_index)) {\n        c.DUCKDB_TYPE_TIMESTAMP, c.DUCKDB_TYPE_TIMESTAMP_TZ => return c.duckdb_bind_timestamp(stmt, bind_index, .{ .micros = value }),\n        c.DUCKDB_TYPE_TIMESTAMP_MS => return c.duckdb_bind_timestamp(stmt, bind_index, .{ .micros = value * std.time.us_per_ms }),\n        c.DUCKDB_TYPE_TIMESTAMP_S => return c.duckdb_bind_timestamp(stmt, bind_index, .{ .micros = value * std.time.us_per_s }),\n        else => return c.duckdb_bind_int64(stmt, bind_index, value),\n    }\n}\n\nfn bindByteArray(stmt: c.duckdb_prepared_statement, bind_index: usize, value: [*c]const u8, len: usize) c_uint {\n    switch (c.duckdb_param_type(stmt, bind_index)) {\n        c.DUCKDB_TYPE_VARCHAR, c.DUCKDB_TYPE_ENUM, c.DUCKDB_TYPE_INTERVAL, c.DUCKDB_TYPE_BIT => return c.duckdb_bind_varchar_length(stmt, bind_index, value, len),\n        c.DUCKDB_TYPE_BLOB => return c.duckdb_bind_blob(stmt, bind_index, @ptrCast(value), len),\n        c.DUCKDB_TYPE_UUID => {\n            if (len != 36) return DuckDBError;\n            return c.duckdb_bind_varchar_length(stmt, bind_index, value, 36);\n        },\n        // this one is weird, but duckdb will return DUCKDB_TYPE_INVALID if it doesn't\n        // know the type, such as: \"select $1\", but binding will still work\n        c.DUCKDB_TYPE_INVALID => return c.duckdb_bind_varchar_length(stmt, bind_index, value, len),\n        else => return DuckDBError,\n    }\n}\n\nfn bindSlice(stmt: c.duckdb_prepared_statement, bind_index: usize, value: anytype) c_uint {\n    const T = @TypeOf(value);\n    if (T == []u8 or T == []const u8) {\n        // this slice is just a string, it maps to a duckdb text, not a list\n        return bindByteArray(stmt, bind_index, value.ptr, value.len);\n    }\n\n    // https://github.com/duckdb/duckdb/discussions/7482\n    // DuckDB doesn't expose an API for binding arrays.\n    bindError(T);\n}\n\nfn bindError(comptime T: type) void {\n    @compileError(\"cannot bind value of type \" ++ @typeName(T));\n}\n\nconst t = std.testing;\nconst DB = @import(\"db.zig\").DB;\ntest \"bind: basic types\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    var rows = try conn.query(\"select $1, $2, $3, $4, $5, $6\", .{\n        99,\n        -32.01,\n        true,\n        false,\n        @as(?i32, null),\n        @as(?i32, 44),\n    });\n    defer rows.deinit();\n\n    const row = (try rows.next()).?;\n    try t.expectEqual(99, row.get(i64, 0));\n    try t.expectEqual(-32.01, row.get(f64, 1));\n    try t.expectEqual(true, row.get(bool, 2));\n    try t.expectEqual(false, row.get(bool, 3));\n    try t.expectEqual(null, row.get(?i32, 4));\n    try t.expectEqual(44, row.get(i32, 5));\n}\n\ntest \"bind: int\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        var rows = try conn.query(\"select $1, $2, $3, $4, $5, $6::hugeint, $7::uhugeint\", .{\n            99,\n            @as(i8, 2),\n            @as(i16, 3),\n            @as(i32, 4),\n            @as(i64, 5),\n            @as(i128, -9955340232221457974987),\n            @as(u128, 1267650600228229401496703205376),\n        });\n        defer rows.deinit();\n\n        const row = (try rows.next()).?;\n        try t.expectEqual(99, row.get(i64, 0));\n        try t.expectEqual(2, row.get(i8, 1));\n        try t.expectEqual(3, row.get(i16, 2));\n        try t.expectEqual(4, row.get(i32, 3));\n        try t.expectEqual(5, row.get(i64, 4));\n        try t.expectEqual(-9955340232221457974987, row.get(i128, 5));\n        try t.expectEqual(1267650600228229401496703205376, row.get(u128, 6));\n    }\n\n    {\n        // positive limit\n        var rows = try conn.query(\"select $1, $2, $3, $4, $5\", .{ @as(i8, 127), @as(i16, 32767), @as(i32, 2147483647), @as(i64, 9223372036854775807), @as(i128, 170141183460469231731687303715884105727) });\n        defer rows.deinit();\n        const row = (try rows.next()).?;\n        try t.expectEqual(127, row.get(i8, 0));\n        try t.expectEqual(32767, row.get(i16, 1));\n        try t.expectEqual(2147483647, row.get(i32, 2));\n        try t.expectEqual(9223372036854775807, row.get(i64, 3));\n        try t.expectEqual(170141183460469231731687303715884105727, row.get(i128, 4));\n    }\n\n    {\n        // negative limit\n        var rows = try conn.query(\"select $1, $2, $3, $4, $5\", .{ @as(i8, -127), @as(i16, -32767), @as(i32, -2147483647), @as(i64, -9223372036854775807), @as(i128, -170141183460469231731687303715884105727) });\n        defer rows.deinit();\n        const row = (try rows.next()).?;\n        try t.expectEqual(-127, row.get(i8, 0));\n        try t.expectEqual(-32767, row.get(i16, 1));\n        try t.expectEqual(-2147483647, row.get(i32, 2));\n        try t.expectEqual(-9223372036854775807, row.get(i64, 3));\n        try t.expectEqual(-170141183460469231731687303715884105727, row.get(i128, 4));\n    }\n\n    {\n        // unsigned positive limit\n        var rows = try conn.query(\"select $1, $2, $3, $4\", .{\n            @as(u8, 255),\n            @as(u16, 65535),\n            @as(u32, 4294967295),\n            @as(u64, 18446744073709551615),\n        });\n        defer rows.deinit();\n        const row = (try rows.next()).?;\n        try t.expectEqual(255, row.get(u8, 0));\n        try t.expectEqual(65535, row.get(u16, 1));\n        try t.expectEqual(4294967295, row.get(u32, 2));\n        try t.expectEqual(18446744073709551615, row.get(u64, 3));\n    }\n}\n\ntest \"bind: floats\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    // floats\n    var rows = try conn.query(\"select $1, $2, $3\", .{\n        99.88, // $1\n        @as(f32, -3.192), // $2\n        @as(f64, 999.182), // $3\n    });\n    defer rows.deinit();\n\n    const row = (try rows.next()).?;\n    try t.expectEqual(99.88, row.get(f64, 0));\n    try t.expectEqual(-3.192, row.get(f32, 1));\n    try t.expectEqual(999.182, row.get(f64, 2));\n}\n\ntest \"bind: decimal\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    // decimal\n    var rows = try conn.query(\"select $1::decimal(3,2), $2::decimal(18,6)\", .{\n        1.23, // $1\n        -0.3291484, // $2\n    });\n    defer rows.deinit();\n\n    const row = (try rows.next()).?;\n    try t.expectEqual(1.23, row.get(f64, 0));\n    try t.expectEqual(-0.329148, row.get(f64, 1));\n}\n\ntest \"bind: uuid\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    // uuid\n    var rows = try conn.query(\"select $1::uuid, $2::uuid, $3::uuid, $4::uuid\", .{ \"578D0DF0-A76F-4A8E-A463-42F8A4F133C8\", \"00000000-0000-0000-0000-000000000000\", \"ffffffff-ffff-ffff-ffff-ffffffffffff\", \"FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF\" });\n    defer rows.deinit();\n\n    const row = (try rows.next()).?;\n    try t.expectEqualStrings(\"578d0df0-a76f-4a8e-a463-42f8a4f133c8\", &(row.get(UUID, 0)));\n    try t.expectEqualStrings(\"00000000-0000-0000-0000-000000000000\", &(row.get(UUID, 1)));\n    try t.expectEqualStrings(\"ffffffff-ffff-ffff-ffff-ffffffffffff\", &(row.get(UUID, 2)));\n    try t.expectEqualStrings(\"ffffffff-ffff-ffff-ffff-ffffffffffff\", &(row.get(UUID, 3)));\n}\n\ntest \"bind: text\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        var rows = try conn.query(\"select $1\", .{\"hello world\"});\n        defer rows.deinit();\n        const row = (try rows.next()).?;\n        try t.expectEqualStrings(\"hello world\", row.get([]u8, 0));\n    }\n\n    {\n        // runtime varchar\n        var list: std.ArrayList([]const u8) = .empty;\n        defer list.deinit(t.allocator);\n        try list.append(t.allocator, \"i love keemun\");\n\n        var rows = try conn.query(\"select $1::varchar\", .{list.items[0]});\n        defer rows.deinit();\n        const row = (try rows.next()).?;\n        try t.expectEqualStrings(\"i love keemun\", row.get([]const u8, 0));\n    }\n\n    {\n        // blob\n        var rows = try conn.query(\"select $1::blob\", .{&[_]u8{ 0, 1, 2 }});\n        defer rows.deinit();\n\n        const row = (try rows.next()).?;\n        try t.expectEqualStrings(&[_]u8{ 0, 1, 2 }, row.get([]const u8, 0));\n    }\n\n    {\n        // runtime blob\n        var list: std.ArrayList([]const u8) = .empty;\n        defer list.deinit(t.allocator);\n        try list.append(t.allocator, \"i love keemun2\");\n\n        var rows = try conn.query(\"select $1::blob\", .{list.items[0]});\n        defer rows.deinit();\n        const row = (try rows.next()).?;\n        try t.expectEqualStrings(\"i love keemun2\", row.get([]const u8, 0));\n    }\n}\n\ntest \"bind: date/time\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    // date & time\n    const date = Date{ .year = 2023, .month = 5, .day = 10 };\n    const time = Time{ .hour = 21, .min = 4, .sec = 49, .micros = 123456 };\n    const interval = Interval{ .months = 3, .days = 7, .micros = 982810 };\n    var rows = try conn.query(\"select $1::date, $2::time, $3::timestamp, $4::timestamp_ms, $5::timestamp_s, $6::interval, $7::interval\", .{ date, time, 751203002000000, 751203002000, 751203002, interval, \"9298392 days\" });\n    defer rows.deinit();\n\n    const row = (try rows.next()).?;\n    try t.expectEqual(date, row.get(Date, 0));\n    try t.expectEqual(time, row.get(Time, 1));\n    try t.expectEqual(751203002000000, row.get(i64, 2));\n    try t.expectEqual(751203002000, row.get(i64, 3));\n    try t.expectEqual(751203002, row.get(i64, 4));\n    try t.expectEqual(interval, row.get(Interval, 5));\n    try t.expectEqual(Interval{ .months = 0, .days = 9298392, .micros = 0 }, row.get(Interval, 6));\n}\n\ntest \"bind: enum\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"create type my_type as enum ('type_a', 'type_b')\", .{});\n    _ = try conn.exec(\"create type tea_type as enum ('keemun', 'silver_needle')\", .{});\n\n    var rows = try conn.query(\"select $1::my_type, $2::tea_type, $3::my_type\", .{ \"type_a\", \"keemun\", null });\n    defer rows.deinit();\n    const row = (try rows.next()).?;\n    try t.expectEqualStrings(\"type_a\", std.mem.span(row.get(lib.Enum, 0).raw()));\n    try t.expectEqualStrings(\"keemun\", try row.get(?lib.Enum, 1).?.rowCache());\n    try t.expectEqual(null, row.get(?lib.Enum, 2));\n}\n\ntest \"bind: bistring\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    var rows = try conn.query(\n        \\\\ select $1::bit, $1::bit::varchar union all\n        \\\\ select $2::bit, $2::bit::varchar union all\n        \\\\ select $3::bit, $3::bit::varchar union all\n        \\\\ select $4::bit, $4::bit::varchar union all\n        \\\\ select $5::bit, $5::bit::varchar union all\n        \\\\ select $6::bit, $6::bit::varchar union all\n        \\\\ select $7::bit, $7::bit::varchar union all\n        \\\\ select $8::bit, $8::bit::varchar union all\n        \\\\ select $9::bit, $9::bit::varchar\n    , .{ \"0\", \"1\", \"0001111\", \"010\", \"101\", \"1111111110\", \"101010101010010101010100000101001001\", \"00000000000000000\", \"11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111\" });\n    defer rows.deinit();\n\n    // check that our toString is the same as duckdb's\n    while (try rows.next()) |row| {\n        const converted = try @import(\"zuckdb.zig\").bitToString(t.allocator, row.get([]u8, 0));\n        defer t.allocator.free(converted);\n        try t.expectEqualStrings(row.get([]u8, 1), converted);\n    }\n}\n\ntest \"bind: dynamic\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    var stmt = try conn.prepare(\"select $1::int, $2::varchar, $3::smallint\", .{});\n    defer stmt.deinit();\n    try stmt.bindValue(null, 0);\n    try stmt.bindValue(\"over\", 1);\n    try stmt.bindValue(9000, 2);\n\n    var rows = try stmt.query(null);\n    defer rows.deinit();\n\n    const row = (try rows.next()).?;\n    try t.expectEqual(null, row.get(?i32, 0));\n    try t.expectEqualStrings(\"over\", row.get([]u8, 1));\n    try t.expectEqual(9000, row.get(i16, 2));\n}\n\ntest \"query parameters\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    const stmt = try conn.prepare(\n        \\\\select\n        \\\\ $1::bool,\n        \\\\ $2::tinyint, $3::smallint, $4::integer, $5::bigint, $6::hugeint,\n        \\\\ $7::utinyint, $8::usmallint, $9::uinteger, $10::ubigint,\n        \\\\ $11::real, $12::double, $13::decimal,\n        \\\\ $14::timestamp, $15::timestamp_ms, $16::timestamp_s,\n        \\\\ $17::date, $18::time, $19::interval,\n        \\\\ $20::varchar, $21::blob\n    , .{ .auto_release = false });\n    defer stmt.deinit();\n\n    try t.expectEqual(21, stmt.numberOfParameters());\n\n    // bool\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_BOOLEAN), stmt.dataTypeC(0));\n    try t.expectEqual(DataType.boolean, stmt.dataType(0));\n\n    // int\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_TINYINT), stmt.dataTypeC(1));\n    try t.expectEqual(DataType.tinyint, stmt.dataType(1));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_SMALLINT), stmt.dataTypeC(2));\n    try t.expectEqual(DataType.smallint, stmt.dataType(2));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_INTEGER), stmt.dataTypeC(3));\n    try t.expectEqual(DataType.integer, stmt.dataType(3));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_BIGINT), stmt.dataTypeC(4));\n    try t.expectEqual(DataType.bigint, stmt.dataType(4));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_HUGEINT), stmt.dataTypeC(5));\n    try t.expectEqual(DataType.hugeint, stmt.dataType(5));\n\n    // uint\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_UTINYINT), stmt.dataTypeC(6));\n    try t.expectEqual(DataType.utinyint, stmt.dataType(6));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_USMALLINT), stmt.dataTypeC(7));\n    try t.expectEqual(DataType.usmallint, stmt.dataType(7));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_UINTEGER), stmt.dataTypeC(8));\n    try t.expectEqual(DataType.uinteger, stmt.dataType(8));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_UBIGINT), stmt.dataTypeC(9));\n    try t.expectEqual(DataType.ubigint, stmt.dataType(9));\n\n    // float & decimal\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_FLOAT), stmt.dataTypeC(10));\n    try t.expectEqual(DataType.real, stmt.dataType(10));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_DOUBLE), stmt.dataTypeC(11));\n    try t.expectEqual(DataType.double, stmt.dataType(11));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_DECIMAL), stmt.dataTypeC(12));\n    try t.expectEqual(DataType.decimal, stmt.dataType(12));\n\n    // time\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_TIMESTAMP), stmt.dataTypeC(13));\n    try t.expectEqual(DataType.timestamp, stmt.dataType(13));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_TIMESTAMP_MS), stmt.dataTypeC(14));\n    try t.expectEqual(DataType.timestamp_ms, stmt.dataType(14));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_TIMESTAMP_S), stmt.dataTypeC(15));\n    try t.expectEqual(DataType.timestamp_s, stmt.dataType(15));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_DATE), stmt.dataTypeC(16));\n    try t.expectEqual(DataType.date, stmt.dataType(16));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_TIME), stmt.dataTypeC(17));\n    try t.expectEqual(DataType.time, stmt.dataType(17));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_INTERVAL), stmt.dataTypeC(18));\n    try t.expectEqual(DataType.interval, stmt.dataType(18));\n\n    // varchar & blob\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_VARCHAR), stmt.dataTypeC(19));\n    try t.expectEqual(DataType.varchar, stmt.dataType(19));\n    try t.expectEqual(@as(c_uint, c.DUCKDB_TYPE_BLOB), stmt.dataTypeC(20));\n    try t.expectEqual(DataType.blob, stmt.dataType(20));\n}\n\ntest \"Stmt: exec\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        const stmt = try conn.prepare(\"create table exec(id integer)\", .{});\n        defer stmt.deinit();\n        try t.expectEqual(0, try stmt.exec());\n    }\n\n    {\n        const stmt = try conn.prepare(\"insert into exec (id) values ($1)\", .{});\n        defer stmt.deinit();\n        try stmt.bindValue(2, 0);\n        try t.expectEqual(1, try stmt.exec());\n\n        try stmt.clearBindings();\n\n        try stmt.bindValue(3, 0);\n        try t.expectEqual(1, try stmt.exec());\n    }\n\n    var rows = try conn.query(\"select id from exec order by id\", .{});\n    defer rows.deinit();\n\n    try t.expectEqual(2, (try rows.next()).?.get(i32, 0));\n    try t.expectEqual(3, (try rows.next()).?.get(i32, 0));\n    try t.expectEqual(null, try rows.next());\n}\n\ntest \"Stmt: statementType\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    {\n        const stmt = try conn.prepare(\"create table exec(id integer)\", .{});\n        defer stmt.deinit();\n        _ = try stmt.exec();\n        try t.expectEqual(.create, stmt.statementType());\n    }\n\n    {\n        const stmt = try conn.prepare(\"select * from exec\", .{});\n        defer stmt.deinit();\n        try t.expectEqual(.select, stmt.statementType());\n    }\n\n    {\n        const stmt = try conn.prepare(\"update exec set id = 3\", .{});\n        defer stmt.deinit();\n        try t.expectEqual(.update, stmt.statementType());\n    }\n\n    {\n        const stmt = try conn.prepare(\"delete from exec\", .{});\n        defer stmt.deinit();\n        try t.expectEqual(.delete, stmt.statementType());\n    }\n\n    {\n        const stmt = try conn.prepare(\"drop table exec\", .{});\n        defer stmt.deinit();\n        try t.expectEqual(.drop, stmt.statementType());\n    }\n\n    {\n        _ = try conn.exec(\"SET autoinstall_known_extensions=1\", .{});\n        _ = try conn.exec(\"SET autoload_known_extensions=1\", .{});\n        const stmt = try conn.prepare(\"describe\", .{});\n        defer stmt.deinit();\n        _ = try stmt.exec();\n        try t.expectEqual(.select, stmt.statementType());\n    }\n\n    {\n        const stmt = try conn.prepare(\"explain select 1\", .{});\n        defer stmt.deinit();\n        _ = try stmt.exec();\n        try t.expectEqual(.explain, stmt.statementType());\n    }\n}\n"
  },
  {
    "path": "src/vector.zig",
    "content": "const std = @import(\"std\");\nconst lib = @import(\"lib.zig\");\n\nconst c = lib.c;\nconst Rows = lib.Rows;\nconst Allocator = std.mem.Allocator;\n\n// DuckDB exposes data as \"vectors\", which is essentially a pointer to memory\n// that holds data based on the column type (a vector is data for a column, not\n// a row). Our ColumnData is a typed wrapper to the (a) data and (b) the validity\n// mask (null) of a vector.\npub const Vector = struct {\n    type: Type,\n    data: Data,\n    validity: ?[*c]u64,\n    vector: c.duckdb_vector,\n    logical_type: c.duckdb_logical_type,\n\n    pub fn init(allocator: Allocator, logical_type: c.duckdb_logical_type) !Vector {\n        return .{\n            // these are loaded as data chunks are loaded (when reading) or created (when appending)\n            .data = undefined,\n            .vector = undefined,\n            .validity = undefined,\n            .logical_type = logical_type,\n            .type = try Vector.Type.init(allocator, logical_type),\n        };\n    }\n\n    pub fn deinit(self: *Vector) void {\n        c.duckdb_destroy_logical_type(&self.logical_type);\n        switch (self.type) {\n            .scalar => {},\n            .list => |*list| list.deinit(),\n        }\n    }\n\n    pub fn writeType(self: *const Vector, writer: *std.Io.Writer) !void {\n        switch (self.type) {\n            .list => |list| {\n                try list.child.writeType(writer, self.logical_type);\n                return writer.writeAll(\"[]\");\n            },\n            .scalar => |s| return s.writeType(writer, self.logical_type),\n        }\n    }\n\n    pub fn loadVector(self: *Vector, real_vector: c.duckdb_vector) void {\n        self.vector = real_vector;\n        self.data = switch (self.type) {\n            .list => |*l| .{ .list = listData(l, real_vector) },\n            .scalar => |*s| .{ .scalar = scalarData(s, real_vector) },\n        };\n    }\n\n    pub const Type = union(enum) {\n        list: Vector.Type.List,\n        scalar: Vector.Type.Scalar,\n\n        // We expect allocator to be an Arena. Currently, we only need allocator for\n        // our Enum cache.\n        pub fn init(allocator: Allocator, logical_type: c.duckdb_logical_type) !Type {\n            const type_id = c.duckdb_get_type_id(logical_type);\n            switch (type_id) {\n                c.DUCKDB_TYPE_LIST => {\n                    const child_type = c.duckdb_list_type_child_type(logical_type);\n                    return .{ .list = try Vector.Type.List.init(allocator, child_type) };\n                },\n                else => return .{ .scalar = try Vector.Type.Scalar.init(allocator, type_id, logical_type) },\n            }\n        }\n\n        pub fn deinit(self: *Type) void {\n            switch (self.*) {\n                .list => |*scalar| scalar.deinit(),\n                .scalar => |*scalar| scalar.deinit(),\n            }\n        }\n\n        pub const List = struct {\n            child: Vector.Type.Scalar,\n            child_logical_type: c.duckdb_logical_type,\n\n            fn init(allocator: Allocator, child_logical_type: c.duckdb_logical_type) !Vector.Type.List {\n                return .{\n                    .child_logical_type = child_logical_type,\n                    .child = try Vector.Type.Scalar.init(allocator, c.duckdb_get_type_id(child_logical_type), child_logical_type),\n                };\n            }\n\n            fn deinit(self: *@This()) void {\n                c.duckdb_destroy_logical_type(&self.child_logical_type);\n            }\n        };\n\n        pub const Scalar = union(enum) {\n            simple: c.duckdb_type,\n            @\"enum\": Vector.Type.Enum,\n            decimal: Vector.Type.Decimal,\n\n            fn init(allocator: Allocator, type_id: c.duckdb_type, logical_type: c.duckdb_logical_type) !Vector.Type.Scalar {\n                switch (type_id) {\n                    c.DUCKDB_TYPE_ENUM => {\n                        const internal_type: Vector.Type.Enum.Type = switch (c.duckdb_enum_internal_type(logical_type)) {\n                            c.DUCKDB_TYPE_UTINYINT => .u8,\n                            c.DUCKDB_TYPE_USMALLINT => .u16,\n                            c.DUCKDB_TYPE_UINTEGER => .u32,\n                            c.DUCKDB_TYPE_UBIGINT => .u64,\n                            else => unreachable,\n                        };\n                        return .{ .@\"enum\" = .{\n                            .type = internal_type,\n                            .logical_type = logical_type,\n                            .cache = std.AutoHashMap(u64, []const u8).init(allocator),\n                        } };\n                    },\n                    c.DUCKDB_TYPE_DECIMAL => {\n                        const scale = c.duckdb_decimal_scale(logical_type);\n                        const width = c.duckdb_decimal_width(logical_type);\n                        const internal_type: Vector.Type.Decimal.Type = switch (c.duckdb_decimal_internal_type(logical_type)) {\n                            c.DUCKDB_TYPE_SMALLINT => .i16,\n                            c.DUCKDB_TYPE_INTEGER => .i32,\n                            c.DUCKDB_TYPE_BIGINT => .i64,\n                            c.DUCKDB_TYPE_HUGEINT => .i128,\n                            else => unreachable,\n                        };\n                        return .{ .decimal = .{ .width = width, .scale = scale, .type = internal_type } };\n                    },\n                    c.DUCKDB_TYPE_BLOB, c.DUCKDB_TYPE_VARCHAR, c.DUCKDB_TYPE_BIT, c.DUCKDB_TYPE_TINYINT, c.DUCKDB_TYPE_SMALLINT, c.DUCKDB_TYPE_INTEGER, c.DUCKDB_TYPE_BIGINT, c.DUCKDB_TYPE_HUGEINT, c.DUCKDB_TYPE_UUID, c.DUCKDB_TYPE_UHUGEINT, c.DUCKDB_TYPE_UTINYINT, c.DUCKDB_TYPE_USMALLINT, c.DUCKDB_TYPE_UINTEGER, c.DUCKDB_TYPE_UBIGINT, c.DUCKDB_TYPE_BOOLEAN, c.DUCKDB_TYPE_FLOAT, c.DUCKDB_TYPE_DOUBLE, c.DUCKDB_TYPE_DATE, c.DUCKDB_TYPE_TIME, c.DUCKDB_TYPE_TIMESTAMP, c.DUCKDB_TYPE_TIMESTAMP_MS, c.DUCKDB_TYPE_TIMESTAMP_S, c.DUCKDB_TYPE_TIMESTAMP_TZ, c.DUCKDB_TYPE_INTERVAL => return .{ .simple = type_id },\n                    else => return error.UnknownDataType,\n                }\n            }\n\n            pub fn writeType(self: *const Vector.Type.Scalar, writer: *std.Io.Writer, logical_type: c.duckdb_logical_type) !void {\n                switch (self.*) {\n                    .simple => |duckdb_type| {\n                        if (duckdb_type == c.DUCKDB_TYPE_VARCHAR) {\n                            // For example, a JSON column has a \"varchar\" duckdb_type, but\n                            // a \"JSON\" alias. So the alias seems to always be more specific.\n                            const alias = c.duckdb_logical_type_get_alias(logical_type);\n                            if (alias != null) {\n                                defer c.duckdb_free(alias);\n                                return writer.writeAll(std.mem.span(alias));\n                            }\n                        }\n                        return writer.writeAll(@tagName(lib.DataType.fromDuckDBType(duckdb_type)));\n                    },\n                    .@\"enum\" => return writer.writeAll(\"enum\"),\n                    .decimal => |d| return writer.print(\"decimal({d},{d})\", .{ d.width, d.scale }),\n                }\n            }\n        };\n\n        const Decimal = struct {\n            width: u8,\n            scale: u8,\n            type: Vector.Type.Decimal.Type,\n\n            const Type = enum { i16, i32, i64, i128 };\n        };\n\n        const Enum = struct {\n            type: Vector.Type.Enum.Type,\n            // will be freed when the vector is freed\n            logical_type: c.duckdb_logical_type,\n            cache: std.AutoHashMap(u64, []const u8),\n\n            const Type = enum { u8, u16, u32, u64 };\n        };\n    };\n\n    pub const Data = union(enum) {\n        scalar: Scalar,\n        list: Vector.List,\n    };\n\n    pub const Scalar = union(enum) {\n        i8: [*c]i8,\n        i16: [*c]i16,\n        i32: [*c]i32,\n        i64: [*c]i64,\n        i128: [*c]i128,\n        u128: [*c]u128,\n        u8: [*c]u8,\n        u16: [*c]u16,\n        u32: [*c]u32,\n        u64: [*c]u64,\n        bool: [*c]bool,\n        f32: [*c]f32,\n        f64: [*c]f64,\n        blob: [*]c.duckdb_string_t,\n        varchar: [*]c.duckdb_string_t,\n        date: [*]c.duckdb_date,\n        time: [*]c.duckdb_time,\n        timestamp: [*]i64,\n        interval: [*]c.duckdb_interval,\n        decimal: Vector.Decimal,\n        uuid: [*c]i128,\n        @\"enum\": Vector.Enum,\n    };\n\n    pub const Decimal = struct {\n        width: u8,\n        scale: u8,\n        internal: Internal,\n\n        pub const Internal = union(Vector.Type.Decimal.Type) {\n            i16: [*c]i16,\n            i32: [*c]i32,\n            i64: [*c]i64,\n            i128: [*c]i128,\n        };\n    };\n\n    pub const List = struct {\n        child: Scalar,\n        validity: [*c]u64,\n        type: c.duckdb_type,\n        entries: [*]c.duckdb_list_entry,\n\n        // used by the appender to track the current number of entries\n        size: usize = 0,\n\n        // used by the appender when writing a text child (for non-text children\n        // the child scalar is all we need).\n        child_vector: c.duckdb_vector,\n\n        // used when appender when writing a null child\n        child_validity: ?[*c]u64 = null,\n\n        pub fn childValidity(self: *List) [*c]u64 {\n            if (self.child_validity) |cv| {\n                return cv;\n            }\n            const child_vector = self.child_vector;\n            c.duckdb_vector_ensure_validity_writable(child_vector);\n            const validity = c.duckdb_vector_get_validity(child_vector);\n            self.child_validity = validity;\n            return validity;\n        }\n    };\n\n    pub const Enum = struct {\n        internal: Internal,\n        logical_type: c.duckdb_logical_type,\n        cache: *std.AutoHashMap(u64, []const u8),\n\n        pub const Internal = union(Vector.Type.Enum.Type) {\n            u8: [*c]u8,\n            u16: [*c]u16,\n            u32: [*c]u32,\n            u64: [*c]u64,\n        };\n    };\n};\n\nfn scalarData(scalar_type: *Vector.Type.Scalar, real_vector: c.duckdb_vector) Vector.Scalar {\n    const raw_data = c.duckdb_vector_get_data(real_vector);\n    switch (scalar_type.*) {\n        .@\"enum\" => |*e| {\n            return .{ .@\"enum\" = .{\n                .cache = &e.cache,\n                .logical_type = e.logical_type,\n                .internal = switch (e.type) {\n                    .u8 => .{ .u8 = @ptrCast(raw_data) },\n                    .u16 => .{ .u16 = @ptrCast(@alignCast(raw_data)) },\n                    .u32 => .{ .u32 = @ptrCast(@alignCast(raw_data)) },\n                    .u64 => .{ .u64 = @ptrCast(@alignCast(raw_data)) },\n                },\n            } };\n        },\n        .decimal => |d| {\n            return .{ .decimal = .{\n                .width = d.width,\n                .scale = d.scale,\n                .internal = switch (d.type) {\n                    .i16 => .{ .i16 = @ptrCast(@alignCast(raw_data)) },\n                    .i32 => .{ .i32 = @ptrCast(@alignCast(raw_data)) },\n                    .i64 => .{ .i64 = @ptrCast(@alignCast(raw_data)) },\n                    .i128 => .{ .i128 = @ptrCast(@alignCast(raw_data)) },\n                },\n            } };\n        },\n        .simple => |s| switch (s) {\n            c.DUCKDB_TYPE_BLOB, c.DUCKDB_TYPE_BIT => return .{ .blob = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_VARCHAR => return .{ .varchar = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_TINYINT => return .{ .i8 = @ptrCast(raw_data) },\n            c.DUCKDB_TYPE_SMALLINT => return .{ .i16 = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_INTEGER => return .{ .i32 = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_BIGINT => return .{ .i64 = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_HUGEINT => return .{ .i128 = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_UUID => return .{ .uuid = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_UHUGEINT => return .{ .u128 = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_UTINYINT => return .{ .u8 = @ptrCast(raw_data) },\n            c.DUCKDB_TYPE_USMALLINT => return .{ .u16 = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_UINTEGER => return .{ .u32 = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_UBIGINT => return .{ .u64 = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_BOOLEAN => return .{ .bool = @ptrCast(raw_data) },\n            c.DUCKDB_TYPE_FLOAT => return .{ .f32 = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_DOUBLE => return .{ .f64 = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_DATE => return .{ .date = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_TIME => return .{ .time = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_TIMESTAMP => return .{ .timestamp = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_TIMESTAMP_MS => return .{ .timestamp = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_TIMESTAMP_S => return .{ .timestamp = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_TIMESTAMP_TZ => return .{ .timestamp = @ptrCast(@alignCast(raw_data)) },\n            c.DUCKDB_TYPE_INTERVAL => return .{ .interval = @ptrCast(@alignCast(raw_data)) },\n            else => unreachable,\n        },\n    }\n}\n\nfn listData(list: *Vector.Type.List, real_vector: c.duckdb_vector) Vector.List {\n    const raw_data = c.duckdb_vector_get_data(real_vector);\n\n    const child_vector = c.duckdb_list_vector_get_child(real_vector);\n    const child_data = scalarData(&list.child, child_vector);\n    const child_validity = c.duckdb_vector_get_validity(child_vector);\n\n    return .{\n        .child = child_data,\n        .validity = child_validity,\n        .entries = @ptrCast(@alignCast(raw_data)),\n        .child_vector = child_vector,\n        .type = switch (list.child) {\n            .@\"enum\" => c.DUCKDB_TYPE_ENUM,\n            .decimal => c.DUCKDB_TYPE_DECIMAL,\n            .simple => |s| s,\n        },\n    };\n}\n\nconst t = std.testing;\nconst DB = lib.DB;\ntest \"Vector: write type\" {\n    const db = try DB.init(t.io, t.allocator, \":memory:\", .{});\n    defer db.deinit();\n\n    var conn = try db.conn();\n    defer conn.deinit();\n\n    _ = try conn.exec(\"SET autoinstall_known_extensions=1\", .{});\n    _ = try conn.exec(\"SET autoload_known_extensions=1\", .{});\n    _ = try conn.exec(\n        \\\\ create table all_types (\n        \\\\   col_tinyint tinyint,\n        \\\\   col_smallint smallint,\n        \\\\   col_integer integer,\n        \\\\   col_bigint bigint,\n        \\\\   col_hugeint hugeint,\n        \\\\   col_utinyint utinyint,\n        \\\\   col_usmallint usmallint,\n        \\\\   col_uinteger uinteger,\n        \\\\   col_ubigint ubigint,\n        \\\\   col_uhugeint uhugeint,\n        \\\\   col_bool bool,\n        \\\\   col_real real,\n        \\\\   col_double double,\n        \\\\   col_text text,\n        \\\\   col_blob blob,\n        \\\\   col_uuid uuid,\n        \\\\   col_date date,\n        \\\\   col_time time,\n        \\\\   col_interval interval,\n        \\\\   col_timestamp timestamp,\n        \\\\   col_timestamp_ms timestamp_ms,\n        \\\\   col_timestamp_s timestamp_s,\n        \\\\   col_decimal decimal(18, 6),\n        \\\\   col_tinyint_arr tinyint[],\n        \\\\   col_decimal_arr decimal(5, 4)[],\n        \\\\   col_json json,\n        \\\\   col_timetamptz timestamptz,\n        \\\\ )\n    , .{});\n\n    const rows = try conn.query(\"select * from all_types\", .{});\n    defer rows.deinit();\n\n    var arr: std.Io.Writer.Allocating = .init(t.allocator);\n    defer arr.deinit();\n\n    try expectTypeName(&arr, rows.vectors[0], \"tinyint\");\n    try expectTypeName(&arr, rows.vectors[1], \"smallint\");\n    try expectTypeName(&arr, rows.vectors[2], \"integer\");\n    try expectTypeName(&arr, rows.vectors[3], \"bigint\");\n    try expectTypeName(&arr, rows.vectors[4], \"hugeint\");\n    try expectTypeName(&arr, rows.vectors[5], \"utinyint\");\n    try expectTypeName(&arr, rows.vectors[6], \"usmallint\");\n    try expectTypeName(&arr, rows.vectors[7], \"uinteger\");\n    try expectTypeName(&arr, rows.vectors[8], \"ubigint\");\n    try expectTypeName(&arr, rows.vectors[9], \"uhugeint\");\n    try expectTypeName(&arr, rows.vectors[10], \"boolean\");\n    try expectTypeName(&arr, rows.vectors[11], \"real\");\n    try expectTypeName(&arr, rows.vectors[12], \"double\");\n    try expectTypeName(&arr, rows.vectors[13], \"varchar\");\n    try expectTypeName(&arr, rows.vectors[14], \"blob\");\n    try expectTypeName(&arr, rows.vectors[15], \"uuid\");\n    try expectTypeName(&arr, rows.vectors[16], \"date\");\n    try expectTypeName(&arr, rows.vectors[17], \"time\");\n    try expectTypeName(&arr, rows.vectors[18], \"interval\");\n    try expectTypeName(&arr, rows.vectors[19], \"timestamp\");\n    try expectTypeName(&arr, rows.vectors[20], \"timestamp_ms\");\n    try expectTypeName(&arr, rows.vectors[21], \"timestamp_s\");\n    try expectTypeName(&arr, rows.vectors[22], \"decimal(18,6)\");\n    try expectTypeName(&arr, rows.vectors[23], \"tinyint[]\");\n    try expectTypeName(&arr, rows.vectors[24], \"decimal(5,4)[]\");\n    try expectTypeName(&arr, rows.vectors[25], \"JSON\");\n    try expectTypeName(&arr, rows.vectors[26], \"timestamptz\");\n}\n\nfn expectTypeName(arr: *std.Io.Writer.Allocating, vector: Vector, expected: []const u8) !void {\n    arr.clearRetainingCapacity();\n    try vector.writeType(&arr.writer);\n    try t.expectEqualStrings(expected, arr.written());\n}\n"
  },
  {
    "path": "src/zuckdb.zig",
    "content": "const std = @import(\"std\");\nconst lib = @import(\"lib.zig\");\n\npub const c = lib.c;\npub const DB = lib.DB;\npub const Row = lib.Row;\npub const List = lib.List;\npub const Enum = lib.Enum;\npub const Rows = lib.Rows;\npub const Conn = lib.Conn;\npub const Pool = lib.Pool;\npub const Stmt = lib.Stmt;\npub const Vector = lib.Vector;\npub const Appender = lib.Appender;\npub const LazyList = lib.LazyList;\npub const OwningRow = lib.OwningRow;\n\npub const UUID = lib.UUID;\npub const Date = lib.Date;\npub const Time = lib.Time;\npub const Interval = lib.Interval;\npub const DataType = lib.DataType;\n\npub fn StaticState(comptime N: usize) type {\n    return struct {\n        vector: [N]Vector = undefined,\n\n        const Self = @This();\n\n        pub fn getVectors(self: *Self, count: usize) ![]Vector {\n            std.debug.assert(count <= N);\n            return self.vector[0..count];\n        }\n    };\n}\n\n// tested in stmt's bit binding test\npub fn bitToString(allocator: std.mem.Allocator, data: []const u8) ![]u8 {\n    const shl = std.math.shl;\n\n    var i: usize = 0;\n    var padding = data[0];\n    var out = try allocator.alloc(u8, 8 - padding + (8 * (data.len - 2)));\n    // std.debug.print(\"{any} {d}\\n\", .{data, padding});\n\n    while (padding < 8) : (padding += 1) {\n        out[i] = if (data[1] & shl(u8, 1, (7 - padding)) != 0) '1' else '0';\n        i += 1;\n    }\n\n    for (data[2..]) |byte| {\n        for (0..8) |bit| {\n            out[i] = if (byte & shl(u8, 1, (7 - bit)) != 0) '1' else '0';\n            i += 1;\n        }\n    }\n\n    return out;\n}\n\npub fn isDuplicate(err: []const u8) bool {\n    // no better way right nows\n    return std.mem.startsWith(u8, err, \"Constraint Error: Duplicate key\");\n}\n\n// Only meant to be used in advanced cases, largely with the appender, but\n// even then, in normal cases, the appender will handle this for you.\npub const encodeUUID = lib.encodeUUID;\n\ntest {\n    std.testing.refAllDecls(@This());\n}\n"
  },
  {
    "path": "test_runner.zig",
    "content": "const std = @import(\"std\");\nconst Io = std.Io;\nconst builtin = @import(\"builtin\");\n\nconst Allocator = std.mem.Allocator;\n\nconst BORDER = \"=\" ** 80;\n\n// use in custom panic handler\nvar current_test: ?[]const u8 = null;\n\npub fn main(init: std.process.Init) !void {\n    var mem: [8192]u8 = undefined;\n    var fba = std.heap.FixedBufferAllocator.init(&mem);\n\n    const allocator = fba.allocator();\n\n    const env = Env.init(init.environ_map);\n\n    std.testing.io_instance = .init(init.gpa, .{\n        .argv0 = .init(init.minimal.args),\n        .environ = init.minimal.environ,\n    });\n    defer std.testing.io_instance.deinit();\n\n    const io = std.testing.io;\n\n    var slowest = SlowTracker.init(allocator, io, 5);\n    defer slowest.deinit();\n\n    var pass: usize = 0;\n    var fail: usize = 0;\n    var skip: usize = 0;\n    var leak: usize = 0;\n\n    Printer.fmt(\"\\r\\x1b[0K\", .{}); // beginning of line and clear to end of line\n\n    for (builtin.test_functions) |t| {\n        if (isSetup(t)) {\n            t.func() catch |err| {\n                Printer.status(.fail, \"\\nsetup \\\"{s}\\\" failed: {}\\n\", .{ t.name, err });\n                return err;\n            };\n        }\n    }\n\n    for (builtin.test_functions) |t| {\n        if (isSetup(t) or isTeardown(t)) {\n            continue;\n        }\n\n        var status = Status.pass;\n        slowest.startTiming(io);\n\n        const is_unnamed_test = isUnnamed(t);\n        if (env.filter) |f| {\n            if (!is_unnamed_test and std.mem.indexOf(u8, t.name, f) == null) {\n                continue;\n            }\n        }\n\n        const friendly_name = blk: {\n            const name = t.name;\n            var it = std.mem.splitScalar(u8, name, '.');\n            while (it.next()) |value| {\n                if (std.mem.eql(u8, value, \"test\")) {\n                    const rest = it.rest();\n                    break :blk if (rest.len > 0) rest else name;\n                }\n            }\n            break :blk name;\n        };\n\n        current_test = friendly_name;\n        std.testing.allocator_instance = .{};\n        const result = t.func();\n        current_test = null;\n\n        const ns_taken = slowest.endTiming(io, friendly_name);\n\n        if (std.testing.allocator_instance.deinit() == .leak) {\n            leak += 1;\n            Printer.status(.fail, \"\\n{s}\\n\\\"{s}\\\" - Memory Leak\\n{s}\\n\", .{ BORDER, friendly_name, BORDER });\n        }\n\n        if (result) |_| {\n            pass += 1;\n        } else |err| switch (err) {\n            error.SkipZigTest => {\n                skip += 1;\n                status = .skip;\n            },\n            else => {\n                status = .fail;\n                fail += 1;\n                Printer.status(.fail, \"\\n{s}\\n\\\"{s}\\\" - {s}\\n{s}\\n\", .{ BORDER, friendly_name, @errorName(err), BORDER });\n                if (@errorReturnTrace()) |trace| {\n                    std.debug.dumpErrorReturnTrace(trace);\n                }\n                if (env.fail_first) {\n                    break;\n                }\n            },\n        }\n\n        if (env.verbose) {\n            const ms = @as(f64, @floatFromInt(ns_taken)) / 1_000_000.0;\n            Printer.status(status, \"{s} ({d:.2}ms)\\n\", .{ friendly_name, ms });\n        } else {\n            Printer.status(status, \".\", .{});\n        }\n    }\n\n    for (builtin.test_functions) |t| {\n        if (isTeardown(t)) {\n            t.func() catch |err| {\n                Printer.status(.fail, \"\\nteardown \\\"{s}\\\" failed: {}\\n\", .{ t.name, err });\n                return err;\n            };\n        }\n    }\n\n    const total_tests = pass + fail;\n    const status = if (fail == 0) Status.pass else Status.fail;\n    Printer.status(status, \"\\n{d} of {d} test{s} passed\\n\", .{ pass, total_tests, if (total_tests != 1) \"s\" else \"\" });\n    if (skip > 0) {\n        Printer.status(.skip, \"{d} test{s} skipped\\n\", .{ skip, if (skip != 1) \"s\" else \"\" });\n    }\n    if (leak > 0) {\n        Printer.status(.fail, \"{d} test{s} leaked\\n\", .{ leak, if (leak != 1) \"s\" else \"\" });\n    }\n    Printer.fmt(\"\\n\", .{});\n    try slowest.display();\n    Printer.fmt(\"\\n\", .{});\n    std.process.exit(if (fail == 0) 0 else 1);\n}\n\nconst Printer = struct {\n    fn fmt(comptime format: []const u8, args: anytype) void {\n        std.debug.print(format, args);\n    }\n\n    fn status(s: Status, comptime format: []const u8, args: anytype) void {\n        switch (s) {\n            .pass => std.debug.print(\"\\x1b[32m\", .{}),\n            .fail => std.debug.print(\"\\x1b[31m\", .{}),\n            .skip => std.debug.print(\"\\x1b[33m\", .{}),\n            else => {},\n        }\n        std.debug.print(format ++ \"\\x1b[0m\", args);\n    }\n};\n\nconst Status = enum {\n    pass,\n    fail,\n    skip,\n    text,\n};\n\nconst SlowTracker = struct {\n    max: usize,\n    slowest: SlowestQueue,\n    start: Io.Timestamp,\n    allocator: Allocator,\n\n    const SlowestQueue = std.PriorityDequeue(TestInfo, void, compareTiming);\n\n    fn init(allocator: Allocator, io: Io, count: u32) SlowTracker {\n        const timestamp = Io.Clock.awake.now(io);\n        var slowest: SlowestQueue = .empty;\n        slowest.ensureTotalCapacity(allocator, count) catch @panic(\"OOM\");\n        return .{\n            .max = count,\n            .start = timestamp,\n            .slowest = slowest,\n            .allocator = allocator,\n        };\n    }\n\n    const TestInfo = struct {\n        ns: u64,\n        name: []const u8,\n    };\n\n    fn deinit(self: *SlowTracker) void {\n        self.slowest.deinit(self.allocator);\n    }\n\n    fn startTiming(self: *SlowTracker, io: Io) void {\n        self.start = Io.Clock.awake.now(io);\n    }\n\n    fn endTiming(self: *SlowTracker, io: Io, test_name: []const u8) u64 {\n        const timestamp = Io.Clock.awake.now(io);\n        const start = self.start;\n        self.start = timestamp;\n        const ns: u64 = @intCast(start.durationTo(timestamp).toNanoseconds());\n\n        var slowest = &self.slowest;\n\n        if (slowest.count() < self.max) {\n            // Capacity is fixed to the # of slow tests we want to track\n            // If we've tracked fewer tests than this capacity, than always add\n            slowest.push(self.allocator, TestInfo{ .ns = ns, .name = test_name }) catch @panic(\"failed to track test timing\");\n            return ns;\n        }\n\n        {\n            // Optimization to avoid shifting the dequeue for the common case\n            // where the test isn't one of our slowest.\n            const fastest_of_the_slow = slowest.peekMin() orelse unreachable;\n            if (fastest_of_the_slow.ns > ns) {\n                // the test was faster than our fastest slow test, don't add\n                return ns;\n            }\n        }\n\n        // the previous fastest of our slow tests, has been pushed off.\n        _ = slowest.popMin();\n        slowest.push(self.allocator, TestInfo{ .ns = ns, .name = test_name }) catch @panic(\"failed to track test timing\");\n        return ns;\n    }\n\n    fn display(self: *SlowTracker) !void {\n        var slowest = self.slowest;\n        const count = slowest.count();\n        Printer.fmt(\"Slowest {d} test{s}: \\n\", .{ count, if (count != 1) \"s\" else \"\" });\n        while (slowest.popMin()) |info| {\n            const ms = @as(f64, @floatFromInt(info.ns)) / 1_000_000.0;\n            Printer.fmt(\"  {d:.2}ms\\t{s}\\n\", .{ ms, info.name });\n        }\n    }\n\n    fn compareTiming(context: void, a: TestInfo, b: TestInfo) std.math.Order {\n        _ = context;\n        return std.math.order(a.ns, b.ns);\n    }\n};\n\nconst Env = struct {\n    verbose: bool,\n    fail_first: bool,\n    filter: ?[]const u8,\n\n    fn init(map: *const std.process.Environ.Map) Env {\n        return .{\n            .verbose = readEnvBool(map, \"TEST_VERBOSE\", true),\n            .fail_first = readEnvBool(map, \"TEST_FAIL_FIRST\", false),\n            .filter = readEnv(map, \"TEST_FILTER\"),\n        };\n    }\n\n    fn readEnv(map: *const std.process.Environ.Map, key: []const u8) ?[]const u8 {\n        return map.get(key);\n    }\n\n    fn readEnvBool(map: *const std.process.Environ.Map, key: []const u8, deflt: bool) bool {\n        const value = readEnv(map, key) orelse return deflt;\n        return std.ascii.eqlIgnoreCase(value, \"true\");\n    }\n};\n\npub const panic = std.debug.FullPanic(struct {\n    pub fn panicFn(msg: []const u8, first_trace_addr: ?usize) noreturn {\n        if (current_test) |ct| {\n            std.debug.print(\"\\x1b[31m{s}\\npanic running \\\"{s}\\\"\\n{s}\\x1b[0m\\n\", .{ BORDER, ct, BORDER });\n        }\n        std.debug.defaultPanic(msg, first_trace_addr);\n    }\n}.panicFn);\n\nfn isUnnamed(t: std.builtin.TestFn) bool {\n    const marker = \".test_\";\n    const test_name = t.name;\n    const index = std.mem.indexOf(u8, test_name, marker) orelse return false;\n    _ = std.fmt.parseInt(u32, test_name[index + marker.len ..], 10) catch return false;\n    return true;\n}\n\nfn isSetup(t: std.builtin.TestFn) bool {\n    return std.mem.endsWith(u8, t.name, \"tests:beforeAll\");\n}\n\nfn isTeardown(t: std.builtin.TestFn) bool {\n    return std.mem.endsWith(u8, t.name, \"tests:afterAll\");\n}\n"
  }
]