packages feed

duckdb-ffi (empty) → 0.1.4.1

raw patch · 71 files changed

+19824/−0 lines, 71 filesdep +basedep +bytestringdep +containersbuild-type:Customsetup-changed

Dependencies added: base, bytestring, containers, duckdb-ffi, exceptions, mtl, tasty, tasty-expected-failure, tasty-hunit, text, time, transformers

Files

+ CHANGELOG.md view
@@ -0,0 +1,4 @@+# Changelog++## 0.1.4.1+- Initial release
+ LICENSE view
@@ -0,0 +1,373 @@+Mozilla Public License Version 2.0+==================================++1. Definitions+--------------++1.1. "Contributor"+    means each individual or legal entity that creates, contributes to+    the creation of, or owns Covered Software.++1.2. "Contributor Version"+    means the combination of the Contributions of others (if any) used+    by a Contributor and that particular Contributor's Contribution.++1.3. "Contribution"+    means Covered Software of a particular Contributor.++1.4. "Covered Software"+    means Source Code Form to which the initial Contributor has attached+    the notice in Exhibit A, the Executable Form of such Source Code+    Form, and Modifications of such Source Code Form, in each case+    including portions thereof.++1.5. "Incompatible With Secondary Licenses"+    means++    (a) that the initial Contributor has attached the notice described+        in Exhibit B to the Covered Software; or++    (b) that the Covered Software was made available under the terms of+        version 1.1 or earlier of the License, but not also under the+        terms of a Secondary License.++1.6. "Executable Form"+    means any form of the work other than Source Code Form.++1.7. "Larger Work"+    means a work that combines Covered Software with other material, in+    a separate file or files, that is not Covered Software.++1.8. "License"+    means this document.++1.9. "Licensable"+    means having the right to grant, to the maximum extent possible,+    whether at the time of the initial grant or subsequently, any and+    all of the rights conveyed by this License.++1.10. "Modifications"+    means any of the following:++    (a) any file in Source Code Form that results from an addition to,+        deletion from, or modification of the contents of Covered+        Software; or++    (b) any new file in Source Code Form that contains any Covered+        Software.++1.11. "Patent Claims" of a Contributor+    means any patent claim(s), including without limitation, method,+    process, and apparatus claims, in any patent Licensable by such+    Contributor that would be infringed, but for the grant of the+    License, by the making, using, selling, offering for sale, having+    made, import, or transfer of either its Contributions or its+    Contributor Version.++1.12. "Secondary License"+    means either the GNU General Public License, Version 2.0, the GNU+    Lesser General Public License, Version 2.1, the GNU Affero General+    Public License, Version 3.0, or any later versions of those+    licenses.++1.13. "Source Code Form"+    means the form of the work preferred for making modifications.++1.14. "You" (or "Your")+    means an individual or a legal entity exercising rights under this+    License. For legal entities, "You" includes any entity that+    controls, is controlled by, or is under common control with You. For+    purposes of this definition, "control" means (a) the power, direct+    or indirect, to cause the direction or management of such entity,+    whether by contract or otherwise, or (b) ownership of more than+    fifty percent (50%) of the outstanding shares or beneficial+    ownership of such entity.++2. License Grants and Conditions+--------------------------------++2.1. Grants++Each Contributor hereby grants You a world-wide, royalty-free,+non-exclusive license:++(a) under intellectual property rights (other than patent or trademark)+    Licensable by such Contributor to use, reproduce, make available,+    modify, display, perform, distribute, and otherwise exploit its+    Contributions, either on an unmodified basis, with Modifications, or+    as part of a Larger Work; and++(b) under Patent Claims of such Contributor to make, use, sell, offer+    for sale, have made, import, and otherwise transfer either its+    Contributions or its Contributor Version.++2.2. Effective Date++The licenses granted in Section 2.1 with respect to any Contribution+become effective for each Contribution on the date the Contributor first+distributes such Contribution.++2.3. Limitations on Grant Scope++The licenses granted in this Section 2 are the only rights granted under+this License. No additional rights or licenses will be implied from the+distribution or licensing of Covered Software under this License.+Notwithstanding Section 2.1(b) above, no patent license is granted by a+Contributor:++(a) for any code that a Contributor has removed from Covered Software;+    or++(b) for infringements caused by: (i) Your and any other third party's+    modifications of Covered Software, or (ii) the combination of its+    Contributions with other software (except as part of its Contributor+    Version); or++(c) under Patent Claims infringed by Covered Software in the absence of+    its Contributions.++This License does not grant any rights in the trademarks, service marks,+or logos of any Contributor (except as may be necessary to comply with+the notice requirements in Section 3.4).++2.4. Subsequent Licenses++No Contributor makes additional grants as a result of Your choice to+distribute the Covered Software under a subsequent version of this+License (see Section 10.2) or under the terms of a Secondary License (if+permitted under the terms of Section 3.3).++2.5. Representation++Each Contributor represents that the Contributor believes its+Contributions are its original creation(s) or it has sufficient rights+to grant the rights to its Contributions conveyed by this License.++2.6. Fair Use++This License is not intended to limit any rights You have under+applicable copyright doctrines of fair use, fair dealing, or other+equivalents.++2.7. Conditions++Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted+in Section 2.1.++3. Responsibilities+-------------------++3.1. Distribution of Source Form++All distribution of Covered Software in Source Code Form, including any+Modifications that You create or to which You contribute, must be under+the terms of this License. You must inform recipients that the Source+Code Form of the Covered Software is governed by the terms of this+License, and how they can obtain a copy of this License. You may not+attempt to alter or restrict the recipients' rights in the Source Code+Form.++3.2. Distribution of Executable Form++If You distribute Covered Software in Executable Form then:++(a) such Covered Software must also be made available in Source Code+    Form, as described in Section 3.1, and You must inform recipients of+    the Executable Form how they can obtain a copy of such Source Code+    Form by reasonable means in a timely manner, at a charge no more+    than the cost of distribution to the recipient; and++(b) You may distribute such Executable Form under the terms of this+    License, or sublicense it under different terms, provided that the+    license for the Executable Form does not attempt to limit or alter+    the recipients' rights in the Source Code Form under this License.++3.3. Distribution of a Larger Work++You may create and distribute a Larger Work under terms of Your choice,+provided that You also comply with the requirements of this License for+the Covered Software. If the Larger Work is a combination of Covered+Software with a work governed by one or more Secondary Licenses, and the+Covered Software is not Incompatible With Secondary Licenses, this+License permits You to additionally distribute such Covered Software+under the terms of such Secondary License(s), so that the recipient of+the Larger Work may, at their option, further distribute the Covered+Software under the terms of either this License or such Secondary+License(s).++3.4. Notices++You may not remove or alter the substance of any license notices+(including copyright notices, patent notices, disclaimers of warranty,+or limitations of liability) contained within the Source Code Form of+the Covered Software, except that You may alter any license notices to+the extent required to remedy known factual inaccuracies.++3.5. Application of Additional Terms++You may choose to offer, and to charge a fee for, warranty, support,+indemnity or liability obligations to one or more recipients of Covered+Software. However, You may do so only on Your own behalf, and not on+behalf of any Contributor. You must make it absolutely clear that any+such warranty, support, indemnity, or liability obligation is offered by+You alone, and You hereby agree to indemnify every Contributor for any+liability incurred by such Contributor as a result of warranty, support,+indemnity or liability terms You offer. You may include additional+disclaimers of warranty and limitations of liability specific to any+jurisdiction.++4. Inability to Comply Due to Statute or Regulation+---------------------------------------------------++If it is impossible for You to comply with any of the terms of this+License with respect to some or all of the Covered Software due to+statute, judicial order, or regulation then You must: (a) comply with+the terms of this License to the maximum extent possible; and (b)+describe the limitations and the code they affect. Such description must+be placed in a text file included with all distributions of the Covered+Software under this License. Except to the extent prohibited by statute+or regulation, such description must be sufficiently detailed for a+recipient of ordinary skill to be able to understand it.++5. Termination+--------------++5.1. The rights granted under this License will terminate automatically+if You fail to comply with any of its terms. However, if You become+compliant, then the rights granted under this License from a particular+Contributor are reinstated (a) provisionally, unless and until such+Contributor explicitly and finally terminates Your grants, and (b) on an+ongoing basis, if such Contributor fails to notify You of the+non-compliance by some reasonable means prior to 60 days after You have+come back into compliance. Moreover, Your grants from a particular+Contributor are reinstated on an ongoing basis if such Contributor+notifies You of the non-compliance by some reasonable means, this is the+first time You have received notice of non-compliance with this License+from such Contributor, and You become compliant prior to 30 days after+Your receipt of the notice.++5.2. If You initiate litigation against any entity by asserting a patent+infringement claim (excluding declaratory judgment actions,+counter-claims, and cross-claims) alleging that a Contributor Version+directly or indirectly infringes any patent, then the rights granted to+You by any and all Contributors for the Covered Software under Section+2.1 of this License shall terminate.++5.3. In the event of termination under Sections 5.1 or 5.2 above, all+end user license agreements (excluding distributors and resellers) which+have been validly granted by You or Your distributors under this License+prior to termination shall survive termination.++************************************************************************+*                                                                      *+*  6. Disclaimer of Warranty                                           *+*  -------------------------                                           *+*                                                                      *+*  Covered Software is provided under this License on an "as is"       *+*  basis, without warranty of any kind, either expressed, implied, or  *+*  statutory, including, without limitation, warranties that the       *+*  Covered Software is free of defects, merchantable, fit for a        *+*  particular purpose or non-infringing. The entire risk as to the     *+*  quality and performance of the Covered Software is with You.        *+*  Should any Covered Software prove defective in any respect, You     *+*  (not any Contributor) assume the cost of any necessary servicing,   *+*  repair, or correction. This disclaimer of warranty constitutes an   *+*  essential part of this License. No use of any Covered Software is   *+*  authorized under this License except under this disclaimer.         *+*                                                                      *+************************************************************************++************************************************************************+*                                                                      *+*  7. Limitation of Liability                                          *+*  --------------------------                                          *+*                                                                      *+*  Under no circumstances and under no legal theory, whether tort      *+*  (including negligence), contract, or otherwise, shall any           *+*  Contributor, or anyone who distributes Covered Software as          *+*  permitted above, be liable to You for any direct, indirect,         *+*  special, incidental, or consequential damages of any character      *+*  including, without limitation, damages for lost profits, loss of    *+*  goodwill, work stoppage, computer failure or malfunction, or any    *+*  and all other commercial damages or losses, even if such party      *+*  shall have been informed of the possibility of such damages. This   *+*  limitation of liability shall not apply to liability for death or   *+*  personal injury resulting from such party's negligence to the       *+*  extent applicable law prohibits such limitation. Some               *+*  jurisdictions do not allow the exclusion or limitation of           *+*  incidental or consequential damages, so this exclusion and          *+*  limitation may not apply to You.                                    *+*                                                                      *+************************************************************************++8. Litigation+-------------++Any litigation relating to this License may be brought only in the+courts of a jurisdiction where the defendant maintains its principal+place of business and such litigation shall be governed by laws of that+jurisdiction, without reference to its conflict-of-law provisions.+Nothing in this Section shall prevent a party's ability to bring+cross-claims or counter-claims.++9. Miscellaneous+----------------++This License represents the complete agreement concerning the subject+matter hereof. If any provision of this License is held to be+unenforceable, such provision shall be reformed only to the extent+necessary to make it enforceable. Any law or regulation which provides+that the language of a contract shall be construed against the drafter+shall not be used to construe this License against a Contributor.++10. Versions of the License+---------------------------++10.1. New Versions++Mozilla Foundation is the license steward. Except as provided in Section+10.3, no one other than the license steward has the right to modify or+publish new versions of this License. Each version will be given a+distinguishing version number.++10.2. Effect of New Versions++You may distribute the Covered Software under the terms of the version+of the License under which You originally received the Covered Software,+or under the terms of any subsequent version published by the license+steward.++10.3. Modified Versions++If you create software not governed by this License, and you want to+create a new license for such software, you may create and use a+modified version of this License if you rename the license and remove+any references to the name of the license steward (except to note that+such modified license differs from this License).++10.4. Distributing Source Code Form that is Incompatible With Secondary+Licenses++If You choose to distribute Source Code Form that is Incompatible With+Secondary Licenses under the terms of this version of the License, the+notice described in Exhibit B of this License must be attached.++Exhibit A - Source Code Form License Notice+-------------------------------------------++  This Source Code Form is subject to the terms of the Mozilla Public+  License, v. 2.0. If a copy of the MPL was not distributed with this+  file, You can obtain one at https://mozilla.org/MPL/2.0/.++If it is not possible or desirable to put the notice in a particular+file, then You may include the notice in a location (such as a LICENSE+file in a relevant directory) where a recipient would be likely to look+for such a notice.++You may add additional accurate notices of copyright ownership.++Exhibit B - "Incompatible With Secondary Licenses" Notice+---------------------------------------------------------++  This Source Code Form is "Incompatible With Secondary Licenses", as+  defined by the Mozilla Public License, v. 2.0.
+ Setup.hs view
@@ -0,0 +1,109 @@+-- Setup.hs++import Control.Exception (bracket)+import Control.Monad (filterM, forM_)+import Data.List (nub)+import Data.Maybe (catMaybes, isJust, listToMaybe)+import Distribution.PackageDescription (+  GenericPackageDescription (..),+  PackageDescription (..),+  extraLibDirs,+  libBuildInfo,+  library,+  package,+ )+import Distribution.Simple+import Distribution.Simple.Compiler (compilerInfo)+import Distribution.Simple.InstallDirs (absoluteInstallDirs, dynlibdir, libdir)+import Distribution.Simple.LocalBuildInfo (LocalBuildInfo (..), localUnitId)+import Distribution.Simple.Setup (configExtraLibDirs, configVerbosity, copyDest, defaultCopyFlags, fromFlag)+import Distribution.Simple.Utils (copyFileVerbose, createDirectoryIfMissingVerbose, createTempDirectory, info)+import Distribution.Verbosity (Verbosity)+import System.Directory (+  doesDirectoryExist,+  doesFileExist,+  findFile,+  getCurrentDirectory,+  getTemporaryDirectory,+  removeDirectoryRecursive,+ )+import System.Environment (lookupEnv)+import System.FilePath ((</>), splitSearchPath)+import System.Process (callProcess)++main :: IO ()+main =+  defaultMainWithHooks+    simpleUserHooks+      { preConf = \args flags -> do+          ensureDuckDBAvailable (fromFlag (configVerbosity flags))+          preConf simpleUserHooks args flags+      }++systemLibraryCandidates :: [FilePath]+systemLibraryCandidates =+  [ "libduckdb.so"+  , "libduckdb.so.1"+  , "libduckdb.so.1.4"+  , "libduckdb.so.1.4.0"+  , "libduckdb.so.1.4.1"+  ]+++ensureDuckDBAvailable :: Verbosity -> IO ()+ensureDuckDBAvailable verb = do+  available <- systemDuckDBAvailable+  if available+    then info verb "libduckdb found"+    else error "libduckdb not found, please install libduckdb (available at https://duckdb.org/install/)"+++systemDuckDBAvailable :: IO Bool+systemDuckDBAvailable = do+  header <- findDuckDBHeader+  lib <- findDuckDBLibrary+  pure (isJust header && isJust lib)++findDuckDBHeader :: IO (Maybe FilePath)+findDuckDBHeader = do+  paths <- headerSearchPaths+  findFile paths "duckdb.h"++findDuckDBLibrary :: IO (Maybe FilePath)+findDuckDBLibrary = do+  paths <- librarySearchPaths+  listToMaybe . catMaybes <$> mapM (findFile paths) systemLibraryCandidates++headerSearchPaths :: IO [FilePath]+headerSearchPaths = do+  envPaths <- gatherEnvPaths ["C_INCLUDE_PATH", "CPATH", "INCLUDE"]+  let defaults =+        [ "/usr/include"+        , "/usr/local/include"+        , "/usr/include/x86_64-linux-gnu"+        , "/usr/include/aarch64-linux-gnu"+        , "/opt/homebrew/include"+        , "/opt/local/include"+        ]+  pure (nub (envPaths ++ defaults))++librarySearchPaths :: IO [FilePath]+librarySearchPaths = do+  envPaths <- gatherEnvPaths ["LIBRARY_PATH", "LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH", "LIB"]+  let defaults =+        [ "/usr/lib"+        , "/usr/local/lib"+        , "/usr/lib64"+        , "/usr/local/lib64"+        , "/usr/lib/x86_64-linux-gnu"+        , "/usr/lib/aarch64-linux-gnu"+        , "/opt/homebrew/lib"+        , "/opt/local/lib"+        ]+  pure (nub (envPaths ++ defaults))++gatherEnvPaths :: [String] -> IO [FilePath]+gatherEnvPaths names = do+  vals <- mapM lookupEnv names+  let paths = concatMap splitSearchPath (catMaybes vals)+  pure (filter (not . null) paths)
+ cbits/duckdb.h view
@@ -0,0 +1,5168 @@+//===----------------------------------------------------------------------===//+//+//                         DuckDB+//+// duckdb.h+//+//+//===----------------------------------------------------------------------===//+//+// !!!!!!!+// WARNING: this file is autogenerated by scripts/generate_c_api.py, manual changes will be overwritten+// !!!!!!!++#pragma once++#ifndef DUCKDB_C_API+#ifdef _WIN32+#ifdef DUCKDB_STATIC_BUILD+#define DUCKDB_C_API+#else+#if defined(DUCKDB_BUILD_LIBRARY) && !defined(DUCKDB_BUILD_LOADABLE_EXTENSION)+#define DUCKDB_C_API __declspec(dllexport)+#else+#define DUCKDB_C_API __declspec(dllimport)+#endif+#endif+#else+#define DUCKDB_C_API+#endif+#endif++//! duplicate of duckdb/main/winapi.hpp+#ifndef DUCKDB_EXTENSION_API+#ifdef _WIN32+#ifdef DUCKDB_STATIC_BUILD+#define DUCKDB_EXTENSION_API+#else+#define DUCKDB_EXTENSION_API __declspec(dllexport)+#endif+#else+#define DUCKDB_EXTENSION_API __attribute__((visibility("default")))+#endif+#endif++#include <stdbool.h>+#include <stdint.h>+#include <stddef.h>++#ifdef __cplusplus+extern "C" {+#endif++//===--------------------------------------------------------------------===//+// Enums+//===--------------------------------------------------------------------===//++//! WARNING: The numbers of these enums should not be changed, as changing the numbers breaks ABI compatibility.+//! Always add enums at the END of the enum++//! An enum over DuckDB's internal types.+typedef enum DUCKDB_TYPE {+	DUCKDB_TYPE_INVALID = 0,+	// bool+	DUCKDB_TYPE_BOOLEAN = 1,+	// int8_t+	DUCKDB_TYPE_TINYINT = 2,+	// int16_t+	DUCKDB_TYPE_SMALLINT = 3,+	// int32_t+	DUCKDB_TYPE_INTEGER = 4,+	// int64_t+	DUCKDB_TYPE_BIGINT = 5,+	// uint8_t+	DUCKDB_TYPE_UTINYINT = 6,+	// uint16_t+	DUCKDB_TYPE_USMALLINT = 7,+	// uint32_t+	DUCKDB_TYPE_UINTEGER = 8,+	// uint64_t+	DUCKDB_TYPE_UBIGINT = 9,+	// float+	DUCKDB_TYPE_FLOAT = 10,+	// double+	DUCKDB_TYPE_DOUBLE = 11,+	// duckdb_timestamp (microseconds)+	DUCKDB_TYPE_TIMESTAMP = 12,+	// duckdb_date+	DUCKDB_TYPE_DATE = 13,+	// duckdb_time+	DUCKDB_TYPE_TIME = 14,+	// duckdb_interval+	DUCKDB_TYPE_INTERVAL = 15,+	// duckdb_hugeint+	DUCKDB_TYPE_HUGEINT = 16,+	// duckdb_uhugeint+	DUCKDB_TYPE_UHUGEINT = 32,+	// const char*+	DUCKDB_TYPE_VARCHAR = 17,+	// duckdb_blob+	DUCKDB_TYPE_BLOB = 18,+	// duckdb_decimal+	DUCKDB_TYPE_DECIMAL = 19,+	// duckdb_timestamp_s (seconds)+	DUCKDB_TYPE_TIMESTAMP_S = 20,+	// duckdb_timestamp_ms (milliseconds)+	DUCKDB_TYPE_TIMESTAMP_MS = 21,+	// duckdb_timestamp_ns (nanoseconds)+	DUCKDB_TYPE_TIMESTAMP_NS = 22,+	// enum type, only useful as logical type+	DUCKDB_TYPE_ENUM = 23,+	// list type, only useful as logical type+	DUCKDB_TYPE_LIST = 24,+	// struct type, only useful as logical type+	DUCKDB_TYPE_STRUCT = 25,+	// map type, only useful as logical type+	DUCKDB_TYPE_MAP = 26,+	// duckdb_array, only useful as logical type+	DUCKDB_TYPE_ARRAY = 33,+	// duckdb_hugeint+	DUCKDB_TYPE_UUID = 27,+	// union type, only useful as logical type+	DUCKDB_TYPE_UNION = 28,+	// duckdb_bit+	DUCKDB_TYPE_BIT = 29,+	// duckdb_time_tz+	DUCKDB_TYPE_TIME_TZ = 30,+	// duckdb_timestamp (microseconds)+	DUCKDB_TYPE_TIMESTAMP_TZ = 31,+	// enum type, only useful as logical type+	DUCKDB_TYPE_ANY = 34,+	// duckdb_bignum+	DUCKDB_TYPE_BIGNUM = 35,+	// enum type, only useful as logical type+	DUCKDB_TYPE_SQLNULL = 36,+	// enum type, only useful as logical type+	DUCKDB_TYPE_STRING_LITERAL = 37,+	// enum type, only useful as logical type+	DUCKDB_TYPE_INTEGER_LITERAL = 38,+	// duckdb_time_ns (nanoseconds)+	DUCKDB_TYPE_TIME_NS = 39,+} duckdb_type;++//! An enum over the returned state of different functions.+typedef enum duckdb_state { DuckDBSuccess = 0, DuckDBError = 1 } duckdb_state;++//! An enum over the pending state of a pending query result.+typedef enum duckdb_pending_state {+	DUCKDB_PENDING_RESULT_READY = 0,+	DUCKDB_PENDING_RESULT_NOT_READY = 1,+	DUCKDB_PENDING_ERROR = 2,+	DUCKDB_PENDING_NO_TASKS_AVAILABLE = 3+} duckdb_pending_state;++//! An enum over DuckDB's different result types.+typedef enum duckdb_result_type {+	DUCKDB_RESULT_TYPE_INVALID = 0,+	DUCKDB_RESULT_TYPE_CHANGED_ROWS = 1,+	DUCKDB_RESULT_TYPE_NOTHING = 2,+	DUCKDB_RESULT_TYPE_QUERY_RESULT = 3,+} duckdb_result_type;++//! An enum over DuckDB's different statement types.+typedef enum duckdb_statement_type {+	DUCKDB_STATEMENT_TYPE_INVALID = 0,+	DUCKDB_STATEMENT_TYPE_SELECT = 1,+	DUCKDB_STATEMENT_TYPE_INSERT = 2,+	DUCKDB_STATEMENT_TYPE_UPDATE = 3,+	DUCKDB_STATEMENT_TYPE_EXPLAIN = 4,+	DUCKDB_STATEMENT_TYPE_DELETE = 5,+	DUCKDB_STATEMENT_TYPE_PREPARE = 6,+	DUCKDB_STATEMENT_TYPE_CREATE = 7,+	DUCKDB_STATEMENT_TYPE_EXECUTE = 8,+	DUCKDB_STATEMENT_TYPE_ALTER = 9,+	DUCKDB_STATEMENT_TYPE_TRANSACTION = 10,+	DUCKDB_STATEMENT_TYPE_COPY = 11,+	DUCKDB_STATEMENT_TYPE_ANALYZE = 12,+	DUCKDB_STATEMENT_TYPE_VARIABLE_SET = 13,+	DUCKDB_STATEMENT_TYPE_CREATE_FUNC = 14,+	DUCKDB_STATEMENT_TYPE_DROP = 15,+	DUCKDB_STATEMENT_TYPE_EXPORT = 16,+	DUCKDB_STATEMENT_TYPE_PRAGMA = 17,+	DUCKDB_STATEMENT_TYPE_VACUUM = 18,+	DUCKDB_STATEMENT_TYPE_CALL = 19,+	DUCKDB_STATEMENT_TYPE_SET = 20,+	DUCKDB_STATEMENT_TYPE_LOAD = 21,+	DUCKDB_STATEMENT_TYPE_RELATION = 22,+	DUCKDB_STATEMENT_TYPE_EXTENSION = 23,+	DUCKDB_STATEMENT_TYPE_LOGICAL_PLAN = 24,+	DUCKDB_STATEMENT_TYPE_ATTACH = 25,+	DUCKDB_STATEMENT_TYPE_DETACH = 26,+	DUCKDB_STATEMENT_TYPE_MULTI = 27,+} duckdb_statement_type;++//! An enum over DuckDB's different error types.+typedef enum duckdb_error_type {+	DUCKDB_ERROR_INVALID = 0,+	DUCKDB_ERROR_OUT_OF_RANGE = 1,+	DUCKDB_ERROR_CONVERSION = 2,+	DUCKDB_ERROR_UNKNOWN_TYPE = 3,+	DUCKDB_ERROR_DECIMAL = 4,+	DUCKDB_ERROR_MISMATCH_TYPE = 5,+	DUCKDB_ERROR_DIVIDE_BY_ZERO = 6,+	DUCKDB_ERROR_OBJECT_SIZE = 7,+	DUCKDB_ERROR_INVALID_TYPE = 8,+	DUCKDB_ERROR_SERIALIZATION = 9,+	DUCKDB_ERROR_TRANSACTION = 10,+	DUCKDB_ERROR_NOT_IMPLEMENTED = 11,+	DUCKDB_ERROR_EXPRESSION = 12,+	DUCKDB_ERROR_CATALOG = 13,+	DUCKDB_ERROR_PARSER = 14,+	DUCKDB_ERROR_PLANNER = 15,+	DUCKDB_ERROR_SCHEDULER = 16,+	DUCKDB_ERROR_EXECUTOR = 17,+	DUCKDB_ERROR_CONSTRAINT = 18,+	DUCKDB_ERROR_INDEX = 19,+	DUCKDB_ERROR_STAT = 20,+	DUCKDB_ERROR_CONNECTION = 21,+	DUCKDB_ERROR_SYNTAX = 22,+	DUCKDB_ERROR_SETTINGS = 23,+	DUCKDB_ERROR_BINDER = 24,+	DUCKDB_ERROR_NETWORK = 25,+	DUCKDB_ERROR_OPTIMIZER = 26,+	DUCKDB_ERROR_NULL_POINTER = 27,+	DUCKDB_ERROR_IO = 28,+	DUCKDB_ERROR_INTERRUPT = 29,+	DUCKDB_ERROR_FATAL = 30,+	DUCKDB_ERROR_INTERNAL = 31,+	DUCKDB_ERROR_INVALID_INPUT = 32,+	DUCKDB_ERROR_OUT_OF_MEMORY = 33,+	DUCKDB_ERROR_PERMISSION = 34,+	DUCKDB_ERROR_PARAMETER_NOT_RESOLVED = 35,+	DUCKDB_ERROR_PARAMETER_NOT_ALLOWED = 36,+	DUCKDB_ERROR_DEPENDENCY = 37,+	DUCKDB_ERROR_HTTP = 38,+	DUCKDB_ERROR_MISSING_EXTENSION = 39,+	DUCKDB_ERROR_AUTOLOAD = 40,+	DUCKDB_ERROR_SEQUENCE = 41,+	DUCKDB_INVALID_CONFIGURATION = 42+} duckdb_error_type;++//! An enum over DuckDB's different cast modes.+typedef enum duckdb_cast_mode { DUCKDB_CAST_NORMAL = 0, DUCKDB_CAST_TRY = 1 } duckdb_cast_mode;++//===--------------------------------------------------------------------===//+// General type definitions+//===--------------------------------------------------------------------===//++//! DuckDB's index type.+typedef uint64_t idx_t;++//! Type definition for the data pointers of selection vectors.+typedef uint32_t sel_t;++//! The callback to destroy data, e.g.,+//! bind data (if any), init data (if any), extra data for replacement scans (if any), etc.+typedef void (*duckdb_delete_callback_t)(void *data);++//! The callback to copy data, e.g., bind data (if any).+typedef void *(*duckdb_copy_callback_t)(void *data);++//! Used for threading, contains a task state.+//! Must be destroyed with `duckdb_destroy_task_state`.+typedef void *duckdb_task_state;++//===--------------------------------------------------------------------===//+// Types (no explicit freeing)+//===--------------------------------------------------------------------===//++//! DATE is stored as days since 1970-01-01.+//! Use the `duckdb_from_date` and `duckdb_to_date` functions to extract individual information.+typedef struct {+	int32_t days;+} duckdb_date;++typedef struct {+	int32_t year;+	int8_t month;+	int8_t day;+} duckdb_date_struct;++//! TIME is stored as microseconds since 00:00:00.+//! Use the `duckdb_from_time` and `duckdb_to_time` functions to extract individual information.+typedef struct {+	int64_t micros;+} duckdb_time;++typedef struct {+	int8_t hour;+	int8_t min;+	int8_t sec;+	int32_t micros;+} duckdb_time_struct;++//! TIME_NS is stored as nanoseconds since 00:00:00.+typedef struct {+	int64_t nanos;+} duckdb_time_ns;++//! TIME_TZ is stored as 40 bits for the int64_t microseconds, and 24 bits for the int32_t offset.+//! Use the `duckdb_from_time_tz` function to extract individual information.+typedef struct {+	uint64_t bits;+} duckdb_time_tz;++typedef struct {+	duckdb_time_struct time;+	int32_t offset;+} duckdb_time_tz_struct;++//! TIMESTAMP is stored as microseconds since 1970-01-01.+//! Use the `duckdb_from_timestamp` and `duckdb_to_timestamp` functions to extract individual information.+typedef struct {+	int64_t micros;+} duckdb_timestamp;++typedef struct {+	duckdb_date_struct date;+	duckdb_time_struct time;+} duckdb_timestamp_struct;++//! TIMESTAMP_S is stored as seconds since 1970-01-01.+typedef struct {+	int64_t seconds;+} duckdb_timestamp_s;++//! TIMESTAMP_MS is stored as milliseconds since 1970-01-01.+typedef struct {+	int64_t millis;+} duckdb_timestamp_ms;++//! TIMESTAMP_NS is stored as nanoseconds since 1970-01-01.+typedef struct {+	int64_t nanos;+} duckdb_timestamp_ns;++//! INTERVAL is stored in months, days, and micros.+typedef struct {+	int32_t months;+	int32_t days;+	int64_t micros;+} duckdb_interval;++//! HUGEINT is composed of a lower and upper component.+//! Its value is upper * 2^64 + lower.+//! For simplified usage, use `duckdb_hugeint_to_double` and `duckdb_double_to_hugeint`.+typedef struct {+	uint64_t lower;+	int64_t upper;+} duckdb_hugeint;++//! UHUGEINT is composed of a lower and upper component.+//! Its value is upper * 2^64 + lower.+//! For simplified usage, use `duckdb_uhugeint_to_double` and `duckdb_double_to_uhugeint`.+typedef struct {+	uint64_t lower;+	uint64_t upper;+} duckdb_uhugeint;++//! DECIMAL is composed of a width and a scale.+//! Their value is stored in a HUGEINT.+typedef struct {+	uint8_t width;+	uint8_t scale;+	duckdb_hugeint value;+} duckdb_decimal;++//! A type holding information about the query execution progress.+typedef struct {+	double percentage;+	uint64_t rows_processed;+	uint64_t total_rows_to_process;+} duckdb_query_progress_type;++//! The internal representation of a VARCHAR (string_t). If the VARCHAR does not+//! exceed 12 characters, then we inline it. Otherwise, we inline a four-byte prefix for faster+//! string comparisons and store a pointer to the remaining characters. This is a non-+//! owning structure, i.e., it does not have to be freed.+typedef struct {+	union {+		struct {+			uint32_t length;+			char prefix[4];+			char *ptr;+		} pointer;+		struct {+			uint32_t length;+			char inlined[12];+		} inlined;+	} value;+} duckdb_string_t;++//! DuckDB's LISTs are composed of a 'parent' vector holding metadata of each list,+//! and a child vector holding the entries of the lists.+//! The `duckdb_list_entry` struct contains the internal representation of a LIST metadata entry.+//! A metadata entry contains the length of the list, and its offset in the child vector.+typedef struct {+	uint64_t offset;+	uint64_t length;+} duckdb_list_entry;++//! A column consists of a pointer to its internal data. Don't operate on this type directly.+//! Instead, use functions such as `duckdb_column_data`, `duckdb_nullmask_data`,+//! `duckdb_column_type`, and `duckdb_column_name`.+typedef struct {+	// Deprecated, use `duckdb_column_data`.+	void *deprecated_data;+	// Deprecated, use `duckdb_nullmask_data`.+	bool *deprecated_nullmask;+	// Deprecated, use `duckdb_column_type`.+	duckdb_type deprecated_type;+	// Deprecated, use `duckdb_column_name`.+	char *deprecated_name;+	void *internal_data;+} duckdb_column;++//! 1. A standalone vector that must be destroyed, or+//! 2. A vector to a column in a data chunk that lives as long as the data chunk lives.+typedef struct _duckdb_vector {+	void *internal_ptr;+} * duckdb_vector;++//! A selection vector is a vector of indices, which usually refer to values in a vector.+//! Can be used to slice vectors, changing their length and the order of their entries.+//! Standalone selection vectors must be destroyed.+typedef struct _duckdb_selection_vector {+	void *internal_ptr;+} * duckdb_selection_vector;++//===--------------------------------------------------------------------===//+// Types (explicit freeing/destroying)+//===--------------------------------------------------------------------===//++//! Strings are composed of a `char` pointer and a size.+//! You must free `string.data` with `duckdb_free`.+typedef struct {+	char *data;+	idx_t size;+} duckdb_string;++//! BLOBs are composed of a byte pointer and a size.+//! You must free `blob.data` with `duckdb_free`.+typedef struct {+	void *data;+	idx_t size;+} duckdb_blob;++//! BITs are composed of a byte pointer and a size.+//! BIT byte data has 0 to 7 bits of padding.+//! The first byte contains the number of padding bits.+//! The padding bits of the second byte are set to 1, starting from the MSB.+//! You must free `data` with `duckdb_free`.+typedef struct {+	uint8_t *data;+	idx_t size;+} duckdb_bit;++//! BIGNUMs are composed of a byte pointer, a size, and an `is_negative` bool.+//! The absolute value of the number is stored in `data` in little endian format.+//! You must free `data` with `duckdb_free`.+typedef struct {+	uint8_t *data;+	idx_t size;+	bool is_negative;+} duckdb_bignum;++//! A query result consists of a pointer to its internal data.+//! Must be freed with 'duckdb_destroy_result'.+typedef struct {+	// Deprecated, use `duckdb_column_count`.+	idx_t deprecated_column_count;+	// Deprecated, use `duckdb_row_count`.+	idx_t deprecated_row_count;+	// Deprecated, use `duckdb_rows_changed`.+	idx_t deprecated_rows_changed;+	// Deprecated, use `duckdb_column_*`-family of functions.+	duckdb_column *deprecated_columns;+	// Deprecated, use `duckdb_result_error`.+	char *deprecated_error_message;+	void *internal_data;+} duckdb_result;++//! A database instance cache object. Must be destroyed with `duckdb_destroy_instance_cache`.+typedef struct _duckdb_instance_cache {+	void *internal_ptr;+} * duckdb_instance_cache;++//! A database object. Must be closed with `duckdb_close`.+typedef struct _duckdb_database {+	void *internal_ptr;+} * duckdb_database;++//! A connection to a duckdb database. Must be closed with `duckdb_disconnect`.+typedef struct _duckdb_connection {+	void *internal_ptr;+} * duckdb_connection;++//! A client context of a duckdb connection. Must be destroyed with `duckdb_destroy_context`.+typedef struct _duckdb_client_context {+	void *internal_ptr;+} * duckdb_client_context;++//! A prepared statement is a parameterized query that allows you to bind parameters to it.+//! Must be destroyed with `duckdb_destroy_prepare`.+typedef struct _duckdb_prepared_statement {+	void *internal_ptr;+} * duckdb_prepared_statement;++//! Extracted statements. Must be destroyed with `duckdb_destroy_extracted`.+typedef struct _duckdb_extracted_statements {+	void *internal_ptr;+} * duckdb_extracted_statements;++//! The pending result represents an intermediate structure for a query that is not yet fully executed.+//! Must be destroyed with `duckdb_destroy_pending`.+typedef struct _duckdb_pending_result {+	void *internal_ptr;+} * duckdb_pending_result;++//! The appender enables fast data loading into DuckDB.+//! Must be destroyed with `duckdb_appender_destroy`.+typedef struct _duckdb_appender {+	void *internal_ptr;+} * duckdb_appender;++//! The table description allows querying information about the table.+//! Must be destroyed with `duckdb_table_description_destroy`.+typedef struct _duckdb_table_description {+	void *internal_ptr;+} * duckdb_table_description;++//! The configuration can be used to provide start-up options for a database.+//! Must be destroyed with `duckdb_destroy_config`.+typedef struct _duckdb_config {+	void *internal_ptr;+} * duckdb_config;++//! A logical type.+//! Must be destroyed with `duckdb_destroy_logical_type`.+typedef struct _duckdb_logical_type {+	void *internal_ptr;+} * duckdb_logical_type;++//! Holds extra information to register a custom logical type.+//! Reserved for future use.+typedef struct _duckdb_create_type_info {+	void *internal_ptr;+} * duckdb_create_type_info;++//! Contains a data chunk of a duckdb_result.+//! Must be destroyed with `duckdb_destroy_data_chunk`.+typedef struct _duckdb_data_chunk {+	void *internal_ptr;+} * duckdb_data_chunk;++//! A value of a logical type.+//! Must be destroyed with `duckdb_destroy_value`.+typedef struct _duckdb_value {+	void *internal_ptr;+} * duckdb_value;++//! Holds a recursive tree containing profiling metrics.+//! The tree matches the query plan, and has a top-level node.+typedef struct _duckdb_profiling_info {+	void *internal_ptr;+} * duckdb_profiling_info;++//! Holds error data.+//! Must be destroyed with `duckdb_destroy_error_data`.+typedef struct _duckdb_error_data {+	void *internal_ptr;+} * duckdb_error_data;++//! Holds a bound expression.+//! Must be destroyed with `duckdb_destroy_expression`.+typedef struct _duckdb_expression {+	void *internal_ptr;+} * duckdb_expression;++//===--------------------------------------------------------------------===//+// C API extension information+//===--------------------------------------------------------------------===//++//! Holds the state of the C API extension initialization process.+typedef struct _duckdb_extension_info {+	void *internal_ptr;+} * duckdb_extension_info;++//===--------------------------------------------------------------------===//+// Function types+//===--------------------------------------------------------------------===//++//! Additional function info.+//! When setting this info, it is necessary to pass a destroy-callback function.+typedef struct _duckdb_function_info {+	void *internal_ptr;+} * duckdb_function_info;++//! The bind info of a function.+//! When setting this info, it is necessary to pass a destroy-callback function.+typedef struct _duckdb_bind_info {+	void *internal_ptr;+} * duckdb_bind_info;++//===--------------------------------------------------------------------===//+// Scalar function types+//===--------------------------------------------------------------------===//++//! A scalar function. Must be destroyed with `duckdb_destroy_scalar_function`.+typedef struct _duckdb_scalar_function {+	void *internal_ptr;+} * duckdb_scalar_function;++//! A scalar function set. Must be destroyed with `duckdb_destroy_scalar_function_set`.+typedef struct _duckdb_scalar_function_set {+	void *internal_ptr;+} * duckdb_scalar_function_set;++//! The bind function callback of the scalar function.+typedef void (*duckdb_scalar_function_bind_t)(duckdb_bind_info info);++//! The function to execute the scalar function on an input chunk.+typedef void (*duckdb_scalar_function_t)(duckdb_function_info info, duckdb_data_chunk input, duckdb_vector output);++//===--------------------------------------------------------------------===//+// Aggregate function types+//===--------------------------------------------------------------------===//++//! An aggregate function. Must be destroyed with `duckdb_destroy_aggregate_function`.+typedef struct _duckdb_aggregate_function {+	void *internal_ptr;+} * duckdb_aggregate_function;++//! A aggregate function set. Must be destroyed with `duckdb_destroy_aggregate_function_set`.+typedef struct _duckdb_aggregate_function_set {+	void *internal_ptr;+} * duckdb_aggregate_function_set;++//! The state of an aggregate function.+typedef struct _duckdb_aggregate_state {+	void *internal_ptr;+} * duckdb_aggregate_state;++//! A function to return the aggregate state's size.+typedef idx_t (*duckdb_aggregate_state_size)(duckdb_function_info info);++//! A function to initialize an aggregate state.+typedef void (*duckdb_aggregate_init_t)(duckdb_function_info info, duckdb_aggregate_state state);++//! An optional function to destroy an aggregate state.+typedef void (*duckdb_aggregate_destroy_t)(duckdb_aggregate_state *states, idx_t count);++//! A function to update a set of aggregate states with new values.+typedef void (*duckdb_aggregate_update_t)(duckdb_function_info info, duckdb_data_chunk input,+                                          duckdb_aggregate_state *states);++//! A function to combine aggregate states.+typedef void (*duckdb_aggregate_combine_t)(duckdb_function_info info, duckdb_aggregate_state *source,+                                           duckdb_aggregate_state *target, idx_t count);++//! A function to finalize aggregate states into a result vector.+typedef void (*duckdb_aggregate_finalize_t)(duckdb_function_info info, duckdb_aggregate_state *source,+                                            duckdb_vector result, idx_t count, idx_t offset);++//===--------------------------------------------------------------------===//+// Table function types+//===--------------------------------------------------------------------===//++//! A table function. Must be destroyed with `duckdb_destroy_table_function`.+typedef struct _duckdb_table_function {+	void *internal_ptr;+} * duckdb_table_function;++//! Additional function initialization info.+//! When setting this info, it is necessary to pass a destroy-callback function.+typedef struct _duckdb_init_info {+	void *internal_ptr;+} * duckdb_init_info;++//! The bind function of the table function.+typedef void (*duckdb_table_function_bind_t)(duckdb_bind_info info);++//! The possibly thread-local initialization function of the table function.+typedef void (*duckdb_table_function_init_t)(duckdb_init_info info);++//! The function to generate an output chunk during table function execution.+typedef void (*duckdb_table_function_t)(duckdb_function_info info, duckdb_data_chunk output);++//===--------------------------------------------------------------------===//+// Cast types+//===--------------------------------------------------------------------===//++//! A cast function. Must be destroyed with `duckdb_destroy_cast_function`.+typedef struct _duckdb_cast_function {+	void *internal_ptr;+} * duckdb_cast_function;++//! The function to cast from an input vector to an output vector.+typedef bool (*duckdb_cast_function_t)(duckdb_function_info info, idx_t count, duckdb_vector input,+                                       duckdb_vector output);++//===--------------------------------------------------------------------===//+// Replacement scan types+//===--------------------------------------------------------------------===//++//! Additional replacement scan info. When setting this info, it is necessary to pass a destroy-callback function.+typedef struct _duckdb_replacement_scan_info {+	void *internal_ptr;+} * duckdb_replacement_scan_info;++//! A replacement scan function.+typedef void (*duckdb_replacement_callback_t)(duckdb_replacement_scan_info info, const char *table_name, void *data);++//===--------------------------------------------------------------------===//+// Arrow-related types+//===--------------------------------------------------------------------===//++//! Forward declare Arrow structs+//! It is important to notice that these structs are not defined by DuckDB but are actually Arrow external objects.+//! They're defined by the C Data Interface Arrow spec: https://arrow.apache.org/docs/format/CDataInterface.html++struct ArrowArray;++struct ArrowSchema;++//! Holds an arrow query result. Must be destroyed with `duckdb_destroy_arrow`.+typedef struct _duckdb_arrow {+	void *internal_ptr;+} * duckdb_arrow;++//! Holds an arrow array stream. Must be destroyed with `duckdb_destroy_arrow_stream`.+typedef struct _duckdb_arrow_stream {+	void *internal_ptr;+} * duckdb_arrow_stream;++//! Holds an arrow schema. Remember to release the respective ArrowSchema object.+typedef struct _duckdb_arrow_schema {+	void *internal_ptr;+} * duckdb_arrow_schema;++//! Holds an arrow converted schema (i.e., duckdb::ArrowTableSchema).+//! In practice, this object holds the information necessary to do proper conversion between Arrow Types and DuckDB+//! Types. Check duckdb/function/table/arrow/arrow_duck_schema.hpp for more details! Must be destroyed with+//! `duckdb_destroy_arrow_converted_schema`+typedef struct _duckdb_arrow_converted_schema {+	void *internal_ptr;+} * duckdb_arrow_converted_schema;++//! Holds an arrow array. Remember to release the respective ArrowSchema object.+typedef struct _duckdb_arrow_array {+	void *internal_ptr;+} * duckdb_arrow_array;++//! The arrow options used when transforming the DuckDB schema and datachunks into Arrow schema and arrays.+//! Used in `duckdb_to_arrow_schema` and `duckdb_data_chunk_to_arrow`+typedef struct _duckdb_arrow_options {+	void *internal_ptr;+} * duckdb_arrow_options;++//===--------------------------------------------------------------------===//+// DuckDB extension access+//===--------------------------------------------------------------------===//++//! Passed to C API extension as a parameter to the entrypoint.+struct duckdb_extension_access {+	//! Indicate that an error has occurred.+	void (*set_error)(duckdb_extension_info info, const char *error);+	//! Fetch the database on which to register the extension.+	duckdb_database *(*get_database)(duckdb_extension_info info);+	//! Fetch the API struct pointer.+	const void *(*get_api)(duckdb_extension_info info, const char *version);+};++#ifndef DUCKDB_API_EXCLUDE_FUNCTIONS++//===--------------------------------------------------------------------===//+// Functions+//===--------------------------------------------------------------------===//++//===--------------------------------------------------------------------===//+// Open Connect+//===--------------------------------------------------------------------===//++/*!+Creates a new database instance cache.+The instance cache is necessary if a client/program (re)opens multiple databases to the same file within the same+process. Must be destroyed with 'duckdb_destroy_instance_cache'.++* @return The database instance cache.+*/+DUCKDB_C_API duckdb_instance_cache duckdb_create_instance_cache();++/*!+Creates a new database instance in the instance cache, or retrieves an existing database instance.+Must be closed with 'duckdb_close'.++* @param instance_cache The instance cache in which to create the database, or from which to take the database.+* @param path Path to the database file on disk. Both `nullptr` and `:memory:` open or retrieve an in-memory database.+* @param out_database The resulting cached database.+* @param config (Optional) configuration used to create the database.+* @param out_error If set and the function returns `DuckDBError`, this contains the error message.+Note that the error message must be freed using `duckdb_free`.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_get_or_create_from_cache(duckdb_instance_cache instance_cache, const char *path,+                                                          duckdb_database *out_database, duckdb_config config,+                                                          char **out_error);++/*!+Destroys an existing database instance cache and de-allocates its memory.++* @param instance_cache The instance cache to destroy.+*/+DUCKDB_C_API void duckdb_destroy_instance_cache(duckdb_instance_cache *instance_cache);++/*!+Creates a new database or opens an existing database file stored at the given path.+If no path is given a new in-memory database is created instead.+The database must be closed with 'duckdb_close'.++* @param path Path to the database file on disk. Both `nullptr` and `:memory:` open an in-memory database.+* @param out_database The result database object.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_open(const char *path, duckdb_database *out_database);++/*!+Extended version of duckdb_open. Creates a new database or opens an existing database file stored at the given path.+The database must be closed with 'duckdb_close'.++* @param path Path to the database file on disk. Both `nullptr` and `:memory:` open an in-memory database.+* @param out_database The result database object.+* @param config (Optional) configuration used to start up the database.+* @param out_error If set and the function returns `DuckDBError`, this contains the error message.+Note that the error message must be freed using `duckdb_free`.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_open_ext(const char *path, duckdb_database *out_database, duckdb_config config,+                                          char **out_error);++/*!+Closes the specified database and de-allocates all memory allocated for that database.+This should be called after you are done with any database allocated through `duckdb_open` or `duckdb_open_ext`.+Note that failing to call `duckdb_close` (in case of e.g. a program crash) will not cause data corruption.+Still, it is recommended to always correctly close a database object after you are done with it.++* @param database The database object to shut down.+*/+DUCKDB_C_API void duckdb_close(duckdb_database *database);++/*!+Opens a connection to a database. Connections are required to query the database, and store transactional state+associated with the connection.+The instantiated connection should be closed using 'duckdb_disconnect'.++* @param database The database file to connect to.+* @param out_connection The result connection object.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_connect(duckdb_database database, duckdb_connection *out_connection);++/*!+Interrupt running query++* @param connection The connection to interrupt+*/+DUCKDB_C_API void duckdb_interrupt(duckdb_connection connection);++/*!+Get progress of the running query++* @param connection The working connection+* @return -1 if no progress or a percentage of the progress+*/+DUCKDB_C_API duckdb_query_progress_type duckdb_query_progress(duckdb_connection connection);++/*!+Closes the specified connection and de-allocates all memory allocated for that connection.++* @param connection The connection to close.+*/+DUCKDB_C_API void duckdb_disconnect(duckdb_connection *connection);++/*!+Retrieves the client context of the connection.++* @param connection The connection.+* @param out_context The client context of the connection. Must be destroyed with `duckdb_destroy_client_context`.+*/+DUCKDB_C_API void duckdb_connection_get_client_context(duckdb_connection connection,+                                                       duckdb_client_context *out_context);++/*!+Retrieves the arrow options of the connection.++* @param connection The connection.+*/+DUCKDB_C_API void duckdb_connection_get_arrow_options(duckdb_connection connection,+                                                      duckdb_arrow_options *out_arrow_options);++/*!+Returns the connection id of the client context.++* @param context The client context.+* @return The connection id of the client context.+*/+DUCKDB_C_API idx_t duckdb_client_context_get_connection_id(duckdb_client_context context);++/*!+Destroys the client context and deallocates its memory.++* @param context The client context to destroy.+*/+DUCKDB_C_API void duckdb_destroy_client_context(duckdb_client_context *context);++/*!+Destroys the arrow options and deallocates its memory.++* @param arrow_options The arrow options to destroy.+*/+DUCKDB_C_API void duckdb_destroy_arrow_options(duckdb_arrow_options *arrow_options);++/*!+Returns the version of the linked DuckDB, with a version postfix for dev versions++Usually used for developing C extensions that must return this for a compatibility check.+*/+DUCKDB_C_API const char *duckdb_library_version();++/*!+Get the list of (fully qualified) table names of the query.++* @param connection The connection for which to get the table names.+* @param query The query for which to get the table names.+* @param qualified Returns fully qualified table names (catalog.schema.table), if set to true, else only the (not+escaped) table names.+* @return A duckdb_value of type VARCHAR[] containing the (fully qualified) table names of the query. Must be destroyed+with duckdb_destroy_value.+*/+DUCKDB_C_API duckdb_value duckdb_get_table_names(duckdb_connection connection, const char *query, bool qualified);++//===--------------------------------------------------------------------===//+// Configuration+//===--------------------------------------------------------------------===//++/*!+Initializes an empty configuration object that can be used to provide start-up options for the DuckDB instance+through `duckdb_open_ext`.+The duckdb_config must be destroyed using 'duckdb_destroy_config'++This will always succeed unless there is a malloc failure.++Note that `duckdb_destroy_config` should always be called on the resulting config, even if the function returns+`DuckDBError`.++* @param out_config The result configuration object.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_create_config(duckdb_config *out_config);++/*!+This returns the total amount of configuration options available for usage with `duckdb_get_config_flag`.++This should not be called in a loop as it internally loops over all the options.++* @return The amount of config options available.+*/+DUCKDB_C_API size_t duckdb_config_count();++/*!+Obtains a human-readable name and description of a specific configuration option. This can be used to e.g.+display configuration options. This will succeed unless `index` is out of range (i.e. `>= duckdb_config_count`).++The result name or description MUST NOT be freed.++* @param index The index of the configuration option (between 0 and `duckdb_config_count`)+* @param out_name A name of the configuration flag.+* @param out_description A description of the configuration flag.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_get_config_flag(size_t index, const char **out_name, const char **out_description);++/*!+Sets the specified option for the specified configuration. The configuration option is indicated by name.+To obtain a list of config options, see `duckdb_get_config_flag`.++In the source code, configuration options are defined in `config.cpp`.++This can fail if either the name is invalid, or if the value provided for the option is invalid.++* @param config The configuration object to set the option on.+* @param name The name of the configuration flag to set.+* @param option The value to set the configuration flag to.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_set_config(duckdb_config config, const char *name, const char *option);++/*!+Destroys the specified configuration object and de-allocates all memory allocated for the object.++* @param config The configuration object to destroy.+*/+DUCKDB_C_API void duckdb_destroy_config(duckdb_config *config);++//===--------------------------------------------------------------------===//+// Error Data+//===--------------------------------------------------------------------===//++// Functions that can throw DuckDB errors must return duckdb_error_data.+// Please use this interface for all new functions, as it deprecates all previous error handling approaches.++/*!+Creates duckdb_error_data.+Must be destroyed with `duckdb_destroy_error_data`.++* @param type The error type.+* @param message The error message.+* @return The error data.+*/+DUCKDB_C_API duckdb_error_data duckdb_create_error_data(duckdb_error_type type, const char *message);++/*!+Destroys the error data and deallocates its memory.++* @param error_data The error data to destroy.+*/+DUCKDB_C_API void duckdb_destroy_error_data(duckdb_error_data *error_data);++/*!+Returns the duckdb_error_type of the error data.++* @param error_data The error data.+* @return The error type.+*/+DUCKDB_C_API duckdb_error_type duckdb_error_data_error_type(duckdb_error_data error_data);++/*!+Returns the error message of the error data. Must not be freed.++* @param error_data The error data.+* @return The error message.+*/+DUCKDB_C_API const char *duckdb_error_data_message(duckdb_error_data error_data);++/*!+Returns whether the error data contains an error or not.++* @param error_data The error data.+* @return True, if the error data contains an exception, else false.+*/+DUCKDB_C_API bool duckdb_error_data_has_error(duckdb_error_data error_data);++//===--------------------------------------------------------------------===//+// Query Execution+//===--------------------------------------------------------------------===//++/*!+Executes a SQL query within a connection and stores the full (materialized) result in the out_result pointer.+If the query fails to execute, DuckDBError is returned and the error message can be retrieved by calling+`duckdb_result_error`.++Note that after running `duckdb_query`, `duckdb_destroy_result` must be called on the result object even if the+query fails, otherwise the error stored within the result will not be freed correctly.++* @param connection The connection to perform the query in.+* @param query The SQL query to run.+* @param out_result The query result.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_query(duckdb_connection connection, const char *query, duckdb_result *out_result);++/*!+Closes the result and de-allocates all memory allocated for that result.++* @param result The result to destroy.+*/+DUCKDB_C_API void duckdb_destroy_result(duckdb_result *result);++/*!+Returns the column name of the specified column. The result should not need to be freed; the column names will+automatically be destroyed when the result is destroyed.++Returns `NULL` if the column is out of range.++* @param result The result object to fetch the column name from.+* @param col The column index.+* @return The column name of the specified column.+*/+DUCKDB_C_API const char *duckdb_column_name(duckdb_result *result, idx_t col);++/*!+Returns the column type of the specified column.++Returns `DUCKDB_TYPE_INVALID` if the column is out of range.++* @param result The result object to fetch the column type from.+* @param col The column index.+* @return The column type of the specified column.+*/+DUCKDB_C_API duckdb_type duckdb_column_type(duckdb_result *result, idx_t col);++/*!+Returns the statement type of the statement that was executed++* @param result The result object to fetch the statement type from.+* @return duckdb_statement_type value or DUCKDB_STATEMENT_TYPE_INVALID+*/+DUCKDB_C_API duckdb_statement_type duckdb_result_statement_type(duckdb_result result);++/*!+Returns the logical column type of the specified column.++The return type of this call should be destroyed with `duckdb_destroy_logical_type`.++Returns `NULL` if the column is out of range.++* @param result The result object to fetch the column type from.+* @param col The column index.+* @return The logical column type of the specified column.+*/+DUCKDB_C_API duckdb_logical_type duckdb_column_logical_type(duckdb_result *result, idx_t col);++/*!+Returns the arrow options associated with the given result. These options are definitions of how the arrow arrays/schema+should be produced.+* @param result The result object to fetch arrow options from.+* @return The arrow options associated with the given result. This must be destroyed with+`duckdb_destroy_arrow_options`.+*/+DUCKDB_C_API duckdb_arrow_options duckdb_result_get_arrow_options(duckdb_result *result);++/*!+Returns the number of columns present in a the result object.++* @param result The result object.+* @return The number of columns present in the result object.+*/+DUCKDB_C_API idx_t duckdb_column_count(duckdb_result *result);++#ifndef DUCKDB_API_NO_DEPRECATED+/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Returns the number of rows present in the result object.++* @param result The result object.+* @return The number of rows present in the result object.+*/+DUCKDB_C_API idx_t duckdb_row_count(duckdb_result *result);++#endif++/*!+Returns the number of rows changed by the query stored in the result. This is relevant only for INSERT/UPDATE/DELETE+queries. For other queries the rows_changed will be 0.++* @param result The result object.+* @return The number of rows changed.+*/+DUCKDB_C_API idx_t duckdb_rows_changed(duckdb_result *result);++#ifndef DUCKDB_API_NO_DEPRECATED+/*!+**DEPRECATED**: Prefer using `duckdb_result_get_chunk` instead.++Returns the data of a specific column of a result in columnar format.++The function returns a dense array which contains the result data. The exact type stored in the array depends on the+corresponding duckdb_type (as provided by `duckdb_column_type`). For the exact type by which the data should be+accessed, see the comments in [the types section](types) or the `DUCKDB_TYPE` enum.++For example, for a column of type `DUCKDB_TYPE_INTEGER`, rows can be accessed in the following manner:+```c+int32_t *data = (int32_t *) duckdb_column_data(&result, 0);+printf("Data for row %d: %d\n", row, data[row]);+```++* @param result The result object to fetch the column data from.+* @param col The column index.+* @return The column data of the specified column.+*/+DUCKDB_C_API void *duckdb_column_data(duckdb_result *result, idx_t col);++/*!+**DEPRECATED**: Prefer using `duckdb_result_get_chunk` instead.++Returns the nullmask of a specific column of a result in columnar format. The nullmask indicates for every row+whether or not the corresponding row is `NULL`. If a row is `NULL`, the values present in the array provided+by `duckdb_column_data` are undefined.++```c+int32_t *data = (int32_t *) duckdb_column_data(&result, 0);+bool *nullmask = duckdb_nullmask_data(&result, 0);+if (nullmask[row]) {+    printf("Data for row %d: NULL\n", row);+} else {+    printf("Data for row %d: %d\n", row, data[row]);+}+```++* @param result The result object to fetch the nullmask from.+* @param col The column index.+* @return The nullmask of the specified column.+*/+DUCKDB_C_API bool *duckdb_nullmask_data(duckdb_result *result, idx_t col);++#endif++/*!+Returns the error message contained within the result. The error is only set if `duckdb_query` returns `DuckDBError`.++The result of this function must not be freed. It will be cleaned up when `duckdb_destroy_result` is called.++* @param result The result object to fetch the error from.+* @return The error of the result.+*/+DUCKDB_C_API const char *duckdb_result_error(duckdb_result *result);++/*!+Returns the result error type contained within the result. The error is only set if `duckdb_query` returns+`DuckDBError`.++* @param result The result object to fetch the error from.+* @return The error type of the result.+*/+DUCKDB_C_API duckdb_error_type duckdb_result_error_type(duckdb_result *result);++//===--------------------------------------------------------------------===//+// Result Functions+//===--------------------------------------------------------------------===//++#ifndef DUCKDB_API_NO_DEPRECATED+/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Fetches a data chunk from the duckdb_result. This function should be called repeatedly until the result is exhausted.++The result must be destroyed with `duckdb_destroy_data_chunk`.++This function supersedes all `duckdb_value` functions, as well as the `duckdb_column_data` and `duckdb_nullmask_data`+functions. It results in significantly better performance, and should be preferred in newer code-bases.++If this function is used, none of the other result functions can be used and vice versa (i.e. this function cannot be+mixed with the legacy result functions).++Use `duckdb_result_chunk_count` to figure out how many chunks there are in the result.++* @param result The result object to fetch the data chunk from.+* @param chunk_index The chunk index to fetch from.+* @return The resulting data chunk. Returns `NULL` if the chunk index is out of bounds.+*/+DUCKDB_C_API duckdb_data_chunk duckdb_result_get_chunk(duckdb_result result, idx_t chunk_index);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Checks if the type of the internal result is StreamQueryResult.++* @param result The result object to check.+* @return Whether or not the result object is of the type StreamQueryResult+*/+DUCKDB_C_API bool duckdb_result_is_streaming(duckdb_result result);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Returns the number of data chunks present in the result.++* @param result The result object+* @return Number of data chunks present in the result.+*/+DUCKDB_C_API idx_t duckdb_result_chunk_count(duckdb_result result);++#endif++/*!+Returns the return_type of the given result, or DUCKDB_RETURN_TYPE_INVALID on error++* @param result The result object+* @return The return_type+*/+DUCKDB_C_API duckdb_result_type duckdb_result_return_type(duckdb_result result);++//===--------------------------------------------------------------------===//+// Safe Fetch Functions+//===--------------------------------------------------------------------===//++// These functions will perform conversions if necessary.+// On failure (e.g. if conversion cannot be performed or if the value is NULL) a default value is returned.+// Note that these functions are slow since they perform bounds checking and conversion+// For fast access of values prefer using `duckdb_result_get_chunk`+#ifndef DUCKDB_API_NO_DEPRECATED+/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The boolean value at the specified location, or false if the value cannot be converted.+*/+DUCKDB_C_API bool duckdb_value_boolean(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The int8_t value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API int8_t duckdb_value_int8(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The int16_t value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API int16_t duckdb_value_int16(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The int32_t value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API int32_t duckdb_value_int32(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The int64_t value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API int64_t duckdb_value_int64(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The duckdb_hugeint value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API duckdb_hugeint duckdb_value_hugeint(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The duckdb_uhugeint value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API duckdb_uhugeint duckdb_value_uhugeint(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The duckdb_decimal value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API duckdb_decimal duckdb_value_decimal(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The uint8_t value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API uint8_t duckdb_value_uint8(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The uint16_t value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API uint16_t duckdb_value_uint16(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The uint32_t value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API uint32_t duckdb_value_uint32(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The uint64_t value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API uint64_t duckdb_value_uint64(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The float value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API float duckdb_value_float(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The double value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API double duckdb_value_double(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The duckdb_date value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API duckdb_date duckdb_value_date(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The duckdb_time value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API duckdb_time duckdb_value_time(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The duckdb_timestamp value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API duckdb_timestamp duckdb_value_timestamp(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The duckdb_interval value at the specified location, or 0 if the value cannot be converted.+*/+DUCKDB_C_API duckdb_interval duckdb_value_interval(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATED**: Use duckdb_value_string instead. This function does not work correctly if the string contains null+bytes.++* @return The text value at the specified location as a null-terminated string, or nullptr if the value cannot be+converted. The result must be freed with `duckdb_free`.+*/+DUCKDB_C_API char *duckdb_value_varchar(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++No support for nested types, and for other complex types.+The resulting field "string.data" must be freed with `duckdb_free.`++* @return The string value at the specified location. Attempts to cast the result value to string.+*/+DUCKDB_C_API duckdb_string duckdb_value_string(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATED**: Use duckdb_value_string_internal instead. This function does not work correctly if the string contains+null bytes.++* @return The char* value at the specified location. ONLY works on VARCHAR columns and does not auto-cast.+If the column is NOT a VARCHAR column this function will return NULL.++The result must NOT be freed.+*/+DUCKDB_C_API char *duckdb_value_varchar_internal(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATED**: Use duckdb_value_string_internal instead. This function does not work correctly if the string contains+null bytes.+* @return The char* value at the specified location. ONLY works on VARCHAR columns and does not auto-cast.+If the column is NOT a VARCHAR column this function will return NULL.++The result must NOT be freed.+*/+DUCKDB_C_API duckdb_string duckdb_value_string_internal(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return The duckdb_blob value at the specified location. Returns a blob with blob.data set to nullptr if the+value cannot be converted. The resulting field "blob.data" must be freed with `duckdb_free.`+*/+DUCKDB_C_API duckdb_blob duckdb_value_blob(duckdb_result *result, idx_t col, idx_t row);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++* @return Returns true if the value at the specified index is NULL, and false otherwise.+*/+DUCKDB_C_API bool duckdb_value_is_null(duckdb_result *result, idx_t col, idx_t row);++#endif++//===--------------------------------------------------------------------===//+// Helpers+//===--------------------------------------------------------------------===//++/*!+Allocate `size` bytes of memory using the duckdb internal malloc function. Any memory allocated in this manner+should be freed using `duckdb_free`.++* @param size The number of bytes to allocate.+* @return A pointer to the allocated memory region.+*/+DUCKDB_C_API void *duckdb_malloc(size_t size);++/*!+Free a value returned from `duckdb_malloc`, `duckdb_value_varchar`, `duckdb_value_blob`, or+`duckdb_value_string`.++* @param ptr The memory region to de-allocate.+*/+DUCKDB_C_API void duckdb_free(void *ptr);++/*!+The internal vector size used by DuckDB.+This is the amount of tuples that will fit into a data chunk created by `duckdb_create_data_chunk`.++* @return The vector size.+*/+DUCKDB_C_API idx_t duckdb_vector_size();++/*!+Whether or not the duckdb_string_t value is inlined.+This means that the data of the string does not have a separate allocation.++*/+DUCKDB_C_API bool duckdb_string_is_inlined(duckdb_string_t string);++/*!+Get the string length of a string_t++* @param string The string to get the length of.+* @return The length.+*/+DUCKDB_C_API uint32_t duckdb_string_t_length(duckdb_string_t string);++/*!+Get a pointer to the string data of a string_t++* @param string The string to get the pointer to.+* @return The pointer.+*/+DUCKDB_C_API const char *duckdb_string_t_data(duckdb_string_t *string);++//===--------------------------------------------------------------------===//+// Date Time Timestamp Helpers+//===--------------------------------------------------------------------===//++/*!+Decompose a `duckdb_date` object into year, month and date (stored as `duckdb_date_struct`).++* @param date The date object, as obtained from a `DUCKDB_TYPE_DATE` column.+* @return The `duckdb_date_struct` with the decomposed elements.+*/+DUCKDB_C_API duckdb_date_struct duckdb_from_date(duckdb_date date);++/*!+Re-compose a `duckdb_date` from year, month and date (`duckdb_date_struct`).++* @param date The year, month and date stored in a `duckdb_date_struct`.+* @return The `duckdb_date` element.+*/+DUCKDB_C_API duckdb_date duckdb_to_date(duckdb_date_struct date);++/*!+Test a `duckdb_date` to see if it is a finite value.++* @param date The date object, as obtained from a `DUCKDB_TYPE_DATE` column.+* @return True if the date is finite, false if it is ±infinity.+*/+DUCKDB_C_API bool duckdb_is_finite_date(duckdb_date date);++/*!+Decompose a `duckdb_time` object into hour, minute, second and microsecond (stored as `duckdb_time_struct`).++* @param time The time object, as obtained from a `DUCKDB_TYPE_TIME` column.+* @return The `duckdb_time_struct` with the decomposed elements.+*/+DUCKDB_C_API duckdb_time_struct duckdb_from_time(duckdb_time time);++/*!+Create a `duckdb_time_tz` object from micros and a timezone offset.++* @param micros The microsecond component of the time.+* @param offset The timezone offset component of the time.+* @return The `duckdb_time_tz` element.+*/+DUCKDB_C_API duckdb_time_tz duckdb_create_time_tz(int64_t micros, int32_t offset);++/*!+Decompose a TIME_TZ objects into micros and a timezone offset.++Use `duckdb_from_time` to further decompose the micros into hour, minute, second and microsecond.++* @param micros The time object, as obtained from a `DUCKDB_TYPE_TIME_TZ` column.+*/+DUCKDB_C_API duckdb_time_tz_struct duckdb_from_time_tz(duckdb_time_tz micros);++/*!+Re-compose a `duckdb_time` from hour, minute, second and microsecond (`duckdb_time_struct`).++* @param time The hour, minute, second and microsecond in a `duckdb_time_struct`.+* @return The `duckdb_time` element.+*/+DUCKDB_C_API duckdb_time duckdb_to_time(duckdb_time_struct time);++/*!+Decompose a `duckdb_timestamp` object into a `duckdb_timestamp_struct`.++* @param ts The ts object, as obtained from a `DUCKDB_TYPE_TIMESTAMP` column.+* @return The `duckdb_timestamp_struct` with the decomposed elements.+*/+DUCKDB_C_API duckdb_timestamp_struct duckdb_from_timestamp(duckdb_timestamp ts);++/*!+Re-compose a `duckdb_timestamp` from a duckdb_timestamp_struct.++* @param ts The de-composed elements in a `duckdb_timestamp_struct`.+* @return The `duckdb_timestamp` element.+*/+DUCKDB_C_API duckdb_timestamp duckdb_to_timestamp(duckdb_timestamp_struct ts);++/*!+Test a `duckdb_timestamp` to see if it is a finite value.++* @param ts The duckdb_timestamp object, as obtained from a `DUCKDB_TYPE_TIMESTAMP` column.+* @return True if the timestamp is finite, false if it is ±infinity.+*/+DUCKDB_C_API bool duckdb_is_finite_timestamp(duckdb_timestamp ts);++/*!+Test a `duckdb_timestamp_s` to see if it is a finite value.++* @param ts The duckdb_timestamp_s object, as obtained from a `DUCKDB_TYPE_TIMESTAMP_S` column.+* @return True if the timestamp is finite, false if it is ±infinity.+*/+DUCKDB_C_API bool duckdb_is_finite_timestamp_s(duckdb_timestamp_s ts);++/*!+Test a `duckdb_timestamp_ms` to see if it is a finite value.++* @param ts The duckdb_timestamp_ms object, as obtained from a `DUCKDB_TYPE_TIMESTAMP_MS` column.+* @return True if the timestamp is finite, false if it is ±infinity.+*/+DUCKDB_C_API bool duckdb_is_finite_timestamp_ms(duckdb_timestamp_ms ts);++/*!+Test a `duckdb_timestamp_ns` to see if it is a finite value.++* @param ts The duckdb_timestamp_ns object, as obtained from a `DUCKDB_TYPE_TIMESTAMP_NS` column.+* @return True if the timestamp is finite, false if it is ±infinity.+*/+DUCKDB_C_API bool duckdb_is_finite_timestamp_ns(duckdb_timestamp_ns ts);++//===--------------------------------------------------------------------===//+// Hugeint Helpers+//===--------------------------------------------------------------------===//++/*!+Converts a duckdb_hugeint object (as obtained from a `DUCKDB_TYPE_HUGEINT` column) into a double.++* @param val The hugeint value.+* @return The converted `double` element.+*/+DUCKDB_C_API double duckdb_hugeint_to_double(duckdb_hugeint val);++/*!+Converts a double value to a duckdb_hugeint object.++If the conversion fails because the double value is too big the result will be 0.++* @param val The double value.+* @return The converted `duckdb_hugeint` element.+*/+DUCKDB_C_API duckdb_hugeint duckdb_double_to_hugeint(double val);++//===--------------------------------------------------------------------===//+// Unsigned Hugeint Helpers+//===--------------------------------------------------------------------===//++/*!+Converts a duckdb_uhugeint object (as obtained from a `DUCKDB_TYPE_UHUGEINT` column) into a double.++* @param val The uhugeint value.+* @return The converted `double` element.+*/+DUCKDB_C_API double duckdb_uhugeint_to_double(duckdb_uhugeint val);++/*!+Converts a double value to a duckdb_uhugeint object.++If the conversion fails because the double value is too big the result will be 0.++* @param val The double value.+* @return The converted `duckdb_uhugeint` element.+*/+DUCKDB_C_API duckdb_uhugeint duckdb_double_to_uhugeint(double val);++//===--------------------------------------------------------------------===//+// Decimal Helpers+//===--------------------------------------------------------------------===//++/*!+Converts a double value to a duckdb_decimal object.++If the conversion fails because the double value is too big, or the width/scale are invalid the result will be 0.++* @param val The double value.+* @return The converted `duckdb_decimal` element.+*/+DUCKDB_C_API duckdb_decimal duckdb_double_to_decimal(double val, uint8_t width, uint8_t scale);++/*!+Converts a duckdb_decimal object (as obtained from a `DUCKDB_TYPE_DECIMAL` column) into a double.++* @param val The decimal value.+* @return The converted `double` element.+*/+DUCKDB_C_API double duckdb_decimal_to_double(duckdb_decimal val);++//===--------------------------------------------------------------------===//+// Prepared Statements+//===--------------------------------------------------------------------===//++// A prepared statement is a parameterized query that allows you to bind parameters to it.+// * This is useful to easily supply parameters to functions and avoid SQL injection attacks.+// * This is useful to speed up queries that you will execute several times with different parameters.+// Because the query will only be parsed, bound, optimized and planned once during the prepare stage,+// rather than once per execution.+// For example:+//   SELECT * FROM tbl WHERE id=?+// Or a query with multiple parameters:+//   SELECT * FROM tbl WHERE id=$1 OR name=$2+/*!+Create a prepared statement object from a query.++Note that after calling `duckdb_prepare`, the prepared statement should always be destroyed using+`duckdb_destroy_prepare`, even if the prepare fails.++If the prepare fails, `duckdb_prepare_error` can be called to obtain the reason why the prepare failed.++* @param connection The connection object+* @param query The SQL query to prepare+* @param out_prepared_statement The resulting prepared statement object+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_prepare(duckdb_connection connection, const char *query,+                                         duckdb_prepared_statement *out_prepared_statement);++/*!+Closes the prepared statement and de-allocates all memory allocated for the statement.++* @param prepared_statement The prepared statement to destroy.+*/+DUCKDB_C_API void duckdb_destroy_prepare(duckdb_prepared_statement *prepared_statement);++/*!+Returns the error message associated with the given prepared statement.+If the prepared statement has no error message, this returns `nullptr` instead.++The error message should not be freed. It will be de-allocated when `duckdb_destroy_prepare` is called.++* @param prepared_statement The prepared statement to obtain the error from.+* @return The error message, or `nullptr` if there is none.+*/+DUCKDB_C_API const char *duckdb_prepare_error(duckdb_prepared_statement prepared_statement);++/*!+Returns the number of parameters that can be provided to the given prepared statement.++Returns 0 if the query was not successfully prepared.++* @param prepared_statement The prepared statement to obtain the number of parameters for.+*/+DUCKDB_C_API idx_t duckdb_nparams(duckdb_prepared_statement prepared_statement);++/*!+Returns the name used to identify the parameter+The returned string should be freed using `duckdb_free`.++Returns NULL if the index is out of range for the provided prepared statement.++* @param prepared_statement The prepared statement for which to get the parameter name from.+*/+DUCKDB_C_API const char *duckdb_parameter_name(duckdb_prepared_statement prepared_statement, idx_t index);++/*!+Returns the parameter type for the parameter at the given index.++Returns `DUCKDB_TYPE_INVALID` if the parameter index is out of range or the statement was not successfully prepared.++* @param prepared_statement The prepared statement.+* @param param_idx The parameter index.+* @return The parameter type+*/+DUCKDB_C_API duckdb_type duckdb_param_type(duckdb_prepared_statement prepared_statement, idx_t param_idx);++/*!+Returns the logical type for the parameter at the given index.++Returns `nullptr` if the parameter index is out of range or the statement was not successfully prepared.++The return type of this call should be destroyed with `duckdb_destroy_logical_type`.++* @param prepared_statement The prepared statement.+* @param param_idx The parameter index.+* @return The logical type of the parameter+*/+DUCKDB_C_API duckdb_logical_type duckdb_param_logical_type(duckdb_prepared_statement prepared_statement,+                                                           idx_t param_idx);++/*!+Clear the params bind to the prepared statement.+*/+DUCKDB_C_API duckdb_state duckdb_clear_bindings(duckdb_prepared_statement prepared_statement);++/*!+Returns the statement type of the statement to be executed++* @param statement The prepared statement.+* @return duckdb_statement_type value or DUCKDB_STATEMENT_TYPE_INVALID+*/+DUCKDB_C_API duckdb_statement_type duckdb_prepared_statement_type(duckdb_prepared_statement statement);++/*!+Returns the number of columns present in a the result of the prepared statement. If any of the column types are invalid,+the result will be 1.++* @param prepared_statement The prepared statement.+* @return The number of columns present in the result of the prepared statement.+*/+DUCKDB_C_API idx_t duckdb_prepared_statement_column_count(duckdb_prepared_statement prepared_statement);++/*!+Returns the name of the specified column of the result of the prepared_statement.+The returned string should be freed using `duckdb_free`.++Returns `nullptr` if the column is out of range.++* @param prepared_statement The prepared statement.+* @param col_idx The column index.+* @return The column name of the specified column.+*/+DUCKDB_C_API const char *duckdb_prepared_statement_column_name(duckdb_prepared_statement prepared_statement,+                                                               idx_t col_idx);++/*!+Returns the column type of the specified column of the result of the prepared_statement.++Returns `DUCKDB_TYPE_INVALID` if the column is out of range.+The return type of this call should be destroyed with `duckdb_destroy_logical_type`.++* @param prepared_statement The prepared statement to fetch the column type from.+* @param col_idx The column index.+* @return The logical type of the specified column.+*/+DUCKDB_C_API duckdb_logical_type+duckdb_prepared_statement_column_logical_type(duckdb_prepared_statement prepared_statement, idx_t col_idx);++/*!+Returns the column type of the specified column of the result of the prepared_statement.++Returns `DUCKDB_TYPE_INVALID` if the column is out of range.++* @param prepared_statement The prepared statement to fetch the column type from.+* @param col_idx The column index.+* @return The type of the specified column.+*/+DUCKDB_C_API duckdb_type duckdb_prepared_statement_column_type(duckdb_prepared_statement prepared_statement,+                                                               idx_t col_idx);++//===--------------------------------------------------------------------===//+// Bind Values to Prepared Statements+//===--------------------------------------------------------------------===//++/*!+Binds a value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_value(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                            duckdb_value val);++/*!+Retrieve the index of the parameter for the prepared statement, identified by name+*/+DUCKDB_C_API duckdb_state duckdb_bind_parameter_index(duckdb_prepared_statement prepared_statement,+                                                      idx_t *param_idx_out, const char *name);++/*!+Binds a bool value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_boolean(duckdb_prepared_statement prepared_statement, idx_t param_idx, bool val);++/*!+Binds an int8_t value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_int8(duckdb_prepared_statement prepared_statement, idx_t param_idx, int8_t val);++/*!+Binds an int16_t value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_int16(duckdb_prepared_statement prepared_statement, idx_t param_idx, int16_t val);++/*!+Binds an int32_t value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_int32(duckdb_prepared_statement prepared_statement, idx_t param_idx, int32_t val);++/*!+Binds an int64_t value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_int64(duckdb_prepared_statement prepared_statement, idx_t param_idx, int64_t val);++/*!+Binds a duckdb_hugeint value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_hugeint(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                              duckdb_hugeint val);++/*!+Binds a duckdb_uhugeint value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_uhugeint(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                               duckdb_uhugeint val);++/*!+Binds a duckdb_decimal value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_decimal(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                              duckdb_decimal val);++/*!+Binds a uint8_t value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_uint8(duckdb_prepared_statement prepared_statement, idx_t param_idx, uint8_t val);++/*!+Binds a uint16_t value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_uint16(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                             uint16_t val);++/*!+Binds a uint32_t value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_uint32(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                             uint32_t val);++/*!+Binds a uint64_t value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_uint64(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                             uint64_t val);++/*!+Binds a float value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_float(duckdb_prepared_statement prepared_statement, idx_t param_idx, float val);++/*!+Binds a double value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_double(duckdb_prepared_statement prepared_statement, idx_t param_idx, double val);++/*!+Binds a duckdb_date value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_date(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                           duckdb_date val);++/*!+Binds a duckdb_time value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_time(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                           duckdb_time val);++/*!+Binds a duckdb_timestamp value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_timestamp(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                                duckdb_timestamp val);++/*!+Binds a duckdb_timestamp value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_timestamp_tz(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                                   duckdb_timestamp val);++/*!+Binds a duckdb_interval value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_interval(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                               duckdb_interval val);++/*!+Binds a null-terminated varchar value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_varchar(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                              const char *val);++/*!+Binds a varchar value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_varchar_length(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                                     const char *val, idx_t length);++/*!+Binds a blob value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_blob(duckdb_prepared_statement prepared_statement, idx_t param_idx,+                                           const void *data, idx_t length);++/*!+Binds a NULL value to the prepared statement at the specified index.+*/+DUCKDB_C_API duckdb_state duckdb_bind_null(duckdb_prepared_statement prepared_statement, idx_t param_idx);++//===--------------------------------------------------------------------===//+// Execute Prepared Statements+//===--------------------------------------------------------------------===//++/*!+Executes the prepared statement with the given bound parameters, and returns a materialized query result.++This method can be called multiple times for each prepared statement, and the parameters can be modified+between calls to this function.++Note that the result must be freed with `duckdb_destroy_result`.++* @param prepared_statement The prepared statement to execute.+* @param out_result The query result.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_execute_prepared(duckdb_prepared_statement prepared_statement,+                                                  duckdb_result *out_result);++#ifndef DUCKDB_API_NO_DEPRECATED+/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Executes the prepared statement with the given bound parameters, and returns an optionally-streaming query result.+To determine if the resulting query was in fact streamed, use `duckdb_result_is_streaming`++This method can be called multiple times for each prepared statement, and the parameters can be modified+between calls to this function.++Note that the result must be freed with `duckdb_destroy_result`.++* @param prepared_statement The prepared statement to execute.+* @param out_result The query result.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_execute_prepared_streaming(duckdb_prepared_statement prepared_statement,+                                                            duckdb_result *out_result);++#endif++//===--------------------------------------------------------------------===//+// Extract Statements+//===--------------------------------------------------------------------===//++// A query string can be extracted into multiple SQL statements. Each statement can be prepared and executed separately.+/*!+Extract all statements from a query.+Note that after calling `duckdb_extract_statements`, the extracted statements should always be destroyed using+`duckdb_destroy_extracted`, even if no statements were extracted.++If the extract fails, `duckdb_extract_statements_error` can be called to obtain the reason why the extract failed.++* @param connection The connection object+* @param query The SQL query to extract+* @param out_extracted_statements The resulting extracted statements object+* @return The number of extracted statements or 0 on failure.+*/+DUCKDB_C_API idx_t duckdb_extract_statements(duckdb_connection connection, const char *query,+                                             duckdb_extracted_statements *out_extracted_statements);++/*!+Prepare an extracted statement.+Note that after calling `duckdb_prepare_extracted_statement`, the prepared statement should always be destroyed using+`duckdb_destroy_prepare`, even if the prepare fails.++If the prepare fails, `duckdb_prepare_error` can be called to obtain the reason why the prepare failed.++* @param connection The connection object+* @param extracted_statements The extracted statements object+* @param index The index of the extracted statement to prepare+* @param out_prepared_statement The resulting prepared statement object+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_prepare_extracted_statement(duckdb_connection connection,+                                                             duckdb_extracted_statements extracted_statements,+                                                             idx_t index,+                                                             duckdb_prepared_statement *out_prepared_statement);++/*!+Returns the error message contained within the extracted statements.+The result of this function must not be freed. It will be cleaned up when `duckdb_destroy_extracted` is called.++* @param extracted_statements The extracted statements to fetch the error from.+* @return The error of the extracted statements.+*/+DUCKDB_C_API const char *duckdb_extract_statements_error(duckdb_extracted_statements extracted_statements);++/*!+De-allocates all memory allocated for the extracted statements.+* @param extracted_statements The extracted statements to destroy.+*/+DUCKDB_C_API void duckdb_destroy_extracted(duckdb_extracted_statements *extracted_statements);++//===--------------------------------------------------------------------===//+// Pending Result Interface+//===--------------------------------------------------------------------===//++/*!+Executes the prepared statement with the given bound parameters, and returns a pending result.+The pending result represents an intermediate structure for a query that is not yet fully executed.+The pending result can be used to incrementally execute a query, returning control to the client between tasks.++Note that after calling `duckdb_pending_prepared`, the pending result should always be destroyed using+`duckdb_destroy_pending`, even if this function returns DuckDBError.++* @param prepared_statement The prepared statement to execute.+* @param out_result The pending query result.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_pending_prepared(duckdb_prepared_statement prepared_statement,+                                                  duckdb_pending_result *out_result);++#ifndef DUCKDB_API_NO_DEPRECATED+/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Executes the prepared statement with the given bound parameters, and returns a pending result.+This pending result will create a streaming duckdb_result when executed.+The pending result represents an intermediate structure for a query that is not yet fully executed.++Note that after calling `duckdb_pending_prepared_streaming`, the pending result should always be destroyed using+`duckdb_destroy_pending`, even if this function returns DuckDBError.++* @param prepared_statement The prepared statement to execute.+* @param out_result The pending query result.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_pending_prepared_streaming(duckdb_prepared_statement prepared_statement,+                                                            duckdb_pending_result *out_result);++#endif++/*!+Closes the pending result and de-allocates all memory allocated for the result.++* @param pending_result The pending result to destroy.+*/+DUCKDB_C_API void duckdb_destroy_pending(duckdb_pending_result *pending_result);++/*!+Returns the error message contained within the pending result.++The result of this function must not be freed. It will be cleaned up when `duckdb_destroy_pending` is called.++* @param pending_result The pending result to fetch the error from.+* @return The error of the pending result.+*/+DUCKDB_C_API const char *duckdb_pending_error(duckdb_pending_result pending_result);++/*!+Executes a single task within the query, returning whether or not the query is ready.++If this returns DUCKDB_PENDING_RESULT_READY, the duckdb_execute_pending function can be called to obtain the result.+If this returns DUCKDB_PENDING_RESULT_NOT_READY, the duckdb_pending_execute_task function should be called again.+If this returns DUCKDB_PENDING_ERROR, an error occurred during execution.++The error message can be obtained by calling duckdb_pending_error on the pending_result.++* @param pending_result The pending result to execute a task within.+* @return The state of the pending result after the execution.+*/+DUCKDB_C_API duckdb_pending_state duckdb_pending_execute_task(duckdb_pending_result pending_result);++/*!+If this returns DUCKDB_PENDING_RESULT_READY, the duckdb_execute_pending function can be called to obtain the result.+If this returns DUCKDB_PENDING_RESULT_NOT_READY, the duckdb_pending_execute_check_state function should be called again.+If this returns DUCKDB_PENDING_ERROR, an error occurred during execution.++The error message can be obtained by calling duckdb_pending_error on the pending_result.++* @param pending_result The pending result.+* @return The state of the pending result.+*/+DUCKDB_C_API duckdb_pending_state duckdb_pending_execute_check_state(duckdb_pending_result pending_result);++/*!+Fully execute a pending query result, returning the final query result.++If duckdb_pending_execute_task has been called until DUCKDB_PENDING_RESULT_READY was returned, this will return fast.+Otherwise, all remaining tasks must be executed first.++Note that the result must be freed with `duckdb_destroy_result`.++* @param pending_result The pending result to execute.+* @param out_result The result object.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_execute_pending(duckdb_pending_result pending_result, duckdb_result *out_result);++/*!+Returns whether a duckdb_pending_state is finished executing. For example if `pending_state` is+DUCKDB_PENDING_RESULT_READY, this function will return true.++* @param pending_state The pending state on which to decide whether to finish execution.+* @return Boolean indicating pending execution should be considered finished.+*/+DUCKDB_C_API bool duckdb_pending_execution_is_finished(duckdb_pending_state pending_state);++//===--------------------------------------------------------------------===//+// Value Interface+//===--------------------------------------------------------------------===//++/*!+Destroys the value and de-allocates all memory allocated for that type.++* @param value The value to destroy.+*/+DUCKDB_C_API void duckdb_destroy_value(duckdb_value *value);++/*!+Creates a value from a null-terminated string++* @param text The null-terminated string+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_varchar(const char *text);++/*!+Creates a value from a string++* @param text The text+* @param length The length of the text+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_varchar_length(const char *text, idx_t length);++/*!+Creates a value from a boolean++* @param input The boolean value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_bool(bool input);++/*!+Creates a value from an int8_t (a tinyint)++* @param input The tinyint value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_int8(int8_t input);++/*!+Creates a value from a uint8_t (a utinyint)++* @param input The utinyint value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_uint8(uint8_t input);++/*!+Creates a value from an int16_t (a smallint)++* @param input The smallint value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_int16(int16_t input);++/*!+Creates a value from a uint16_t (a usmallint)++* @param input The usmallint value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_uint16(uint16_t input);++/*!+Creates a value from an int32_t (an integer)++* @param input The integer value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_int32(int32_t input);++/*!+Creates a value from a uint32_t (a uinteger)++* @param input The uinteger value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_uint32(uint32_t input);++/*!+Creates a value from a uint64_t (a ubigint)++* @param input The ubigint value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_uint64(uint64_t input);++/*!+Creates a value from an int64++* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_int64(int64_t val);++/*!+Creates a value from a hugeint++* @param input The hugeint value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_hugeint(duckdb_hugeint input);++/*!+Creates a value from a uhugeint++* @param input The uhugeint value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_uhugeint(duckdb_uhugeint input);++/*!+Creates a BIGNUM value from a duckdb_bignum++* @param input The duckdb_bignum value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_bignum(duckdb_bignum input);++/*!+Creates a DECIMAL value from a duckdb_decimal++* @param input The duckdb_decimal value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_decimal(duckdb_decimal input);++/*!+Creates a value from a float++* @param input The float value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_float(float input);++/*!+Creates a value from a double++* @param input The double value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_double(double input);++/*!+Creates a value from a date++* @param input The date value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_date(duckdb_date input);++/*!+Creates a value from a time++* @param input The time value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_time(duckdb_time input);++/*!+Creates a value from a time_ns++* @param input The time value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_time_ns(duckdb_time_ns input);++/*!+Creates a value from a time_tz.+Not to be confused with `duckdb_create_time_tz`, which creates a duckdb_time_tz_t.++* @param value The time_tz value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_time_tz_value(duckdb_time_tz value);++/*!+Creates a TIMESTAMP value from a duckdb_timestamp++* @param input The duckdb_timestamp value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_timestamp(duckdb_timestamp input);++/*!+Creates a TIMESTAMP_TZ value from a duckdb_timestamp++* @param input The duckdb_timestamp value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_timestamp_tz(duckdb_timestamp input);++/*!+Creates a TIMESTAMP_S value from a duckdb_timestamp_s++* @param input The duckdb_timestamp_s value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_timestamp_s(duckdb_timestamp_s input);++/*!+Creates a TIMESTAMP_MS value from a duckdb_timestamp_ms++* @param input The duckdb_timestamp_ms value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_timestamp_ms(duckdb_timestamp_ms input);++/*!+Creates a TIMESTAMP_NS value from a duckdb_timestamp_ns++* @param input The duckdb_timestamp_ns value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_timestamp_ns(duckdb_timestamp_ns input);++/*!+Creates a value from an interval++* @param input The interval value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_interval(duckdb_interval input);++/*!+Creates a value from a blob++* @param data The blob data+* @param length The length of the blob data+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_blob(const uint8_t *data, idx_t length);++/*!+Creates a BIT value from a duckdb_bit++* @param input The duckdb_bit value+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_bit(duckdb_bit input);++/*!+Creates a UUID value from a uhugeint++* @param input The duckdb_uhugeint containing the UUID+* @return The value. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_uuid(duckdb_uhugeint input);++/*!+Returns the boolean value of the given value.++* @param val A duckdb_value containing a boolean+* @return A boolean, or false if the value cannot be converted+*/+DUCKDB_C_API bool duckdb_get_bool(duckdb_value val);++/*!+Returns the int8_t value of the given value.++* @param val A duckdb_value containing a tinyint+* @return A int8_t, or MinValue<int8> if the value cannot be converted+*/+DUCKDB_C_API int8_t duckdb_get_int8(duckdb_value val);++/*!+Returns the uint8_t value of the given value.++* @param val A duckdb_value containing a utinyint+* @return A uint8_t, or MinValue<uint8> if the value cannot be converted+*/+DUCKDB_C_API uint8_t duckdb_get_uint8(duckdb_value val);++/*!+Returns the int16_t value of the given value.++* @param val A duckdb_value containing a smallint+* @return A int16_t, or MinValue<int16> if the value cannot be converted+*/+DUCKDB_C_API int16_t duckdb_get_int16(duckdb_value val);++/*!+Returns the uint16_t value of the given value.++* @param val A duckdb_value containing a usmallint+* @return A uint16_t, or MinValue<uint16> if the value cannot be converted+*/+DUCKDB_C_API uint16_t duckdb_get_uint16(duckdb_value val);++/*!+Returns the int32_t value of the given value.++* @param val A duckdb_value containing an integer+* @return A int32_t, or MinValue<int32> if the value cannot be converted+*/+DUCKDB_C_API int32_t duckdb_get_int32(duckdb_value val);++/*!+Returns the uint32_t value of the given value.++* @param val A duckdb_value containing a uinteger+* @return A uint32_t, or MinValue<uint32> if the value cannot be converted+*/+DUCKDB_C_API uint32_t duckdb_get_uint32(duckdb_value val);++/*!+Returns the int64_t value of the given value.++* @param val A duckdb_value containing a bigint+* @return A int64_t, or MinValue<int64> if the value cannot be converted+*/+DUCKDB_C_API int64_t duckdb_get_int64(duckdb_value val);++/*!+Returns the uint64_t value of the given value.++* @param val A duckdb_value containing a ubigint+* @return A uint64_t, or MinValue<uint64> if the value cannot be converted+*/+DUCKDB_C_API uint64_t duckdb_get_uint64(duckdb_value val);++/*!+Returns the hugeint value of the given value.++* @param val A duckdb_value containing a hugeint+* @return A duckdb_hugeint, or MinValue<hugeint> if the value cannot be converted+*/+DUCKDB_C_API duckdb_hugeint duckdb_get_hugeint(duckdb_value val);++/*!+Returns the uhugeint value of the given value.++* @param val A duckdb_value containing a uhugeint+* @return A duckdb_uhugeint, or MinValue<uhugeint> if the value cannot be converted+*/+DUCKDB_C_API duckdb_uhugeint duckdb_get_uhugeint(duckdb_value val);++/*!+Returns the duckdb_bignum value of the given value.+The `data` field must be destroyed with `duckdb_free`.++* @param val A duckdb_value containing a BIGNUM+* @return A duckdb_bignum. The `data` field must be destroyed with `duckdb_free`.+*/+DUCKDB_C_API duckdb_bignum duckdb_get_bignum(duckdb_value val);++/*!+Returns the duckdb_decimal value of the given value.++* @param val A duckdb_value containing a DECIMAL+* @return A duckdb_decimal, or MinValue<decimal> if the value cannot be converted+*/+DUCKDB_C_API duckdb_decimal duckdb_get_decimal(duckdb_value val);++/*!+Returns the float value of the given value.++* @param val A duckdb_value containing a float+* @return A float, or NAN if the value cannot be converted+*/+DUCKDB_C_API float duckdb_get_float(duckdb_value val);++/*!+Returns the double value of the given value.++* @param val A duckdb_value containing a double+* @return A double, or NAN if the value cannot be converted+*/+DUCKDB_C_API double duckdb_get_double(duckdb_value val);++/*!+Returns the date value of the given value.++* @param val A duckdb_value containing a date+* @return A duckdb_date, or MinValue<date> if the value cannot be converted+*/+DUCKDB_C_API duckdb_date duckdb_get_date(duckdb_value val);++/*!+Returns the time value of the given value.++* @param val A duckdb_value containing a time+* @return A duckdb_time, or MinValue<time> if the value cannot be converted+*/+DUCKDB_C_API duckdb_time duckdb_get_time(duckdb_value val);++/*!+Returns the time_ns value of the given value.++* @param val A duckdb_value containing a time_ns+* @return A duckdb_time_ns, or MinValue<time_ns> if the value cannot be converted+*/+DUCKDB_C_API duckdb_time_ns duckdb_get_time_ns(duckdb_value val);++/*!+Returns the time_tz value of the given value.++* @param val A duckdb_value containing a time_tz+* @return A duckdb_time_tz, or MinValue<time_tz> if the value cannot be converted+*/+DUCKDB_C_API duckdb_time_tz duckdb_get_time_tz(duckdb_value val);++/*!+Returns the TIMESTAMP value of the given value.++* @param val A duckdb_value containing a TIMESTAMP+* @return A duckdb_timestamp, or MinValue<timestamp> if the value cannot be converted+*/+DUCKDB_C_API duckdb_timestamp duckdb_get_timestamp(duckdb_value val);++/*!+Returns the TIMESTAMP_TZ value of the given value.++* @param val A duckdb_value containing a TIMESTAMP_TZ+* @return A duckdb_timestamp, or MinValue<timestamp_tz> if the value cannot be converted+*/+DUCKDB_C_API duckdb_timestamp duckdb_get_timestamp_tz(duckdb_value val);++/*!+Returns the duckdb_timestamp_s value of the given value.++* @param val A duckdb_value containing a TIMESTAMP_S+* @return A duckdb_timestamp_s, or MinValue<timestamp_s> if the value cannot be converted+*/+DUCKDB_C_API duckdb_timestamp_s duckdb_get_timestamp_s(duckdb_value val);++/*!+Returns the duckdb_timestamp_ms value of the given value.++* @param val A duckdb_value containing a TIMESTAMP_MS+* @return A duckdb_timestamp_ms, or MinValue<timestamp_ms> if the value cannot be converted+*/+DUCKDB_C_API duckdb_timestamp_ms duckdb_get_timestamp_ms(duckdb_value val);++/*!+Returns the duckdb_timestamp_ns value of the given value.++* @param val A duckdb_value containing a TIMESTAMP_NS+* @return A duckdb_timestamp_ns, or MinValue<timestamp_ns> if the value cannot be converted+*/+DUCKDB_C_API duckdb_timestamp_ns duckdb_get_timestamp_ns(duckdb_value val);++/*!+Returns the interval value of the given value.++* @param val A duckdb_value containing a interval+* @return A duckdb_interval, or MinValue<interval> if the value cannot be converted+*/+DUCKDB_C_API duckdb_interval duckdb_get_interval(duckdb_value val);++/*!+Returns the type of the given value. The type is valid as long as the value is not destroyed.+The type itself must not be destroyed.++* @param val A duckdb_value+* @return A duckdb_logical_type.+*/+DUCKDB_C_API duckdb_logical_type duckdb_get_value_type(duckdb_value val);++/*!+Returns the blob value of the given value.++* @param val A duckdb_value containing a blob+* @return A duckdb_blob+*/+DUCKDB_C_API duckdb_blob duckdb_get_blob(duckdb_value val);++/*!+Returns the duckdb_bit value of the given value.+The `data` field must be destroyed with `duckdb_free`.++* @param val A duckdb_value containing a BIT+* @return A duckdb_bit+*/+DUCKDB_C_API duckdb_bit duckdb_get_bit(duckdb_value val);++/*!+Returns a duckdb_uhugeint representing the UUID value of the given value.++* @param val A duckdb_value containing a UUID+* @return A duckdb_uhugeint representing the UUID value+*/+DUCKDB_C_API duckdb_uhugeint duckdb_get_uuid(duckdb_value val);++/*!+Obtains a string representation of the given value.+The result must be destroyed with `duckdb_free`.++* @param value The value+* @return The string value. This must be destroyed with `duckdb_free`.+*/+DUCKDB_C_API char *duckdb_get_varchar(duckdb_value value);++/*!+Creates a struct value from a type and an array of values. Must be destroyed with `duckdb_destroy_value`.++* @param type The type of the struct+* @param values The values for the struct fields+* @return The struct value, or nullptr, if any child type is `DUCKDB_TYPE_ANY` or `DUCKDB_TYPE_INVALID`.+*/+DUCKDB_C_API duckdb_value duckdb_create_struct_value(duckdb_logical_type type, duckdb_value *values);++/*!+Creates a list value from a child (element) type and an array of values of length `value_count`.+Must be destroyed with `duckdb_destroy_value`.++* @param type The type of the list+* @param values The values for the list+* @param value_count The number of values in the list+* @return The list value, or nullptr, if the child type is `DUCKDB_TYPE_ANY` or `DUCKDB_TYPE_INVALID`.+*/+DUCKDB_C_API duckdb_value duckdb_create_list_value(duckdb_logical_type type, duckdb_value *values, idx_t value_count);++/*!+Creates an array value from a child (element) type and an array of values of length `value_count`.+Must be destroyed with `duckdb_destroy_value`.++* @param type The type of the array+* @param values The values for the array+* @param value_count The number of values in the array+* @return The array value, or nullptr, if the child type is `DUCKDB_TYPE_ANY` or `DUCKDB_TYPE_INVALID`.+*/+DUCKDB_C_API duckdb_value duckdb_create_array_value(duckdb_logical_type type, duckdb_value *values, idx_t value_count);++/*!+Creates a map value from a map type and two arrays, one for the keys and one for the values, each of length+`entry_count`. Must be destroyed with `duckdb_destroy_value`.++* @param map_type The map type+* @param keys The keys of the map+* @param values The values of the map+* @param entry_count The number of entrys (key-value pairs) in the map+* @return The map value, or nullptr, if the parameters are invalid.+*/+DUCKDB_C_API duckdb_value duckdb_create_map_value(duckdb_logical_type map_type, duckdb_value *keys,+                                                  duckdb_value *values, idx_t entry_count);++/*!+Creates a union value from a union type, a tag index, and a value.+Must be destroyed with `duckdb_destroy_value`.++* @param union_type The union type+* @param tag_index The index of the tag of the union+* @param value The value of the union for that tag+* @return The union value, or nullptr, if the parameters are invalid.+*/+DUCKDB_C_API duckdb_value duckdb_create_union_value(duckdb_logical_type union_type, idx_t tag_index,+                                                    duckdb_value value);++/*!+Returns the number of elements in a MAP value.++* @param value The MAP value.+* @return The number of elements in the map.+*/+DUCKDB_C_API idx_t duckdb_get_map_size(duckdb_value value);++/*!+Returns the MAP key at index as a duckdb_value.++* @param value The MAP value.+* @param index The index of the key.+* @return The key as a duckdb_value.+*/+DUCKDB_C_API duckdb_value duckdb_get_map_key(duckdb_value value, idx_t index);++/*!+Returns the MAP value at index as a duckdb_value.++* @param value The MAP value.+* @param index The index of the value.+* @return The value as a duckdb_value.+*/+DUCKDB_C_API duckdb_value duckdb_get_map_value(duckdb_value value, idx_t index);++/*!+Returns whether the value's type is SQLNULL or not.++* @param value The value to check.+* @return True, if the value's type is SQLNULL, otherwise false.+*/+DUCKDB_C_API bool duckdb_is_null_value(duckdb_value value);++/*!+Creates a value of type SQLNULL.++* @return The duckdb_value representing SQLNULL. This must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_create_null_value();++/*!+Returns the number of elements in a LIST value.++* @param value The LIST value.+* @return The number of elements in the list.+*/+DUCKDB_C_API idx_t duckdb_get_list_size(duckdb_value value);++/*!+Returns the LIST child at index as a duckdb_value.++* @param value The LIST value.+* @param index The index of the child.+* @return The child as a duckdb_value.+*/+DUCKDB_C_API duckdb_value duckdb_get_list_child(duckdb_value value, idx_t index);++/*!+Creates an enum value from a type and a value. Must be destroyed with `duckdb_destroy_value`.++* @param type The type of the enum+* @param value The value for the enum+* @return The enum value, or nullptr.+*/+DUCKDB_C_API duckdb_value duckdb_create_enum_value(duckdb_logical_type type, uint64_t value);++/*!+Returns the enum value of the given value.++* @param value A duckdb_value containing an enum+* @return A uint64_t, or MinValue<uint64> if the value cannot be converted+*/+DUCKDB_C_API uint64_t duckdb_get_enum_value(duckdb_value value);++/*!+Returns the STRUCT child at index as a duckdb_value.++* @param value The STRUCT value.+* @param index The index of the child.+* @return The child as a duckdb_value.+*/+DUCKDB_C_API duckdb_value duckdb_get_struct_child(duckdb_value value, idx_t index);++/*!+Returns the SQL string representation of the given value.++* @param value A duckdb_value.+* @return The SQL string representation as a null-terminated string. The result must be freed with `duckdb_free`.+*/+DUCKDB_C_API char *duckdb_value_to_string(duckdb_value value);++//===--------------------------------------------------------------------===//+// Logical Type Interface+//===--------------------------------------------------------------------===//++/*!+Creates a `duckdb_logical_type` from a primitive type.+The resulting logical type must be destroyed with `duckdb_destroy_logical_type`.++Returns an invalid logical type, if type is: `DUCKDB_TYPE_INVALID`, `DUCKDB_TYPE_DECIMAL`, `DUCKDB_TYPE_ENUM`,+`DUCKDB_TYPE_LIST`, `DUCKDB_TYPE_STRUCT`, `DUCKDB_TYPE_MAP`, `DUCKDB_TYPE_ARRAY`, or `DUCKDB_TYPE_UNION`.++* @param type The primitive type to create.+* @return The logical type.+*/+DUCKDB_C_API duckdb_logical_type duckdb_create_logical_type(duckdb_type type);++/*!+Returns the alias of a duckdb_logical_type, if set, else `nullptr`.+The result must be destroyed with `duckdb_free`.++* @param type The logical type+* @return The alias or `nullptr`+*/+DUCKDB_C_API char *duckdb_logical_type_get_alias(duckdb_logical_type type);++/*!+Sets the alias of a duckdb_logical_type.++* @param type The logical type+* @param alias The alias to set+*/+DUCKDB_C_API void duckdb_logical_type_set_alias(duckdb_logical_type type, const char *alias);++/*!+Creates a LIST type from its child type.+The return type must be destroyed with `duckdb_destroy_logical_type`.++* @param type The child type of the list+* @return The logical type.+*/+DUCKDB_C_API duckdb_logical_type duckdb_create_list_type(duckdb_logical_type type);++/*!+Creates an ARRAY type from its child type.+The return type must be destroyed with `duckdb_destroy_logical_type`.++* @param type The child type of the array.+* @param array_size The number of elements in the array.+* @return The logical type.+*/+DUCKDB_C_API duckdb_logical_type duckdb_create_array_type(duckdb_logical_type type, idx_t array_size);++/*!+Creates a MAP type from its key type and value type.+The return type must be destroyed with `duckdb_destroy_logical_type`.++* @param key_type The map's key type.+* @param value_type The map's value type.+* @return The logical type.+*/+DUCKDB_C_API duckdb_logical_type duckdb_create_map_type(duckdb_logical_type key_type, duckdb_logical_type value_type);++/*!+Creates a UNION type from the passed arrays.+The return type must be destroyed with `duckdb_destroy_logical_type`.++* @param member_types The array of union member types.+* @param member_names The union member names.+* @param member_count The number of union members.+* @return The logical type.+*/+DUCKDB_C_API duckdb_logical_type duckdb_create_union_type(duckdb_logical_type *member_types, const char **member_names,+                                                          idx_t member_count);++/*!+Creates a STRUCT type based on the member types and names.+The resulting type must be destroyed with `duckdb_destroy_logical_type`.++* @param member_types The array of types of the struct members.+* @param member_names The array of names of the struct members.+* @param member_count The number of members of the struct.+* @return The logical type.+*/+DUCKDB_C_API duckdb_logical_type duckdb_create_struct_type(duckdb_logical_type *member_types, const char **member_names,+                                                           idx_t member_count);++/*!+Creates an ENUM type from the passed member name array.+The resulting type should be destroyed with `duckdb_destroy_logical_type`.++* @param member_names The array of names that the enum should consist of.+* @param member_count The number of elements that were specified in the array.+* @return The logical type.+*/+DUCKDB_C_API duckdb_logical_type duckdb_create_enum_type(const char **member_names, idx_t member_count);++/*!+Creates a DECIMAL type with the specified width and scale.+The resulting type should be destroyed with `duckdb_destroy_logical_type`.++* @param width The width of the decimal type+* @param scale The scale of the decimal type+* @return The logical type.+*/+DUCKDB_C_API duckdb_logical_type duckdb_create_decimal_type(uint8_t width, uint8_t scale);++/*!+Retrieves the enum `duckdb_type` of a `duckdb_logical_type`.++* @param type The logical type.+* @return The `duckdb_type` id.+*/+DUCKDB_C_API duckdb_type duckdb_get_type_id(duckdb_logical_type type);++/*!+Retrieves the width of a decimal type.++* @param type The logical type object+* @return The width of the decimal type+*/+DUCKDB_C_API uint8_t duckdb_decimal_width(duckdb_logical_type type);++/*!+Retrieves the scale of a decimal type.++* @param type The logical type object+* @return The scale of the decimal type+*/+DUCKDB_C_API uint8_t duckdb_decimal_scale(duckdb_logical_type type);++/*!+Retrieves the internal storage type of a decimal type.++* @param type The logical type object+* @return The internal type of the decimal type+*/+DUCKDB_C_API duckdb_type duckdb_decimal_internal_type(duckdb_logical_type type);++/*!+Retrieves the internal storage type of an enum type.++* @param type The logical type object+* @return The internal type of the enum type+*/+DUCKDB_C_API duckdb_type duckdb_enum_internal_type(duckdb_logical_type type);++/*!+Retrieves the dictionary size of the enum type.++* @param type The logical type object+* @return The dictionary size of the enum type+*/+DUCKDB_C_API uint32_t duckdb_enum_dictionary_size(duckdb_logical_type type);++/*!+Retrieves the dictionary value at the specified position from the enum.++The result must be freed with `duckdb_free`.++* @param type The logical type object+* @param index The index in the dictionary+* @return The string value of the enum type. Must be freed with `duckdb_free`.+*/+DUCKDB_C_API char *duckdb_enum_dictionary_value(duckdb_logical_type type, idx_t index);++/*!+Retrieves the child type of the given LIST type. Also accepts MAP types.+The result must be freed with `duckdb_destroy_logical_type`.++* @param type The logical type, either LIST or MAP.+* @return The child type of the LIST or MAP type.+*/+DUCKDB_C_API duckdb_logical_type duckdb_list_type_child_type(duckdb_logical_type type);++/*!+Retrieves the child type of the given ARRAY type.++The result must be freed with `duckdb_destroy_logical_type`.++* @param type The logical type. Must be ARRAY.+* @return The child type of the ARRAY type.+*/+DUCKDB_C_API duckdb_logical_type duckdb_array_type_child_type(duckdb_logical_type type);++/*!+Retrieves the array size of the given array type.++* @param type The logical type object+* @return The fixed number of elements the values of this array type can store.+*/+DUCKDB_C_API idx_t duckdb_array_type_array_size(duckdb_logical_type type);++/*!+Retrieves the key type of the given map type.++The result must be freed with `duckdb_destroy_logical_type`.++* @param type The logical type object+* @return The key type of the map type. Must be destroyed with `duckdb_destroy_logical_type`.+*/+DUCKDB_C_API duckdb_logical_type duckdb_map_type_key_type(duckdb_logical_type type);++/*!+Retrieves the value type of the given map type.++The result must be freed with `duckdb_destroy_logical_type`.++* @param type The logical type object+* @return The value type of the map type. Must be destroyed with `duckdb_destroy_logical_type`.+*/+DUCKDB_C_API duckdb_logical_type duckdb_map_type_value_type(duckdb_logical_type type);++/*!+Returns the number of children of a struct type.++* @param type The logical type object+* @return The number of children of a struct type.+*/+DUCKDB_C_API idx_t duckdb_struct_type_child_count(duckdb_logical_type type);++/*!+Retrieves the name of the struct child.++The result must be freed with `duckdb_free`.++* @param type The logical type object+* @param index The child index+* @return The name of the struct type. Must be freed with `duckdb_free`.+*/+DUCKDB_C_API char *duckdb_struct_type_child_name(duckdb_logical_type type, idx_t index);++/*!+Retrieves the child type of the given struct type at the specified index.++The result must be freed with `duckdb_destroy_logical_type`.++* @param type The logical type object+* @param index The child index+* @return The child type of the struct type. Must be destroyed with `duckdb_destroy_logical_type`.+*/+DUCKDB_C_API duckdb_logical_type duckdb_struct_type_child_type(duckdb_logical_type type, idx_t index);++/*!+Returns the number of members that the union type has.++* @param type The logical type (union) object+* @return The number of members of a union type.+*/+DUCKDB_C_API idx_t duckdb_union_type_member_count(duckdb_logical_type type);++/*!+Retrieves the name of the union member.++The result must be freed with `duckdb_free`.++* @param type The logical type object+* @param index The child index+* @return The name of the union member. Must be freed with `duckdb_free`.+*/+DUCKDB_C_API char *duckdb_union_type_member_name(duckdb_logical_type type, idx_t index);++/*!+Retrieves the child type of the given union member at the specified index.++The result must be freed with `duckdb_destroy_logical_type`.++* @param type The logical type object+* @param index The child index+* @return The child type of the union member. Must be destroyed with `duckdb_destroy_logical_type`.+*/+DUCKDB_C_API duckdb_logical_type duckdb_union_type_member_type(duckdb_logical_type type, idx_t index);++/*!+Destroys the logical type and de-allocates all memory allocated for that type.++* @param type The logical type to destroy.+*/+DUCKDB_C_API void duckdb_destroy_logical_type(duckdb_logical_type *type);++/*!+Registers a custom type within the given connection.+The type must have an alias++* @param con The connection to use+* @param type The custom type to register+* @return Whether or not the registration was successful.+*/+DUCKDB_C_API duckdb_state duckdb_register_logical_type(duckdb_connection con, duckdb_logical_type type,+                                                       duckdb_create_type_info info);++//===--------------------------------------------------------------------===//+// Data Chunk Interface+//===--------------------------------------------------------------------===//++/*!+Creates an empty data chunk with the specified column types.+The result must be destroyed with `duckdb_destroy_data_chunk`.++* @param types An array of column types. Column types can not contain ANY and INVALID types.+* @param column_count The number of columns.+* @return The data chunk.+*/+DUCKDB_C_API duckdb_data_chunk duckdb_create_data_chunk(duckdb_logical_type *types, idx_t column_count);++/*!+Destroys the data chunk and de-allocates all memory allocated for that chunk.++* @param chunk The data chunk to destroy.+*/+DUCKDB_C_API void duckdb_destroy_data_chunk(duckdb_data_chunk *chunk);++/*!+Resets a data chunk, clearing the validity masks and setting the cardinality of the data chunk to 0.+After calling this method, you must call `duckdb_vector_get_validity` and `duckdb_vector_get_data` to obtain current+data and validity pointers++* @param chunk The data chunk to reset.+*/+DUCKDB_C_API void duckdb_data_chunk_reset(duckdb_data_chunk chunk);++/*!+Retrieves the number of columns in a data chunk.++* @param chunk The data chunk to get the data from+* @return The number of columns in the data chunk+*/+DUCKDB_C_API idx_t duckdb_data_chunk_get_column_count(duckdb_data_chunk chunk);++/*!+Retrieves the vector at the specified column index in the data chunk.++The pointer to the vector is valid for as long as the chunk is alive.+It does NOT need to be destroyed.++* @param chunk The data chunk to get the data from+* @return The vector+*/+DUCKDB_C_API duckdb_vector duckdb_data_chunk_get_vector(duckdb_data_chunk chunk, idx_t col_idx);++/*!+Retrieves the current number of tuples in a data chunk.++* @param chunk The data chunk to get the data from+* @return The number of tuples in the data chunk+*/+DUCKDB_C_API idx_t duckdb_data_chunk_get_size(duckdb_data_chunk chunk);++/*!+Sets the current number of tuples in a data chunk.++* @param chunk The data chunk to set the size in+* @param size The number of tuples in the data chunk+*/+DUCKDB_C_API void duckdb_data_chunk_set_size(duckdb_data_chunk chunk, idx_t size);++//===--------------------------------------------------------------------===//+// Vector Interface+//===--------------------------------------------------------------------===//++/*!+Creates a flat vector. Must be destroyed with `duckdb_destroy_vector`.++* @param type The logical type of the vector.+* @param capacity The capacity of the vector.+* @return The vector.+*/+DUCKDB_C_API duckdb_vector duckdb_create_vector(duckdb_logical_type type, idx_t capacity);++/*!+Destroys the vector and de-allocates its memory.++* @param vector A pointer to the vector.+*/+DUCKDB_C_API void duckdb_destroy_vector(duckdb_vector *vector);++/*!+Retrieves the column type of the specified vector.++The result must be destroyed with `duckdb_destroy_logical_type`.++* @param vector The vector get the data from+* @return The type of the vector+*/+DUCKDB_C_API duckdb_logical_type duckdb_vector_get_column_type(duckdb_vector vector);++/*!+Retrieves the data pointer of the vector.++The data pointer can be used to read or write values from the vector.+How to read or write values depends on the type of the vector.++* @param vector The vector to get the data from+* @return The data pointer+*/+DUCKDB_C_API void *duckdb_vector_get_data(duckdb_vector vector);++/*!+Retrieves the validity mask pointer of the specified vector.++If all values are valid, this function MIGHT return NULL!++The validity mask is a bitset that signifies null-ness within the data chunk.+It is a series of uint64_t values, where each uint64_t value contains validity for 64 tuples.+The bit is set to 1 if the value is valid (i.e. not NULL) or 0 if the value is invalid (i.e. NULL).++Validity of a specific value can be obtained like this:++idx_t entry_idx = row_idx / 64;+idx_t idx_in_entry = row_idx % 64;+bool is_valid = validity_mask[entry_idx] & (1 << idx_in_entry);++Alternatively, the (slower) duckdb_validity_row_is_valid function can be used.++* @param vector The vector to get the data from+* @return The pointer to the validity mask, or NULL if no validity mask is present+*/+DUCKDB_C_API uint64_t *duckdb_vector_get_validity(duckdb_vector vector);++/*!+Ensures the validity mask is writable by allocating it.++After this function is called, `duckdb_vector_get_validity` will ALWAYS return non-NULL.+This allows NULL values to be written to the vector, regardless of whether a validity mask was present before.++* @param vector The vector to alter+*/+DUCKDB_C_API void duckdb_vector_ensure_validity_writable(duckdb_vector vector);++/*!+Assigns a string element in the vector at the specified location.++* @param vector The vector to alter+* @param index The row position in the vector to assign the string to+* @param str The null-terminated string+*/+DUCKDB_C_API void duckdb_vector_assign_string_element(duckdb_vector vector, idx_t index, const char *str);++/*!+Assigns a string element in the vector at the specified location. You may also use this function to assign BLOBs.++* @param vector The vector to alter+* @param index The row position in the vector to assign the string to+* @param str The string+* @param str_len The length of the string (in bytes)+*/+DUCKDB_C_API void duckdb_vector_assign_string_element_len(duckdb_vector vector, idx_t index, const char *str,+                                                          idx_t str_len);++/*!+Retrieves the child vector of a list vector.++The resulting vector is valid as long as the parent vector is valid.++* @param vector The vector+* @return The child vector+*/+DUCKDB_C_API duckdb_vector duckdb_list_vector_get_child(duckdb_vector vector);++/*!+Returns the size of the child vector of the list.++* @param vector The vector+* @return The size of the child list+*/+DUCKDB_C_API idx_t duckdb_list_vector_get_size(duckdb_vector vector);++/*!+Sets the total size of the underlying child-vector of a list vector.++* @param vector The list vector.+* @param size The size of the child list.+* @return The duckdb state. Returns DuckDBError if the vector is nullptr.+*/+DUCKDB_C_API duckdb_state duckdb_list_vector_set_size(duckdb_vector vector, idx_t size);++/*!+Sets the total capacity of the underlying child-vector of a list.++After calling this method, you must call `duckdb_vector_get_validity` and `duckdb_vector_get_data` to obtain current+data and validity pointers++* @param vector The list vector.+* @param required_capacity the total capacity to reserve.+* @return The duckdb state. Returns DuckDBError if the vector is nullptr.+*/+DUCKDB_C_API duckdb_state duckdb_list_vector_reserve(duckdb_vector vector, idx_t required_capacity);++/*!+Retrieves the child vector of a struct vector.+The resulting vector is valid as long as the parent vector is valid.++* @param vector The vector+* @param index The child index+* @return The child vector+*/+DUCKDB_C_API duckdb_vector duckdb_struct_vector_get_child(duckdb_vector vector, idx_t index);++/*!+Retrieves the child vector of an array vector.+The resulting vector is valid as long as the parent vector is valid.+The resulting vector has the size of the parent vector multiplied by the array size.++* @param vector The vector+* @return The child vector+*/+DUCKDB_C_API duckdb_vector duckdb_array_vector_get_child(duckdb_vector vector);++/*!+Slice a vector with a selection vector.+The length of the selection vector must be less than or equal to the length of the vector.+Turns the vector into a dictionary vector.++* @param vector The vector to slice.+* @param sel The selection vector.+* @param len The length of the selection vector.+*/+DUCKDB_C_API void duckdb_slice_vector(duckdb_vector vector, duckdb_selection_vector sel, idx_t len);++/*!+Copy the src vector to the dst with a selection vector that identifies which indices to copy.++* @param src The vector to copy from.+* @param dst The vector to copy to.+* @param sel The selection vector. The length of the selection vector should not be more than the length of the src+vector+* @param src_count The number of entries from selection vector to copy. Think of this as the effective length of the+selection vector starting from index 0+* @param src_offset The offset in the selection vector to copy from (important: actual number of items copied =+src_count - src_offset).+* @param dst_offset The offset in the dst vector to start copying to.+*/+DUCKDB_C_API void duckdb_vector_copy_sel(duckdb_vector src, duckdb_vector dst, duckdb_selection_vector sel,+                                         idx_t src_count, idx_t src_offset, idx_t dst_offset);++/*!+Copies the value from `value` to `vector`.++* @param vector The receiving vector.+* @param value The value to copy into the vector.+*/+DUCKDB_C_API void duckdb_vector_reference_value(duckdb_vector vector, duckdb_value value);++/*!+Changes `to_vector` to reference `from_vector. After, the vectors share ownership of the data.++* @param to_vector The receiving vector.+* @param from_vector The vector to reference.+*/+DUCKDB_C_API void duckdb_vector_reference_vector(duckdb_vector to_vector, duckdb_vector from_vector);++//===--------------------------------------------------------------------===//+// Validity Mask Functions+//===--------------------------------------------------------------------===//++/*!+Returns whether or not a row is valid (i.e. not NULL) in the given validity mask.++* @param validity The validity mask, as obtained through `duckdb_vector_get_validity`+* @param row The row index+* @return true if the row is valid, false otherwise+*/+DUCKDB_C_API bool duckdb_validity_row_is_valid(uint64_t *validity, idx_t row);++/*!+In a validity mask, sets a specific row to either valid or invalid.++Note that `duckdb_vector_ensure_validity_writable` should be called before calling `duckdb_vector_get_validity`,+to ensure that there is a validity mask to write to.++* @param validity The validity mask, as obtained through `duckdb_vector_get_validity`.+* @param row The row index+* @param valid Whether or not to set the row to valid, or invalid+*/+DUCKDB_C_API void duckdb_validity_set_row_validity(uint64_t *validity, idx_t row, bool valid);++/*!+In a validity mask, sets a specific row to invalid.++Equivalent to `duckdb_validity_set_row_validity` with valid set to false.++* @param validity The validity mask+* @param row The row index+*/+DUCKDB_C_API void duckdb_validity_set_row_invalid(uint64_t *validity, idx_t row);++/*!+In a validity mask, sets a specific row to valid.++Equivalent to `duckdb_validity_set_row_validity` with valid set to true.++* @param validity The validity mask+* @param row The row index+*/+DUCKDB_C_API void duckdb_validity_set_row_valid(uint64_t *validity, idx_t row);++//===--------------------------------------------------------------------===//+// Scalar Functions+//===--------------------------------------------------------------------===//++/*!+Creates a new empty scalar function.++The return value must be destroyed with `duckdb_destroy_scalar_function`.++* @return The scalar function object.+*/+DUCKDB_C_API duckdb_scalar_function duckdb_create_scalar_function();++/*!+Destroys the given scalar function object.++* @param scalar_function The scalar function to destroy+*/+DUCKDB_C_API void duckdb_destroy_scalar_function(duckdb_scalar_function *scalar_function);++/*!+Sets the name of the given scalar function.++* @param scalar_function The scalar function+* @param name The name of the scalar function+*/+DUCKDB_C_API void duckdb_scalar_function_set_name(duckdb_scalar_function scalar_function, const char *name);++/*!+Sets the parameters of the given scalar function to varargs. Does not require adding parameters with+duckdb_scalar_function_add_parameter.++* @param scalar_function The scalar function.+* @param type The type of the arguments.+* @return The parameter type. Cannot contain INVALID.+*/+DUCKDB_C_API void duckdb_scalar_function_set_varargs(duckdb_scalar_function scalar_function, duckdb_logical_type type);++/*!+Sets the scalar function's null-handling behavior to special.++* @param scalar_function The scalar function.+*/+DUCKDB_C_API void duckdb_scalar_function_set_special_handling(duckdb_scalar_function scalar_function);++/*!+Sets the Function Stability of the scalar function to VOLATILE, indicating the function should be re-run for every row.+This limits optimization that can be performed for the function.++* @param scalar_function The scalar function.+*/+DUCKDB_C_API void duckdb_scalar_function_set_volatile(duckdb_scalar_function scalar_function);++/*!+Adds a parameter to the scalar function.++* @param scalar_function The scalar function.+* @param type The parameter type. Cannot contain INVALID.+*/+DUCKDB_C_API void duckdb_scalar_function_add_parameter(duckdb_scalar_function scalar_function,+                                                       duckdb_logical_type type);++/*!+Sets the return type of the scalar function.++* @param scalar_function The scalar function+* @param type Cannot contain INVALID or ANY.+*/+DUCKDB_C_API void duckdb_scalar_function_set_return_type(duckdb_scalar_function scalar_function,+                                                         duckdb_logical_type type);++/*!+Assigns extra information to the scalar function that can be fetched during binding, etc.++* @param scalar_function The scalar function+* @param extra_info The extra information+* @param destroy The callback that will be called to destroy the extra information (if any)+*/+DUCKDB_C_API void duckdb_scalar_function_set_extra_info(duckdb_scalar_function scalar_function, void *extra_info,+                                                        duckdb_delete_callback_t destroy);++/*!+Sets the (optional) bind function of the scalar function.++* @param scalar_function The scalar function.+* @param bind The bind function.+*/+DUCKDB_C_API void duckdb_scalar_function_set_bind(duckdb_scalar_function scalar_function,+                                                  duckdb_scalar_function_bind_t bind);++/*!+Sets the user-provided bind data in the bind object of the scalar function.+The bind data object can be retrieved again during execution.+In most case, you also need to set the copy-callback of your bind data via duckdb_scalar_function_set_bind_data_copy.++* @param info The bind info of the scalar function.+* @param bind_data The bind data object.+* @param destroy The callback to destroy the bind data (if any).+*/+DUCKDB_C_API void duckdb_scalar_function_set_bind_data(duckdb_bind_info info, void *bind_data,+                                                       duckdb_delete_callback_t destroy);++/*!+Sets the copy-callback for the user-provided bind data in the bind object of the scalar function.++* @param info The bind info of the scalar function.+* @param copy The callback to copy the bind data (if any).+*/+DUCKDB_C_API void duckdb_scalar_function_set_bind_data_copy(duckdb_bind_info info, duckdb_copy_callback_t copy);++/*!+Report that an error has occurred while calling bind on a scalar function.++* @param info The bind info object.+* @param error The error message.+*/+DUCKDB_C_API void duckdb_scalar_function_bind_set_error(duckdb_bind_info info, const char *error);++/*!+Sets the main function of the scalar function.++* @param scalar_function The scalar function+* @param function The function+*/+DUCKDB_C_API void duckdb_scalar_function_set_function(duckdb_scalar_function scalar_function,+                                                      duckdb_scalar_function_t function);++/*!+Register the scalar function object within the given connection.++The function requires at least a name, a function and a return type.++If the function is incomplete or a function with this name already exists DuckDBError is returned.++* @param con The connection to register it in.+* @param scalar_function The function pointer+* @return Whether or not the registration was successful.+*/+DUCKDB_C_API duckdb_state duckdb_register_scalar_function(duckdb_connection con,+                                                          duckdb_scalar_function scalar_function);++/*!+Retrieves the extra info of the function as set in `duckdb_scalar_function_set_extra_info`.++* @param info The info object.+* @return The extra info.+*/+DUCKDB_C_API void *duckdb_scalar_function_get_extra_info(duckdb_function_info info);++/*!+Retrieves the extra info of the function as set in the bind info.++* @param info The info object.+* @return The extra info.+*/+DUCKDB_C_API void *duckdb_scalar_function_bind_get_extra_info(duckdb_bind_info info);++/*!+Gets the scalar function's bind data set by `duckdb_scalar_function_set_bind_data`.+Note that the bind data is read-only.++* @param info The function info.+* @return The bind data object.+*/+DUCKDB_C_API void *duckdb_scalar_function_get_bind_data(duckdb_function_info info);++/*!+Retrieves the client context of the bind info of a scalar function.++* @param info The bind info object of the scalar function.+* @param out_context The client context of the bind info. Must be destroyed with `duckdb_destroy_client_context`.+*/+DUCKDB_C_API void duckdb_scalar_function_get_client_context(duckdb_bind_info info, duckdb_client_context *out_context);++/*!+Report that an error has occurred while executing the scalar function.++* @param info The info object.+* @param error The error message+*/+DUCKDB_C_API void duckdb_scalar_function_set_error(duckdb_function_info info, const char *error);++/*!+Creates a new empty scalar function set.++The return value must be destroyed with `duckdb_destroy_scalar_function_set`.++* @return The scalar function set object.+*/+DUCKDB_C_API duckdb_scalar_function_set duckdb_create_scalar_function_set(const char *name);++/*!+Destroys the given scalar function set object.++*/+DUCKDB_C_API void duckdb_destroy_scalar_function_set(duckdb_scalar_function_set *scalar_function_set);++/*!+Adds the scalar function as a new overload to the scalar function set.++Returns DuckDBError if the function could not be added, for example if the overload already exists.++* @param set The scalar function set+* @param function The function to add+*/+DUCKDB_C_API duckdb_state duckdb_add_scalar_function_to_set(duckdb_scalar_function_set set,+                                                            duckdb_scalar_function function);++/*!+Register the scalar function set within the given connection.++The set requires at least a single valid overload.++If the set is incomplete or a function with this name already exists DuckDBError is returned.++* @param con The connection to register it in.+* @param set The function set to register+* @return Whether or not the registration was successful.+*/+DUCKDB_C_API duckdb_state duckdb_register_scalar_function_set(duckdb_connection con, duckdb_scalar_function_set set);++/*!+Returns the number of input arguments of the scalar function.++* @param info The bind info.+* @return The number of input arguments.+*/+DUCKDB_C_API idx_t duckdb_scalar_function_bind_get_argument_count(duckdb_bind_info info);++/*!+Returns the input argument at index of the scalar function.++* @param info The bind info.+* @param index The argument index.+* @return The input argument at index. Must be destroyed with `duckdb_destroy_expression`.+*/+DUCKDB_C_API duckdb_expression duckdb_scalar_function_bind_get_argument(duckdb_bind_info info, idx_t index);++//===--------------------------------------------------------------------===//+// Selection Vector Interface+//===--------------------------------------------------------------------===//++/*!+Creates a new selection vector of size `size`.+Must be destroyed with `duckdb_destroy_selection_vector`.++* @param size The size of the selection vector.+* @return The selection vector.+*/+DUCKDB_C_API duckdb_selection_vector duckdb_create_selection_vector(idx_t size);++/*!+Destroys the selection vector and de-allocates its memory.++* @param sel The selection vector.+*/+DUCKDB_C_API void duckdb_destroy_selection_vector(duckdb_selection_vector sel);++/*!+Access the data pointer of a selection vector.++* @param sel The selection vector.+* @return The data pointer.+*/+DUCKDB_C_API sel_t *duckdb_selection_vector_get_data_ptr(duckdb_selection_vector sel);++//===--------------------------------------------------------------------===//+// Aggregate Functions+//===--------------------------------------------------------------------===//++/*!+Creates a new empty aggregate function.++The return value should be destroyed with `duckdb_destroy_aggregate_function`.++* @return The aggregate function object.+*/+DUCKDB_C_API duckdb_aggregate_function duckdb_create_aggregate_function();++/*!+Destroys the given aggregate function object.++*/+DUCKDB_C_API void duckdb_destroy_aggregate_function(duckdb_aggregate_function *aggregate_function);++/*!+Sets the name of the given aggregate function.++* @param aggregate_function The aggregate function+* @param name The name of the aggregate function+*/+DUCKDB_C_API void duckdb_aggregate_function_set_name(duckdb_aggregate_function aggregate_function, const char *name);++/*!+Adds a parameter to the aggregate function.++* @param aggregate_function The aggregate function.+* @param type The parameter type. Cannot contain INVALID.+*/+DUCKDB_C_API void duckdb_aggregate_function_add_parameter(duckdb_aggregate_function aggregate_function,+                                                          duckdb_logical_type type);++/*!+Sets the return type of the aggregate function.++* @param aggregate_function The aggregate function.+* @param type The return type. Cannot contain INVALID or ANY.+*/+DUCKDB_C_API void duckdb_aggregate_function_set_return_type(duckdb_aggregate_function aggregate_function,+                                                            duckdb_logical_type type);++/*!+Sets the main functions of the aggregate function.++* @param aggregate_function The aggregate function+* @param state_size state size+* @param state_init state init function+* @param update update states+* @param combine combine states+* @param finalize finalize states+*/+DUCKDB_C_API void duckdb_aggregate_function_set_functions(duckdb_aggregate_function aggregate_function,+                                                          duckdb_aggregate_state_size state_size,+                                                          duckdb_aggregate_init_t state_init,+                                                          duckdb_aggregate_update_t update,+                                                          duckdb_aggregate_combine_t combine,+                                                          duckdb_aggregate_finalize_t finalize);++/*!+Sets the state destructor callback of the aggregate function (optional)++* @param aggregate_function The aggregate function+* @param destroy state destroy callback+*/+DUCKDB_C_API void duckdb_aggregate_function_set_destructor(duckdb_aggregate_function aggregate_function,+                                                           duckdb_aggregate_destroy_t destroy);++/*!+Register the aggregate function object within the given connection.++The function requires at least a name, functions and a return type.++If the function is incomplete or a function with this name already exists DuckDBError is returned.++* @param con The connection to register it in.+* @return Whether or not the registration was successful.+*/+DUCKDB_C_API duckdb_state duckdb_register_aggregate_function(duckdb_connection con,+                                                             duckdb_aggregate_function aggregate_function);++/*!+Sets the NULL handling of the aggregate function to SPECIAL_HANDLING.++* @param aggregate_function The aggregate function+*/+DUCKDB_C_API void duckdb_aggregate_function_set_special_handling(duckdb_aggregate_function aggregate_function);++/*!+Assigns extra information to the scalar function that can be fetched during binding, etc.++* @param aggregate_function The aggregate function+* @param extra_info The extra information+* @param destroy The callback that will be called to destroy the extra information (if any)+*/+DUCKDB_C_API void duckdb_aggregate_function_set_extra_info(duckdb_aggregate_function aggregate_function,+                                                           void *extra_info, duckdb_delete_callback_t destroy);++/*!+Retrieves the extra info of the function as set in `duckdb_aggregate_function_set_extra_info`.++* @param info The info object+* @return The extra info+*/+DUCKDB_C_API void *duckdb_aggregate_function_get_extra_info(duckdb_function_info info);++/*!+Report that an error has occurred while executing the aggregate function.++* @param info The info object+* @param error The error message+*/+DUCKDB_C_API void duckdb_aggregate_function_set_error(duckdb_function_info info, const char *error);++/*!+Creates a new empty aggregate function set.++The return value should be destroyed with `duckdb_destroy_aggregate_function_set`.++* @return The aggregate function set object.+*/+DUCKDB_C_API duckdb_aggregate_function_set duckdb_create_aggregate_function_set(const char *name);++/*!+Destroys the given aggregate function set object.++*/+DUCKDB_C_API void duckdb_destroy_aggregate_function_set(duckdb_aggregate_function_set *aggregate_function_set);++/*!+Adds the aggregate function as a new overload to the aggregate function set.++Returns DuckDBError if the function could not be added, for example if the overload already exists.++* @param set The aggregate function set+* @param function The function to add+*/+DUCKDB_C_API duckdb_state duckdb_add_aggregate_function_to_set(duckdb_aggregate_function_set set,+                                                               duckdb_aggregate_function function);++/*!+Register the aggregate function set within the given connection.++The set requires at least a single valid overload.++If the set is incomplete or a function with this name already exists DuckDBError is returned.++* @param con The connection to register it in.+* @param set The function set to register+* @return Whether or not the registration was successful.+*/+DUCKDB_C_API duckdb_state duckdb_register_aggregate_function_set(duckdb_connection con,+                                                                 duckdb_aggregate_function_set set);++//===--------------------------------------------------------------------===//+// Table Functions+//===--------------------------------------------------------------------===//++/*!+Creates a new empty table function.++The return value should be destroyed with `duckdb_destroy_table_function`.++* @return The table function object.+*/+DUCKDB_C_API duckdb_table_function duckdb_create_table_function();++/*!+Destroys the given table function object.++* @param table_function The table function to destroy+*/+DUCKDB_C_API void duckdb_destroy_table_function(duckdb_table_function *table_function);++/*!+Sets the name of the given table function.++* @param table_function The table function+* @param name The name of the table function+*/+DUCKDB_C_API void duckdb_table_function_set_name(duckdb_table_function table_function, const char *name);++/*!+Adds a parameter to the table function.++* @param table_function The table function.+* @param type The parameter type. Cannot contain INVALID.+*/+DUCKDB_C_API void duckdb_table_function_add_parameter(duckdb_table_function table_function, duckdb_logical_type type);++/*!+Adds a named parameter to the table function.++* @param table_function The table function.+* @param name The parameter name.+* @param type The parameter type. Cannot contain INVALID.+*/+DUCKDB_C_API void duckdb_table_function_add_named_parameter(duckdb_table_function table_function, const char *name,+                                                            duckdb_logical_type type);++/*!+Assigns extra information to the table function that can be fetched during binding, etc.++* @param table_function The table function+* @param extra_info The extra information+* @param destroy The callback that will be called to destroy the extra information (if any)+*/+DUCKDB_C_API void duckdb_table_function_set_extra_info(duckdb_table_function table_function, void *extra_info,+                                                       duckdb_delete_callback_t destroy);++/*!+Sets the bind function of the table function.++* @param table_function The table function+* @param bind The bind function+*/+DUCKDB_C_API void duckdb_table_function_set_bind(duckdb_table_function table_function,+                                                 duckdb_table_function_bind_t bind);++/*!+Sets the init function of the table function.++* @param table_function The table function+* @param init The init function+*/+DUCKDB_C_API void duckdb_table_function_set_init(duckdb_table_function table_function,+                                                 duckdb_table_function_init_t init);++/*!+Sets the thread-local init function of the table function.++* @param table_function The table function+* @param init The init function+*/+DUCKDB_C_API void duckdb_table_function_set_local_init(duckdb_table_function table_function,+                                                       duckdb_table_function_init_t init);++/*!+Sets the main function of the table function.++* @param table_function The table function+* @param function The function+*/+DUCKDB_C_API void duckdb_table_function_set_function(duckdb_table_function table_function,+                                                     duckdb_table_function_t function);++/*!+Sets whether or not the given table function supports projection pushdown.++If this is set to true, the system will provide a list of all required columns in the `init` stage through+the `duckdb_init_get_column_count` and `duckdb_init_get_column_index` functions.+If this is set to false (the default), the system will expect all columns to be projected.++* @param table_function The table function+* @param pushdown True if the table function supports projection pushdown, false otherwise.+*/+DUCKDB_C_API void duckdb_table_function_supports_projection_pushdown(duckdb_table_function table_function,+                                                                     bool pushdown);++/*!+Register the table function object within the given connection.++The function requires at least a name, a bind function, an init function and a main function.++If the function is incomplete or a function with this name already exists DuckDBError is returned.++* @param con The connection to register it in.+* @param function The function pointer+* @return Whether or not the registration was successful.+*/+DUCKDB_C_API duckdb_state duckdb_register_table_function(duckdb_connection con, duckdb_table_function function);++//===--------------------------------------------------------------------===//+// Table Function Bind+//===--------------------------------------------------------------------===//++/*!+Retrieves the extra info of the function as set in `duckdb_table_function_set_extra_info`.++* @param info The info object+* @return The extra info+*/+DUCKDB_C_API void *duckdb_bind_get_extra_info(duckdb_bind_info info);++/*!+Retrieves the client context of the bind info of a table function.++* @param info The bind info object of the table function.+* @param out_context The client context of the bind info. Must be destroyed with `duckdb_destroy_client_context`.+*/+DUCKDB_C_API void duckdb_table_function_get_client_context(duckdb_bind_info info, duckdb_client_context *out_context);++/*!+Adds a result column to the output of the table function.++* @param info The table function's bind info.+* @param name The column name.+* @param type The logical column type.+*/+DUCKDB_C_API void duckdb_bind_add_result_column(duckdb_bind_info info, const char *name, duckdb_logical_type type);++/*!+Retrieves the number of regular (non-named) parameters to the function.++* @param info The info object+* @return The number of parameters+*/+DUCKDB_C_API idx_t duckdb_bind_get_parameter_count(duckdb_bind_info info);++/*!+Retrieves the parameter at the given index.++The result must be destroyed with `duckdb_destroy_value`.++* @param info The info object+* @param index The index of the parameter to get+* @return The value of the parameter. Must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_bind_get_parameter(duckdb_bind_info info, idx_t index);++/*!+Retrieves a named parameter with the given name.++The result must be destroyed with `duckdb_destroy_value`.++* @param info The info object+* @param name The name of the parameter+* @return The value of the parameter. Must be destroyed with `duckdb_destroy_value`.+*/+DUCKDB_C_API duckdb_value duckdb_bind_get_named_parameter(duckdb_bind_info info, const char *name);++/*!+Sets the user-provided bind data in the bind object of the table function.+This object can be retrieved again during execution.++* @param info The bind info of the table function.+* @param bind_data The bind data object.+* @param destroy The callback to destroy the bind data (if any).+*/+DUCKDB_C_API void duckdb_bind_set_bind_data(duckdb_bind_info info, void *bind_data, duckdb_delete_callback_t destroy);++/*!+Sets the cardinality estimate for the table function, used for optimization.++* @param info The bind data object.+* @param is_exact Whether or not the cardinality estimate is exact, or an approximation+*/+DUCKDB_C_API void duckdb_bind_set_cardinality(duckdb_bind_info info, idx_t cardinality, bool is_exact);++/*!+Report that an error has occurred while calling bind on a table function.++* @param info The info object+* @param error The error message+*/+DUCKDB_C_API void duckdb_bind_set_error(duckdb_bind_info info, const char *error);++//===--------------------------------------------------------------------===//+// Table Function Init+//===--------------------------------------------------------------------===//++/*!+Retrieves the extra info of the function as set in `duckdb_table_function_set_extra_info`.++* @param info The info object+* @return The extra info+*/+DUCKDB_C_API void *duckdb_init_get_extra_info(duckdb_init_info info);++/*!+Gets the bind data set by `duckdb_bind_set_bind_data` during the bind.++Note that the bind data should be considered as read-only.+For tracking state, use the init data instead.++* @param info The info object+* @return The bind data object+*/+DUCKDB_C_API void *duckdb_init_get_bind_data(duckdb_init_info info);++/*!+Sets the user-provided init data in the init object. This object can be retrieved again during execution.++* @param info The info object+* @param init_data The init data object.+* @param destroy The callback that will be called to destroy the init data (if any)+*/+DUCKDB_C_API void duckdb_init_set_init_data(duckdb_init_info info, void *init_data, duckdb_delete_callback_t destroy);++/*!+Returns the number of projected columns.++This function must be used if projection pushdown is enabled to figure out which columns to emit.++* @param info The info object+* @return The number of projected columns.+*/+DUCKDB_C_API idx_t duckdb_init_get_column_count(duckdb_init_info info);++/*!+Returns the column index of the projected column at the specified position.++This function must be used if projection pushdown is enabled to figure out which columns to emit.++* @param info The info object+* @param column_index The index at which to get the projected column index, from 0..duckdb_init_get_column_count(info)+* @return The column index of the projected column.+*/+DUCKDB_C_API idx_t duckdb_init_get_column_index(duckdb_init_info info, idx_t column_index);++/*!+Sets how many threads can process this table function in parallel (default: 1)++* @param info The info object+* @param max_threads The maximum amount of threads that can process this table function+*/+DUCKDB_C_API void duckdb_init_set_max_threads(duckdb_init_info info, idx_t max_threads);++/*!+Report that an error has occurred while calling init.++* @param info The info object+* @param error The error message+*/+DUCKDB_C_API void duckdb_init_set_error(duckdb_init_info info, const char *error);++//===--------------------------------------------------------------------===//+// Table Function+//===--------------------------------------------------------------------===//++/*!+Retrieves the extra info of the function as set in `duckdb_table_function_set_extra_info`.++* @param info The info object+* @return The extra info+*/+DUCKDB_C_API void *duckdb_function_get_extra_info(duckdb_function_info info);++/*!+Gets the table function's bind data set by `duckdb_bind_set_bind_data`.++Note that the bind data is read-only.+For tracking state, use the init data instead.++* @param info The function info object.+* @return The bind data object.+*/+DUCKDB_C_API void *duckdb_function_get_bind_data(duckdb_function_info info);++/*!+Gets the init data set by `duckdb_init_set_init_data` during the init.++* @param info The info object+* @return The init data object+*/+DUCKDB_C_API void *duckdb_function_get_init_data(duckdb_function_info info);++/*!+Gets the thread-local init data set by `duckdb_init_set_init_data` during the local_init.++* @param info The info object+* @return The init data object+*/+DUCKDB_C_API void *duckdb_function_get_local_init_data(duckdb_function_info info);++/*!+Report that an error has occurred while executing the function.++* @param info The info object+* @param error The error message+*/+DUCKDB_C_API void duckdb_function_set_error(duckdb_function_info info, const char *error);++//===--------------------------------------------------------------------===//+// Replacement Scans+//===--------------------------------------------------------------------===//++/*!+Add a replacement scan definition to the specified database.++* @param db The database object to add the replacement scan to+* @param replacement The replacement scan callback+* @param extra_data Extra data that is passed back into the specified callback+* @param delete_callback The delete callback to call on the extra data, if any+*/+DUCKDB_C_API void duckdb_add_replacement_scan(duckdb_database db, duckdb_replacement_callback_t replacement,+                                              void *extra_data, duckdb_delete_callback_t delete_callback);++/*!+Sets the replacement function name. If this function is called in the replacement callback,+the replacement scan is performed. If it is not called, the replacement callback is not performed.++* @param info The info object+* @param function_name The function name to substitute.+*/+DUCKDB_C_API void duckdb_replacement_scan_set_function_name(duckdb_replacement_scan_info info,+                                                            const char *function_name);++/*!+Adds a parameter to the replacement scan function.++* @param info The info object+* @param parameter The parameter to add.+*/+DUCKDB_C_API void duckdb_replacement_scan_add_parameter(duckdb_replacement_scan_info info, duckdb_value parameter);++/*!+Report that an error has occurred while executing the replacement scan.++* @param info The info object+* @param error The error message+*/+DUCKDB_C_API void duckdb_replacement_scan_set_error(duckdb_replacement_scan_info info, const char *error);++//===--------------------------------------------------------------------===//+// Profiling Info+//===--------------------------------------------------------------------===//++/*!+Returns the root node of the profiling information. Returns nullptr, if profiling is not enabled.++* @param connection A connection object.+* @return A profiling information object.+*/+DUCKDB_C_API duckdb_profiling_info duckdb_get_profiling_info(duckdb_connection connection);++/*!+Returns the value of the metric of the current profiling info node. Returns nullptr, if the metric does+ not exist or is not enabled. Currently, the value holds a string, and you can retrieve the string+ by calling the corresponding function: char *duckdb_get_varchar(duckdb_value value).++* @param info A profiling information object.+* @param key The name of the requested metric.+* @return The value of the metric. Must be freed with `duckdb_destroy_value`+*/+DUCKDB_C_API duckdb_value duckdb_profiling_info_get_value(duckdb_profiling_info info, const char *key);++/*!+Returns the key-value metric map of this profiling node as a MAP duckdb_value.+The individual elements are accessible via the duckdb_value MAP functions.++* @param info A profiling information object.+* @return The key-value metric map as a MAP duckdb_value.+*/+DUCKDB_C_API duckdb_value duckdb_profiling_info_get_metrics(duckdb_profiling_info info);++/*!+Returns the number of children in the current profiling info node.++* @param info A profiling information object.+* @return The number of children in the current node.+*/+DUCKDB_C_API idx_t duckdb_profiling_info_get_child_count(duckdb_profiling_info info);++/*!+Returns the child node at the specified index.++* @param info A profiling information object.+* @param index The index of the child node.+* @return The child node at the specified index.+*/+DUCKDB_C_API duckdb_profiling_info duckdb_profiling_info_get_child(duckdb_profiling_info info, idx_t index);++//===--------------------------------------------------------------------===//+// Appender+//===--------------------------------------------------------------------===//++// Appenders are the most efficient way of loading data into DuckDB from within the C API.+// They are recommended for fast data loading as they perform better than prepared statements or individual `INSERT+// INTO` statements.++// Appends are possible in row-wise format, and by appending entire data chunks.++// Row-wise: for every column, a `duckdb_append_[type]` call should be made. After finishing all appends to a row, call+// `duckdb_appender_end_row`.++// Chunk-wise: Consecutively call `duckdb_append_data_chunk` until all chunks have been appended.++// After all data has been appended, call `duckdb_appender_close` to finalize the appender followed by+// `duckdb_appender_destroy` to clean up the memory.++/*!+Creates an appender object.++Note that the object must be destroyed with `duckdb_appender_destroy`.++* @param connection The connection context to create the appender in.+* @param schema The schema of the table to append to, or `nullptr` for the default schema.+* @param table The table name to append to.+* @param out_appender The resulting appender object.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_appender_create(duckdb_connection connection, const char *schema, const char *table,+                                                 duckdb_appender *out_appender);++/*!+Creates an appender object.++Note that the object must be destroyed with `duckdb_appender_destroy`.++* @param connection The connection context to create the appender in.+* @param catalog The catalog of the table to append to, or `nullptr` for the default catalog.+* @param schema The schema of the table to append to, or `nullptr` for the default schema.+* @param table The table name to append to.+* @param out_appender The resulting appender object.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_appender_create_ext(duckdb_connection connection, const char *catalog,+                                                     const char *schema, const char *table,+                                                     duckdb_appender *out_appender);++/*!+Creates an appender object that executes the given query with any data appended to it.++Note that the object must be destroyed with `duckdb_appender_destroy`.++* @param connection The connection context to create the appender in.+* @param query The query to execute, can be an INSERT, DELETE, UPDATE or MERGE INTO statement.+* @param column_count The number of columns to append.+* @param types The types of the columns to append.+* @param table_name (optionally) the table name used to refer to the appended data, defaults to "appended_data".+* @param column_names (optionally) the list of column names, defaults to "col1", "col2", ...+* @param out_appender The resulting appender object.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_appender_create_query(duckdb_connection connection, const char *query,+                                                       idx_t column_count, duckdb_logical_type *types,+                                                       const char *table_name, const char **column_names,+                                                       duckdb_appender *out_appender);++/*!+Returns the number of columns that belong to the appender.+If there is no active column list, then this equals the table's physical columns.++* @param appender The appender to get the column count from.+* @return The number of columns in the data chunks.+*/+DUCKDB_C_API idx_t duckdb_appender_column_count(duckdb_appender appender);++/*!+Returns the type of the column at the specified index. This is either a type in the active column list, or the same type+as a column in the receiving table.++Note: The resulting type must be destroyed with `duckdb_destroy_logical_type`.++* @param appender The appender to get the column type from.+* @param col_idx The index of the column to get the type of.+* @return The `duckdb_logical_type` of the column.+*/+DUCKDB_C_API duckdb_logical_type duckdb_appender_column_type(duckdb_appender appender, idx_t col_idx);++#ifndef DUCKDB_API_NO_DEPRECATED+/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.+Use duckdb_appender_error_data instead.++Returns the error message associated with the appender.+If the appender has no error message, this returns `nullptr` instead.++The error message should not be freed. It will be de-allocated when `duckdb_appender_destroy` is called.++* @param appender The appender to get the error from.+* @return The error message, or `nullptr` if there is none.+*/+DUCKDB_C_API const char *duckdb_appender_error(duckdb_appender appender);++#endif++/*!+Returns the error data associated with the appender.+Must be destroyed with duckdb_destroy_error_data.++* @param appender The appender to get the error data from.+* @return The error data.+*/+DUCKDB_C_API duckdb_error_data duckdb_appender_error_data(duckdb_appender appender);++/*!+Flush the appender to the table, forcing the cache of the appender to be cleared. If flushing the data triggers a+constraint violation or any other error, then all data is invalidated, and this function returns DuckDBError.+It is not possible to append more values. Call duckdb_appender_error_data to obtain the error data followed by+duckdb_appender_destroy to destroy the invalidated appender.++* @param appender The appender to flush.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_appender_flush(duckdb_appender appender);++/*!+Closes the appender by flushing all intermediate states and closing it for further appends. If flushing the data+triggers a constraint violation or any other error, then all data is invalidated, and this function returns DuckDBError.+Call duckdb_appender_error_data to obtain the error data followed by duckdb_appender_destroy to destroy the invalidated+appender.++* @param appender The appender to flush and close.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_appender_close(duckdb_appender appender);++/*!+Closes the appender by flushing all intermediate states to the table and destroying it. By destroying it, this function+de-allocates all memory associated with the appender. If flushing the data triggers a constraint violation,+then all data is invalidated, and this function returns DuckDBError. Due to the destruction of the appender, it is no+longer possible to obtain the specific error message with duckdb_appender_error. Therefore, call duckdb_appender_close+before destroying the appender, if you need insights into the specific error.++* @param appender The appender to flush, close and destroy.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_appender_destroy(duckdb_appender *appender);++/*!+Appends a column to the active column list of the appender. Immediately flushes all previous data.++The active column list specifies all columns that are expected when flushing the data. Any non-active columns are filled+with their default values, or NULL.++* @param appender The appender to add the column to.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_appender_add_column(duckdb_appender appender, const char *name);++/*!+Removes all columns from the active column list of the appender, resetting the appender to treat all columns as active.+Immediately flushes all previous data.++* @param appender The appender to clear the columns from.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_appender_clear_columns(duckdb_appender appender);++/*!+A nop function, provided for backwards compatibility reasons. Does nothing. Only `duckdb_appender_end_row` is required.+*/+DUCKDB_C_API duckdb_state duckdb_appender_begin_row(duckdb_appender appender);++/*!+Finish the current row of appends. After end_row is called, the next row can be appended.++* @param appender The appender.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_appender_end_row(duckdb_appender appender);++/*!+Append a DEFAULT value (NULL if DEFAULT not available for column) to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_default(duckdb_appender appender);++/*!+Append a DEFAULT value, at the specified row and column, (NULL if DEFAULT not available for column) to the chunk created+from the specified appender. The default value of the column must be a constant value. Non-deterministic expressions+like nextval('seq') or random() are not supported.++* @param appender The appender to get the default value from.+* @param chunk The data chunk to append the default value to.+* @param col The chunk column index to append the default value to.+* @param row The chunk row index to append the default value to.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_append_default_to_chunk(duckdb_appender appender, duckdb_data_chunk chunk, idx_t col,+                                                         idx_t row);++/*!+Append a bool value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_bool(duckdb_appender appender, bool value);++/*!+Append an int8_t value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_int8(duckdb_appender appender, int8_t value);++/*!+Append an int16_t value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_int16(duckdb_appender appender, int16_t value);++/*!+Append an int32_t value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_int32(duckdb_appender appender, int32_t value);++/*!+Append an int64_t value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_int64(duckdb_appender appender, int64_t value);++/*!+Append a duckdb_hugeint value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_hugeint(duckdb_appender appender, duckdb_hugeint value);++/*!+Append a uint8_t value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_uint8(duckdb_appender appender, uint8_t value);++/*!+Append a uint16_t value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_uint16(duckdb_appender appender, uint16_t value);++/*!+Append a uint32_t value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_uint32(duckdb_appender appender, uint32_t value);++/*!+Append a uint64_t value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_uint64(duckdb_appender appender, uint64_t value);++/*!+Append a duckdb_uhugeint value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_uhugeint(duckdb_appender appender, duckdb_uhugeint value);++/*!+Append a float value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_float(duckdb_appender appender, float value);++/*!+Append a double value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_double(duckdb_appender appender, double value);++/*!+Append a duckdb_date value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_date(duckdb_appender appender, duckdb_date value);++/*!+Append a duckdb_time value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_time(duckdb_appender appender, duckdb_time value);++/*!+Append a duckdb_timestamp value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_timestamp(duckdb_appender appender, duckdb_timestamp value);++/*!+Append a duckdb_interval value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_interval(duckdb_appender appender, duckdb_interval value);++/*!+Append a varchar value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_varchar(duckdb_appender appender, const char *val);++/*!+Append a varchar value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_varchar_length(duckdb_appender appender, const char *val, idx_t length);++/*!+Append a blob value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_blob(duckdb_appender appender, const void *data, idx_t length);++/*!+Append a NULL value to the appender (of any type).+*/+DUCKDB_C_API duckdb_state duckdb_append_null(duckdb_appender appender);++/*!+Append a duckdb_value to the appender.+*/+DUCKDB_C_API duckdb_state duckdb_append_value(duckdb_appender appender, duckdb_value value);++/*!+Appends a pre-filled data chunk to the specified appender.+ Attempts casting, if the data chunk types do not match the active appender types.++* @param appender The appender to append to.+* @param chunk The data chunk to append.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_append_data_chunk(duckdb_appender appender, duckdb_data_chunk chunk);++//===--------------------------------------------------------------------===//+// Table Description+//===--------------------------------------------------------------------===//++/*!+Creates a table description object. Note that `duckdb_table_description_destroy` should always be called on the+resulting table_description, even if the function returns `DuckDBError`.++* @param connection The connection context.+* @param schema The schema of the table, or `nullptr` for the default schema.+* @param table The table name.+* @param out The resulting table description object.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_table_description_create(duckdb_connection connection, const char *schema,+                                                          const char *table, duckdb_table_description *out);++/*!+Creates a table description object. Note that `duckdb_table_description_destroy` must be called on the resulting+table_description, even if the function returns `DuckDBError`.++* @param connection The connection context.+* @param catalog The catalog (database) name of the table, or `nullptr` for the default catalog.+* @param schema The schema of the table, or `nullptr` for the default schema.+* @param table The table name.+* @param out The resulting table description object.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_table_description_create_ext(duckdb_connection connection, const char *catalog,+                                                              const char *schema, const char *table,+                                                              duckdb_table_description *out);++/*!+Destroy the TableDescription object.++* @param table_description The table_description to destroy.+*/+DUCKDB_C_API void duckdb_table_description_destroy(duckdb_table_description *table_description);++/*!+Returns the error message associated with the given table_description.+If the table_description has no error message, this returns `nullptr` instead.+The error message should not be freed. It will be de-allocated when `duckdb_table_description_destroy` is called.++* @param table_description The table_description to get the error from.+* @return The error message, or `nullptr` if there is none.+*/+DUCKDB_C_API const char *duckdb_table_description_error(duckdb_table_description table_description);++/*!+Check if the column at 'index' index of the table has a DEFAULT expression.++* @param table_description The table_description to query.+* @param index The index of the column to query.+* @param out The out-parameter used to store the result.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_column_has_default(duckdb_table_description table_description, idx_t index, bool *out);++/*!+Obtain the column name at 'index'.+The out result must be destroyed with `duckdb_free`.++* @param table_description The table_description to query.+* @param index The index of the column to query.+* @return The column name.+*/+DUCKDB_C_API char *duckdb_table_description_get_column_name(duckdb_table_description table_description, idx_t index);++//===--------------------------------------------------------------------===//+// Arrow Interface+//===--------------------------------------------------------------------===//++/*!+Transforms a DuckDB Schema into an Arrow Schema++* @param arrow_options The Arrow settings used to produce arrow.+* @param types The DuckDB logical types for each column in the schema.+* @param names The names for each column in the schema.+* @param column_count The number of columns that exist in the schema.+* @param out_schema The resulting arrow schema. Must be destroyed with `out_schema->release(out_schema)`.+* @return The error data. Must be destroyed with `duckdb_destroy_error_data`.+*/+DUCKDB_C_API duckdb_error_data duckdb_to_arrow_schema(duckdb_arrow_options arrow_options, duckdb_logical_type *types,+                                                      const char **names, idx_t column_count,+                                                      struct ArrowSchema *out_schema);++/*!+Transforms a DuckDB data chunk into an Arrow array.++* @param arrow_options The Arrow settings used to produce arrow.+* @param chunk The DuckDB data chunk to convert.+* @param out_arrow_array The output Arrow structure that will hold the converted data. Must be released with+`out_arrow_array->release(out_arrow_array)`+* @return The error data. Must be destroyed with `duckdb_destroy_error_data`.+*/+DUCKDB_C_API duckdb_error_data duckdb_data_chunk_to_arrow(duckdb_arrow_options arrow_options, duckdb_data_chunk chunk,+                                                          struct ArrowArray *out_arrow_array);++/*!+Transforms an Arrow Schema into a DuckDB Schema.++* @param connection The connection to get the transformation settings from.+* @param schema The input Arrow schema. Must be released with `schema->release(schema)`.+* @param out_types The Arrow converted schema with extra information about the arrow types. Must be destroyed with+`duckdb_destroy_arrow_converted_schema`.+* @return The error data. Must be destroyed with `duckdb_destroy_error_data`.+*/+DUCKDB_C_API duckdb_error_data duckdb_schema_from_arrow(duckdb_connection connection, struct ArrowSchema *schema,+                                                        duckdb_arrow_converted_schema *out_types);++/*!+Transforms an Arrow array into a DuckDB data chunk. The data chunk will retain ownership of the underlying Arrow data.++* @param connection The connection to get the transformation settings from.+* @param arrow_array The input Arrow array. Data ownership is passed on to DuckDB's DataChunk, the underlying object+does not need to be released and won't have ownership of the data.+* @param converted_schema The Arrow converted schema with extra information about the arrow types.+* @param out_chunk The resulting DuckDB data chunk. Must be destroyed by duckdb_destroy_data_chunk.+* @return The error data. Must be destroyed with `duckdb_destroy_error_data`.+*/+DUCKDB_C_API duckdb_error_data duckdb_data_chunk_from_arrow(duckdb_connection connection,+                                                            struct ArrowArray *arrow_array,+                                                            duckdb_arrow_converted_schema converted_schema,+                                                            duckdb_data_chunk *out_chunk);++/*!+Destroys the arrow converted schema and de-allocates all memory allocated for that arrow converted schema.++* @param arrow_converted_schema The arrow converted schema to destroy.+*/+DUCKDB_C_API void duckdb_destroy_arrow_converted_schema(duckdb_arrow_converted_schema *arrow_converted_schema);++#ifndef DUCKDB_API_NO_DEPRECATED+/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Executes a SQL query within a connection and stores the full (materialized) result in an arrow structure.+If the query fails to execute, DuckDBError is returned and the error message can be retrieved by calling+`duckdb_query_arrow_error`.++Note that after running `duckdb_query_arrow`, `duckdb_destroy_arrow` must be called on the result object even if the+query fails, otherwise the error stored within the result will not be freed correctly.++* @param connection The connection to perform the query in.+* @param query The SQL query to run.+* @param out_result The query result.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_query_arrow(duckdb_connection connection, const char *query, duckdb_arrow *out_result);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Fetch the internal arrow schema from the arrow result. Remember to call release on the respective+ArrowSchema object.++* @param result The result to fetch the schema from.+* @param out_schema The output schema.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_query_arrow_schema(duckdb_arrow result, duckdb_arrow_schema *out_schema);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Fetch the internal arrow schema from the prepared statement. Remember to call release on the respective+ArrowSchema object.++* @param prepared The prepared statement to fetch the schema from.+* @param out_schema The output schema.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_prepared_arrow_schema(duckdb_prepared_statement prepared,+                                                       duckdb_arrow_schema *out_schema);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Convert a data chunk into an arrow struct array. Remember to call release on the respective+ArrowArray object.++* @param result The result object the data chunk have been fetched from.+* @param chunk The data chunk to convert.+* @param out_array The output array.+*/+DUCKDB_C_API void duckdb_result_arrow_array(duckdb_result result, duckdb_data_chunk chunk,+                                            duckdb_arrow_array *out_array);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Fetch an internal arrow struct array from the arrow result. Remember to call release on the respective+ArrowArray object.++This function can be called multiple time to get next chunks, which will free the previous out_array.+So consume the out_array before calling this function again.++* @param result The result to fetch the array from.+* @param out_array The output array.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_query_arrow_array(duckdb_arrow result, duckdb_arrow_array *out_array);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Returns the number of columns present in the arrow result object.++* @param result The result object.+* @return The number of columns present in the result object.+*/+DUCKDB_C_API idx_t duckdb_arrow_column_count(duckdb_arrow result);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Returns the number of rows present in the arrow result object.++* @param result The result object.+* @return The number of rows present in the result object.+*/+DUCKDB_C_API idx_t duckdb_arrow_row_count(duckdb_arrow result);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Returns the number of rows changed by the query stored in the arrow result. This is relevant only for+INSERT/UPDATE/DELETE queries. For other queries the rows_changed will be 0.++* @param result The result object.+* @return The number of rows changed.+*/+DUCKDB_C_API idx_t duckdb_arrow_rows_changed(duckdb_arrow result);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++ Returns the error message contained within the result. The error is only set if `duckdb_query_arrow` returns+`DuckDBError`.++The error message should not be freed. It will be de-allocated when `duckdb_destroy_arrow` is called.++* @param result The result object to fetch the error from.+* @return The error of the result.+*/+DUCKDB_C_API const char *duckdb_query_arrow_error(duckdb_arrow result);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Closes the result and de-allocates all memory allocated for the arrow result.++* @param result The result to destroy.+*/+DUCKDB_C_API void duckdb_destroy_arrow(duckdb_arrow *result);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Releases the arrow array stream and de-allocates its memory.++* @param stream_p The arrow array stream to destroy.+*/+DUCKDB_C_API void duckdb_destroy_arrow_stream(duckdb_arrow_stream *stream_p);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Executes the prepared statement with the given bound parameters, and returns an arrow query result.+Note that after running `duckdb_execute_prepared_arrow`, `duckdb_destroy_arrow` must be called on the result object.++* @param prepared_statement The prepared statement to execute.+* @param out_result The query result.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_execute_prepared_arrow(duckdb_prepared_statement prepared_statement,+                                                        duckdb_arrow *out_result);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Scans the Arrow stream and creates a view with the given name.++* @param connection The connection on which to execute the scan.+* @param table_name Name of the temporary view to create.+* @param arrow Arrow stream wrapper.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_arrow_scan(duckdb_connection connection, const char *table_name,+                                            duckdb_arrow_stream arrow);++/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Scans the Arrow array and creates a view with the given name.+Note that after running `duckdb_arrow_array_scan`, `duckdb_destroy_arrow_stream` must be called on the out stream.++* @param connection The connection on which to execute the scan.+* @param table_name Name of the temporary view to create.+* @param arrow_schema Arrow schema wrapper.+* @param arrow_array Arrow array wrapper.+* @param out_stream Output array stream that wraps around the passed schema, for releasing/deleting once done.+* @return `DuckDBSuccess` on success or `DuckDBError` on failure.+*/+DUCKDB_C_API duckdb_state duckdb_arrow_array_scan(duckdb_connection connection, const char *table_name,+                                                  duckdb_arrow_schema arrow_schema, duckdb_arrow_array arrow_array,+                                                  duckdb_arrow_stream *out_stream);++#endif++//===--------------------------------------------------------------------===//+// Threading Information+//===--------------------------------------------------------------------===//++/*!+Execute DuckDB tasks on this thread.++Will return after `max_tasks` have been executed, or if there are no more tasks present.++* @param database The database object to execute tasks for+* @param max_tasks The maximum amount of tasks to execute+*/+DUCKDB_C_API void duckdb_execute_tasks(duckdb_database database, idx_t max_tasks);++/*!+Creates a task state that can be used with duckdb_execute_tasks_state to execute tasks until+`duckdb_finish_execution` is called on the state.++`duckdb_destroy_state` must be called on the result.++* @param database The database object to create the task state for+* @return The task state that can be used with duckdb_execute_tasks_state.+*/+DUCKDB_C_API duckdb_task_state duckdb_create_task_state(duckdb_database database);++/*!+Execute DuckDB tasks on this thread.++The thread will keep on executing tasks forever, until duckdb_finish_execution is called on the state.+Multiple threads can share the same duckdb_task_state.++* @param state The task state of the executor+*/+DUCKDB_C_API void duckdb_execute_tasks_state(duckdb_task_state state);++/*!+Execute DuckDB tasks on this thread.++The thread will keep on executing tasks until either duckdb_finish_execution is called on the state,+max_tasks tasks have been executed or there are no more tasks to be executed.++Multiple threads can share the same duckdb_task_state.++* @param state The task state of the executor+* @param max_tasks The maximum amount of tasks to execute+* @return The amount of tasks that have actually been executed+*/+DUCKDB_C_API idx_t duckdb_execute_n_tasks_state(duckdb_task_state state, idx_t max_tasks);++/*!+Finish execution on a specific task.++* @param state The task state to finish execution+*/+DUCKDB_C_API void duckdb_finish_execution(duckdb_task_state state);++/*!+Check if the provided duckdb_task_state has finished execution++* @param state The task state to inspect+* @return Whether or not duckdb_finish_execution has been called on the task state+*/+DUCKDB_C_API bool duckdb_task_state_is_finished(duckdb_task_state state);++/*!+Destroys the task state returned from duckdb_create_task_state.++Note that this should not be called while there is an active duckdb_execute_tasks_state running+on the task state.++* @param state The task state to clean up+*/+DUCKDB_C_API void duckdb_destroy_task_state(duckdb_task_state state);++/*!+Returns true if the execution of the current query is finished.++* @param con The connection on which to check+*/+DUCKDB_C_API bool duckdb_execution_is_finished(duckdb_connection con);++//===--------------------------------------------------------------------===//+// Streaming Result Interface+//===--------------------------------------------------------------------===//++#ifndef DUCKDB_API_NO_DEPRECATED+/*!+**DEPRECATION NOTICE**: This method is scheduled for removal in a future release.++Fetches a data chunk from the (streaming) duckdb_result. This function should be called repeatedly until the result is+exhausted.++The result must be destroyed with `duckdb_destroy_data_chunk`.++This function can only be used on duckdb_results created with 'duckdb_pending_prepared_streaming'++If this function is used, none of the other result functions can be used and vice versa (i.e. this function cannot be+mixed with the legacy result functions or the materialized result functions).++It is not known beforehand how many chunks will be returned by this result.++* @param result The result object to fetch the data chunk from.+* @return The resulting data chunk. Returns `NULL` if the result has an error.+*/+DUCKDB_C_API duckdb_data_chunk duckdb_stream_fetch_chunk(duckdb_result result);++#endif++/*!+Fetches a data chunk from a duckdb_result. This function should be called repeatedly until the result is exhausted.++The result must be destroyed with `duckdb_destroy_data_chunk`.++It is not known beforehand how many chunks will be returned by this result.++* @param result The result object to fetch the data chunk from.+* @return The resulting data chunk. Returns `NULL` if the result has an error.+*/+DUCKDB_C_API duckdb_data_chunk duckdb_fetch_chunk(duckdb_result result);++//===--------------------------------------------------------------------===//+// Cast Functions+//===--------------------------------------------------------------------===//++/*!+Creates a new cast function object.++* @return The cast function object.+*/+DUCKDB_C_API duckdb_cast_function duckdb_create_cast_function();++/*!+Sets the source type of the cast function.++* @param cast_function The cast function object.+* @param source_type The source type to set.+*/+DUCKDB_C_API void duckdb_cast_function_set_source_type(duckdb_cast_function cast_function,+                                                       duckdb_logical_type source_type);++/*!+Sets the target type of the cast function.++* @param cast_function The cast function object.+* @param target_type The target type to set.+*/+DUCKDB_C_API void duckdb_cast_function_set_target_type(duckdb_cast_function cast_function,+                                                       duckdb_logical_type target_type);++/*!+Sets the "cost" of implicitly casting the source type to the target type using this function.++* @param cast_function The cast function object.+* @param cost The cost to set.+*/+DUCKDB_C_API void duckdb_cast_function_set_implicit_cast_cost(duckdb_cast_function cast_function, int64_t cost);++/*!+Sets the actual cast function to use.++* @param cast_function The cast function object.+* @param function The function to set.+*/+DUCKDB_C_API void duckdb_cast_function_set_function(duckdb_cast_function cast_function,+                                                    duckdb_cast_function_t function);++/*!+Assigns extra information to the cast function that can be fetched during execution, etc.++* @param extra_info The extra information+* @param destroy The callback that will be called to destroy the extra information (if any)+*/+DUCKDB_C_API void duckdb_cast_function_set_extra_info(duckdb_cast_function cast_function, void *extra_info,+                                                      duckdb_delete_callback_t destroy);++/*!+Retrieves the extra info of the function as set in `duckdb_cast_function_set_extra_info`.++* @param info The info object.+* @return The extra info.+*/+DUCKDB_C_API void *duckdb_cast_function_get_extra_info(duckdb_function_info info);++/*!+Get the cast execution mode from the given function info.++* @param info The info object.+* @return The cast mode.+*/+DUCKDB_C_API duckdb_cast_mode duckdb_cast_function_get_cast_mode(duckdb_function_info info);++/*!+Report that an error has occurred while executing the cast function.++* @param info The info object.+* @param error The error message.+*/+DUCKDB_C_API void duckdb_cast_function_set_error(duckdb_function_info info, const char *error);++/*!+Report that an error has occurred while executing the cast function, setting the corresponding output row to NULL.++* @param info The info object.+* @param error The error message.+* @param row The index of the row within the output vector to set to NULL.+* @param output The output vector.+*/+DUCKDB_C_API void duckdb_cast_function_set_row_error(duckdb_function_info info, const char *error, idx_t row,+                                                     duckdb_vector output);++/*!+Registers a cast function within the given connection.++* @param con The connection to use.+* @param cast_function The cast function to register.+* @return Whether or not the registration was successful.+*/+DUCKDB_C_API duckdb_state duckdb_register_cast_function(duckdb_connection con, duckdb_cast_function cast_function);++/*!+Destroys the cast function object.++* @param cast_function The cast function object.+*/+DUCKDB_C_API void duckdb_destroy_cast_function(duckdb_cast_function *cast_function);++//===--------------------------------------------------------------------===//+// Expression Interface+//===--------------------------------------------------------------------===//++/*!+Destroys the expression and de-allocates its memory.++* @param expr A pointer to the expression.+*/+DUCKDB_C_API void duckdb_destroy_expression(duckdb_expression *expr);++/*!+Returns the return type of an expression.++* @param expr The expression.+* @return The return type. Must be destroyed with `duckdb_destroy_logical_type`.+*/+DUCKDB_C_API duckdb_logical_type duckdb_expression_return_type(duckdb_expression expr);++/*!+Returns whether the expression is foldable into a value or not.++* @param expr The expression.+* @return True, if the expression is foldable, else false.+*/+DUCKDB_C_API bool duckdb_expression_is_foldable(duckdb_expression expr);++/*!+Folds an expression creating a folded value.++* @param context The client context.+* @param expr The expression. Must be foldable.+* @param out_value The folded value, if folding was successful. Must be destroyed with `duckdb_destroy_value`.+* @return The error data. Must be destroyed with `duckdb_destroy_error_data`.+*/+DUCKDB_C_API duckdb_error_data duckdb_expression_fold(duckdb_client_context context, duckdb_expression expr,+                                                      duckdb_value *out_value);++#endif++#ifdef __cplusplus+}+#endif
+ cbits/duckdb_stub.c view
@@ -0,0 +1,546 @@+#include "duckdb.h"+#include <stdint.h>++// duckdb_query_progress returns a struct by value; expose a pointer-based version+// so the Haskell FFI can marshal the result and so we can gracefully handle NULL.+void wrapped_duckdb_query_progress(duckdb_connection connection, duckdb_query_progress_type *out_progress) {+  if (!out_progress) {+    return;+  }+  *out_progress = duckdb_query_progress(connection);+}++// duckdb_result_statement_type consumes a duckdb_result by value; this wrapper lets+// us pass a pointer from Haskell and returns a safe default when NULL is supplied.+duckdb_statement_type wrapped_duckdb_result_statement_type(const duckdb_result *result) {+  if (!result) {+    return DUCKDB_STATEMENT_TYPE_INVALID;+  }+  return duckdb_result_statement_type(*result);+}++// duckdb_result_return_type also expects the result struct by value; wrap it so we+// can call it from Haskell while avoiding undefined behaviour on NULL.+duckdb_result_type wrapped_duckdb_result_return_type(const duckdb_result *result) {+  if (!result) {+    return DUCKDB_RESULT_TYPE_INVALID;+  }+  return duckdb_result_return_type(*result);+}++// duckdb_result_get_arrow_options mutates the result structure; we pass the pointer+// through after checking for NULL and casting away const for the FFI surface.+duckdb_arrow_options wrapped_duckdb_result_get_arrow_options(const duckdb_result *result) {+  if (!result) {+    return NULL;+  }+  return duckdb_result_get_arrow_options((duckdb_result *)result);+}++// duckdb_result_get_chunk takes the result by value; we provide a pointer-friendly+// wrapper that also protects against accidental NULL dereferences.+duckdb_data_chunk wrapped_duckdb_result_get_chunk(const duckdb_result *result, idx_t chunk_index) {+  if (!result) {+    return NULL;+  }+  return duckdb_result_get_chunk(*result, chunk_index);+}++// duckdb_result_chunk_count takes a duckdb_result by value; mirror it with pointer+// semantics for the Haskell FFI.+idx_t wrapped_duckdb_result_chunk_count(const duckdb_result *result) {+  if (!result) {+    return 0;+  }+  return duckdb_result_chunk_count(*result);+}++// duckdb_result_is_streaming takes the result by value; add a pointer-based shim+// so we can pass NULL safely from the managed side.+bool wrapped_duckdb_result_is_streaming(const duckdb_result *result) {+  if (!result) {+    return false;+  }+  return duckdb_result_is_streaming(*result);+}++// duckdb_value_hugeint returns a struct by value; expose an out-parameter variant+// with NULL guards for easier use from Haskell.+void wrapped_duckdb_value_hugeint(duckdb_result *result, idx_t col, idx_t row, duckdb_hugeint *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_value_hugeint(result, col, row);+}++// duckdb_value_uhugeint returns a struct by value; wrap it to write into caller-+// supplied storage and to tolerate NULL pointers.+void wrapped_duckdb_value_uhugeint(duckdb_result *result, idx_t col, idx_t row, duckdb_uhugeint *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_value_uhugeint(result, col, row);+}++// duckdb_value_decimal returns a struct by value; provide an out-parameter version+// so we can marshal it easily.+void wrapped_duckdb_value_decimal(duckdb_result *result, idx_t col, idx_t row, duckdb_decimal *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_value_decimal(result, col, row);+}++// duckdb_value_string returns a struct by value; expose a wrapper that fills a+// duckdb_string for the caller if the output pointer is non-NULL.+void wrapped_duckdb_value_string(duckdb_result *result, idx_t col, idx_t row, duckdb_string *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_value_string(result, col, row);+}++// duckdb_value_string_internal also returns a struct by value; same rationale as+// wrapped_duckdb_value_string.+void wrapped_duckdb_value_string_internal(duckdb_result *result, idx_t col, idx_t row, duckdb_string *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_value_string_internal(result, col, row);+}++// duckdb_value_blob returns a struct by value; copy into caller storage and guard+// against NULL in the wrapper.+void wrapped_duckdb_value_blob(duckdb_result *result, idx_t col, idx_t row, duckdb_blob *out_blob) {+  if (!out_blob) {+    return;+  }+  *out_blob = duckdb_value_blob(result, col, row);+}++// duckdb_value_interval returns a struct by value; expose pointer-friendly variant+// for the managed bindings.+void wrapped_duckdb_value_interval(duckdb_result *result, idx_t col, idx_t row, duckdb_interval *out_interval) {+  if (!out_interval) {+    return;+  }+  *out_interval = duckdb_value_interval(result, col, row);+}++// duckdb_string_is_inlined expects a struct by value; we pass a pointer and guard+// against NULL before delegating.+bool wrapped_duckdb_string_is_inlined(const duckdb_string_t *string) {+  if (!string) {+    return false;+  }+  return duckdb_string_is_inlined(*string);+}++// duckdb_string_t_length reads from duckdb_string_t by value; provide a pointer+// based wrapper that handles NULL safely.+uint32_t wrapped_duckdb_string_t_length(const duckdb_string_t *string) {+  if (!string) {+    return 0;+  }+  return duckdb_string_t_length(*string);+}++// duckdb_result_arrow_array modifies the arrow array; we add NULL checks so FFI+// callers can omit either pointer without crashing the process.+void wrapped_duckdb_result_arrow_array(const duckdb_result *result, duckdb_data_chunk chunk, duckdb_arrow_array *out_array) {+  if (!result || !chunk || !out_array) {+    return;+  }+  duckdb_result_arrow_array(*result, chunk, out_array);+}++void *wrapped_duckdb_arrow_schema_internal_ptr(duckdb_arrow_schema schema) {+  if (!schema) {+    return NULL;+  }+  return schema->internal_ptr;+}++void wrapped_duckdb_arrow_schema_clear_internal_ptr(duckdb_arrow_schema schema) {+  if (!schema) {+    return;+  }+  schema->internal_ptr = NULL;+}++void *wrapped_duckdb_arrow_array_internal_ptr(duckdb_arrow_array array) {+  if (!array) {+    return NULL;+  }+  return array->internal_ptr;+}++void wrapped_duckdb_arrow_array_clear_internal_ptr(duckdb_arrow_array array) {+  if (!array) {+    return;+  }+  array->internal_ptr = NULL;+}++void *wrapped_duckdb_arrow_stream_internal_ptr(duckdb_arrow_stream stream) {+  if (!stream) {+    return NULL;+  }+  return stream->internal_ptr;+}++void wrapped_duckdb_arrow_stream_clear_internal_ptr(duckdb_arrow_stream stream) {+  if (!stream) {+    return;+  }+  stream->internal_ptr = NULL;+}++// duckdb_stream_fetch_chunk takes duckdb_result by value; provide a pointer form+// so we can handle NULLs and avoid struct copies in the FFI.+duckdb_data_chunk wrapped_duckdb_stream_fetch_chunk(const duckdb_result *result) {+  if (!result) {+    return NULL;+  }+  return duckdb_stream_fetch_chunk(*result);+}++// duckdb_fetch_chunk also expects duckdb_result by value; same pointer-friendly wrap+// as above.+duckdb_data_chunk wrapped_duckdb_fetch_chunk(const duckdb_result *result) {+  if (!result) {+    return NULL;+  }+  return duckdb_fetch_chunk(*result);+}++// duckdb_bind_interval consumes duckdb_interval by value; we copy from the pointer+// so Haskell can pass NULL and avoid struct marshalling complexities.+duckdb_state wrapped_duckdb_bind_interval(duckdb_prepared_statement statement, idx_t param_idx, const duckdb_interval *val) {+  duckdb_interval interval_value = {0};+  if (val) {+    interval_value = *val;+  }+  return duckdb_bind_interval(statement, param_idx, interval_value);+}++// duckdb_bind_hugeint requires a struct by value; wrap to accept a pointer and+// provide a zero-initialised default when NULL.+duckdb_state wrapped_duckdb_bind_hugeint(duckdb_prepared_statement statement, idx_t param_idx, const duckdb_hugeint *val) {+  duckdb_hugeint hugeint_value = {0};+  if (val) {+    hugeint_value = *val;+  }+  return duckdb_bind_hugeint(statement, param_idx, hugeint_value);+}++// duckdb_bind_uhugeint requires a struct by value; same pointer-based shim as for+// duckdb_bind_hugeint.+duckdb_state wrapped_duckdb_bind_uhugeint(duckdb_prepared_statement statement, idx_t param_idx, const duckdb_uhugeint *val) {+  duckdb_uhugeint uhugeint_value = {0};+  if (val) {+    uhugeint_value = *val;+  }+  return duckdb_bind_uhugeint(statement, param_idx, uhugeint_value);+}++// duckdb_bind_decimal consumes duckdb_decimal by value; wrap it so we can accept a+// pointer and default to zero when NULL arrives from Haskell.+duckdb_state wrapped_duckdb_bind_decimal(duckdb_prepared_statement statement, idx_t param_idx, const duckdb_decimal *val) {+  duckdb_decimal decimal_value = {0};+  if (val) {+    decimal_value = *val;+  }+  return duckdb_bind_decimal(statement, param_idx, decimal_value);+}++// duckdb_create_hugeint takes the struct by value; the wrapper copies from the+// user pointer so Haskell can choose to pass NULL.+duckdb_value wrapped_duckdb_create_hugeint(const duckdb_hugeint *input) {+  duckdb_hugeint value = {0};+  if (input) {+    value = *input;+  }+  return duckdb_create_hugeint(value);+}++// duckdb_create_uhugeint takes the struct by value; wrap to accept optional pointer+// input from the FFI.+duckdb_value wrapped_duckdb_create_uhugeint(const duckdb_uhugeint *input) {+  duckdb_uhugeint value = {0};+  if (input) {+    value = *input;+  }+  return duckdb_create_uhugeint(value);+}++// duckdb_create_bignum consumes duckdb_bignum by value; copy from pointer so the+// managed caller can omit it.+duckdb_value wrapped_duckdb_create_bignum(const duckdb_bignum *input) {+  duckdb_bignum value = {0};+  if (input) {+    value = *input;+  }+  return duckdb_create_bignum(value);+}++// duckdb_create_decimal expects duckdb_decimal by value; pointer wrapper allows us+// to default the argument and plays nicely with the FFI.+duckdb_value wrapped_duckdb_create_decimal(const duckdb_decimal *input) {+  duckdb_decimal value = {0};+  if (input) {+    value = *input;+  }+  return duckdb_create_decimal(value);+}++// duckdb_create_interval expects duckdb_interval by value; copy from pointer so+// the managed caller can pass NULL and avoid struct marshalling.+duckdb_value wrapped_duckdb_create_interval(const duckdb_interval *input) {+  duckdb_interval value = {0};+  if (input) {+    value = *input;+  }+  return duckdb_create_interval(value);+}++// duckdb_create_bit requires duckdb_bit by value; copy from pointer for ease of use+// on the Haskell side.+duckdb_value wrapped_duckdb_create_bit(const duckdb_bit *input) {+  duckdb_bit value = {0};+  if (input) {+    value = *input;+  }+  return duckdb_create_bit(value);+}++// duckdb_create_uuid expects a duckdb_uhugeint by value; wrap it so NULL pointers+// are handled and the FFI can reuse the helper.+duckdb_value wrapped_duckdb_create_uuid(const duckdb_uhugeint *input) {+  duckdb_uhugeint value = {0};+  if (input) {+    value = *input;+  }+  return duckdb_create_uuid(value);+}++// duckdb_append_hugeint consumes a struct by value; copy from pointer before+// delegating to avoid marshalling issues from Haskell.+duckdb_state wrapped_duckdb_append_hugeint(duckdb_appender appender, const duckdb_hugeint *value) {+  duckdb_hugeint tmp = {0};+  if (value) {+    tmp = *value;+  }+  return duckdb_append_hugeint(appender, tmp);+}++// duckdb_append_uhugeint also needs the value struct by copy; pointer wrapper keeps+// the FFI simple and guards NULL.+duckdb_state wrapped_duckdb_append_uhugeint(duckdb_appender appender, const duckdb_uhugeint *value) {+  duckdb_uhugeint tmp = {0};+  if (value) {+    tmp = *value;+  }+  return duckdb_append_uhugeint(appender, tmp);+}++// duckdb_append_interval expects a duckdb_interval by value; use a pointer wrapper+// to appease the Haskell FFI and handle absent values.+duckdb_state wrapped_duckdb_append_interval(duckdb_appender appender, const duckdb_interval *value) {+  duckdb_interval tmp = {0};+  if (value) {+    tmp = *value;+  }+  return duckdb_append_interval(appender, tmp);+}++// duckdb_get_hugeint returns a struct by value; write the result into caller+// storage to avoid struct-return ABI issues in Haskell.+void wrapped_duckdb_get_hugeint(duckdb_value val, duckdb_hugeint *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_get_hugeint(val);+}++// duckdb_get_uhugeint returns a struct by value; same pointer-based extraction as+// wrapped_duckdb_get_hugeint.+void wrapped_duckdb_get_uhugeint(duckdb_value val, duckdb_uhugeint *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_get_uhugeint(val);+}++// duckdb_get_bignum returns a struct by value; write the result into an out pointer+// for the managed caller.+void wrapped_duckdb_get_bignum(duckdb_value val, duckdb_bignum *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_get_bignum(val);+}++// duckdb_get_decimal returns a struct by value; provide the same pointer-based+// access pattern as the other get wrappers.+void wrapped_duckdb_get_decimal(duckdb_value val, duckdb_decimal *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_get_decimal(val);+}++// duckdb_get_interval returns a struct by value; wrap it to write into caller+// memory.+void wrapped_duckdb_get_interval(duckdb_value val, duckdb_interval *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_get_interval(val);+}++// duckdb_get_blob returns duckdb_blob by value; expose an out-parameter wrapper for+// the FFI.+void wrapped_duckdb_get_blob(duckdb_value val, duckdb_blob *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_get_blob(val);+}++// duckdb_get_bit returns duckdb_bit by value; wrap it to fill caller-provided+// storage.+void wrapped_duckdb_get_bit(duckdb_value val, duckdb_bit *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_get_bit(val);+}++// duckdb_get_uuid returns duckdb_uhugeint by value; write into an out pointer for+// the managed binding.+void wrapped_duckdb_get_uuid(duckdb_value val, duckdb_uhugeint *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_get_uuid(val);+}++// duckdb_hugeint_to_double consumes its argument by value; copy from pointer so we+// can call it from Haskell without struct marshalling.+double wrapped_duckdb_hugeint_to_double(const duckdb_hugeint *value) {+  duckdb_hugeint tmp = {0};+  if (value) {+    tmp = *value;+  }+  return duckdb_hugeint_to_double(tmp);+}++// duckdb_double_to_hugeint returns a struct by value; provide an out pointer for+// the managed side.+void wrapped_duckdb_double_to_hugeint(double input, duckdb_hugeint *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_double_to_hugeint(input);+}++// duckdb_double_to_decimal returns duckdb_decimal by value; write into caller+// storage instead.+void wrapped_duckdb_double_to_decimal(double input, uint8_t width, uint8_t scale, duckdb_decimal *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_double_to_decimal(input, width, scale);+}++// duckdb_decimal_to_double consumes its argument by value; copy from pointer so+// the FFI can pass nullable inputs.+double wrapped_duckdb_decimal_to_double(const duckdb_decimal *value) {+  duckdb_decimal tmp = {0};+  if (value) {+    tmp = *value;+  }+  return duckdb_decimal_to_double(tmp);+}++// duckdb_from_date returns a struct by value; emit the result through an+// out-parameter to avoid struct-return plumbing on the Haskell side.+void wrapped_duckdb_from_date(duckdb_date date, duckdb_date_struct *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_from_date(date);+}++// duckdb_to_date expects a struct by value; copy from pointer so NULL can be passed+// and so the ABI stays simple.+duckdb_date wrapped_duckdb_to_date(const duckdb_date_struct *input) {+  duckdb_date_struct tmp = {0};+  if (input) {+    tmp = *input;+  }+  return duckdb_to_date(tmp);+}++// duckdb_from_time returns a struct by value; wrap to emit via an out pointer.+void wrapped_duckdb_from_time(duckdb_time time, duckdb_time_struct *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_from_time(time);+}++// duckdb_from_time_tz returns a struct by value; provide pointer-based output.+void wrapped_duckdb_from_time_tz(duckdb_time_tz micros, duckdb_time_tz_struct *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_from_time_tz(micros);+}++// duckdb_to_time expects a struct by value; copy from pointer for the bindings.+duckdb_time wrapped_duckdb_to_time(const duckdb_time_struct *input) {+  duckdb_time_struct tmp = {0};+  if (input) {+    tmp = *input;+  }+  return duckdb_to_time(tmp);+}++// duckdb_from_timestamp returns a struct by value; write into caller-provided+// storage instead.+void wrapped_duckdb_from_timestamp(duckdb_timestamp ts, duckdb_timestamp_struct *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_from_timestamp(ts);+}++// duckdb_to_timestamp expects a struct by value; copy from pointer before calling.+duckdb_timestamp wrapped_duckdb_to_timestamp(const duckdb_timestamp_struct *input) {+  duckdb_timestamp_struct tmp = {0};+  if (input) {+    tmp = *input;+  }+  return duckdb_to_timestamp(tmp);+}++// duckdb_uhugeint_to_double consumes its argument by value; copy from pointer to+// make it callable from Haskell.+double wrapped_duckdb_uhugeint_to_double(const duckdb_uhugeint *value) {+  duckdb_uhugeint tmp = {0};+  if (value) {+    tmp = *value;+  }+  return duckdb_uhugeint_to_double(tmp);+}++// duckdb_double_to_uhugeint returns a struct by value; write the result into caller+// storage instead of relying on struct-return ABI support.+void wrapped_duckdb_double_to_uhugeint(double input, duckdb_uhugeint *out_value) {+  if (!out_value) {+    return;+  }+  *out_value = duckdb_double_to_uhugeint(input);+}
+ duckdb-ffi.cabal view
@@ -0,0 +1,148 @@+cabal-version:      3.4+name:               duckdb-ffi+version:            0.1.4.1+author:             Matthias Pall Gissurarson+maintainer:         mpg@mpg.is+category:           Database+build-type:         Custom+license:            MPL-2.0+license-file:       LICENSE+synopsis:           Haskell FFI bindings for DuckDB+homepage:       https://github.com/Tritlo/duckdb-haskell+bug-reports:    https://github.com/Tritlo/duckdb-haskell/issues+description:+    This library provides low-level FFI bindings to DuckDB.+    Tested with DuckDB version 1.4.0, and 1.4.0.+    Exposes the full DuckDB C API.+    Note: requires libduckdb and duckdb.h to be installed on the system.+extra-doc-files:    CHANGELOG.md++source-repository head+  type:     git+  location: https://github.com/Tritlo/duckdb-haskell.git+  subdir:  duckdb-ffi+++custom-setup+  setup-depends: base >=4.18 && <4.21,+                 Cabal >=3.4 && <3.16,+                 directory >= 1.3 && <1.4,+                 filepath >= 1.4 && <1.5,+                 process >= 1.6 && <1.7,++library+  default-language:+    Haskell2010+  default-extensions:+        AllowAmbiguousTypes+      , DataKinds+      , GADTs+      , GeneralizedNewtypeDeriving+      , ImportQualifiedPost+      , LambdaCase+      , MultiParamTypeClasses+      , OverloadedStrings+      , RankNTypes+      , TypeApplications+      , ScopedTypeVariables+  hs-source-dirs:+    src+  exposed-modules:+    Database.DuckDB.FFI+  other-modules:+    Database.DuckDB.FFI.AggregateFunctions+    Database.DuckDB.FFI.Appender+    Database.DuckDB.FFI.Arrow+    Database.DuckDB.FFI.BindValues+    Database.DuckDB.FFI.CastFunctions+    Database.DuckDB.FFI.Configuration+    Database.DuckDB.FFI.DataChunk+    Database.DuckDB.FFI.ErrorData+    Database.DuckDB.FFI.ExecutePrepared+    Database.DuckDB.FFI.Expression+    Database.DuckDB.FFI.ExtractStatements+    Database.DuckDB.FFI.Helpers+    Database.DuckDB.FFI.LogicalTypes+    Database.DuckDB.FFI.OpenConnect+    Database.DuckDB.FFI.PendingResult+    Database.DuckDB.FFI.PreparedStatements+    Database.DuckDB.FFI.ProfilingInfo+    Database.DuckDB.FFI.QueryExecution+    Database.DuckDB.FFI.ReplacementScans+    Database.DuckDB.FFI.ResultFunctions+    Database.DuckDB.FFI.SafeFetch+    Database.DuckDB.FFI.ScalarFunctions+    Database.DuckDB.FFI.SelectionVector+    Database.DuckDB.FFI.StreamingResult+    Database.DuckDB.FFI.TableDescription+    Database.DuckDB.FFI.TableFunctions+    Database.DuckDB.FFI.Threading+    Database.DuckDB.FFI.Types+    Database.DuckDB.FFI.Validity+    Database.DuckDB.FFI.ValueInterface+    Database.DuckDB.FFI.Vector+  include-dirs: cbits/+  install-includes: duckdb.h+  extra-libraries: duckdb++  c-sources: cbits/duckdb_stub.c+  build-depends:+      base >=4.18 && <4.19+    , bytestring >= 0.11 && <0.12+    , containers >= 0.6 && <0.7+    , exceptions >= 0.10 && <0.11+    , mtl >= 2.3 && <2.4+    , text >= 2.0 && <2.1+    , transformers >= 0.6 && <0.7+    , time >= 1.12 && <1.13+  if os(linux)+    ghc-options:    -optl-Wl,-rpath,'$ORIGIN/../cbits/duckdb'+    ld-options:       -Wl,-rpath,'$ORIGIN/../cbits/duckdb'++test-suite duckdb-ffi-tests+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Main.hs+  other-modules:+    ConfigurationTest+    ErrorDataTest+    OpenConnectTest+    PreparedStatementsTest+    BindValuesTest+    ExecutePreparedStatementsTest+    ExtractStatementsTest+    LogicalTypesTest+    DataChunkTest+    VectorTest+    ValidityMaskTest+    ScalarFunctionsTest+    TableFunctionsTest+    SelectionVectorTest+    AggregateFunctionsTest+    PendingResultTest+    ValueInterfaceTest+    QueryExecutionTest+    ResultFunctionsTest+    SafeFetchTest+    HelpersTest+    TableDescriptionTest+    AppenderTest+    ProfilingInfoTest+    CastFunctionsTest+    ExpressionTest+    ReplacementScansTest+    ArrowInterfaceDeprecatedTests+    ArrowInterfaceTest+    StreamingResultTest+    ThreadingTest+    Utils+  ghc-options:        -threaded+  default-language:    Haskell2010+  build-depends:+      base >=4.18 && <4.19+    , duckdb-ffi+    , tasty >=1.5 && <1.6+    , tasty-hunit >=0.10 && <0.11+    , tasty-expected-failure >= 0.12 && < 0.13+    , text >= 2.0 && <2.1+    , time >= 1.12 && <1.13
+ src/Database/DuckDB/FFI.hs view
@@ -0,0 +1,73 @@+{- |+Module      : Database.DuckDB.FFI+Description : Aggregated re-exports for the DuckDB C API bindings++The individual modules under @Database.DuckDB.FFI.*@ group the raw FFI imports+by the sections documented in @api.md@.  This module preserves the historic+surface area by re-exporting all of those symbols.+-}+module Database.DuckDB.FFI (+    module Database.DuckDB.FFI.Types,+    module Database.DuckDB.FFI.OpenConnect,+    module Database.DuckDB.FFI.Configuration,+    module Database.DuckDB.FFI.ErrorData,+    module Database.DuckDB.FFI.QueryExecution,+    module Database.DuckDB.FFI.ResultFunctions,+    module Database.DuckDB.FFI.SafeFetch,+    module Database.DuckDB.FFI.Helpers,+    module Database.DuckDB.FFI.PreparedStatements,+    module Database.DuckDB.FFI.BindValues,+    module Database.DuckDB.FFI.ExecutePrepared,+    module Database.DuckDB.FFI.ExtractStatements,+    module Database.DuckDB.FFI.PendingResult,+    module Database.DuckDB.FFI.ValueInterface,+    module Database.DuckDB.FFI.LogicalTypes,+    module Database.DuckDB.FFI.DataChunk,+    module Database.DuckDB.FFI.Vector,+    module Database.DuckDB.FFI.Validity,+    module Database.DuckDB.FFI.ScalarFunctions,+    module Database.DuckDB.FFI.TableFunctions,+    module Database.DuckDB.FFI.SelectionVector,+    module Database.DuckDB.FFI.AggregateFunctions,+    module Database.DuckDB.FFI.ReplacementScans,+    module Database.DuckDB.FFI.ProfilingInfo,+    module Database.DuckDB.FFI.Appender,+    module Database.DuckDB.FFI.TableDescription,+    module Database.DuckDB.FFI.Arrow,+    module Database.DuckDB.FFI.Threading,+    module Database.DuckDB.FFI.StreamingResult,+    module Database.DuckDB.FFI.CastFunctions,+    module Database.DuckDB.FFI.Expression,+) where++import Database.DuckDB.FFI.AggregateFunctions+import Database.DuckDB.FFI.Appender+import Database.DuckDB.FFI.Arrow+import Database.DuckDB.FFI.BindValues+import Database.DuckDB.FFI.CastFunctions+import Database.DuckDB.FFI.Configuration+import Database.DuckDB.FFI.DataChunk+import Database.DuckDB.FFI.ErrorData+import Database.DuckDB.FFI.ExecutePrepared+import Database.DuckDB.FFI.Expression+import Database.DuckDB.FFI.ExtractStatements+import Database.DuckDB.FFI.Helpers+import Database.DuckDB.FFI.LogicalTypes+import Database.DuckDB.FFI.OpenConnect+import Database.DuckDB.FFI.PendingResult+import Database.DuckDB.FFI.PreparedStatements+import Database.DuckDB.FFI.ProfilingInfo+import Database.DuckDB.FFI.QueryExecution+import Database.DuckDB.FFI.ReplacementScans+import Database.DuckDB.FFI.ResultFunctions+import Database.DuckDB.FFI.SafeFetch+import Database.DuckDB.FFI.ScalarFunctions+import Database.DuckDB.FFI.SelectionVector+import Database.DuckDB.FFI.StreamingResult+import Database.DuckDB.FFI.TableDescription+import Database.DuckDB.FFI.TableFunctions+import Database.DuckDB.FFI.Threading+import Database.DuckDB.FFI.Types+import Database.DuckDB.FFI.Validity+import Database.DuckDB.FFI.ValueInterface+import Database.DuckDB.FFI.Vector
+ src/Database/DuckDB/FFI/AggregateFunctions.hs view
@@ -0,0 +1,182 @@+module Database.DuckDB.FFI.AggregateFunctions (+    c_duckdb_create_aggregate_function,+    c_duckdb_destroy_aggregate_function,+    c_duckdb_aggregate_function_set_name,+    c_duckdb_aggregate_function_add_parameter,+    c_duckdb_aggregate_function_set_return_type,+    c_duckdb_aggregate_function_set_functions,+    c_duckdb_aggregate_function_set_destructor,+    c_duckdb_register_aggregate_function,+    c_duckdb_aggregate_function_set_special_handling,+    c_duckdb_aggregate_function_set_extra_info,+    c_duckdb_aggregate_function_get_extra_info,+    c_duckdb_aggregate_function_set_error,+    c_duckdb_create_aggregate_function_set,+    c_duckdb_destroy_aggregate_function_set,+    c_duckdb_add_aggregate_function_to_set,+    c_duckdb_register_aggregate_function_set,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CInt (..))+import Foreign.Ptr (Ptr)++{- | Creates a new empty aggregate function.++The return value should be destroyed with @duckdb_destroy_aggregate_function@.++Returns The aggregate function object.+-}+foreign import ccall unsafe "duckdb_create_aggregate_function"+    c_duckdb_create_aggregate_function :: IO DuckDBAggregateFunction++-- | Destroys the given aggregate function object.+foreign import ccall unsafe "duckdb_destroy_aggregate_function"+    c_duckdb_destroy_aggregate_function :: Ptr DuckDBAggregateFunction -> IO ()++{- | Sets the name of the given aggregate function.++Parameters:+* @aggregate_function@: The aggregate function+* @name@: The name of the aggregate function+-}+foreign import ccall unsafe "duckdb_aggregate_function_set_name"+    c_duckdb_aggregate_function_set_name :: DuckDBAggregateFunction -> CString -> IO ()++{- | Adds a parameter to the aggregate function.++Parameters:+* @aggregate_function@: The aggregate function.+* @type@: The parameter type. Cannot contain INVALID.+-}+foreign import ccall unsafe "duckdb_aggregate_function_add_parameter"+    c_duckdb_aggregate_function_add_parameter :: DuckDBAggregateFunction -> DuckDBLogicalType -> IO ()++{- | Sets the return type of the aggregate function.++Parameters:+* @aggregate_function@: The aggregate function.+* @type@: The return type. Cannot contain INVALID or ANY.+-}+foreign import ccall unsafe "duckdb_aggregate_function_set_return_type"+    c_duckdb_aggregate_function_set_return_type :: DuckDBAggregateFunction -> DuckDBLogicalType -> IO ()++{- | Sets the main functions of the aggregate function.++Parameters:+* @aggregate_function@: The aggregate function+* @state_size@: state size+* @state_init@: state init function+* @update@: update states+* @combine@: combine states+* @finalize@: finalize states+-}+foreign import ccall unsafe "duckdb_aggregate_function_set_functions"+    c_duckdb_aggregate_function_set_functions :: DuckDBAggregateFunction -> DuckDBAggregateStateSizeFun -> DuckDBAggregateInitFun -> DuckDBAggregateUpdateFun -> DuckDBAggregateCombineFun -> DuckDBAggregateFinalizeFun -> IO ()++{- | Sets the state destructor callback of the aggregate function (optional)++Parameters:+* @aggregate_function@: The aggregate function+* @destroy@: state destroy callback+-}+foreign import ccall unsafe "duckdb_aggregate_function_set_destructor"+    c_duckdb_aggregate_function_set_destructor :: DuckDBAggregateFunction -> DuckDBAggregateDestroyFun -> IO ()++{- | Register the aggregate function object within the given connection.++The function requires at least a name, functions and a return type.++If the function is incomplete or a function with this name already exists+DuckDBError is returned.++Parameters:+* @con@: The connection to register it in.++Returns Whether or not the registration was successful.+-}+foreign import ccall unsafe "duckdb_register_aggregate_function"+    c_duckdb_register_aggregate_function :: DuckDBConnection -> DuckDBAggregateFunction -> IO DuckDBState++{- | Sets the NULL handling of the aggregate function to SPECIAL_HANDLING.++Parameters:+* @aggregate_function@: The aggregate function+-}+foreign import ccall unsafe "duckdb_aggregate_function_set_special_handling"+    c_duckdb_aggregate_function_set_special_handling :: DuckDBAggregateFunction -> IO ()++{- | Assigns extra information to the scalar function that can be fetched during+binding, etc.++Parameters:+* @aggregate_function@: The aggregate function+* @extra_info@: The extra information+* @destroy@: The callback that will be called to destroy the extra information+  (if any)+-}+foreign import ccall unsafe "duckdb_aggregate_function_set_extra_info"+    c_duckdb_aggregate_function_set_extra_info :: DuckDBAggregateFunction -> Ptr () -> DuckDBDeleteCallback -> IO ()++{- | Retrieves the extra info of the function as set in+@duckdb_aggregate_function_set_extra_info@.++Parameters:+* @info@: The info object++Returns The extra info+-}+foreign import ccall safe "duckdb_aggregate_function_get_extra_info"+    c_duckdb_aggregate_function_get_extra_info :: DuckDBFunctionInfo -> IO (Ptr ())++{- | Report that an error has occurred while executing the aggregate function.++Parameters:+* @info@: The info object+* @error@: The error message+-}+foreign import ccall safe "duckdb_aggregate_function_set_error"+    c_duckdb_aggregate_function_set_error :: DuckDBFunctionInfo -> CString -> IO ()++{- | Creates a new empty aggregate function set.++The return value should be destroyed with+@duckdb_destroy_aggregate_function_set@.++Returns The aggregate function set object.+-}+foreign import ccall unsafe "duckdb_create_aggregate_function_set"+    c_duckdb_create_aggregate_function_set :: CString -> IO DuckDBAggregateFunctionSet++-- | Destroys the given aggregate function set object.+foreign import ccall unsafe "duckdb_destroy_aggregate_function_set"+    c_duckdb_destroy_aggregate_function_set :: Ptr DuckDBAggregateFunctionSet -> IO ()++{- | Adds the aggregate function as a new overload to the aggregate function set.++Returns DuckDBError if the function could not be added, for example if the+overload already exists.++Parameters:+* @set@: The aggregate function set+* @function@: The function to add+-}+foreign import ccall unsafe "duckdb_add_aggregate_function_to_set"+    c_duckdb_add_aggregate_function_to_set :: DuckDBAggregateFunctionSet -> DuckDBAggregateFunction -> IO DuckDBState++{- | Register the aggregate function set within the given connection.++The set requires at least a single valid overload.++If the set is incomplete or a function with this name already exists+DuckDBError is returned.++Parameters:+* @con@: The connection to register it in.+* @set@: The function set to register++Returns Whether or not the registration was successful.+-}+foreign import ccall unsafe "duckdb_register_aggregate_function_set"+    c_duckdb_register_aggregate_function_set :: DuckDBConnection -> DuckDBAggregateFunctionSet -> IO DuckDBState
+ src/Database/DuckDB/FFI/Appender.hs view
@@ -0,0 +1,382 @@+module Database.DuckDB.FFI.Appender (+    c_duckdb_appender_create,+    c_duckdb_appender_create_ext,+    c_duckdb_appender_create_query,+    c_duckdb_appender_column_count,+    c_duckdb_appender_column_type,+    c_duckdb_appender_error,+    c_duckdb_appender_error_data,+    c_duckdb_appender_flush,+    c_duckdb_appender_close,+    c_duckdb_appender_destroy,+    c_duckdb_appender_add_column,+    c_duckdb_appender_clear_columns,+    c_duckdb_appender_begin_row,+    c_duckdb_appender_end_row,+    c_duckdb_append_default,+    c_duckdb_append_default_to_chunk,+    c_duckdb_append_bool,+    c_duckdb_append_int8,+    c_duckdb_append_int16,+    c_duckdb_append_int32,+    c_duckdb_append_int64,+    c_duckdb_append_hugeint,+    c_duckdb_append_uint8,+    c_duckdb_append_uint16,+    c_duckdb_append_uint32,+    c_duckdb_append_uint64,+    c_duckdb_append_uhugeint,+    c_duckdb_append_float,+    c_duckdb_append_double,+    c_duckdb_append_date,+    c_duckdb_append_time,+    c_duckdb_append_timestamp,+    c_duckdb_append_interval,+    c_duckdb_append_varchar,+    c_duckdb_append_varchar_length,+    c_duckdb_append_blob,+    c_duckdb_append_null,+    c_duckdb_append_value,+    c_duckdb_append_data_chunk,+) where++import Data.Int (Int16, Int32, Int64, Int8)+import Data.Word (Word16, Word32, Word64, Word8)+import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CBool (..), CDouble (..), CFloat (..), CInt (..))+import Foreign.Ptr (Ptr)++{- | Creates an appender object.++Note that the object must be destroyed with @duckdb_appender_destroy@.++Parameters:+* @connection@: The connection context to create the appender in.+* @schema@: The schema of the table to append to, or @nullptr@ for the default+  schema.+* @table@: The table name to append to.+* @out_appender@: The resulting appender object.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_appender_create"+    c_duckdb_appender_create :: DuckDBConnection -> CString -> CString -> Ptr DuckDBAppender -> IO DuckDBState++{- | Creates an appender object.++Note that the object must be destroyed with @duckdb_appender_destroy@.++Parameters:+* @connection@: The connection context to create the appender in.+* @catalog@: The catalog of the table to append to, or @nullptr@ for the+  default catalog.+* @schema@: The schema of the table to append to, or @nullptr@ for the default+  schema.+* @table@: The table name to append to.+* @out_appender@: The resulting appender object.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_appender_create_ext"+    c_duckdb_appender_create_ext :: DuckDBConnection -> CString -> CString -> CString -> Ptr DuckDBAppender -> IO DuckDBState++{- | Creates an appender object that executes the given query with any data+appended to it.++Note that the object must be destroyed with @duckdb_appender_destroy@.++Parameters:+* @connection@: The connection context to create the appender in.+* @query@: The query to execute, can be an INSERT, DELETE, UPDATE or MERGE+  INTO statement.+* @column_count@: The number of columns to append.+* @types@: The types of the columns to append.+* @table_name@: (optionally) the table name used to refer to the appended+  data, defaults to "appended_data".+* @column_names@: (optionally) the list of column names, defaults to "col1",+  "col2", ...+* @out_appender@: The resulting appender object.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_appender_create_query"+    c_duckdb_appender_create_query :: DuckDBConnection -> CString -> DuckDBIdx -> Ptr DuckDBLogicalType -> CString -> Ptr CString -> Ptr DuckDBAppender -> IO DuckDBState++{- | Returns the number of columns that belong to the appender. If there is no+active column list, then this equals the table's physical columns.++Parameters:+* @appender@: The appender to get the column count from.++Returns The number of columns in the data chunks.+-}+foreign import ccall safe "duckdb_appender_column_count"+    c_duckdb_appender_column_count :: DuckDBAppender -> IO DuckDBIdx++{- | Returns the type of the column at the specified index. This is either a type+in the active column list, or the same type as a column in the receiving+table.++Note: The resulting type must be destroyed with @duckdb_destroy_logical_type@.++Parameters:+* @appender@: The appender to get the column type from.+* @col_idx@: The index of the column to get the type of.++Returns The @duckdb_logical_type@ of the column.+-}+foreign import ccall safe "duckdb_appender_column_type"+    c_duckdb_appender_column_type :: DuckDBAppender -> DuckDBIdx -> IO DuckDBLogicalType++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release. Use duckdb_appender_error_data instead.++Returns the error message associated with the appender. If the appender has no+error message, this returns @nullptr@ instead.++The error message should not be freed. It will be de-allocated when+@duckdb_appender_destroy@ is called.++Parameters:+* @appender@: The appender to get the error from.++Returns The error message, or @nullptr@ if there is none.+-}+foreign import ccall safe "duckdb_appender_error"+    c_duckdb_appender_error :: DuckDBAppender -> IO CString++{- | Returns the error data associated with the appender. Must be destroyed with+duckdb_destroy_error_data.++Parameters:+* @appender@: The appender to get the error data from.++Returns The error data.+-}+foreign import ccall safe "duckdb_appender_error_data"+    c_duckdb_appender_error_data :: DuckDBAppender -> IO DuckDBErrorData++{- | Flush the appender to the table, forcing the cache of the appender to be+cleared. If flushing the data triggers a constraint violation or any other+error, then all data is invalidated, and this function returns DuckDBError. It+is not possible to append more values. Call duckdb_appender_error_data to+obtain the error data followed by duckdb_appender_destroy to destroy the+invalidated appender.++Parameters:+* @appender@: The appender to flush.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_appender_flush"+    c_duckdb_appender_flush :: DuckDBAppender -> IO DuckDBState++{- | Closes the appender by flushing all intermediate states and closing it for+further appends. If flushing the data triggers a constraint violation or any+other error, then all data is invalidated, and this function returns+DuckDBError. Call duckdb_appender_error_data to obtain the error data followed+by duckdb_appender_destroy to destroy the invalidated appender.++Parameters:+* @appender@: The appender to flush and close.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_appender_close"+    c_duckdb_appender_close :: DuckDBAppender -> IO DuckDBState++{- | Closes the appender by flushing all intermediate states to the table and+destroying it. By destroying it, this function de-allocates all memory+associated with the appender. If flushing the data triggers a constraint+violation, then all data is invalidated, and this function returns+DuckDBError. Due to the destruction of the appender, it is no longer possible+to obtain the specific error message with duckdb_appender_error. Therefore,+call duckdb_appender_close before destroying the appender, if you need+insights into the specific error.++Parameters:+* @appender@: The appender to flush, close and destroy.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_appender_destroy"+    c_duckdb_appender_destroy :: Ptr DuckDBAppender -> IO DuckDBState++{- | Appends a column to the active column list of the appender. Immediately+flushes all previous data.++The active column list specifies all columns that are expected when flushing+the data. Any non-active columns are filled with their default values, or+NULL.++Parameters:+* @appender@: The appender to add the column to.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_appender_add_column"+    c_duckdb_appender_add_column :: DuckDBAppender -> CString -> IO DuckDBState++{- | Removes all columns from the active column list of the appender, resetting the+appender to treat all columns as active. Immediately flushes all previous+data.++Parameters:+* @appender@: The appender to clear the columns from.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_appender_clear_columns"+    c_duckdb_appender_clear_columns :: DuckDBAppender -> IO DuckDBState++{- | A nop function, provided for backwards compatibility reasons. Does nothing.+Only @duckdb_appender_end_row@ is required.+-}+foreign import ccall safe "duckdb_appender_begin_row"+    c_duckdb_appender_begin_row :: DuckDBAppender -> IO DuckDBState++{- | Finish the current row of appends. After end_row is called, the next row can+be appended.++Parameters:+* @appender@: The appender.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_appender_end_row"+    c_duckdb_appender_end_row :: DuckDBAppender -> IO DuckDBState++{- | Append a DEFAULT value (NULL if DEFAULT not available for column) to the+appender.+-}+foreign import ccall safe "duckdb_append_default"+    c_duckdb_append_default :: DuckDBAppender -> IO DuckDBState++{- | Append a DEFAULT value, at the specified row and column, (NULL if DEFAULT not+available for column) to the chunk created from the specified appender. The+default value of the column must be a constant value. Non-deterministic+expressions like nextval(@seq@) or random() are not supported.++Parameters:+* @appender@: The appender to get the default value from.+* @chunk@: The data chunk to append the default value to.+* @col@: The chunk column index to append the default value to.+* @row@: The chunk row index to append the default value to.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_append_default_to_chunk"+    c_duckdb_append_default_to_chunk :: DuckDBAppender -> DuckDBDataChunk -> DuckDBIdx -> DuckDBIdx -> IO DuckDBState++-- | Append a bool value to the appender.+foreign import ccall safe "duckdb_append_bool"+    c_duckdb_append_bool :: DuckDBAppender -> CBool -> IO DuckDBState++-- | Append an int8_t value to the appender.+foreign import ccall safe "duckdb_append_int8"+    c_duckdb_append_int8 :: DuckDBAppender -> Int8 -> IO DuckDBState++-- | Append an int16_t value to the appender.+foreign import ccall safe "duckdb_append_int16"+    c_duckdb_append_int16 :: DuckDBAppender -> Int16 -> IO DuckDBState++-- | Append an int32_t value to the appender.+foreign import ccall safe "duckdb_append_int32"+    c_duckdb_append_int32 :: DuckDBAppender -> Int32 -> IO DuckDBState++-- | Append an int64_t value to the appender.+foreign import ccall safe "duckdb_append_int64"+    c_duckdb_append_int64 :: DuckDBAppender -> Int64 -> IO DuckDBState++{- | Append a duckdb_hugeint value to the appender.++These bindings call the wrapper symbol @wrapped_duckdb_append_hugeint@+but mirror the DuckDB C API semantics of @duckdb_append_hugeint@.+-}+foreign import ccall safe "wrapped_duckdb_append_hugeint"+    c_duckdb_append_hugeint :: DuckDBAppender -> Ptr DuckDBHugeInt -> IO DuckDBState++-- | Append a uint8_t value to the appender.+foreign import ccall safe "duckdb_append_uint8"+    c_duckdb_append_uint8 :: DuckDBAppender -> Word8 -> IO DuckDBState++-- | Append a uint16_t value to the appender.+foreign import ccall safe "duckdb_append_uint16"+    c_duckdb_append_uint16 :: DuckDBAppender -> Word16 -> IO DuckDBState++-- | Append a uint32_t value to the appender.+foreign import ccall safe "duckdb_append_uint32"+    c_duckdb_append_uint32 :: DuckDBAppender -> Word32 -> IO DuckDBState++-- | Append a uint64_t value to the appender.+foreign import ccall safe "duckdb_append_uint64"+    c_duckdb_append_uint64 :: DuckDBAppender -> Word64 -> IO DuckDBState++{- | Append a duckdb_uhugeint value to the appender.++These bindings call the wrapper symbol @wrapped_duckdb_append_uhugeint@+but mirror the DuckDB C API semantics of @duckdb_append_uhugeint@.+-}+foreign import ccall safe "wrapped_duckdb_append_uhugeint"+    c_duckdb_append_uhugeint :: DuckDBAppender -> Ptr DuckDBUHugeInt -> IO DuckDBState++-- | Append a float value to the appender.+foreign import ccall safe "duckdb_append_float"+    c_duckdb_append_float :: DuckDBAppender -> CFloat -> IO DuckDBState++-- | Append a double value to the appender.+foreign import ccall safe "duckdb_append_double"+    c_duckdb_append_double :: DuckDBAppender -> CDouble -> IO DuckDBState++-- | Append a duckdb_date value to the appender.+foreign import ccall safe "duckdb_append_date"+    c_duckdb_append_date :: DuckDBAppender -> DuckDBDate -> IO DuckDBState++-- | Append a duckdb_time value to the appender.+foreign import ccall safe "duckdb_append_time"+    c_duckdb_append_time :: DuckDBAppender -> DuckDBTime -> IO DuckDBState++-- | Append a duckdb_timestamp value to the appender.+foreign import ccall safe "duckdb_append_timestamp"+    c_duckdb_append_timestamp :: DuckDBAppender -> DuckDBTimestamp -> IO DuckDBState++{- | Append a duckdb_interval value to the appender.++These bindings call the wrapper symbol @wrapped_duckdb_append_interval@+but mirror the DuckDB C API semantics of @duckdb_append_interval@.+-}+foreign import ccall safe "wrapped_duckdb_append_interval"+    c_duckdb_append_interval :: DuckDBAppender -> Ptr DuckDBInterval -> IO DuckDBState++-- | Append a varchar value to the appender.+foreign import ccall safe "duckdb_append_varchar"+    c_duckdb_append_varchar :: DuckDBAppender -> CString -> IO DuckDBState++-- | Append a varchar value to the appender.+foreign import ccall safe "duckdb_append_varchar_length"+    c_duckdb_append_varchar_length :: DuckDBAppender -> CString -> DuckDBIdx -> IO DuckDBState++-- | Append a blob value to the appender.+foreign import ccall safe "duckdb_append_blob"+    c_duckdb_append_blob :: DuckDBAppender -> Ptr () -> DuckDBIdx -> IO DuckDBState++-- | Append a NULL value to the appender (of any type).+foreign import ccall safe "duckdb_append_null"+    c_duckdb_append_null :: DuckDBAppender -> IO DuckDBState++-- | Append a duckdb_value to the appender.+foreign import ccall safe "duckdb_append_value"+    c_duckdb_append_value :: DuckDBAppender -> DuckDBValue -> IO DuckDBState++{- | Appends a pre-filled data chunk to the specified appender. Attempts casting,+if the data chunk types do not match the active appender types.++Parameters:+* @appender@: The appender to append to.+* @chunk@: The data chunk to append.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_append_data_chunk"+    c_duckdb_append_data_chunk :: DuckDBAppender -> DuckDBDataChunk -> IO DuckDBState
+ src/Database/DuckDB/FFI/Arrow.hs view
@@ -0,0 +1,395 @@+module Database.DuckDB.FFI.Arrow (+    c_duckdb_to_arrow_schema,+    c_duckdb_data_chunk_to_arrow,+    c_duckdb_schema_from_arrow,+    c_duckdb_data_chunk_from_arrow,+    c_duckdb_destroy_arrow_converted_schema,+    c_duckdb_query_arrow,+    c_duckdb_query_arrow_schema,+    c_duckdb_prepared_arrow_schema,+    c_duckdb_result_arrow_array,+    c_duckdb_query_arrow_array,+    c_duckdb_arrow_column_count,+    c_duckdb_arrow_row_count,+    c_duckdb_arrow_rows_changed,+    c_duckdb_query_arrow_error,+    c_duckdb_destroy_arrow,+    c_duckdb_destroy_arrow_stream,+    c_duckdb_execute_prepared_arrow,+    c_duckdb_arrow_scan,+    c_duckdb_arrow_array_scan,+    duckdbArrowSchemaInternal,+    duckdbArrowSchemaClear,+    duckdbArrowArrayInternal,+    duckdbArrowArrayClear,+    duckdbArrowStreamInternal,+    duckdbArrowStreamClear,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CInt (..))+import Foreign.Ptr (Ptr, castPtr, nullPtr)++{- | Transforms a DuckDB Schema into an Arrow Schema++Parameters:+* @arrow_options@: The Arrow settings used to produce arrow.+* @types@: The DuckDB logical types for each column in the schema.+* @names@: The names for each column in the schema.+* @column_count@: The number of columns that exist in the schema.+* @out_schema@: The resulting arrow schema. Must be destroyed with+  @out_schema->release(out_schema)@.++Returns The error data. Must be destroyed with @duckdb_destroy_error_data@.+-}+foreign import ccall unsafe "duckdb_to_arrow_schema"+    c_duckdb_to_arrow_schema :: DuckDBArrowOptions -> Ptr DuckDBLogicalType -> Ptr CString -> DuckDBIdx -> Ptr ArrowSchema -> IO DuckDBErrorData++{- | Transforms a DuckDB data chunk into an Arrow array.++Parameters:+* @arrow_options@: The Arrow settings used to produce arrow.+* @chunk@: The DuckDB data chunk to convert.+* @out_arrow_array@: The output Arrow structure that will hold the converted+  data. Must be released with @out_arrow_array->release(out_arrow_array)@++Returns The error data. Must be destroyed with @duckdb_destroy_error_data@.+-}+foreign import ccall unsafe "duckdb_data_chunk_to_arrow"+    c_duckdb_data_chunk_to_arrow :: DuckDBArrowOptions -> DuckDBDataChunk -> Ptr ArrowArray -> IO DuckDBErrorData++{- | Transforms an Arrow Schema into a DuckDB Schema.++Parameters:+* @connection@: The connection to get the transformation settings from.+* @schema@: The input Arrow schema. Must be released with+  @schema->release(schema)@.+* @out_types@: The Arrow converted schema with extra information about the+  arrow types. Must be destroyed with @duckdb_destroy_arrow_converted_schema@.++Returns The error data. Must be destroyed with @duckdb_destroy_error_data@.+-}+foreign import ccall unsafe "duckdb_schema_from_arrow"+    c_duckdb_schema_from_arrow :: DuckDBConnection -> Ptr ArrowSchema -> Ptr DuckDBArrowConvertedSchema -> IO DuckDBErrorData++{- | Transforms an Arrow array into a DuckDB data chunk. The data chunk will retain+ownership of the underlying Arrow data.++Parameters:+* @connection@: The connection to get the transformation settings from.+* @arrow_array@: The input Arrow array. Data ownership is passed on to+  DuckDB's DataChunk, the underlying object does not need to be released and+  won't have ownership of the data.+* @converted_schema@: The Arrow converted schema with extra information about+  the arrow types.+* @out_chunk@: The resulting DuckDB data chunk. Must be destroyed by+  duckdb_destroy_data_chunk.++Returns The error data. Must be destroyed with @duckdb_destroy_error_data@.+-}+foreign import ccall unsafe "duckdb_data_chunk_from_arrow"+    c_duckdb_data_chunk_from_arrow :: DuckDBConnection -> Ptr ArrowArray -> DuckDBArrowConvertedSchema -> Ptr DuckDBDataChunk -> IO DuckDBErrorData++{- | Destroys the arrow converted schema and de-allocates all memory allocated for+that arrow converted schema.++Parameters:+* @arrow_converted_schema@: The arrow converted schema to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_arrow_converted_schema"+    c_duckdb_destroy_arrow_converted_schema :: Ptr DuckDBArrowConvertedSchema -> IO ()++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Executes a SQL query within a connection and stores the full (materialized)+result in an arrow structure. If the query fails to execute, DuckDBError is+returned and the error message can be retrieved by calling+@duckdb_query_arrow_error@.++Note that after running @duckdb_query_arrow@, @duckdb_destroy_arrow@ must be+called on the result object even if the query fails, otherwise the error+stored within the result will not be freed correctly.++Parameters:+* @connection@: The connection to perform the query in.+* @query@: The SQL query to run.+* @out_result@: The query result.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_query_arrow"+    c_duckdb_query_arrow :: DuckDBConnection -> CString -> Ptr DuckDBArrow -> IO DuckDBState++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Fetch the internal arrow schema from the arrow result. Remember to call+release on the respective ArrowSchema object.++Parameters:+* @result@: The result to fetch the schema from.+* @out_schema@: The output schema.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall unsafe "duckdb_query_arrow_schema"+    c_duckdb_query_arrow_schema :: DuckDBArrow -> Ptr DuckDBArrowSchema -> IO DuckDBState++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Fetch the internal arrow schema from the prepared statement. Remember to call+release on the respective ArrowSchema object.++Parameters:+* @prepared@: The prepared statement to fetch the schema from.+* @out_schema@: The output schema.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall unsafe "duckdb_prepared_arrow_schema"+    c_duckdb_prepared_arrow_schema :: DuckDBPreparedStatement -> Ptr DuckDBArrowSchema -> IO DuckDBState++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Convert a data chunk into an arrow struct array. Remember to call release on+the respective ArrowArray object.++Parameters:+* @result@: The result object the data chunk have been fetched from.+* @chunk@: The data chunk to convert.+* @out_array@: The output array.++These bindings call the wrapper symbol+@wrapped_duckdb_result_arrow_array@ but mirror the DuckDB C API semantics of+@duckdb_result_arrow_array@.+-}+foreign import ccall unsafe "wrapped_duckdb_result_arrow_array"+    c_duckdb_result_arrow_array :: Ptr DuckDBResult -> DuckDBDataChunk -> Ptr DuckDBArrowArray -> IO ()++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Fetch an internal arrow struct array from the arrow result. Remember to call+release on the respective ArrowArray object.++This function can be called multiple time to get next chunks, which will free+the previous out_array. So consume the out_array before calling this function+again.++Parameters:+* @result@: The result to fetch the array from.+* @out_array@: The output array.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_query_arrow_array"+    c_duckdb_query_arrow_array :: DuckDBArrow -> Ptr DuckDBArrowArray -> IO DuckDBState++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns the number of columns present in the arrow result object.++Parameters:+* @result@: The result object.++Returns The number of columns present in the result object.+-}+foreign import ccall unsafe "duckdb_arrow_column_count"+    c_duckdb_arrow_column_count :: DuckDBArrow -> IO DuckDBIdx++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns the number of rows present in the arrow result object.++Parameters:+* @result@: The result object.++Returns The number of rows present in the result object.+-}+foreign import ccall unsafe "duckdb_arrow_row_count"+    c_duckdb_arrow_row_count :: DuckDBArrow -> IO DuckDBIdx++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns the number of rows changed by the query stored in the arrow result.+This is relevant only for INSERT/UPDATE/DELETE queries. For other queries the+rows_changed will be 0.++Parameters:+* @result@: The result object.++Returns The number of rows changed.+-}+foreign import ccall unsafe "duckdb_arrow_rows_changed"+    c_duckdb_arrow_rows_changed :: DuckDBArrow -> IO DuckDBIdx++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns the error message contained within the result. The error is only set+if @duckdb_query_arrow@ returns @DuckDBError@.++The error message should not be freed. It will be de-allocated when+@duckdb_destroy_arrow@ is called.++Parameters:+* @result@: The result object to fetch the error from.++Returns The error of the result.+-}+foreign import ccall unsafe "duckdb_query_arrow_error"+    c_duckdb_query_arrow_error :: DuckDBArrow -> IO CString++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Closes the result and de-allocates all memory allocated for the arrow result.++Parameters:+* @result@: The result to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_arrow"+    c_duckdb_destroy_arrow :: Ptr DuckDBArrow -> IO ()++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Releases the arrow array stream and de-allocates its memory.++Parameters:+* @stream_p@: The arrow array stream to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_arrow_stream"+    c_duckdb_destroy_arrow_stream :: Ptr DuckDBArrowStream -> IO ()++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Executes the prepared statement with the given bound parameters, and returns+an arrow query result. Note that after running+@duckdb_execute_prepared_arrow@, @duckdb_destroy_arrow@ must be called on the+result object.++Parameters:+* @prepared_statement@: The prepared statement to execute.+* @out_result@: The query result.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_execute_prepared_arrow"+    c_duckdb_execute_prepared_arrow :: DuckDBPreparedStatement -> Ptr DuckDBArrow -> IO DuckDBState++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Scans the Arrow stream and creates a view with the given name.++Parameters:+* @connection@: The connection on which to execute the scan.+* @table_name@: Name of the temporary view to create.+* @arrow@: Arrow stream wrapper.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_arrow_scan"+    c_duckdb_arrow_scan :: DuckDBConnection -> CString -> DuckDBArrowStream -> IO DuckDBState++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Scans the Arrow array and creates a view with the given name. Note that after+running @duckdb_arrow_array_scan@, @duckdb_destroy_arrow_stream@ must be+called on the out stream.++Parameters:+* @connection@: The connection on which to execute the scan.+* @table_name@: Name of the temporary view to create.+* @arrow_schema@: Arrow schema wrapper.+* @arrow_array@: Arrow array wrapper.+* @out_stream@: Output array stream that wraps around the passed schema, for+  releasing/deleting once done.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_arrow_array_scan"+    c_duckdb_arrow_array_scan :: DuckDBConnection -> CString -> DuckDBArrowSchema -> DuckDBArrowArray -> Ptr DuckDBArrowStream -> IO DuckDBState++foreign import ccall unsafe "wrapped_duckdb_arrow_schema_internal_ptr"+    c_duckdb_arrow_schema_internal_ptr :: DuckDBArrowSchema -> IO (Ptr ())++foreign import ccall unsafe "wrapped_duckdb_arrow_schema_clear_internal_ptr"+    c_duckdb_arrow_schema_clear_internal_ptr :: DuckDBArrowSchema -> IO ()++foreign import ccall unsafe "wrapped_duckdb_arrow_array_internal_ptr"+    c_duckdb_arrow_array_internal_ptr :: DuckDBArrowArray -> IO (Ptr ())++foreign import ccall unsafe "wrapped_duckdb_arrow_array_clear_internal_ptr"+    c_duckdb_arrow_array_clear_internal_ptr :: DuckDBArrowArray -> IO ()++foreign import ccall unsafe "wrapped_duckdb_arrow_stream_internal_ptr"+    c_duckdb_arrow_stream_internal_ptr :: DuckDBArrowStream -> IO (Ptr ())++foreign import ccall unsafe "wrapped_duckdb_arrow_stream_clear_internal_ptr"+    c_duckdb_arrow_stream_clear_internal_ptr :: DuckDBArrowStream -> IO ()++{- | Read the @internal_ptr@ field of a deprecated Arrow schema wrapper.+Returns 'Nothing' when the wrapper is null or DuckDB has already cleared the+pointer.+-}+duckdbArrowSchemaInternal :: DuckDBArrowSchema -> IO (Maybe ArrowSchemaPtr)+duckdbArrowSchemaInternal schema = do+    raw <- c_duckdb_arrow_schema_internal_ptr schema+    pure $+        if raw == nullPtr+            then Nothing+            else Just (ArrowSchemaPtr (castPtr raw))++{- | Clear the @internal_ptr@ field on a deprecated Arrow schema wrapper.+Useful after copying the schema to application-managed storage.+-}+duckdbArrowSchemaClear :: DuckDBArrowSchema -> IO ()+duckdbArrowSchemaClear = c_duckdb_arrow_schema_clear_internal_ptr++{- | Read the @internal_ptr@ field of a deprecated Arrow array wrapper.+Returns 'Nothing' when the wrapper is null or the internal pointer has been+cleared by DuckDB.+-}+duckdbArrowArrayInternal :: DuckDBArrowArray -> IO (Maybe ArrowArrayPtr)+duckdbArrowArrayInternal array = do+    raw <- c_duckdb_arrow_array_internal_ptr array+    pure $+        if raw == nullPtr+            then Nothing+            else Just (ArrowArrayPtr (castPtr raw))++{- | Clear the @internal_ptr@ field on a deprecated Arrow array wrapper.+After this call DuckDB treats the wrapper as null.+-}+duckdbArrowArrayClear :: DuckDBArrowArray -> IO ()+duckdbArrowArrayClear = c_duckdb_arrow_array_clear_internal_ptr++{- | Read the @internal_ptr@ field of a deprecated Arrow stream wrapper.+Returns 'Nothing' when the wrapper is null or the stream has already been+released.+-}+duckdbArrowStreamInternal :: DuckDBArrowStream -> IO (Maybe ArrowStreamPtr)+duckdbArrowStreamInternal stream = do+    raw <- c_duckdb_arrow_stream_internal_ptr stream+    pure $+        if raw == nullPtr+            then Nothing+            else Just (ArrowStreamPtr raw)++{- | Clear the @internal_ptr@ field on a deprecated Arrow stream wrapper.+Use this after consuming the stream via @duckdb_arrow_scan@ to avoid stale+pointers on the DuckDB side.+-}+duckdbArrowStreamClear :: DuckDBArrowStream -> IO ()+duckdbArrowStreamClear = c_duckdb_arrow_stream_clear_internal_ptr
+ src/Database/DuckDB/FFI/BindValues.hs view
@@ -0,0 +1,160 @@+module Database.DuckDB.FFI.BindValues (+    c_duckdb_bind_value,+    c_duckdb_bind_parameter_index,+    c_duckdb_bind_boolean,+    c_duckdb_bind_int8,+    c_duckdb_bind_int16,+    c_duckdb_bind_int32,+    c_duckdb_bind_int64,+    c_duckdb_bind_hugeint,+    c_duckdb_bind_uhugeint,+    c_duckdb_bind_decimal,+    c_duckdb_bind_uint8,+    c_duckdb_bind_uint16,+    c_duckdb_bind_uint32,+    c_duckdb_bind_uint64,+    c_duckdb_bind_float,+    c_duckdb_bind_double,+    c_duckdb_bind_date,+    c_duckdb_bind_time,+    c_duckdb_bind_timestamp,+    c_duckdb_bind_timestamp_tz,+    c_duckdb_bind_interval,+    c_duckdb_bind_varchar,+    c_duckdb_bind_varchar_length,+    c_duckdb_bind_blob,+    c_duckdb_bind_null,+) where++import Data.Int (Int16, Int32, Int64, Int8)+import Data.Word (Word16, Word32, Word64, Word8)+import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CBool (..), CDouble (..), CFloat (..), CInt (..))+import Foreign.Ptr (Ptr)++-- | Binds a value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_value"+    c_duckdb_bind_value :: DuckDBPreparedStatement -> DuckDBIdx -> DuckDBValue -> IO DuckDBState++{- | Retrieve the index of the parameter for the prepared statement, identified by+name+-}+foreign import ccall unsafe "duckdb_bind_parameter_index"+    c_duckdb_bind_parameter_index :: DuckDBPreparedStatement -> Ptr DuckDBIdx -> CString -> IO DuckDBState++-- | Binds a bool value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_boolean"+    c_duckdb_bind_boolean :: DuckDBPreparedStatement -> DuckDBIdx -> CBool -> IO DuckDBState++-- | Binds an int8_t value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_int8"+    c_duckdb_bind_int8 :: DuckDBPreparedStatement -> DuckDBIdx -> Int8 -> IO DuckDBState++-- | Binds an int16_t value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_int16"+    c_duckdb_bind_int16 :: DuckDBPreparedStatement -> DuckDBIdx -> Int16 -> IO DuckDBState++-- | Binds an int32_t value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_int32"+    c_duckdb_bind_int32 :: DuckDBPreparedStatement -> DuckDBIdx -> Int32 -> IO DuckDBState++-- | Binds an int64_t value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_int64"+    c_duckdb_bind_int64 :: DuckDBPreparedStatement -> DuckDBIdx -> Int64 -> IO DuckDBState++{- | Binds a duckdb_hugeint value to the prepared statement at the specified index.++These bindings call the wrapper symbol @wrapped_duckdb_bind_hugeint@+but mirror the DuckDB C API semantics of @duckdb_bind_hugeint@.+-}+foreign import ccall unsafe "wrapped_duckdb_bind_hugeint"+    c_duckdb_bind_hugeint :: DuckDBPreparedStatement -> DuckDBIdx -> Ptr DuckDBHugeInt -> IO DuckDBState++{- | Binds a duckdb_uhugeint value to the prepared statement at the specified+index.++These bindings call the wrapper symbol @wrapped_duckdb_bind_uhugeint@+but mirror the DuckDB C API semantics of @duckdb_bind_uhugeint@.+-}+foreign import ccall unsafe "wrapped_duckdb_bind_uhugeint"+    c_duckdb_bind_uhugeint :: DuckDBPreparedStatement -> DuckDBIdx -> Ptr DuckDBUHugeInt -> IO DuckDBState++{- | Binds a duckdb_decimal value to the prepared statement at the specified index.++These bindings call the wrapper symbol @wrapped_duckdb_bind_decimal@+but mirror the DuckDB C API semantics of @duckdb_bind_decimal@.+-}+foreign import ccall unsafe "wrapped_duckdb_bind_decimal"+    c_duckdb_bind_decimal :: DuckDBPreparedStatement -> DuckDBIdx -> Ptr DuckDBDecimal -> IO DuckDBState++-- | Binds a uint8_t value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_uint8"+    c_duckdb_bind_uint8 :: DuckDBPreparedStatement -> DuckDBIdx -> Word8 -> IO DuckDBState++-- | Binds a uint16_t value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_uint16"+    c_duckdb_bind_uint16 :: DuckDBPreparedStatement -> DuckDBIdx -> Word16 -> IO DuckDBState++-- | Binds a uint32_t value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_uint32"+    c_duckdb_bind_uint32 :: DuckDBPreparedStatement -> DuckDBIdx -> Word32 -> IO DuckDBState++-- | Binds a uint64_t value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_uint64"+    c_duckdb_bind_uint64 :: DuckDBPreparedStatement -> DuckDBIdx -> Word64 -> IO DuckDBState++-- | Binds a float value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_float"+    c_duckdb_bind_float :: DuckDBPreparedStatement -> DuckDBIdx -> CFloat -> IO DuckDBState++-- | Binds a double value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_double"+    c_duckdb_bind_double :: DuckDBPreparedStatement -> DuckDBIdx -> CDouble -> IO DuckDBState++-- | Binds a duckdb_date value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_date"+    c_duckdb_bind_date :: DuckDBPreparedStatement -> DuckDBIdx -> DuckDBDate -> IO DuckDBState++-- | Binds a duckdb_time value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_time"+    c_duckdb_bind_time :: DuckDBPreparedStatement -> DuckDBIdx -> DuckDBTime -> IO DuckDBState++{- | Binds a duckdb_timestamp value to the prepared statement at the specified+index.+-}+foreign import ccall unsafe "duckdb_bind_timestamp"+    c_duckdb_bind_timestamp :: DuckDBPreparedStatement -> DuckDBIdx -> DuckDBTimestamp -> IO DuckDBState++{- | Binds a duckdb_timestamp value to the prepared statement at the specified+index.+-}+foreign import ccall unsafe "duckdb_bind_timestamp_tz"+    c_duckdb_bind_timestamp_tz :: DuckDBPreparedStatement -> DuckDBIdx -> DuckDBTimestamp -> IO DuckDBState++{- | Binds a duckdb_interval value to the prepared statement at the specified+index.++These bindings call the wrapper symbol @wrapped_duckdb_bind_interval@+but mirror the DuckDB C API semantics of @duckdb_bind_interval@.+-}+foreign import ccall unsafe "wrapped_duckdb_bind_interval"+    c_duckdb_bind_interval :: DuckDBPreparedStatement -> DuckDBIdx -> Ptr DuckDBInterval -> IO DuckDBState++{- | Binds a null-terminated varchar value to the prepared statement at the+specified index.+-}+foreign import ccall unsafe "duckdb_bind_varchar"+    c_duckdb_bind_varchar :: DuckDBPreparedStatement -> DuckDBIdx -> CString -> IO DuckDBState++-- | Binds a varchar value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_varchar_length"+    c_duckdb_bind_varchar_length :: DuckDBPreparedStatement -> DuckDBIdx -> CString -> DuckDBIdx -> IO DuckDBState++-- | Binds a blob value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_blob"+    c_duckdb_bind_blob :: DuckDBPreparedStatement -> DuckDBIdx -> Ptr () -> DuckDBIdx -> IO DuckDBState++-- | Binds a NULL value to the prepared statement at the specified index.+foreign import ccall unsafe "duckdb_bind_null"+    c_duckdb_bind_null :: DuckDBPreparedStatement -> DuckDBIdx -> IO DuckDBState
+ src/Database/DuckDB/FFI/CastFunctions.hs view
@@ -0,0 +1,136 @@+module Database.DuckDB.FFI.CastFunctions (+    c_duckdb_create_cast_function,+    c_duckdb_cast_function_set_source_type,+    c_duckdb_cast_function_set_target_type,+    c_duckdb_cast_function_set_implicit_cast_cost,+    c_duckdb_cast_function_set_function,+    c_duckdb_cast_function_set_extra_info,+    c_duckdb_cast_function_get_extra_info,+    c_duckdb_cast_function_get_cast_mode,+    c_duckdb_cast_function_set_error,+    c_duckdb_cast_function_set_row_error,+    c_duckdb_register_cast_function,+    c_duckdb_destroy_cast_function,+) where++import Data.Int (Int64)+import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CInt (..))+import Foreign.Ptr (Ptr)++{- | Creates a new cast function object.++Returns The cast function object.+-}+foreign import ccall unsafe "duckdb_create_cast_function"+    c_duckdb_create_cast_function :: IO DuckDBCastFunction++{- | Sets the source type of the cast function.++Parameters:+* @cast_function@: The cast function object.+* @source_type@: The source type to set.+-}+foreign import ccall unsafe "duckdb_cast_function_set_source_type"+    c_duckdb_cast_function_set_source_type :: DuckDBCastFunction -> DuckDBLogicalType -> IO ()++{- | Sets the target type of the cast function.++Parameters:+* @cast_function@: The cast function object.+* @target_type@: The target type to set.+-}+foreign import ccall unsafe "duckdb_cast_function_set_target_type"+    c_duckdb_cast_function_set_target_type :: DuckDBCastFunction -> DuckDBLogicalType -> IO ()++{- | Sets the "cost" of implicitly casting the source type to the target type using+this function.++Parameters:+* @cast_function@: The cast function object.+* @cost@: The cost to set.+-}+foreign import ccall unsafe "duckdb_cast_function_set_implicit_cast_cost"+    c_duckdb_cast_function_set_implicit_cast_cost :: DuckDBCastFunction -> Int64 -> IO ()++{- | Sets the actual cast function to use.++Parameters:+* @cast_function@: The cast function object.+* @function@: The function to set.+-}+foreign import ccall unsafe "duckdb_cast_function_set_function"+    c_duckdb_cast_function_set_function :: DuckDBCastFunction -> DuckDBCastFunctionFun -> IO ()++{- | Assigns extra information to the cast function that can be fetched during+execution, etc.++Parameters:+* @extra_info@: The extra information+* @destroy@: The callback that will be called to destroy the extra information+  (if any)+-}+foreign import ccall unsafe "duckdb_cast_function_set_extra_info"+    c_duckdb_cast_function_set_extra_info :: DuckDBCastFunction -> Ptr () -> DuckDBDeleteCallback -> IO ()++{- | Retrieves the extra info of the function as set in+@duckdb_cast_function_set_extra_info@.++Parameters:+* @info@: The info object.++Returns The extra info.+-}+foreign import ccall unsafe "duckdb_cast_function_get_extra_info"+    c_duckdb_cast_function_get_extra_info :: DuckDBFunctionInfo -> IO (Ptr ())++{- | Get the cast execution mode from the given function info.++Parameters:+* @info@: The info object.++Returns The cast mode.+-}+foreign import ccall unsafe "duckdb_cast_function_get_cast_mode"+    c_duckdb_cast_function_get_cast_mode :: DuckDBFunctionInfo -> IO DuckDBCastMode++{- | Report that an error has occurred while executing the cast function.++Parameters:+* @info@: The info object.+* @error@: The error message.+-}+foreign import ccall unsafe "duckdb_cast_function_set_error"+    c_duckdb_cast_function_set_error :: DuckDBFunctionInfo -> CString -> IO ()++{- | Report that an error has occurred while executing the cast function, setting+the corresponding output row to NULL.++Parameters:+* @info@: The info object.+* @error@: The error message.+* @row@: The index of the row within the output vector to set to NULL.+* @output@: The output vector.+-}+foreign import ccall unsafe "duckdb_cast_function_set_row_error"+    c_duckdb_cast_function_set_row_error :: DuckDBFunctionInfo -> CString -> DuckDBIdx -> DuckDBVector -> IO ()++{- | Registers a cast function within the given connection.++Parameters:+* @con@: The connection to use.+* @cast_function@: The cast function to register.++Returns Whether or not the registration was successful.+-}+foreign import ccall unsafe "duckdb_register_cast_function"+    c_duckdb_register_cast_function :: DuckDBConnection -> DuckDBCastFunction -> IO DuckDBState++{- | Destroys the cast function object.++Parameters:+* @cast_function@: The cast function object.+-}+foreign import ccall unsafe "duckdb_destroy_cast_function"+    c_duckdb_destroy_cast_function :: Ptr DuckDBCastFunction -> IO ()
+ src/Database/DuckDB/FFI/Configuration.hs view
@@ -0,0 +1,85 @@+module Database.DuckDB.FFI.Configuration (+    c_duckdb_create_config,+    c_duckdb_config_count,+    c_duckdb_get_config_flag,+    c_duckdb_set_config,+    c_duckdb_destroy_config,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CInt (..), CSize (..))+import Foreign.Ptr (Ptr)++{- | Initializes an empty configuration object that can be used to provide start-up+options for the DuckDB instance through @duckdb_open_ext@. The duckdb_config+must be destroyed using @duckdb_destroy_config@++This will always succeed unless there is a malloc failure.++Note that @duckdb_destroy_config@ should always be called on the resulting+config, even if the function returns @DuckDBError@.++Parameters:+* @out_config@: The result configuration object.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall unsafe "duckdb_create_config"+    c_duckdb_create_config :: Ptr DuckDBConfig -> IO DuckDBState++{- | This returns the total amount of configuration options available for usage+with @duckdb_get_config_flag@.++This should not be called in a loop as it internally loops over all the+options.++Returns The amount of config options available.+-}+foreign import ccall unsafe "duckdb_config_count"+    c_duckdb_config_count :: IO CSize++{- | Obtains a human-readable name and description of a specific configuration+option. This can be used to e.g. display configuration options. This will+succeed unless @index@ is out of range (i.e., @>= duckdb_config_count@).++The result name or description MUST NOT be freed.++Parameters:+* @index@: The index of the configuration option (between 0 and+  @duckdb_config_count@)+* @out_name@: A name of the configuration flag.+* @out_description@: A description of the configuration flag.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall unsafe "duckdb_get_config_flag"+    c_duckdb_get_config_flag :: CSize -> Ptr CString -> Ptr CString -> IO DuckDBState++{- | Sets the specified option for the specified configuration. The configuration+option is indicated by name. To obtain a list of config options, see+@duckdb_get_config_flag@.++In the source code, configuration options are defined in @config.cpp@.++This can fail if either the name is invalid, or if the value provided for the+option is invalid.++Parameters:+* @config@: The configuration object to set the option on.+* @name@: The name of the configuration flag to set.+* @option@: The value to set the configuration flag to.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall unsafe "duckdb_set_config"+    c_duckdb_set_config :: DuckDBConfig -> CString -> CString -> IO DuckDBState++{- | Destroys the specified configuration object and de-allocates all memory+allocated for the object.++Parameters:+* @config@: The configuration object to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_config"+    c_duckdb_destroy_config :: Ptr DuckDBConfig -> IO ()
+ src/Database/DuckDB/FFI/DataChunk.hs view
@@ -0,0 +1,86 @@+module Database.DuckDB.FFI.DataChunk (+    c_duckdb_create_data_chunk,+    c_duckdb_destroy_data_chunk,+    c_duckdb_data_chunk_reset,+    c_duckdb_data_chunk_get_column_count,+    c_duckdb_data_chunk_get_vector,+    c_duckdb_data_chunk_get_size,+    c_duckdb_data_chunk_set_size,+) where++import Database.DuckDB.FFI.Types+import Foreign.Ptr (Ptr)++{- | Creates an empty data chunk with the specified column types. The result must+be destroyed with @duckdb_destroy_data_chunk@.++Parameters:+* @types@: An array of column types. Column types can not contain ANY and+  INVALID types.+* @column_count@: The number of columns.++Returns The data chunk.+-}+foreign import ccall unsafe "duckdb_create_data_chunk"+    c_duckdb_create_data_chunk :: Ptr DuckDBLogicalType -> DuckDBIdx -> IO DuckDBDataChunk++{- | Destroys the data chunk and de-allocates all memory allocated for that chunk.++Parameters:+* @chunk@: The data chunk to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_data_chunk"+    c_duckdb_destroy_data_chunk :: Ptr DuckDBDataChunk -> IO ()++{- | Resets a data chunk, clearing the validity masks and setting the cardinality+of the data chunk to 0. After calling this method, you must call+@duckdb_vector_get_validity@ and @duckdb_vector_get_data@ to obtain current+data and validity pointers++Parameters:+* @chunk@: The data chunk to reset.+-}+foreign import ccall unsafe "duckdb_data_chunk_reset"+    c_duckdb_data_chunk_reset :: DuckDBDataChunk -> IO ()++{- | Retrieves the number of columns in a data chunk.++Parameters:+* @chunk@: The data chunk to get the data from++Returns The number of columns in the data chunk+-}+foreign import ccall unsafe "duckdb_data_chunk_get_column_count"+    c_duckdb_data_chunk_get_column_count :: DuckDBDataChunk -> IO DuckDBIdx++{- | Retrieves the vector at the specified column index in the data chunk.++The pointer to the vector is valid for as long as the chunk is alive. It does+NOT need to be destroyed.++Parameters:+* @chunk@: The data chunk to get the data from++Returns The vector+-}+foreign import ccall unsafe "duckdb_data_chunk_get_vector"+    c_duckdb_data_chunk_get_vector :: DuckDBDataChunk -> DuckDBIdx -> IO DuckDBVector++{- | Retrieves the current number of tuples in a data chunk.++Parameters:+* @chunk@: The data chunk to get the data from++Returns The number of tuples in the data chunk+-}+foreign import ccall unsafe "duckdb_data_chunk_get_size"+    c_duckdb_data_chunk_get_size :: DuckDBDataChunk -> IO DuckDBIdx++{- | Sets the current number of tuples in a data chunk.++Parameters:+* @chunk@: The data chunk to set the size in+* @size@: The number of tuples in the data chunk+-}+foreign import ccall unsafe "duckdb_data_chunk_set_size"+    c_duckdb_data_chunk_set_size :: DuckDBDataChunk -> DuckDBIdx -> IO ()
+ src/Database/DuckDB/FFI/ErrorData.hs view
@@ -0,0 +1,61 @@+module Database.DuckDB.FFI.ErrorData (+    c_duckdb_create_error_data,+    c_duckdb_destroy_error_data,+    c_duckdb_error_data_error_type,+    c_duckdb_error_data_message,+    c_duckdb_error_data_has_error,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CBool (..), CInt (..))+import Foreign.Ptr (Ptr)++{- | Creates duckdb_error_data. Must be destroyed with @duckdb_destroy_error_data@.++Parameters:+* @type@: The error type.+* @message@: The error message.++Returns The error data.+-}+foreign import ccall unsafe "duckdb_create_error_data"+    c_duckdb_create_error_data :: DuckDBErrorType -> CString -> IO DuckDBErrorData++{- | Destroys the error data and deallocates its memory.++Parameters:+* @error_data@: The error data to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_error_data"+    c_duckdb_destroy_error_data :: Ptr DuckDBErrorData -> IO ()++{- | Returns the duckdb_error_type of the error data.++Parameters:+* @error_data@: The error data.++Returns The error type.+-}+foreign import ccall unsafe "duckdb_error_data_error_type"+    c_duckdb_error_data_error_type :: DuckDBErrorData -> IO DuckDBErrorType++{- | Returns the error message of the error data. Must not be freed.++Parameters:+* @error_data@: The error data.++Returns The error message.+-}+foreign import ccall unsafe "duckdb_error_data_message"+    c_duckdb_error_data_message :: DuckDBErrorData -> IO CString++{- | Returns whether the error data contains an error or not.++Parameters:+* @error_data@: The error data.++Returns True, if the error data contains an exception, else false.+-}+foreign import ccall unsafe "duckdb_error_data_has_error"+    c_duckdb_error_data_has_error :: DuckDBErrorData -> IO CBool
+ src/Database/DuckDB/FFI/ExecutePrepared.hs view
@@ -0,0 +1,46 @@+module Database.DuckDB.FFI.ExecutePrepared (+    c_duckdb_execute_prepared,+    c_duckdb_execute_prepared_streaming,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.Types (CInt (..))+import Foreign.Ptr (Ptr)++{- | Executes the prepared statement with the given bound parameters, and returns a+materialized query result.++This method can be called multiple times for each prepared statement, and the+parameters can be modified between calls to this function.++Note that the result must be freed with @duckdb_destroy_result@.++Parameters:+* @prepared_statement@: The prepared statement to execute.+* @out_result@: The query result.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_execute_prepared"+    c_duckdb_execute_prepared :: DuckDBPreparedStatement -> Ptr DuckDBResult -> IO DuckDBState++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Executes the prepared statement with the given bound parameters, and returns+an optionally-streaming query result. To determine if the resulting query was+in fact streamed, use @duckdb_result_is_streaming@++This method can be called multiple times for each prepared statement, and the+parameters can be modified between calls to this function.++Note that the result must be freed with @duckdb_destroy_result@.++Parameters:+* @prepared_statement@: The prepared statement to execute.+* @out_result@: The query result.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_execute_prepared_streaming"+    c_duckdb_execute_prepared_streaming :: DuckDBPreparedStatement -> Ptr DuckDBResult -> IO DuckDBState
+ src/Database/DuckDB/FFI/Expression.hs view
@@ -0,0 +1,51 @@+module Database.DuckDB.FFI.Expression (+    c_duckdb_destroy_expression,+    c_duckdb_expression_return_type,+    c_duckdb_expression_is_foldable,+    c_duckdb_expression_fold,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.Types (CBool (..))+import Foreign.Ptr (Ptr)++{- | Destroys the expression and de-allocates its memory.++Parameters:+* @expr@: A pointer to the expression.+-}+foreign import ccall unsafe "duckdb_destroy_expression"+    c_duckdb_destroy_expression :: Ptr DuckDBExpression -> IO ()++{- | Returns the return type of an expression.++Parameters:+* @expr@: The expression.++Returns The return type. Must be destroyed with @duckdb_destroy_logical_type@.+-}+foreign import ccall unsafe "duckdb_expression_return_type"+    c_duckdb_expression_return_type :: DuckDBExpression -> IO DuckDBLogicalType++{- | Returns whether the expression is foldable into a value or not.++Parameters:+* @expr@: The expression.++Returns True, if the expression is foldable, else false.+-}+foreign import ccall unsafe "duckdb_expression_is_foldable"+    c_duckdb_expression_is_foldable :: DuckDBExpression -> IO CBool++{- | Folds an expression creating a folded value.++Parameters:+* @context@: The client context.+* @expr@: The expression. Must be foldable.+* @out_value@: The folded value, if folding was successful. Must be destroyed+  with @duckdb_destroy_value@.++Returns The error data. Must be destroyed with @duckdb_destroy_error_data@.+-}+foreign import ccall safe "duckdb_expression_fold"+    c_duckdb_expression_fold :: DuckDBClientContext -> DuckDBExpression -> Ptr DuckDBValue -> IO DuckDBErrorData
+ src/Database/DuckDB/FFI/ExtractStatements.hs view
@@ -0,0 +1,67 @@+module Database.DuckDB.FFI.ExtractStatements (+    c_duckdb_extract_statements,+    c_duckdb_prepare_extracted_statement,+    c_duckdb_extract_statements_error,+    c_duckdb_destroy_extracted,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CInt (..))+import Foreign.Ptr (Ptr)++{- | Extract all statements from a query. Note that after calling+@duckdb_extract_statements@, the extracted statements should always be+destroyed using @duckdb_destroy_extracted@, even if no statements were+extracted.++If the extract fails, @duckdb_extract_statements_error@ can be called to+obtain the reason why the extract failed.++Parameters:+* @connection@: The connection object+* @query@: The SQL query to extract+* @out_extracted_statements@: The resulting extracted statements object++Returns The number of extracted statements or 0 on failure.+-}+foreign import ccall safe "duckdb_extract_statements"+    c_duckdb_extract_statements :: DuckDBConnection -> CString -> Ptr DuckDBExtractedStatements -> IO DuckDBIdx++{- | Prepare an extracted statement. Note that after calling+@duckdb_prepare_extracted_statement@, the prepared statement should always be+destroyed using @duckdb_destroy_prepare@, even if the prepare fails.++If the prepare fails, @duckdb_prepare_error@ can be called to obtain the+reason why the prepare failed.++Parameters:+* @connection@: The connection object+* @extracted_statements@: The extracted statements object+* @index@: The index of the extracted statement to prepare+* @out_prepared_statement@: The resulting prepared statement object++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_prepare_extracted_statement"+    c_duckdb_prepare_extracted_statement :: DuckDBConnection -> DuckDBExtractedStatements -> DuckDBIdx -> Ptr DuckDBPreparedStatement -> IO DuckDBState++{- | Returns the error message contained within the extracted statements. The+result of this function must not be freed. It will be cleaned up when+@duckdb_destroy_extracted@ is called.++Parameters:+* @extracted_statements@: The extracted statements to fetch the error from.++Returns The error of the extracted statements.+-}+foreign import ccall unsafe "duckdb_extract_statements_error"+    c_duckdb_extract_statements_error :: DuckDBExtractedStatements -> IO CString++{- | De-allocates all memory allocated for the extracted statements.++Parameters:+* @extracted_statements@: The extracted statements to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_extracted"+    c_duckdb_destroy_extracted :: Ptr DuckDBExtractedStatements -> IO ()
+ src/Database/DuckDB/FFI/Helpers.hs view
@@ -0,0 +1,351 @@+module Database.DuckDB.FFI.Helpers (+    c_duckdb_malloc,+    c_duckdb_free,+    c_duckdb_vector_size,+    c_duckdb_string_is_inlined,+    c_duckdb_string_t_length,+    c_duckdb_string_t_data,+    c_duckdb_from_date,+    c_duckdb_to_date,+    c_duckdb_is_finite_date,+    c_duckdb_from_time,+    c_duckdb_create_time_tz,+    c_duckdb_from_time_tz,+    c_duckdb_to_time,+    c_duckdb_from_timestamp,+    c_duckdb_to_timestamp,+    c_duckdb_is_finite_timestamp,+    c_duckdb_is_finite_timestamp_s,+    c_duckdb_is_finite_timestamp_ms,+    c_duckdb_is_finite_timestamp_ns,+    c_duckdb_hugeint_to_double,+    c_duckdb_double_to_hugeint,+    c_duckdb_uhugeint_to_double,+    c_duckdb_double_to_uhugeint,+    c_duckdb_double_to_decimal,+    c_duckdb_decimal_to_double,+) where++import Data.Int (Int32, Int64)+import Data.Word (Word32, Word8)+import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CBool (..), CDouble (..), CSize (..))+import Foreign.Ptr (Ptr)++{- | Allocate @size@ bytes of memory using the duckdb internal malloc function. Any+memory allocated in this manner should be freed using @duckdb_free@.++Parameters:+* @size@: The number of bytes to allocate.++Returns A pointer to the allocated memory region.+-}+foreign import ccall unsafe "duckdb_malloc"+    c_duckdb_malloc :: CSize -> IO (Ptr ())++{- | Free a value returned from @duckdb_malloc@, @duckdb_value_varchar@,+@duckdb_value_blob@, or @duckdb_value_string@.++Parameters:+* @ptr@: The memory region to de-allocate.+-}+foreign import ccall unsafe "duckdb_free"+    c_duckdb_free :: Ptr () -> IO ()++{- | The internal vector size used by DuckDB. This is the amount of tuples that+will fit into a data chunk created by @duckdb_create_data_chunk@.++Returns The vector size.+-}+foreign import ccall unsafe "duckdb_vector_size"+    c_duckdb_vector_size :: IO DuckDBIdx++{- | Whether or not the duckdb_string_t value is inlined. This means that the data+of the string does not have a separate allocation.++These bindings call the wrapper symbol+@wrapped_duckdb_string_is_inlined@ but mirror the DuckDB C API semantics of+@duckdb_string_is_inlined@.+-}+foreign import ccall unsafe "wrapped_duckdb_string_is_inlined"+    c_duckdb_string_is_inlined :: Ptr DuckDBStringT -> IO CBool++{- | Get the string length of a string_t++Parameters:+* @string@: The string to get the length of.++Returns The length.++These bindings call the wrapper symbol @wrapped_duckdb_string_t_length@+but mirror the DuckDB C API semantics of @duckdb_string_t_length@.+-}+foreign import ccall unsafe "wrapped_duckdb_string_t_length"+    c_duckdb_string_t_length :: Ptr DuckDBStringT -> IO Word32++{- | Get a pointer to the string data of a string_t++Parameters:+* @string@: The string to get the pointer to.++Returns The pointer.+-}+foreign import ccall unsafe "duckdb_string_t_data"+    c_duckdb_string_t_data :: Ptr DuckDBStringT -> IO CString++{- | Decompose a @duckdb_date@ object into year, month and date (stored as+@duckdb_date_struct@).++Parameters:+* @date@: The date object, as obtained from a @DUCKDB_TYPE_DATE@ column.++Returns The @duckdb_date_struct@ with the decomposed elements.++These bindings call the wrapper symbol @wrapped_duckdb_from_date@ but+mirror the DuckDB C API semantics of @duckdb_from_date@.+-}+foreign import ccall unsafe "wrapped_duckdb_from_date"+    c_duckdb_from_date :: DuckDBDate -> Ptr DuckDBDateStruct -> IO ()++{- | Re-compose a @duckdb_date@ from year, month and date (@duckdb_date_struct@).++Parameters:+* @date@: The year, month and date stored in a @duckdb_date_struct@.++Returns The @duckdb_date@ element.++These bindings call the wrapper symbol @wrapped_duckdb_to_date@ but+mirror the DuckDB C API semantics of @duckdb_to_date@.+-}+foreign import ccall unsafe "wrapped_duckdb_to_date"+    c_duckdb_to_date :: Ptr DuckDBDateStruct -> IO DuckDBDate++{- | Test a @duckdb_date@ to see if it is a finite value.++Parameters:+* @date@: The date object, as obtained from a @DUCKDB_TYPE_DATE@ column.++Returns True if the date is finite, false if it is ±infinity.+-}+foreign import ccall unsafe "duckdb_is_finite_date"+    c_duckdb_is_finite_date :: DuckDBDate -> IO CBool++{- | Decompose a @duckdb_time@ object into hour, minute, second and microsecond+(stored as @duckdb_time_struct@).++Parameters:+* @time@: The time object, as obtained from a @DUCKDB_TYPE_TIME@ column.++Returns The @duckdb_time_struct@ with the decomposed elements.++These bindings call the wrapper symbol @wrapped_duckdb_from_time@ but+mirror the DuckDB C API semantics of @duckdb_from_time@.+-}+foreign import ccall unsafe "wrapped_duckdb_from_time"+    c_duckdb_from_time :: DuckDBTime -> Ptr DuckDBTimeStruct -> IO ()++{- | Create a @duckdb_time_tz@ object from micros and a timezone offset.++Parameters:+* @micros@: The microsecond component of the time.+* @offset@: The timezone offset component of the time.++Returns The @duckdb_time_tz@ element.+-}+foreign import ccall unsafe "duckdb_create_time_tz"+    c_duckdb_create_time_tz :: Int64 -> Int32 -> IO DuckDBTimeTz++{- | Decompose a TIME_TZ objects into micros and a timezone offset.++Use @duckdb_from_time@ to further decompose the micros into hour, minute,+second and microsecond.++Parameters:+* @micros@: The time object, as obtained from a @DUCKDB_TYPE_TIME_TZ@ column.++These bindings call the wrapper symbol @wrapped_duckdb_from_time_tz@+but mirror the DuckDB C API semantics of @duckdb_from_time_tz@.+-}+foreign import ccall unsafe "wrapped_duckdb_from_time_tz"+    c_duckdb_from_time_tz :: DuckDBTimeTz -> Ptr DuckDBTimeTzStruct -> IO ()++{- | Re-compose a @duckdb_time@ from hour, minute, second and microsecond+(@duckdb_time_struct@).++Parameters:+* @time@: The hour, minute, second and microsecond in a @duckdb_time_struct@.++Returns The @duckdb_time@ element.++These bindings call the wrapper symbol @wrapped_duckdb_to_time@ but+mirror the DuckDB C API semantics of @duckdb_to_time@.+-}+foreign import ccall unsafe "wrapped_duckdb_to_time"+    c_duckdb_to_time :: Ptr DuckDBTimeStruct -> IO DuckDBTime++{- | Decompose a @duckdb_timestamp@ object into a @duckdb_timestamp_struct@.++Parameters:+* @ts@: The ts object, as obtained from a @DUCKDB_TYPE_TIMESTAMP@ column.++Returns The @duckdb_timestamp_struct@ with the decomposed elements.++These bindings call the wrapper symbol @wrapped_duckdb_from_timestamp@+but mirror the DuckDB C API semantics of @duckdb_from_timestamp@.+-}+foreign import ccall unsafe "wrapped_duckdb_from_timestamp"+    c_duckdb_from_timestamp :: DuckDBTimestamp -> Ptr DuckDBTimestampStruct -> IO ()++{- | Re-compose a @duckdb_timestamp@ from a duckdb_timestamp_struct.++Parameters:+* @ts@: The de-composed elements in a @duckdb_timestamp_struct@.++Returns The @duckdb_timestamp@ element.++These bindings call the wrapper symbol @wrapped_duckdb_to_timestamp@+but mirror the DuckDB C API semantics of @duckdb_to_timestamp@.+-}+foreign import ccall unsafe "wrapped_duckdb_to_timestamp"+    c_duckdb_to_timestamp :: Ptr DuckDBTimestampStruct -> IO DuckDBTimestamp++{- | Test a @duckdb_timestamp@ to see if it is a finite value.++Parameters:+* @ts@: The duckdb_timestamp object, as obtained from a+  @DUCKDB_TYPE_TIMESTAMP@ column.++Returns True if the timestamp is finite, false if it is ±infinity.+-}+foreign import ccall unsafe "duckdb_is_finite_timestamp"+    c_duckdb_is_finite_timestamp :: DuckDBTimestamp -> IO CBool++{- | Test a @duckdb_timestamp_s@ to see if it is a finite value.++Parameters:+* @ts@: The duckdb_timestamp_s object, as obtained from a+  @DUCKDB_TYPE_TIMESTAMP_S@ column.++Returns True if the timestamp is finite, false if it is ±infinity.+-}+foreign import ccall unsafe "duckdb_is_finite_timestamp_s"+    c_duckdb_is_finite_timestamp_s :: DuckDBTimestampS -> IO CBool++{- | Test a @duckdb_timestamp_ms@ to see if it is a finite value.++Parameters:+* @ts@: The duckdb_timestamp_ms object, as obtained from a+  @DUCKDB_TYPE_TIMESTAMP_MS@ column.++Returns True if the timestamp is finite, false if it is ±infinity.+-}+foreign import ccall unsafe "duckdb_is_finite_timestamp_ms"+    c_duckdb_is_finite_timestamp_ms :: DuckDBTimestampMs -> IO CBool++{- | Test a @duckdb_timestamp_ns@ to see if it is a finite value.++Parameters:+* @ts@: The duckdb_timestamp_ns object, as obtained from a+  @DUCKDB_TYPE_TIMESTAMP_NS@ column.++Returns True if the timestamp is finite, false if it is ±infinity.+-}+foreign import ccall unsafe "duckdb_is_finite_timestamp_ns"+    c_duckdb_is_finite_timestamp_ns :: DuckDBTimestampNs -> IO CBool++{- | Converts a duckdb_hugeint object (as obtained from a @DUCKDB_TYPE_HUGEINT@+column) into a double.++Parameters:+* @val@: The hugeint value.++Returns The converted @double@ element.++These bindings call the wrapper symbol+@wrapped_duckdb_hugeint_to_double@ but mirror the DuckDB C API semantics of+@duckdb_hugeint_to_double@.+-}+foreign import ccall unsafe "wrapped_duckdb_hugeint_to_double"+    c_duckdb_hugeint_to_double :: Ptr DuckDBHugeInt -> IO CDouble++{- | Converts a double value to a duckdb_hugeint object.++If the conversion fails because the double value is too big the result will be+0.++Parameters:+* @val@: The double value.++Returns The converted @duckdb_hugeint@ element.++These bindings call the wrapper symbol+@wrapped_duckdb_double_to_hugeint@ but mirror the DuckDB C API semantics of+@duckdb_double_to_hugeint@.+-}+foreign import ccall unsafe "wrapped_duckdb_double_to_hugeint"+    c_duckdb_double_to_hugeint :: CDouble -> Ptr DuckDBHugeInt -> IO ()++{- | Converts a duckdb_uhugeint object (as obtained from a @DUCKDB_TYPE_UHUGEINT@+column) into a double.++Parameters:+* @val@: The uhugeint value.++Returns The converted @double@ element.++These bindings call the wrapper symbol+@wrapped_duckdb_uhugeint_to_double@ but mirror the DuckDB C API semantics of+@duckdb_uhugeint_to_double@.+-}+foreign import ccall unsafe "wrapped_duckdb_uhugeint_to_double"+    c_duckdb_uhugeint_to_double :: Ptr DuckDBUHugeInt -> IO CDouble++{- | Converts a double value to a duckdb_uhugeint object.++If the conversion fails because the double value is too big the result will be+0.++Parameters:+* @val@: The double value.++Returns The converted @duckdb_uhugeint@ element.++These bindings call the wrapper symbol+@wrapped_duckdb_double_to_uhugeint@ but mirror the DuckDB C API semantics of+@duckdb_double_to_uhugeint@.+-}+foreign import ccall unsafe "wrapped_duckdb_double_to_uhugeint"+    c_duckdb_double_to_uhugeint :: CDouble -> Ptr DuckDBUHugeInt -> IO ()++{- | Converts a double value to a duckdb_decimal object.++If the conversion fails because the double value is too big, or the+width/scale are invalid the result will be 0.++Parameters:+* @val@: The double value.++Returns The converted @duckdb_decimal@ element.++These bindings call the wrapper symbol+@wrapped_duckdb_double_to_decimal@ but mirror the DuckDB C API semantics of+@duckdb_double_to_decimal@.+-}+foreign import ccall unsafe "wrapped_duckdb_double_to_decimal"+    c_duckdb_double_to_decimal :: CDouble -> Word8 -> Word8 -> Ptr DuckDBDecimal -> IO ()++{- | Converts a duckdb_decimal object (as obtained from a @DUCKDB_TYPE_DECIMAL@+column) into a double.++Parameters:+* @val@: The decimal value.++Returns The converted @double@ element.++These bindings call the wrapper symbol+@wrapped_duckdb_decimal_to_double@ but mirror the DuckDB C API semantics of+@duckdb_decimal_to_double@.+-}+foreign import ccall unsafe "wrapped_duckdb_decimal_to_double"+    c_duckdb_decimal_to_double :: Ptr DuckDBDecimal -> IO CDouble
+ src/Database/DuckDB/FFI/LogicalTypes.hs view
@@ -0,0 +1,385 @@+module Database.DuckDB.FFI.LogicalTypes (+    c_duckdb_create_logical_type,+    c_duckdb_logical_type_get_alias,+    c_duckdb_logical_type_set_alias,+    c_duckdb_create_list_type,+    c_duckdb_create_array_type,+    c_duckdb_create_map_type,+    c_duckdb_create_union_type,+    c_duckdb_create_struct_type,+    c_duckdb_create_enum_type,+    c_duckdb_create_decimal_type,+    c_duckdb_get_type_id,+    c_duckdb_decimal_width,+    c_duckdb_decimal_scale,+    c_duckdb_decimal_internal_type,+    c_duckdb_enum_internal_type,+    c_duckdb_enum_dictionary_size,+    c_duckdb_enum_dictionary_value,+    c_duckdb_list_type_child_type,+    c_duckdb_array_type_child_type,+    c_duckdb_array_type_array_size,+    c_duckdb_map_type_key_type,+    c_duckdb_map_type_value_type,+    c_duckdb_struct_type_child_count,+    c_duckdb_struct_type_child_name,+    c_duckdb_struct_type_child_type,+    c_duckdb_union_type_member_count,+    c_duckdb_union_type_member_name,+    c_duckdb_union_type_member_type,+    c_duckdb_destroy_logical_type,+    c_duckdb_register_logical_type,+) where++import Data.Word (Word32, Word8)+import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CInt (..))+import Foreign.Ptr (Ptr)++{- | Creates a @duckdb_logical_type@ from a primitive type. The resulting logical+type must be destroyed with @duckdb_destroy_logical_type@.++Returns an invalid logical type, if type is: @DUCKDB_TYPE_INVALID@,+@DUCKDB_TYPE_DECIMAL@, @DUCKDB_TYPE_ENUM@, @DUCKDB_TYPE_LIST@,+@DUCKDB_TYPE_STRUCT@, @DUCKDB_TYPE_MAP@, @DUCKDB_TYPE_ARRAY@, or+@DUCKDB_TYPE_UNION@.++Parameters:+* @type@: The primitive type to create.++Returns The logical type.+-}+foreign import ccall unsafe "duckdb_create_logical_type"+    c_duckdb_create_logical_type :: DuckDBType -> IO DuckDBLogicalType++{- | Returns the alias of a duckdb_logical_type, if set, else @nullptr@. The result+must be destroyed with @duckdb_free@.++Parameters:+* @type@: The logical type++Returns The alias or @nullptr@+-}+foreign import ccall unsafe "duckdb_logical_type_get_alias"+    c_duckdb_logical_type_get_alias :: DuckDBLogicalType -> IO CString++{- | Sets the alias of a duckdb_logical_type.++Parameters:+* @type@: The logical type+* @alias@: The alias to set+-}+foreign import ccall unsafe "duckdb_logical_type_set_alias"+    c_duckdb_logical_type_set_alias :: DuckDBLogicalType -> CString -> IO ()++{- | Creates a LIST type from its child type. The return type must be destroyed+with @duckdb_destroy_logical_type@.++Parameters:+* @type@: The child type of the list++Returns The logical type.+-}+foreign import ccall unsafe "duckdb_create_list_type"+    c_duckdb_create_list_type :: DuckDBLogicalType -> IO DuckDBLogicalType++{- | Creates an ARRAY type from its child type. The return type must be destroyed+with @duckdb_destroy_logical_type@.++Parameters:+* @type@: The child type of the array.+* @array_size@: The number of elements in the array.++Returns The logical type.+-}+foreign import ccall unsafe "duckdb_create_array_type"+    c_duckdb_create_array_type :: DuckDBLogicalType -> DuckDBIdx -> IO DuckDBLogicalType++{- | Creates a MAP type from its key type and value type. The return type must be+destroyed with @duckdb_destroy_logical_type@.++Parameters:+* @key_type@: The map's key type.+* @value_type@: The map's value type.++Returns The logical type.+-}+foreign import ccall unsafe "duckdb_create_map_type"+    c_duckdb_create_map_type :: DuckDBLogicalType -> DuckDBLogicalType -> IO DuckDBLogicalType++{- | Creates a UNION type from the passed arrays. The return type must be destroyed+with @duckdb_destroy_logical_type@.++Parameters:+* @member_types@: The array of union member types.+* @member_names@: The union member names.+* @member_count@: The number of union members.++Returns The logical type.+-}+foreign import ccall unsafe "duckdb_create_union_type"+    c_duckdb_create_union_type :: Ptr DuckDBLogicalType -> Ptr CString -> DuckDBIdx -> IO DuckDBLogicalType++{- | Creates a STRUCT type based on the member types and names. The resulting type+must be destroyed with @duckdb_destroy_logical_type@.++Parameters:+* @member_types@: The array of types of the struct members.+* @member_names@: The array of names of the struct members.+* @member_count@: The number of members of the struct.++Returns The logical type.+-}+foreign import ccall unsafe "duckdb_create_struct_type"+    c_duckdb_create_struct_type :: Ptr DuckDBLogicalType -> Ptr CString -> DuckDBIdx -> IO DuckDBLogicalType++{- | Creates an ENUM type from the passed member name array. The resulting type+should be destroyed with @duckdb_destroy_logical_type@.++Parameters:+* @member_names@: The array of names that the enum should consist of.+* @member_count@: The number of elements that were specified in the array.++Returns The logical type.+-}+foreign import ccall unsafe "duckdb_create_enum_type"+    c_duckdb_create_enum_type :: Ptr CString -> DuckDBIdx -> IO DuckDBLogicalType++{- | Creates a DECIMAL type with the specified width and scale. The resulting type+should be destroyed with @duckdb_destroy_logical_type@.++Parameters:+* @width@: The width of the decimal type+* @scale@: The scale of the decimal type++Returns The logical type.+-}+foreign import ccall unsafe "duckdb_create_decimal_type"+    c_duckdb_create_decimal_type :: Word8 -> Word8 -> IO DuckDBLogicalType++{- | Retrieves the enum @duckdb_type@ of a @duckdb_logical_type@.++Parameters:+* @type@: The logical type.++Returns The @duckdb_type@ id.+-}+foreign import ccall unsafe "duckdb_get_type_id"+    c_duckdb_get_type_id :: DuckDBLogicalType -> IO DuckDBType++{- | Retrieves the width of a decimal type.++Parameters:+* @type@: The logical type object++Returns The width of the decimal type+-}+foreign import ccall unsafe "duckdb_decimal_width"+    c_duckdb_decimal_width :: DuckDBLogicalType -> IO Word8++{- | Retrieves the scale of a decimal type.++Parameters:+* @type@: The logical type object++Returns The scale of the decimal type+-}+foreign import ccall unsafe "duckdb_decimal_scale"+    c_duckdb_decimal_scale :: DuckDBLogicalType -> IO Word8++{- | Retrieves the internal storage type of a decimal type.++Parameters:+* @type@: The logical type object++Returns The internal type of the decimal type+-}+foreign import ccall unsafe "duckdb_decimal_internal_type"+    c_duckdb_decimal_internal_type :: DuckDBLogicalType -> IO DuckDBType++{- | Retrieves the internal storage type of an enum type.++Parameters:+* @type@: The logical type object++Returns The internal type of the enum type+-}+foreign import ccall unsafe "duckdb_enum_internal_type"+    c_duckdb_enum_internal_type :: DuckDBLogicalType -> IO DuckDBType++{- | Retrieves the dictionary size of the enum type.++Parameters:+* @type@: The logical type object++Returns The dictionary size of the enum type+-}+foreign import ccall unsafe "duckdb_enum_dictionary_size"+    c_duckdb_enum_dictionary_size :: DuckDBLogicalType -> IO Word32++{- | Retrieves the dictionary value at the specified position from the enum.++The result must be freed with @duckdb_free@.++Parameters:+* @type@: The logical type object+* @index@: The index in the dictionary++Returns The string value of the enum type. Must be freed with @duckdb_free@.+-}+foreign import ccall unsafe "duckdb_enum_dictionary_value"+    c_duckdb_enum_dictionary_value :: DuckDBLogicalType -> DuckDBIdx -> IO CString++{- | Retrieves the child type of the given LIST type. Also accepts MAP types. The+result must be freed with @duckdb_destroy_logical_type@.++Parameters:+* @type@: The logical type, either LIST or MAP.++Returns The child type of the LIST or MAP type.+-}+foreign import ccall unsafe "duckdb_list_type_child_type"+    c_duckdb_list_type_child_type :: DuckDBLogicalType -> IO DuckDBLogicalType++{- | Retrieves the child type of the given ARRAY type.++The result must be freed with @duckdb_destroy_logical_type@.++Parameters:+* @type@: The logical type. Must be ARRAY.++Returns The child type of the ARRAY type.+-}+foreign import ccall unsafe "duckdb_array_type_child_type"+    c_duckdb_array_type_child_type :: DuckDBLogicalType -> IO DuckDBLogicalType++{- | Retrieves the array size of the given array type.++Parameters:+* @type@: The logical type object++Returns The fixed number of elements the values of this array type can store.+-}+foreign import ccall unsafe "duckdb_array_type_array_size"+    c_duckdb_array_type_array_size :: DuckDBLogicalType -> IO DuckDBIdx++{- | Retrieves the key type of the given map type.++The result must be freed with @duckdb_destroy_logical_type@.++Parameters:+* @type@: The logical type object++Returns The key type of the map type. Must be destroyed with+@duckdb_destroy_logical_type@.+-}+foreign import ccall unsafe "duckdb_map_type_key_type"+    c_duckdb_map_type_key_type :: DuckDBLogicalType -> IO DuckDBLogicalType++{- | Retrieves the value type of the given map type.++The result must be freed with @duckdb_destroy_logical_type@.++Parameters:+* @type@: The logical type object++Returns The value type of the map type. Must be destroyed with+@duckdb_destroy_logical_type@.+-}+foreign import ccall unsafe "duckdb_map_type_value_type"+    c_duckdb_map_type_value_type :: DuckDBLogicalType -> IO DuckDBLogicalType++{- | Returns the number of children of a struct type.++Parameters:+* @type@: The logical type object++Returns The number of children of a struct type.+-}+foreign import ccall unsafe "duckdb_struct_type_child_count"+    c_duckdb_struct_type_child_count :: DuckDBLogicalType -> IO DuckDBIdx++{- | Retrieves the name of the struct child.++The result must be freed with @duckdb_free@.++Parameters:+* @type@: The logical type object+* @index@: The child index++Returns The name of the struct type. Must be freed with @duckdb_free@.+-}+foreign import ccall unsafe "duckdb_struct_type_child_name"+    c_duckdb_struct_type_child_name :: DuckDBLogicalType -> DuckDBIdx -> IO CString++{- | Retrieves the child type of the given struct type at the specified index.++The result must be freed with @duckdb_destroy_logical_type@.++Parameters:+* @type@: The logical type object+* @index@: The child index++Returns The child type of the struct type. Must be destroyed with+@duckdb_destroy_logical_type@.+-}+foreign import ccall unsafe "duckdb_struct_type_child_type"+    c_duckdb_struct_type_child_type :: DuckDBLogicalType -> DuckDBIdx -> IO DuckDBLogicalType++{- | Returns the number of members that the union type has.++Parameters:+* @type@: The logical type (union) object++Returns The number of members of a union type.+-}+foreign import ccall unsafe "duckdb_union_type_member_count"+    c_duckdb_union_type_member_count :: DuckDBLogicalType -> IO DuckDBIdx++{- | Retrieves the name of the union member.++The result must be freed with @duckdb_free@.++Parameters:+* @type@: The logical type object+* @index@: The child index++Returns The name of the union member. Must be freed with @duckdb_free@.+-}+foreign import ccall unsafe "duckdb_union_type_member_name"+    c_duckdb_union_type_member_name :: DuckDBLogicalType -> DuckDBIdx -> IO CString++{- | Retrieves the child type of the given union member at the specified index.++The result must be freed with @duckdb_destroy_logical_type@.++Parameters:+* @type@: The logical type object+* @index@: The child index++Returns The child type of the union member. Must be destroyed with+@duckdb_destroy_logical_type@.+-}+foreign import ccall unsafe "duckdb_union_type_member_type"+    c_duckdb_union_type_member_type :: DuckDBLogicalType -> DuckDBIdx -> IO DuckDBLogicalType++{- | Destroys the logical type and de-allocates all memory allocated for that type.++Parameters:+* @type@: The logical type to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_logical_type"+    c_duckdb_destroy_logical_type :: Ptr DuckDBLogicalType -> IO ()++{- | Registers a custom type within the given connection. The type must have an+alias++Parameters:+* @con@: The connection to use+* @type@: The custom type to register++Returns Whether or not the registration was successful.+-}+foreign import ccall unsafe "duckdb_register_logical_type"+    c_duckdb_register_logical_type :: DuckDBConnection -> DuckDBLogicalType -> DuckDBCreateTypeInfo -> IO DuckDBState
+ src/Database/DuckDB/FFI/OpenConnect.hs view
@@ -0,0 +1,215 @@+module Database.DuckDB.FFI.OpenConnect (+    c_duckdb_create_instance_cache,+    c_duckdb_get_or_create_from_cache,+    c_duckdb_destroy_instance_cache,+    c_duckdb_open,+    c_duckdb_open_ext,+    c_duckdb_close,+    c_duckdb_connect,+    c_duckdb_interrupt,+    c_duckdb_query_progress,+    c_duckdb_disconnect,+    c_duckdb_connection_get_client_context,+    c_duckdb_connection_get_arrow_options,+    c_duckdb_client_context_get_connection_id,+    c_duckdb_destroy_client_context,+    c_duckdb_destroy_arrow_options,+    c_duckdb_library_version,+    c_duckdb_get_table_names,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CBool (..), CInt (..))+import Foreign.Ptr (Ptr)++{- | Creates a new database instance cache. The instance cache is necessary if a+client/program (re)opens multiple databases to the same file within the same+process. Must be destroyed with @duckdb_destroy_instance_cache@.++Returns The database instance cache.+-}+foreign import ccall unsafe "duckdb_create_instance_cache"+    c_duckdb_create_instance_cache :: IO DuckDBInstanceCache++{- | Creates a new database instance in the instance cache, or retrieves an+existing database instance. Must be closed with @duckdb_close@.++Parameters:+* @instance_cache@: The instance cache in which to create the database, or+  from which to take the database.+* @path@: Path to the database file on disk. Both @nullptr@ and @:memory:@+  open or retrieve an in-memory database.+* @out_database@: The resulting cached database.+* @config@: (Optional) configuration used to create the database.+* @out_error@: If set and the function returns @DuckDBError@, this contains+  the error message. Note that the error message must be freed using+  @duckdb_free@.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_get_or_create_from_cache"+    c_duckdb_get_or_create_from_cache :: DuckDBInstanceCache -> CString -> Ptr DuckDBDatabase -> DuckDBConfig -> Ptr CString -> IO DuckDBState++{- | Destroys an existing database instance cache and de-allocates its memory.++Parameters:+* @instance_cache@: The instance cache to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_instance_cache"+    c_duckdb_destroy_instance_cache :: Ptr DuckDBInstanceCache -> IO ()++{- | Creates a new database or opens an existing database file stored at the given+path. If no path is given a new in-memory database is created instead. The+database must be closed with @duckdb_close@.++Parameters:+* @path@: Path to the database file on disk. Both @nullptr@ and @:memory:@+  open an in-memory database.+* @out_database@: The result database object.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_open"+    c_duckdb_open :: CString -> Ptr DuckDBDatabase -> IO DuckDBState++{- | Extended version of duckdb_open. Creates a new database or opens an existing+database file stored at the given path. The database must be closed with+@duckdb_close@.++Parameters:+* @path@: Path to the database file on disk. Both @nullptr@ and @:memory:@+  open an in-memory database.+* @out_database@: The result database object.+* @config@: (Optional) configuration used to start up the database.+* @out_error@: If set and the function returns @DuckDBError@, this contains+  the error message. Note that the error message must be freed using+  @duckdb_free@.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_open_ext"+    c_duckdb_open_ext :: CString -> Ptr DuckDBDatabase -> DuckDBConfig -> Ptr CString -> IO DuckDBState++{- | Closes the specified database and de-allocates all memory allocated for that+database. This should be called after you are done with any database allocated+through @duckdb_open@ or @duckdb_open_ext@. Note that failing to call+@duckdb_close@ (in case of e.g., a program crash) will not cause data+corruption. Still, it is recommended to always correctly close a database+object after you are done with it.++Parameters:+* @database@: The database object to shut down.+-}+foreign import ccall safe "duckdb_close"+    c_duckdb_close :: Ptr DuckDBDatabase -> IO ()++{- | Opens a connection to a database. Connections are required to query the+database, and store transactional state associated with the connection. The+instantiated connection should be closed using @duckdb_disconnect@.++Parameters:+* @database@: The database file to connect to.+* @out_connection@: The result connection object.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_connect"+    c_duckdb_connect :: DuckDBDatabase -> Ptr DuckDBConnection -> IO DuckDBState++{- | Interrupt running query++Parameters:+* @connection@: The connection to interrupt+-}+foreign import ccall unsafe "duckdb_interrupt"+    c_duckdb_interrupt :: DuckDBConnection -> IO ()++{- | Get progress of the running query++Parameters:+* @connection@: The working connection++Returns -1 if no progress or a percentage of the progress++These bindings call the wrapper symbol @wrapped_duckdb_query_progress@+but mirror the DuckDB C API semantics of @duckdb_query_progress@.+-}+foreign import ccall unsafe "wrapped_duckdb_query_progress"+    c_duckdb_query_progress :: DuckDBConnection -> Ptr DuckDBQueryProgress -> IO ()++{- | Closes the specified connection and de-allocates all memory allocated for that+connection.++Parameters:+* @connection@: The connection to close.+-}+foreign import ccall safe "duckdb_disconnect"+    c_duckdb_disconnect :: Ptr DuckDBConnection -> IO ()++{- | Retrieves the client context of the connection.++Parameters:+* @connection@: The connection.+* @out_context@: The client context of the connection. Must be destroyed with+  @duckdb_destroy_client_context@.+-}+foreign import ccall unsafe "duckdb_connection_get_client_context"+    c_duckdb_connection_get_client_context :: DuckDBConnection -> Ptr DuckDBClientContext -> IO ()++{- | Retrieves the arrow options of the connection.++Parameters:+* @connection@: The connection.+-}+foreign import ccall unsafe "duckdb_connection_get_arrow_options"+    c_duckdb_connection_get_arrow_options :: DuckDBConnection -> Ptr DuckDBArrowOptions -> IO ()++{- | Returns the connection id of the client context.++Parameters:+* @context@: The client context.++Returns The connection id of the client context.+-}+foreign import ccall unsafe "duckdb_client_context_get_connection_id"+    c_duckdb_client_context_get_connection_id :: DuckDBClientContext -> IO DuckDBIdx++{- | Destroys the client context and deallocates its memory.++Parameters:+* @context@: The client context to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_client_context"+    c_duckdb_destroy_client_context :: Ptr DuckDBClientContext -> IO ()++{- | Destroys the arrow options and deallocates its memory.++Parameters:+* @arrow_options@: The arrow options to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_arrow_options"+    c_duckdb_destroy_arrow_options :: Ptr DuckDBArrowOptions -> IO ()++{- | Returns the version of the linked DuckDB, with a version postfix for dev+versions++Usually used for developing C extensions that must return this for a+compatibility check.+-}+foreign import ccall unsafe "duckdb_library_version"+    c_duckdb_library_version :: IO CString++{- | Get the list of (fully qualified) table names of the query.++Parameters:+* @connection@: The connection for which to get the table names.+* @query@: The query for which to get the table names.+* @qualified@: Returns fully qualified table names (catalog.schema.table), if+  set to true, else only the (not escaped) table names.++Returns A duckdb_value of type VARCHAR[] containing the (fully qualified)+table names of the query. Must be destroyed with duckdb_destroy_value.+-}+foreign import ccall safe "duckdb_get_table_names"+    c_duckdb_get_table_names :: DuckDBConnection -> CString -> CBool -> IO DuckDBValue
+ src/Database/DuckDB/FFI/PendingResult.hs view
@@ -0,0 +1,143 @@+module Database.DuckDB.FFI.PendingResult (+    c_duckdb_pending_prepared,+    c_duckdb_pending_prepared_streaming,+    c_duckdb_destroy_pending,+    c_duckdb_pending_error,+    c_duckdb_pending_execute_task,+    c_duckdb_pending_execute_check_state,+    c_duckdb_execute_pending,+    c_duckdb_pending_execution_is_finished,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CBool (..), CInt (..))+import Foreign.Ptr (Ptr)++{- | Executes the prepared statement with the given bound parameters, and returns a+pending result. The pending result represents an intermediate structure for a+query that is not yet fully executed. The pending result can be used to+incrementally execute a query, returning control to the client between tasks.++Note that after calling @duckdb_pending_prepared@, the pending result should+always be destroyed using @duckdb_destroy_pending@, even if this function+returns DuckDBError.++Parameters:+* @prepared_statement@: The prepared statement to execute.+* @out_result@: The pending query result.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_pending_prepared"+    c_duckdb_pending_prepared :: DuckDBPreparedStatement -> Ptr DuckDBPendingResult -> IO DuckDBState++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Executes the prepared statement with the given bound parameters, and returns a+pending result. This pending result will create a streaming duckdb_result when+executed. The pending result represents an intermediate structure for a query+that is not yet fully executed.++Note that after calling @duckdb_pending_prepared_streaming@, the pending+result should always be destroyed using @duckdb_destroy_pending@, even if this+function returns DuckDBError.++Parameters:+* @prepared_statement@: The prepared statement to execute.+* @out_result@: The pending query result.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_pending_prepared_streaming"+    c_duckdb_pending_prepared_streaming :: DuckDBPreparedStatement -> Ptr DuckDBPendingResult -> IO DuckDBState++{- | Closes the pending result and de-allocates all memory allocated for the+result.++Parameters:+* @pending_result@: The pending result to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_pending"+    c_duckdb_destroy_pending :: Ptr DuckDBPendingResult -> IO ()++{- | Returns the error message contained within the pending result.++The result of this function must not be freed. It will be cleaned up when+@duckdb_destroy_pending@ is called.++Parameters:+* @pending_result@: The pending result to fetch the error from.++Returns The error of the pending result.+-}+foreign import ccall unsafe "duckdb_pending_error"+    c_duckdb_pending_error :: DuckDBPendingResult -> IO CString++{- | Executes a single task within the query, returning whether or not the query is+ready.++If this returns DUCKDB_PENDING_RESULT_READY, the duckdb_execute_pending+function can be called to obtain the result. If this returns+DUCKDB_PENDING_RESULT_NOT_READY, the duckdb_pending_execute_task function+should be called again. If this returns DUCKDB_PENDING_ERROR, an error+occurred during execution.++The error message can be obtained by calling duckdb_pending_error on the+pending_result.++Parameters:+* @pending_result@: The pending result to execute a task within.++Returns The state of the pending result after the execution.+-}+foreign import ccall safe "duckdb_pending_execute_task"+    c_duckdb_pending_execute_task :: DuckDBPendingResult -> IO DuckDBPendingState++{- | If this returns DUCKDB_PENDING_RESULT_READY, the duckdb_execute_pending+function can be called to obtain the result. If this returns+DUCKDB_PENDING_RESULT_NOT_READY, the duckdb_pending_execute_check_state+function should be called again. If this returns DUCKDB_PENDING_ERROR, an+error occurred during execution.++The error message can be obtained by calling duckdb_pending_error on the+pending_result.++Parameters:+* @pending_result@: The pending result.++Returns The state of the pending result.+-}+foreign import ccall unsafe "duckdb_pending_execute_check_state"+    c_duckdb_pending_execute_check_state :: DuckDBPendingResult -> IO DuckDBPendingState++{- | Fully execute a pending query result, returning the final query result.++If duckdb_pending_execute_task has been called until+DUCKDB_PENDING_RESULT_READY was returned, this will return fast. Otherwise,+all remaining tasks must be executed first.++Note that the result must be freed with @duckdb_destroy_result@.++Parameters:+* @pending_result@: The pending result to execute.+* @out_result@: The result object.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_execute_pending"+    c_duckdb_execute_pending :: DuckDBPendingResult -> Ptr DuckDBResult -> IO DuckDBState++{- | Returns whether a duckdb_pending_state is finished executing. For example if+@pending_state@ is DUCKDB_PENDING_RESULT_READY, this function will return+true.++Parameters:+* @pending_state@: The pending state on which to decide whether to finish+  execution.++Returns Boolean indicating pending execution should be considered finished.+-}+foreign import ccall unsafe "duckdb_pending_execution_is_finished"+    c_duckdb_pending_execution_is_finished :: DuckDBPendingState -> IO CBool
+ src/Database/DuckDB/FFI/PreparedStatements.hs view
@@ -0,0 +1,184 @@+module Database.DuckDB.FFI.PreparedStatements (+    c_duckdb_prepare,+    c_duckdb_destroy_prepare,+    c_duckdb_prepare_error,+    c_duckdb_nparams,+    c_duckdb_parameter_name,+    c_duckdb_param_type,+    c_duckdb_param_logical_type,+    c_duckdb_clear_bindings,+    c_duckdb_prepared_statement_type,+    c_duckdb_prepared_statement_column_count,+    c_duckdb_prepared_statement_column_name,+    c_duckdb_prepared_statement_column_logical_type,+    c_duckdb_prepared_statement_column_type,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CInt (..))+import Foreign.Ptr (Ptr)++{- | Create a prepared statement object from a query.++Note that after calling @duckdb_prepare@, the prepared statement should always+be destroyed using @duckdb_destroy_prepare@, even if the prepare fails.++If the prepare fails, @duckdb_prepare_error@ can be called to obtain the+reason why the prepare failed.++Parameters:+* @connection@: The connection object+* @query@: The SQL query to prepare+* @out_prepared_statement@: The resulting prepared statement object++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_prepare"+    c_duckdb_prepare :: DuckDBConnection -> CString -> Ptr DuckDBPreparedStatement -> IO DuckDBState++{- | Closes the prepared statement and de-allocates all memory allocated for the+statement.++Parameters:+* @prepared_statement@: The prepared statement to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_prepare"+    c_duckdb_destroy_prepare :: Ptr DuckDBPreparedStatement -> IO ()++{- | Returns the error message associated with the given prepared statement. If the+prepared statement has no error message, this returns @nullptr@ instead.++The error message should not be freed. It will be de-allocated when+@duckdb_destroy_prepare@ is called.++Parameters:+* @prepared_statement@: The prepared statement to obtain the error from.++Returns The error message, or @nullptr@ if there is none.+-}+foreign import ccall unsafe "duckdb_prepare_error"+    c_duckdb_prepare_error :: DuckDBPreparedStatement -> IO CString++{- | Returns the number of parameters that can be provided to the given prepared+statement.++Returns 0 if the query was not successfully prepared.++Parameters:+* @prepared_statement@: The prepared statement to obtain the number of+  parameters for.+-}+foreign import ccall unsafe "duckdb_nparams"+    c_duckdb_nparams :: DuckDBPreparedStatement -> IO DuckDBIdx++{- | Returns the name used to identify the parameter The returned string should be+freed using @duckdb_free@.++Returns NULL if the index is out of range for the provided prepared statement.++Parameters:+* @prepared_statement@: The prepared statement for which to get the parameter+  name from.+-}+foreign import ccall unsafe "duckdb_parameter_name"+    c_duckdb_parameter_name :: DuckDBPreparedStatement -> DuckDBIdx -> IO CString++{- | Returns the parameter type for the parameter at the given index.++Returns @DUCKDB_TYPE_INVALID@ if the parameter index is out of range or the+statement was not successfully prepared.++Parameters:+* @prepared_statement@: The prepared statement.+* @param_idx@: The parameter index.++Returns The parameter type+-}+foreign import ccall unsafe "duckdb_param_type"+    c_duckdb_param_type :: DuckDBPreparedStatement -> DuckDBIdx -> IO DuckDBType++{- | Returns the logical type for the parameter at the given index.++Returns @nullptr@ if the parameter index is out of range or the statement was+not successfully prepared.++The return type of this call should be destroyed with+@duckdb_destroy_logical_type@.++Parameters:+* @prepared_statement@: The prepared statement.+* @param_idx@: The parameter index.++Returns The logical type of the parameter+-}+foreign import ccall unsafe "duckdb_param_logical_type"+    c_duckdb_param_logical_type :: DuckDBPreparedStatement -> DuckDBIdx -> IO DuckDBLogicalType++-- | Clear the params bind to the prepared statement.+foreign import ccall unsafe "duckdb_clear_bindings"+    c_duckdb_clear_bindings :: DuckDBPreparedStatement -> IO DuckDBState++{- | Returns the statement type of the statement to be executed++Parameters:+* @statement@: The prepared statement.++Returns duckdb_statement_type value or DUCKDB_STATEMENT_TYPE_INVALID+-}+foreign import ccall unsafe "duckdb_prepared_statement_type"+    c_duckdb_prepared_statement_type :: DuckDBPreparedStatement -> IO DuckDBStatementType++{- | Returns the number of columns present in a the result of the prepared+statement. If any of the column types are invalid, the result will be 1.++Parameters:+* @prepared_statement@: The prepared statement.++Returns The number of columns present in the result of the prepared statement.+-}+foreign import ccall unsafe "duckdb_prepared_statement_column_count"+    c_duckdb_prepared_statement_column_count :: DuckDBPreparedStatement -> IO DuckDBIdx++{- | Returns the name of the specified column of the result of the+prepared_statement. The returned string should be freed using @duckdb_free@.++Returns @nullptr@ if the column is out of range.++Parameters:+* @prepared_statement@: The prepared statement.+* @col_idx@: The column index.++Returns The column name of the specified column.+-}+foreign import ccall unsafe "duckdb_prepared_statement_column_name"+    c_duckdb_prepared_statement_column_name :: DuckDBPreparedStatement -> DuckDBIdx -> IO CString++{- | Returns the column type of the specified column of the result of the+prepared_statement.++Returns @DUCKDB_TYPE_INVALID@ if the column is out of range. The return type+of this call should be destroyed with @duckdb_destroy_logical_type@.++Parameters:+* @prepared_statement@: The prepared statement to fetch the column type from.+* @col_idx@: The column index.++Returns The logical type of the specified column.+-}+foreign import ccall unsafe "duckdb_prepared_statement_column_logical_type"+    c_duckdb_prepared_statement_column_logical_type :: DuckDBPreparedStatement -> DuckDBIdx -> IO DuckDBLogicalType++{- | Returns the column type of the specified column of the result of the+prepared_statement.++Returns @DUCKDB_TYPE_INVALID@ if the column is out of range.++Parameters:+* @prepared_statement@: The prepared statement to fetch the column type from.+* @col_idx@: The column index.++Returns The type of the specified column.+-}+foreign import ccall unsafe "duckdb_prepared_statement_column_type"+    c_duckdb_prepared_statement_column_type :: DuckDBPreparedStatement -> DuckDBIdx -> IO DuckDBType
+ src/Database/DuckDB/FFI/ProfilingInfo.hs view
@@ -0,0 +1,67 @@+module Database.DuckDB.FFI.ProfilingInfo (+    c_duckdb_get_profiling_info,+    c_duckdb_profiling_info_get_value,+    c_duckdb_profiling_info_get_metrics,+    c_duckdb_profiling_info_get_child_count,+    c_duckdb_profiling_info_get_child,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)++{- | Returns the root node of the profiling information. Returns nullptr, if+profiling is not enabled.++Parameters:+* @connection@: A connection object.++Returns A profiling information object.+-}+foreign import ccall unsafe "duckdb_get_profiling_info"+    c_duckdb_get_profiling_info :: DuckDBConnection -> IO DuckDBProfilingInfo++{- | Returns the value of the metric of the current profiling info node. Returns+nullptr, if the metric does not exist or is not enabled. Currently, the value+holds a string, and you can retrieve the string by calling the corresponding+function: char *duckdb_get_varchar(duckdb_value value).++Parameters:+* @info@: A profiling information object.+* @key@: The name of the requested metric.++Returns The value of the metric. Must be freed with @duckdb_destroy_value@+-}+foreign import ccall unsafe "duckdb_profiling_info_get_value"+    c_duckdb_profiling_info_get_value :: DuckDBProfilingInfo -> CString -> IO DuckDBValue++{- | Returns the key-value metric map of this profiling node as a MAP duckdb_value.+The individual elements are accessible via the duckdb_value MAP functions.++Parameters:+* @info@: A profiling information object.++Returns The key-value metric map as a MAP duckdb_value.+-}+foreign import ccall unsafe "duckdb_profiling_info_get_metrics"+    c_duckdb_profiling_info_get_metrics :: DuckDBProfilingInfo -> IO DuckDBValue++{- | Returns the number of children in the current profiling info node.++Parameters:+* @info@: A profiling information object.++Returns The number of children in the current node.+-}+foreign import ccall unsafe "duckdb_profiling_info_get_child_count"+    c_duckdb_profiling_info_get_child_count :: DuckDBProfilingInfo -> IO DuckDBIdx++{- | Returns the child node at the specified index.++Parameters:+* @info@: A profiling information object.+* @index@: The index of the child node.++Returns The child node at the specified index.+-}+foreign import ccall unsafe "duckdb_profiling_info_get_child"+    c_duckdb_profiling_info_get_child :: DuckDBProfilingInfo -> DuckDBIdx -> IO DuckDBProfilingInfo
+ src/Database/DuckDB/FFI/QueryExecution.hs view
@@ -0,0 +1,229 @@+module Database.DuckDB.FFI.QueryExecution (+    c_duckdb_query,+    c_duckdb_destroy_result,+    c_duckdb_column_name,+    c_duckdb_column_type,+    c_duckdb_result_statement_type,+    c_duckdb_column_logical_type,+    c_duckdb_result_get_arrow_options,+    c_duckdb_column_count,+    c_duckdb_row_count,+    c_duckdb_rows_changed,+    c_duckdb_column_data,+    c_duckdb_nullmask_data,+    c_duckdb_result_error,+    c_duckdb_result_error_type,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CBool (..), CInt (..))+import Foreign.Ptr (Ptr)++{- | Executes a SQL query within a connection and stores the full (materialized)+result in the out_result pointer. If the query fails to execute, DuckDBError+is returned and the error message can be retrieved by calling+@duckdb_result_error@.++Note that after running @duckdb_query@, @duckdb_destroy_result@ must be called+on the result object even if the query fails, otherwise the error stored+within the result will not be freed correctly.++Parameters:+* @connection@: The connection to perform the query in.+* @query@: The SQL query to run.+* @out_result@: The query result.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_query"+    c_duckdb_query :: DuckDBConnection -> CString -> Ptr DuckDBResult -> IO DuckDBState++{- | Closes the result and de-allocates all memory allocated for that result.++Parameters:+* @result@: The result to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_result"+    c_duckdb_destroy_result :: Ptr DuckDBResult -> IO ()++{- | Returns the column name of the specified column. The result should not need to+be freed; the column names will automatically be destroyed when the result is+destroyed.++Returns @NULL@ if the column is out of range.++Parameters:+* @result@: The result object to fetch the column name from.+* @col@: The column index.++Returns The column name of the specified column.+-}+foreign import ccall unsafe "duckdb_column_name"+    c_duckdb_column_name :: Ptr DuckDBResult -> DuckDBIdx -> IO CString++{- | Returns the column type of the specified column.++Returns @DUCKDB_TYPE_INVALID@ if the column is out of range.++Parameters:+* @result@: The result object to fetch the column type from.+* @col@: The column index.++Returns The column type of the specified column.+-}+foreign import ccall unsafe "duckdb_column_type"+    c_duckdb_column_type :: Ptr DuckDBResult -> DuckDBIdx -> IO DuckDBType++{- | Returns the statement type of the statement that was executed++Parameters:+* @result@: The result object to fetch the statement type from.++Returns duckdb_statement_type value or DUCKDB_STATEMENT_TYPE_INVALID++These bindings call the wrapper symbol+@wrapped_duckdb_result_statement_type@ but mirror the DuckDB C API semantics of+@duckdb_result_statement_type@.+-}+foreign import ccall unsafe "wrapped_duckdb_result_statement_type"+    c_duckdb_result_statement_type :: Ptr DuckDBResult -> IO DuckDBStatementType++{- | Returns the logical column type of the specified column.++The return type of this call should be destroyed with+@duckdb_destroy_logical_type@.++Returns @NULL@ if the column is out of range.++Parameters:+* @result@: The result object to fetch the column type from.+* @col@: The column index.++Returns The logical column type of the specified column.+-}+foreign import ccall unsafe "duckdb_column_logical_type"+    c_duckdb_column_logical_type :: Ptr DuckDBResult -> DuckDBIdx -> IO DuckDBLogicalType++{- | Returns the arrow options associated with the given result. These options are+definitions of how the arrow arrays/schema should be produced.++Parameters:+* @result@: The result object to fetch arrow options from.++Returns The arrow options associated with the given result. This must be+destroyed with @duckdb_destroy_arrow_options@.++These bindings call the wrapper symbol+@wrapped_duckdb_result_get_arrow_options@ but mirror the DuckDB C API semantics+of @duckdb_result_get_arrow_options@.+-}+foreign import ccall unsafe "wrapped_duckdb_result_get_arrow_options"+    c_duckdb_result_get_arrow_options :: Ptr DuckDBResult -> IO DuckDBArrowOptions++{- | Returns the number of columns present in a the result object.++Parameters:+* @result@: The result object.++Returns The number of columns present in the result object.+-}+foreign import ccall unsafe "duckdb_column_count"+    c_duckdb_column_count :: Ptr DuckDBResult -> IO DuckDBIdx++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns the number of rows present in the result object.++Parameters:+* @result@: The result object.++Returns The number of rows present in the result object.+-}+foreign import ccall unsafe "duckdb_row_count"+    c_duckdb_row_count :: Ptr DuckDBResult -> IO DuckDBIdx++{- | Returns the number of rows changed by the query stored in the result. This is+relevant only for INSERT/UPDATE/DELETE queries. For other queries the+rows_changed will be 0.++Parameters:+* @result@: The result object.++Returns The number of rows changed.+-}+foreign import ccall unsafe "duckdb_rows_changed"+    c_duckdb_rows_changed :: Ptr DuckDBResult -> IO DuckDBIdx++{- | > Deprecated This method has been deprecated. Prefer using+@duckdb_result_get_chunk@ instead.++Returns the data of a specific column of a result in columnar format.++The function returns a dense array which contains the result data. The exact+type stored in the array depends on the corresponding duckdb_type (as provided+by @duckdb_column_type@). For the exact type by which the data should be+accessed, see the comments in [the types section](types) or the @DUCKDB_TYPE@+enum.++For example, for a column of type @DUCKDB_TYPE_INTEGER@, rows can be accessed+in the following manner: ``@c int32_t *data = (int32_t *)+duckdb_column_data(&result, 0); printf("Data for row %d: %d\n", row,+data[row]); @``++Parameters:+* @result@: The result object to fetch the column data from.+* @col@: The column index.++Returns The column data of the specified column.+-}+foreign import ccall unsafe "duckdb_column_data"+    c_duckdb_column_data :: Ptr DuckDBResult -> DuckDBIdx -> IO (Ptr ())++{- | > Deprecated This method has been deprecated. Prefer using+@duckdb_result_get_chunk@ instead.++Returns the nullmask of a specific column of a result in columnar format. The+nullmask indicates for every row whether or not the corresponding row is+@NULL@. If a row is @NULL@, the values present in the array provided by+@duckdb_column_data@ are undefined.++``@c int32_t *data = (int32_t *) duckdb_column_data(&result, 0); bool+*nullmask = duckdb_nullmask_data(&result, 0); if (nullmask[row]) {+printf("Data for row %d: NULL\n", row); } else { printf("Data for row %d:+%d\n", row, data[row]); } @``++Parameters:+* @result@: The result object to fetch the nullmask from.+* @col@: The column index.++Returns The nullmask of the specified column.+-}+foreign import ccall unsafe "duckdb_nullmask_data"+    c_duckdb_nullmask_data :: Ptr DuckDBResult -> DuckDBIdx -> IO (Ptr CBool)++{- | Returns the error message contained within the result. The error is only set+if @duckdb_query@ returns @DuckDBError@.++The result of this function must not be freed. It will be cleaned up when+@duckdb_destroy_result@ is called.++Parameters:+* @result@: The result object to fetch the error from.++Returns The error of the result.+-}+foreign import ccall unsafe "duckdb_result_error"+    c_duckdb_result_error :: Ptr DuckDBResult -> IO CString++{- | Returns the result error type contained within the result. The error is only+set if @duckdb_query@ returns @DuckDBError@.++Parameters:+* @result@: The result object to fetch the error from.++Returns The error type of the result.+-}+foreign import ccall unsafe "duckdb_result_error_type"+    c_duckdb_result_error_type :: Ptr DuckDBResult -> IO DuckDBErrorType
+ src/Database/DuckDB/FFI/ReplacementScans.hs view
@@ -0,0 +1,50 @@+module Database.DuckDB.FFI.ReplacementScans (+    c_duckdb_add_replacement_scan,+    c_duckdb_replacement_scan_set_function_name,+    c_duckdb_replacement_scan_add_parameter,+    c_duckdb_replacement_scan_set_error,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.Ptr (Ptr)++{- | Add a replacement scan definition to the specified database.++Parameters:+* @db@: The database object to add the replacement scan to+* @replacement@: The replacement scan callback+* @extra_data@: Extra data that is passed back into the specified callback+* @delete_callback@: The delete callback to call on the extra data, if any+-}+foreign import ccall unsafe "duckdb_add_replacement_scan"+    c_duckdb_add_replacement_scan :: DuckDBDatabase -> DuckDBReplacementCallback -> Ptr () -> DuckDBDeleteCallback -> IO ()++{- | Sets the replacement function name. If this function is called in the+replacement callback, the replacement scan is performed. If it is not called,+the replacement callback is not performed.++Parameters:+* @info@: The info object+* @function_name@: The function name to substitute.+-}+foreign import ccall unsafe "duckdb_replacement_scan_set_function_name"+    c_duckdb_replacement_scan_set_function_name :: DuckDBReplacementScanInfo -> CString -> IO ()++{- | Adds a parameter to the replacement scan function.++Parameters:+* @info@: The info object+* @parameter@: The parameter to add.+-}+foreign import ccall unsafe "duckdb_replacement_scan_add_parameter"+    c_duckdb_replacement_scan_add_parameter :: DuckDBReplacementScanInfo -> DuckDBValue -> IO ()++{- | Report that an error has occurred while executing the replacement scan.++Parameters:+* @info@: The info object+* @error@: The error message+-}+foreign import ccall unsafe "duckdb_replacement_scan_set_error"+    c_duckdb_replacement_scan_set_error :: DuckDBReplacementScanInfo -> CString -> IO ()
+ src/Database/DuckDB/FFI/ResultFunctions.hs view
@@ -0,0 +1,92 @@+module Database.DuckDB.FFI.ResultFunctions (+    c_duckdb_result_get_chunk,+    c_duckdb_result_is_streaming,+    c_duckdb_result_chunk_count,+    c_duckdb_result_return_type,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.Types (CBool (..), CInt (..))+import Foreign.Ptr (Ptr)++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Fetches a data chunk from the duckdb_result. This function should be called+repeatedly until the result is exhausted.++The result must be destroyed with @duckdb_destroy_data_chunk@.++This function supersedes all @duckdb_value@ functions, as well as the+@duckdb_column_data@ and @duckdb_nullmask_data@ functions. It results in+significantly better performance, and should be preferred in newer code-bases.++If this function is used, none of the other result functions can be used and+vice versa (i.e., this function cannot be mixed with the legacy result+functions).++Use @duckdb_result_chunk_count@ to figure out how many chunks there are in the+result.++Parameters:+* @result@: The result object to fetch the data chunk from.+* @chunk_index@: The chunk index to fetch from.++Returns The resulting data chunk. Returns @NULL@ if the chunk index is out of+bounds.++These bindings call the wrapper symbol+@wrapped_duckdb_result_get_chunk@ but mirror the DuckDB C API semantics of+@duckdb_result_get_chunk@.+-}+foreign import ccall unsafe "wrapped_duckdb_result_get_chunk"+    c_duckdb_result_get_chunk :: Ptr DuckDBResult -> DuckDBIdx -> IO DuckDBDataChunk++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Checks if the type of the internal result is StreamQueryResult.++Parameters:+* @result@: The result object to check.++Returns Whether or not the result object is of the type StreamQueryResult++These bindings call the wrapper symbol+@wrapped_duckdb_result_is_streaming@ but mirror the DuckDB C API semantics of+@duckdb_result_is_streaming@.+-}+foreign import ccall unsafe "wrapped_duckdb_result_is_streaming"+    c_duckdb_result_is_streaming :: Ptr DuckDBResult -> IO CBool++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns the number of data chunks present in the result.++Parameters:+* @result@: The result object++Returns Number of data chunks present in the result.++These bindings call the wrapper symbol+@wrapped_duckdb_result_chunk_count@ but mirror the DuckDB C API semantics of+@duckdb_result_chunk_count@.+-}+foreign import ccall unsafe "wrapped_duckdb_result_chunk_count"+    c_duckdb_result_chunk_count :: Ptr DuckDBResult -> IO DuckDBIdx++{- | Returns the return_type of the given result, or DUCKDB_RETURN_TYPE_INVALID on+error++Parameters:+* @result@: The result object++Returns The return_type++These bindings call the wrapper symbol+@wrapped_duckdb_result_return_type@ but mirror the DuckDB C API semantics of+@duckdb_result_return_type@.+-}+foreign import ccall unsafe "wrapped_duckdb_result_return_type"+    c_duckdb_result_return_type :: Ptr DuckDBResult -> IO DuckDBResultType
+ src/Database/DuckDB/FFI/SafeFetch.hs view
@@ -0,0 +1,280 @@+module Database.DuckDB.FFI.SafeFetch (+    c_duckdb_value_boolean,+    c_duckdb_value_int8,+    c_duckdb_value_int16,+    c_duckdb_value_int32,+    c_duckdb_value_int64,+    c_duckdb_value_hugeint,+    c_duckdb_value_uhugeint,+    c_duckdb_value_decimal,+    c_duckdb_value_uint8,+    c_duckdb_value_uint16,+    c_duckdb_value_uint32,+    c_duckdb_value_uint64,+    c_duckdb_value_float,+    c_duckdb_value_double,+    c_duckdb_value_date,+    c_duckdb_value_time,+    c_duckdb_value_timestamp,+    c_duckdb_value_interval,+    c_duckdb_value_varchar,+    c_duckdb_value_string,+    c_duckdb_value_varchar_internal,+    c_duckdb_value_string_internal,+    c_duckdb_value_blob,+    c_duckdb_value_is_null,+) where++import Data.Int (Int16, Int32, Int64, Int8)+import Data.Word (Word16, Word32, Word64, Word8)+import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CBool (..), CDouble (..), CFloat (..))+import Foreign.Ptr (Ptr)++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The boolean value at the specified location, or false if the value+cannot be converted.+-}+foreign import ccall unsafe "duckdb_value_boolean"+    c_duckdb_value_boolean :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO CBool++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The int8_t value at the specified location, or 0 if the value cannot+be converted.+-}+foreign import ccall unsafe "duckdb_value_int8"+    c_duckdb_value_int8 :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO Int8++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The int16_t value at the specified location, or 0 if the value cannot+be converted.+-}+foreign import ccall unsafe "duckdb_value_int16"+    c_duckdb_value_int16 :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO Int16++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The int32_t value at the specified location, or 0 if the value cannot+be converted.+-}+foreign import ccall unsafe "duckdb_value_int32"+    c_duckdb_value_int32 :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO Int32++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The int64_t value at the specified location, or 0 if the value cannot+be converted.+-}+foreign import ccall unsafe "duckdb_value_int64"+    c_duckdb_value_int64 :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO Int64++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The duckdb_hugeint value at the specified location, or 0 if the value+cannot be converted.++These bindings call the wrapper symbol @wrapped_duckdb_value_hugeint@+but mirror the DuckDB C API semantics of @duckdb_value_hugeint@.+-}+foreign import ccall unsafe "wrapped_duckdb_value_hugeint"+    c_duckdb_value_hugeint :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> Ptr DuckDBHugeInt -> IO ()++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The duckdb_uhugeint value at the specified location, or 0 if the value+cannot be converted.++These bindings call the wrapper symbol @wrapped_duckdb_value_uhugeint@+but mirror the DuckDB C API semantics of @duckdb_value_uhugeint@.+-}+foreign import ccall unsafe "wrapped_duckdb_value_uhugeint"+    c_duckdb_value_uhugeint :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> Ptr DuckDBUHugeInt -> IO ()++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The duckdb_decimal value at the specified location, or 0 if the value+cannot be converted.++These bindings call the wrapper symbol @wrapped_duckdb_value_decimal@+but mirror the DuckDB C API semantics of @duckdb_value_decimal@.+-}+foreign import ccall unsafe "wrapped_duckdb_value_decimal"+    c_duckdb_value_decimal :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> Ptr DuckDBDecimal -> IO ()++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The uint8_t value at the specified location, or 0 if the value cannot+be converted.+-}+foreign import ccall unsafe "duckdb_value_uint8"+    c_duckdb_value_uint8 :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO Word8++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The uint16_t value at the specified location, or 0 if the value cannot+be converted.+-}+foreign import ccall unsafe "duckdb_value_uint16"+    c_duckdb_value_uint16 :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO Word16++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The uint32_t value at the specified location, or 0 if the value cannot+be converted.+-}+foreign import ccall unsafe "duckdb_value_uint32"+    c_duckdb_value_uint32 :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO Word32++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The uint64_t value at the specified location, or 0 if the value cannot+be converted.+-}+foreign import ccall unsafe "duckdb_value_uint64"+    c_duckdb_value_uint64 :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO Word64++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The float value at the specified location, or 0 if the value cannot be+converted.+-}+foreign import ccall unsafe "duckdb_value_float"+    c_duckdb_value_float :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO CFloat++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The double value at the specified location, or 0 if the value cannot+be converted.+-}+foreign import ccall unsafe "duckdb_value_double"+    c_duckdb_value_double :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO CDouble++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The duckdb_date value at the specified location, or 0 if the value+cannot be converted.+-}+foreign import ccall unsafe "duckdb_value_date"+    c_duckdb_value_date :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO DuckDBDate++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The duckdb_time value at the specified location, or 0 if the value+cannot be converted.+-}+foreign import ccall unsafe "duckdb_value_time"+    c_duckdb_value_time :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO DuckDBTime++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The duckdb_timestamp value at the specified location, or 0 if the+value cannot be converted.+-}+foreign import ccall unsafe "duckdb_value_timestamp"+    c_duckdb_value_timestamp :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO DuckDBTimestamp++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The duckdb_interval value at the specified location, or 0 if the value+cannot be converted.++These bindings call the wrapper symbol @wrapped_duckdb_value_interval@+but mirror the DuckDB C API semantics of @duckdb_value_interval@.+-}+foreign import ccall unsafe "wrapped_duckdb_value_interval"+    c_duckdb_value_interval :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> Ptr DuckDBInterval -> IO ()++{- | > Deprecated This method has been deprecated. Use duckdb_value_string instead.+This function does not work correctly if the string contains null bytes.++Returns The text value at the specified location as a null-terminated string,+or nullptr if the value cannot be converted. The result must be freed with+@duckdb_free@.+-}+foreign import ccall unsafe "duckdb_value_varchar"+    c_duckdb_value_varchar :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO CString++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++No support for nested types, and for other complex types. The resulting field+"string.data" must be freed with @duckdb_free.@++Returns The string value at the specified location. Attempts to cast the+result value to string.++These bindings call the wrapper symbol @wrapped_duckdb_value_string@+but mirror the DuckDB C API semantics of @duckdb_value_string@.+-}+foreign import ccall unsafe "wrapped_duckdb_value_string"+    c_duckdb_value_string :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> Ptr DuckDBString -> IO ()++{- | > Deprecated This method has been deprecated. Use duckdb_value_string_internal+instead. This function does not work correctly if the string contains null+bytes.++Returns The char* value at the specified location. ONLY works on VARCHAR+columns and does not auto-cast. If the column is NOT a VARCHAR column this+function will return NULL. The result must NOT be freed.+-}+foreign import ccall unsafe "duckdb_value_varchar_internal"+    c_duckdb_value_varchar_internal :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO CString++{- | > Deprecated This method has been deprecated. Use duckdb_value_string_internal+instead. This function does not work correctly if the string contains null+bytes.++Returns The char* value at the specified location. ONLY works on VARCHAR+columns and does not auto-cast. If the column is NOT a VARCHAR column this+function will return NULL. The result must NOT be freed.++These bindings call the wrapper symbol+@wrapped_duckdb_value_string_internal@ but mirror the DuckDB C API semantics of+@duckdb_value_string_internal@.+-}+foreign import ccall unsafe "wrapped_duckdb_value_string_internal"+    c_duckdb_value_string_internal :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> Ptr DuckDBString -> IO ()++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns The duckdb_blob value at the specified location. Returns a blob with+blob.data set to nullptr if the value cannot be converted. The resulting field+"blob.data" must be freed with @duckdb_free.@++These bindings call the wrapper symbol @wrapped_duckdb_value_blob@ but+mirror the DuckDB C API semantics of @duckdb_value_blob@.+-}+foreign import ccall unsafe "wrapped_duckdb_value_blob"+    c_duckdb_value_blob :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> Ptr DuckDBBlob -> IO ()++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Returns Returns true if the value at the specified index is NULL, and false+otherwise.+-}+foreign import ccall unsafe "duckdb_value_is_null"+    c_duckdb_value_is_null :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO CBool
+ src/Database/DuckDB/FFI/ScalarFunctions.hs view
@@ -0,0 +1,299 @@+module Database.DuckDB.FFI.ScalarFunctions (+    c_duckdb_create_scalar_function,+    c_duckdb_destroy_scalar_function,+    c_duckdb_scalar_function_set_name,+    c_duckdb_scalar_function_set_varargs,+    c_duckdb_scalar_function_set_special_handling,+    c_duckdb_scalar_function_set_volatile,+    c_duckdb_scalar_function_add_parameter,+    c_duckdb_scalar_function_set_return_type,+    c_duckdb_scalar_function_set_extra_info,+    c_duckdb_scalar_function_set_bind,+    c_duckdb_scalar_function_set_bind_data,+    c_duckdb_scalar_function_set_bind_data_copy,+    c_duckdb_scalar_function_bind_set_error,+    c_duckdb_scalar_function_set_function,+    c_duckdb_register_scalar_function,+    c_duckdb_scalar_function_get_extra_info,+    c_duckdb_scalar_function_bind_get_extra_info,+    c_duckdb_scalar_function_get_bind_data,+    c_duckdb_scalar_function_get_client_context,+    c_duckdb_scalar_function_set_error,+    c_duckdb_create_scalar_function_set,+    c_duckdb_destroy_scalar_function_set,+    c_duckdb_add_scalar_function_to_set,+    c_duckdb_register_scalar_function_set,+    c_duckdb_scalar_function_bind_get_argument_count,+    c_duckdb_scalar_function_bind_get_argument,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CInt (..))+import Foreign.Ptr (Ptr)++{- | Creates a new empty scalar function.++The return value must be destroyed with @duckdb_destroy_scalar_function@.++Returns The scalar function object.+-}+foreign import ccall unsafe "duckdb_create_scalar_function"+    c_duckdb_create_scalar_function :: IO DuckDBScalarFunction++{- | Destroys the given scalar function object.++Parameters:+* @scalar_function@: The scalar function to destroy+-}+foreign import ccall unsafe "duckdb_destroy_scalar_function"+    c_duckdb_destroy_scalar_function :: Ptr DuckDBScalarFunction -> IO ()++{- | Sets the name of the given scalar function.++Parameters:+* @scalar_function@: The scalar function+* @name@: The name of the scalar function+-}+foreign import ccall unsafe "duckdb_scalar_function_set_name"+    c_duckdb_scalar_function_set_name :: DuckDBScalarFunction -> CString -> IO ()++{- | Sets the parameters of the given scalar function to varargs. Does not require+adding parameters with duckdb_scalar_function_add_parameter.++Parameters:+* @scalar_function@: The scalar function.+* @type@: The type of the arguments.++Returns The parameter type. Cannot contain INVALID.+-}+foreign import ccall unsafe "duckdb_scalar_function_set_varargs"+    c_duckdb_scalar_function_set_varargs :: DuckDBScalarFunction -> DuckDBLogicalType -> IO ()++{- | Sets the scalar function's null-handling behavior to special.++Parameters:+* @scalar_function@: The scalar function.+-}+foreign import ccall unsafe "duckdb_scalar_function_set_special_handling"+    c_duckdb_scalar_function_set_special_handling :: DuckDBScalarFunction -> IO ()++{- | Sets the Function Stability of the scalar function to VOLATILE, indicating the+function should be re-run for every row. This limits optimization that can be+performed for the function.++Parameters:+* @scalar_function@: The scalar function.+-}+foreign import ccall unsafe "duckdb_scalar_function_set_volatile"+    c_duckdb_scalar_function_set_volatile :: DuckDBScalarFunction -> IO ()++{- | Adds a parameter to the scalar function.++Parameters:+* @scalar_function@: The scalar function.+* @type@: The parameter type. Cannot contain INVALID.+-}+foreign import ccall unsafe "duckdb_scalar_function_add_parameter"+    c_duckdb_scalar_function_add_parameter :: DuckDBScalarFunction -> DuckDBLogicalType -> IO ()++{- | Sets the return type of the scalar function.++Parameters:+* @scalar_function@: The scalar function+* @type@: Cannot contain INVALID or ANY.+-}+foreign import ccall unsafe "duckdb_scalar_function_set_return_type"+    c_duckdb_scalar_function_set_return_type :: DuckDBScalarFunction -> DuckDBLogicalType -> IO ()++{- | Assigns extra information to the scalar function that can be fetched during+binding, etc.++Parameters:+* @scalar_function@: The scalar function+* @extra_info@: The extra information+* @destroy@: The callback that will be called to destroy the extra information+  (if any)+-}+foreign import ccall unsafe "duckdb_scalar_function_set_extra_info"+    c_duckdb_scalar_function_set_extra_info :: DuckDBScalarFunction -> Ptr () -> DuckDBDeleteCallback -> IO ()++{- | Sets the (optional) bind function of the scalar function.++Parameters:+* @scalar_function@: The scalar function.+* @bind@: The bind function.+-}+foreign import ccall unsafe "duckdb_scalar_function_set_bind"+    c_duckdb_scalar_function_set_bind :: DuckDBScalarFunction -> DuckDBScalarFunctionBindFun -> IO ()++{- | Sets the user-provided bind data in the bind object of the scalar function.+The bind data object can be retrieved again during execution. In most case,+you also need to set the copy-callback of your bind data via+duckdb_scalar_function_set_bind_data_copy.++Parameters:+* @info@: The bind info of the scalar function.+* @bind_data@: The bind data object.+* @destroy@: The callback to destroy the bind data (if any).+-}+foreign import ccall unsafe "duckdb_scalar_function_set_bind_data"+    c_duckdb_scalar_function_set_bind_data :: DuckDBBindInfo -> Ptr () -> DuckDBDeleteCallback -> IO ()++{- | Sets the copy-callback for the user-provided bind data in the bind object of+the scalar function.++Parameters:+* @info@: The bind info of the scalar function.+* @copy@: The callback to copy the bind data (if any).+-}+foreign import ccall unsafe "duckdb_scalar_function_set_bind_data_copy"+    c_duckdb_scalar_function_set_bind_data_copy :: DuckDBBindInfo -> DuckDBCopyCallback -> IO ()++{- | Report that an error has occurred while calling bind on a scalar function.++Parameters:+* @info@: The bind info object.+* @error@: The error message.+-}+foreign import ccall unsafe "duckdb_scalar_function_bind_set_error"+    c_duckdb_scalar_function_bind_set_error :: DuckDBBindInfo -> CString -> IO ()++{- | Sets the main function of the scalar function.++Parameters:+* @scalar_function@: The scalar function+* @function@: The function+-}+foreign import ccall unsafe "duckdb_scalar_function_set_function"+    c_duckdb_scalar_function_set_function :: DuckDBScalarFunction -> DuckDBScalarFunctionFun -> IO ()++{- | Register the scalar function object within the given connection.++The function requires at least a name, a function and a return type.++If the function is incomplete or a function with this name already exists+DuckDBError is returned.++Parameters:+* @con@: The connection to register it in.+* @scalar_function@: The function pointer++Returns Whether or not the registration was successful.+-}+foreign import ccall unsafe "duckdb_register_scalar_function"+    c_duckdb_register_scalar_function :: DuckDBConnection -> DuckDBScalarFunction -> IO DuckDBState++{- | Retrieves the extra info of the function as set in+@duckdb_scalar_function_set_extra_info@.++Parameters:+* @info@: The info object.++Returns The extra info.+-}+foreign import ccall unsafe "duckdb_scalar_function_get_extra_info"+    c_duckdb_scalar_function_get_extra_info :: DuckDBFunctionInfo -> IO (Ptr ())++{- | Retrieves the extra info of the function as set in the bind info.++Parameters:+* @info@: The info object.++Returns The extra info.+-}+foreign import ccall unsafe "duckdb_scalar_function_bind_get_extra_info"+    c_duckdb_scalar_function_bind_get_extra_info :: DuckDBBindInfo -> IO (Ptr ())++{- | Gets the scalar function's bind data set by+@duckdb_scalar_function_set_bind_data@. Note that the bind data is read-only.++Parameters:+* @info@: The function info.++Returns The bind data object.+-}+foreign import ccall unsafe "duckdb_scalar_function_get_bind_data"+    c_duckdb_scalar_function_get_bind_data :: DuckDBFunctionInfo -> IO (Ptr ())++{- | Retrieves the client context of the bind info of a scalar function.++Parameters:+* @info@: The bind info object of the scalar function.+* @out_context@: The client context of the bind info. Must be destroyed with+  @duckdb_destroy_client_context@.+-}+foreign import ccall unsafe "duckdb_scalar_function_get_client_context"+    c_duckdb_scalar_function_get_client_context :: DuckDBBindInfo -> Ptr DuckDBClientContext -> IO ()++{- | Report that an error has occurred while executing the scalar function.++Parameters:+* @info@: The info object.+* @error@: The error message+-}+foreign import ccall unsafe "duckdb_scalar_function_set_error"+    c_duckdb_scalar_function_set_error :: DuckDBFunctionInfo -> CString -> IO ()++{- | Creates a new empty scalar function set.++The return value must be destroyed with @duckdb_destroy_scalar_function_set@.++Returns The scalar function set object.+-}+foreign import ccall unsafe "duckdb_create_scalar_function_set"+    c_duckdb_create_scalar_function_set :: CString -> IO DuckDBScalarFunctionSet++-- | Destroys the given scalar function set object.+foreign import ccall unsafe "duckdb_destroy_scalar_function_set"+    c_duckdb_destroy_scalar_function_set :: Ptr DuckDBScalarFunctionSet -> IO ()++{- | Adds the scalar function as a new overload to the scalar function set.++Returns DuckDBError if the function could not be added, for example if the+overload already exists.++Parameters:+* @set@: The scalar function set+* @function@: The function to add+-}+foreign import ccall unsafe "duckdb_add_scalar_function_to_set"+    c_duckdb_add_scalar_function_to_set :: DuckDBScalarFunctionSet -> DuckDBScalarFunction -> IO DuckDBState++{- | Register the scalar function set within the given connection.++The set requires at least a single valid overload.++If the set is incomplete or a function with this name already exists+DuckDBError is returned.++Parameters:+* @con@: The connection to register it in.+* @set@: The function set to register++Returns Whether or not the registration was successful.+-}+foreign import ccall unsafe "duckdb_register_scalar_function_set"+    c_duckdb_register_scalar_function_set :: DuckDBConnection -> DuckDBScalarFunctionSet -> IO DuckDBState++{- | Returns the number of input arguments of the scalar function.++Parameters:+* @info@: The bind info.++Returns The number of input arguments.+-}+foreign import ccall unsafe "duckdb_scalar_function_bind_get_argument_count"+    c_duckdb_scalar_function_bind_get_argument_count :: DuckDBBindInfo -> IO DuckDBIdx++{- | Returns the input argument at index of the scalar function.++Parameters:+* @info@: The bind info.+* @index@: The argument index.++Returns The input argument at index. Must be destroyed with+@duckdb_destroy_expression@.+-}+foreign import ccall unsafe "duckdb_scalar_function_bind_get_argument"+    c_duckdb_scalar_function_bind_get_argument :: DuckDBBindInfo -> DuckDBIdx -> IO DuckDBExpression
+ src/Database/DuckDB/FFI/SelectionVector.hs view
@@ -0,0 +1,37 @@+module Database.DuckDB.FFI.SelectionVector (+    c_duckdb_create_selection_vector,+    c_duckdb_destroy_selection_vector,+    c_duckdb_selection_vector_get_data_ptr,+) where++import Database.DuckDB.FFI.Types+import Foreign.Ptr (Ptr)++{- | Creates a new selection vector of size @size@. Must be destroyed with+@duckdb_destroy_selection_vector@.++Parameters:+* @size@: The size of the selection vector.++Returns The selection vector.+-}+foreign import ccall unsafe "duckdb_create_selection_vector"+    c_duckdb_create_selection_vector :: DuckDBIdx -> IO DuckDBSelectionVector++{- | Destroys the selection vector and de-allocates its memory.++Parameters:+* @sel@: The selection vector.+-}+foreign import ccall unsafe "duckdb_destroy_selection_vector"+    c_duckdb_destroy_selection_vector :: DuckDBSelectionVector -> IO ()++{- | Access the data pointer of a selection vector.++Parameters:+* @sel@: The selection vector.++Returns The data pointer.+-}+foreign import ccall unsafe "duckdb_selection_vector_get_data_ptr"+    c_duckdb_selection_vector_get_data_ptr :: DuckDBSelectionVector -> IO (Ptr DuckDBSel)
+ src/Database/DuckDB/FFI/StreamingResult.hs view
@@ -0,0 +1,54 @@+module Database.DuckDB.FFI.StreamingResult (+    c_duckdb_stream_fetch_chunk,+    c_duckdb_fetch_chunk,+) where++import Database.DuckDB.FFI.Types+import Foreign.Ptr (Ptr)++{- | > Warning Deprecation notice. This method is scheduled for removal in a future+release.++Fetches a data chunk from the (streaming) duckdb_result. This function should+be called repeatedly until the result is exhausted.++The result must be destroyed with @duckdb_destroy_data_chunk@.++This function can only be used on duckdb_results created with+@duckdb_pending_prepared_streaming@++If this function is used, none of the other result functions can be used and+vice versa (i.e., this function cannot be mixed with the legacy result+functions or the materialized result functions).++It is not known beforehand how many chunks will be returned by this result.++Parameters:+* @result@: The result object to fetch the data chunk from.++Returns The resulting data chunk. Returns @NULL@ if the result has an error.++These bindings call the wrapper symbol+@wrapped_duckdb_stream_fetch_chunk@ but mirror the DuckDB C API semantics of+@duckdb_stream_fetch_chunk@.+-}+foreign import ccall unsafe "wrapped_duckdb_stream_fetch_chunk"+    c_duckdb_stream_fetch_chunk :: Ptr DuckDBResult -> IO DuckDBDataChunk++{- | Fetches a data chunk from a duckdb_result. This function should be called+repeatedly until the result is exhausted.++The result must be destroyed with @duckdb_destroy_data_chunk@.++It is not known beforehand how many chunks will be returned by this result.++Parameters:+* @result@: The result object to fetch the data chunk from.++Returns The resulting data chunk. Returns @NULL@ if the result has an error.++These bindings call the wrapper symbol @wrapped_duckdb_fetch_chunk@ but+mirror the DuckDB C API semantics of @duckdb_fetch_chunk@.+-}+foreign import ccall unsafe "wrapped_duckdb_fetch_chunk"+    c_duckdb_fetch_chunk :: Ptr DuckDBResult -> IO DuckDBDataChunk
+ src/Database/DuckDB/FFI/TableDescription.hs view
@@ -0,0 +1,90 @@+module Database.DuckDB.FFI.TableDescription (+    c_duckdb_table_description_create,+    c_duckdb_table_description_create_ext,+    c_duckdb_table_description_destroy,+    c_duckdb_table_description_error,+    c_duckdb_column_has_default,+    c_duckdb_table_description_get_column_name,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CBool (..), CInt (..))+import Foreign.Ptr (Ptr)++{- | Creates a table description object. Note that+@duckdb_table_description_destroy@ should always be called on the resulting+table_description, even if the function returns @DuckDBError@.++Parameters:+* @connection@: The connection context.+* @schema@: The schema of the table, or @nullptr@ for the default schema.+* @table@: The table name.+* @out@: The resulting table description object.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_table_description_create"+    c_duckdb_table_description_create :: DuckDBConnection -> CString -> CString -> Ptr DuckDBTableDescription -> IO DuckDBState++{- | Creates a table description object. Note that+@duckdb_table_description_destroy@ must be called on the resulting+table_description, even if the function returns @DuckDBError@.++Parameters:+* @connection@: The connection context.+* @catalog@: The catalog (database) name of the table, or @nullptr@ for the+  default catalog.+* @schema@: The schema of the table, or @nullptr@ for the default schema.+* @table@: The table name.+* @out@: The resulting table description object.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall safe "duckdb_table_description_create_ext"+    c_duckdb_table_description_create_ext :: DuckDBConnection -> CString -> CString -> CString -> Ptr DuckDBTableDescription -> IO DuckDBState++{- | Destroy the TableDescription object.++Parameters:+* @table_description@: The table_description to destroy.+-}+foreign import ccall unsafe "duckdb_table_description_destroy"+    c_duckdb_table_description_destroy :: Ptr DuckDBTableDescription -> IO ()++{- | Returns the error message associated with the given table_description. If the+table_description has no error message, this returns @nullptr@ instead. The+error message should not be freed. It will be de-allocated when+@duckdb_table_description_destroy@ is called.++Parameters:+* @table_description@: The table_description to get the error from.++Returns The error message, or @nullptr@ if there is none.+-}+foreign import ccall unsafe "duckdb_table_description_error"+    c_duckdb_table_description_error :: DuckDBTableDescription -> IO CString++{- | Check if the column at @index@ index of the table has a DEFAULT expression.++Parameters:+* @table_description@: The table_description to query.+* @index@: The index of the column to query.+* @out@: The out-parameter used to store the result.++Returns @DuckDBSuccess@ on success or @DuckDBError@ on failure.+-}+foreign import ccall unsafe "duckdb_column_has_default"+    c_duckdb_column_has_default :: DuckDBTableDescription -> DuckDBIdx -> Ptr CBool -> IO DuckDBState++{- | Obtain the column name at @index@. The out result must be destroyed with+@duckdb_free@.++Parameters:+* @table_description@: The table_description to query.+* @index@: The index of the column to query.++Returns The column name.+-}+foreign import ccall unsafe "duckdb_table_description_get_column_name"+    c_duckdb_table_description_get_column_name :: DuckDBTableDescription -> DuckDBIdx -> IO CString
+ src/Database/DuckDB/FFI/TableFunctions.hs view
@@ -0,0 +1,405 @@+module Database.DuckDB.FFI.TableFunctions (+    -- Table function definition+    c_duckdb_create_table_function,+    c_duckdb_destroy_table_function,+    c_duckdb_table_function_set_name,+    c_duckdb_table_function_add_parameter,+    c_duckdb_table_function_add_named_parameter,+    c_duckdb_table_function_set_extra_info,+    c_duckdb_table_function_set_bind,+    c_duckdb_table_function_set_init,+    c_duckdb_table_function_set_local_init,+    c_duckdb_table_function_set_function,+    c_duckdb_table_function_supports_projection_pushdown,+    c_duckdb_register_table_function,+    -- Bind helpers+    c_duckdb_bind_get_extra_info,+    c_duckdb_table_function_get_client_context,+    c_duckdb_bind_add_result_column,+    c_duckdb_bind_get_parameter_count,+    c_duckdb_bind_get_parameter,+    c_duckdb_bind_get_named_parameter,+    c_duckdb_bind_set_bind_data,+    c_duckdb_bind_set_cardinality,+    c_duckdb_bind_set_error,+    -- Init helpers+    c_duckdb_init_get_extra_info,+    c_duckdb_init_get_bind_data,+    c_duckdb_init_set_init_data,+    c_duckdb_init_get_column_count,+    c_duckdb_init_get_column_index,+    c_duckdb_init_set_max_threads,+    c_duckdb_init_set_error,+    -- Execution helpers+    c_duckdb_function_get_extra_info,+    c_duckdb_function_get_bind_data,+    c_duckdb_function_get_init_data,+    c_duckdb_function_get_local_init_data,+    c_duckdb_function_set_error,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CBool (..), CInt (..))+import Foreign.Ptr (Ptr)++{- | Creates a new empty table function.++The return value should be destroyed with @duckdb_destroy_table_function@.++Returns The table function object.+-}+foreign import ccall unsafe "duckdb_create_table_function"+    c_duckdb_create_table_function :: IO DuckDBTableFunction++{- | Destroys the given table function object.++Parameters:+* @table_function@: The table function to destroy+-}+foreign import ccall unsafe "duckdb_destroy_table_function"+    c_duckdb_destroy_table_function :: Ptr DuckDBTableFunction -> IO ()++{- | Sets the name of the given table function.++Parameters:+* @table_function@: The table function+* @name@: The name of the table function+-}+foreign import ccall unsafe "duckdb_table_function_set_name"+    c_duckdb_table_function_set_name :: DuckDBTableFunction -> CString -> IO ()++{- | Adds a parameter to the table function.++Parameters:+* @table_function@: The table function.+* @type@: The parameter type. Cannot contain INVALID.+-}+foreign import ccall unsafe "duckdb_table_function_add_parameter"+    c_duckdb_table_function_add_parameter :: DuckDBTableFunction -> DuckDBLogicalType -> IO ()++{- | Adds a named parameter to the table function.++Parameters:+* @table_function@: The table function.+* @name@: The parameter name.+* @type@: The parameter type. Cannot contain INVALID.+-}+foreign import ccall unsafe "duckdb_table_function_add_named_parameter"+    c_duckdb_table_function_add_named_parameter :: DuckDBTableFunction -> CString -> DuckDBLogicalType -> IO ()++{- | Assigns extra information to the table function that can be fetched during+binding, etc.++Parameters:+* @table_function@: The table function+* @extra_info@: The extra information+* @destroy@: The callback that will be called to destroy the extra information+  (if any)+-}+foreign import ccall unsafe "duckdb_table_function_set_extra_info"+    c_duckdb_table_function_set_extra_info :: DuckDBTableFunction -> Ptr () -> DuckDBDeleteCallback -> IO ()++{- | Sets the bind function of the table function.++Parameters:+* @table_function@: The table function+* @bind@: The bind function+-}+foreign import ccall unsafe "duckdb_table_function_set_bind"+    c_duckdb_table_function_set_bind :: DuckDBTableFunction -> DuckDBTableFunctionBindFun -> IO ()++{- | Sets the init function of the table function.++Parameters:+* @table_function@: The table function+* @init@: The init function+-}+foreign import ccall unsafe "duckdb_table_function_set_init"+    c_duckdb_table_function_set_init :: DuckDBTableFunction -> DuckDBTableFunctionInitFun -> IO ()++{- | Sets the thread-local init function of the table function.++Parameters:+* @table_function@: The table function+* @init@: The init function+-}+foreign import ccall unsafe "duckdb_table_function_set_local_init"+    c_duckdb_table_function_set_local_init :: DuckDBTableFunction -> DuckDBTableFunctionInitFun -> IO ()++{- | Sets the main function of the table function.++Parameters:+* @table_function@: The table function+* @function@: The function+-}+foreign import ccall unsafe "duckdb_table_function_set_function"+    c_duckdb_table_function_set_function :: DuckDBTableFunction -> DuckDBTableFunctionFun -> IO ()++{- | Sets whether or not the given table function supports projection pushdown.++If this is set to true, the system will provide a list of all required columns+in the @init@ stage through the @duckdb_init_get_column_count@ and+@duckdb_init_get_column_index@ functions. If this is set to false (the+default), the system will expect all columns to be projected.++Parameters:+* @table_function@: The table function+* @pushdown@: True if the table function supports projection pushdown, false+  otherwise.+-}+foreign import ccall unsafe "duckdb_table_function_supports_projection_pushdown"+    c_duckdb_table_function_supports_projection_pushdown :: DuckDBTableFunction -> CBool -> IO ()++{- | Register the table function object within the given connection.++The function requires at least a name, a bind function, an init function and a+main function.++If the function is incomplete or a function with this name already exists+DuckDBError is returned.++Parameters:+* @con@: The connection to register it in.+* @function@: The function pointer++Returns Whether or not the registration was successful.+-}+foreign import ccall unsafe "duckdb_register_table_function"+    c_duckdb_register_table_function :: DuckDBConnection -> DuckDBTableFunction -> IO DuckDBState++{- | Retrieves the extra info of the function as set in+@duckdb_table_function_set_extra_info@.++Parameters:+* @info@: The info object++Returns The extra info+-}+foreign import ccall unsafe "duckdb_bind_get_extra_info"+    c_duckdb_bind_get_extra_info :: DuckDBBindInfo -> IO (Ptr ())++{- | Retrieves the client context of the bind info of a table function.++Parameters:+* @info@: The bind info object of the table function.+* @out_context@: The client context of the bind info. Must be destroyed with+  @duckdb_destroy_client_context@.+-}+foreign import ccall unsafe "duckdb_table_function_get_client_context"+    c_duckdb_table_function_get_client_context :: DuckDBBindInfo -> Ptr DuckDBClientContext -> IO ()++{- | Adds a result column to the output of the table function.++Parameters:+* @info@: The table function's bind info.+* @name@: The column name.+* @type@: The logical column type.+-}+foreign import ccall unsafe "duckdb_bind_add_result_column"+    c_duckdb_bind_add_result_column :: DuckDBBindInfo -> CString -> DuckDBLogicalType -> IO ()++{- | Retrieves the number of regular (non-named) parameters to the function.++Parameters:+* @info@: The info object++Returns The number of parameters+-}+foreign import ccall unsafe "duckdb_bind_get_parameter_count"+    c_duckdb_bind_get_parameter_count :: DuckDBBindInfo -> IO DuckDBIdx++{- | Retrieves the parameter at the given index.++The result must be destroyed with @duckdb_destroy_value@.++Parameters:+* @info@: The info object+* @index@: The index of the parameter to get++Returns The value of the parameter. Must be destroyed with+@duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_bind_get_parameter"+    c_duckdb_bind_get_parameter :: DuckDBBindInfo -> DuckDBIdx -> IO DuckDBValue++{- | Retrieves a named parameter with the given name.++The result must be destroyed with @duckdb_destroy_value@.++Parameters:+* @info@: The info object+* @name@: The name of the parameter++Returns The value of the parameter. Must be destroyed with+@duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_bind_get_named_parameter"+    c_duckdb_bind_get_named_parameter :: DuckDBBindInfo -> CString -> IO DuckDBValue++{- | Sets the user-provided bind data in the bind object of the table function.+This object can be retrieved again during execution.++Parameters:+* @info@: The bind info of the table function.+* @bind_data@: The bind data object.+* @destroy@: The callback to destroy the bind data (if any).+-}+foreign import ccall unsafe "duckdb_bind_set_bind_data"+    c_duckdb_bind_set_bind_data :: DuckDBBindInfo -> Ptr () -> DuckDBDeleteCallback -> IO ()++{- | Sets the cardinality estimate for the table function, used for optimization.++Parameters:+* @info@: The bind data object.+* @is_exact@: Whether or not the cardinality estimate is exact, or an+  approximation+-}+foreign import ccall unsafe "duckdb_bind_set_cardinality"+    c_duckdb_bind_set_cardinality :: DuckDBBindInfo -> DuckDBIdx -> CBool -> IO ()++{- | Report that an error has occurred while calling bind on a table function.++Parameters:+* @info@: The info object+* @error@: The error message+-}+foreign import ccall unsafe "duckdb_bind_set_error"+    c_duckdb_bind_set_error :: DuckDBBindInfo -> CString -> IO ()++{- | Retrieves the extra info of the function as set in+@duckdb_table_function_set_extra_info@.++Parameters:+* @info@: The info object++Returns The extra info+-}+foreign import ccall unsafe "duckdb_init_get_extra_info"+    c_duckdb_init_get_extra_info :: DuckDBInitInfo -> IO (Ptr ())++{- | Gets the bind data set by @duckdb_bind_set_bind_data@ during the bind.++Note that the bind data should be considered as read-only. For tracking state,+use the init data instead.++Parameters:+* @info@: The info object++Returns The bind data object+-}+foreign import ccall unsafe "duckdb_init_get_bind_data"+    c_duckdb_init_get_bind_data :: DuckDBInitInfo -> IO (Ptr ())++{- | Sets the user-provided init data in the init object. This object can be+retrieved again during execution.++Parameters:+* @info@: The info object+* @init_data@: The init data object.+* @destroy@: The callback that will be called to destroy the init data (if+  any)+-}+foreign import ccall unsafe "duckdb_init_set_init_data"+    c_duckdb_init_set_init_data :: DuckDBInitInfo -> Ptr () -> DuckDBDeleteCallback -> IO ()++{- | Returns the number of projected columns.++This function must be used if projection pushdown is enabled to figure out+which columns to emit.++Parameters:+* @info@: The info object++Returns The number of projected columns.+-}+foreign import ccall unsafe "duckdb_init_get_column_count"+    c_duckdb_init_get_column_count :: DuckDBInitInfo -> IO DuckDBIdx++{- | Returns the column index of the projected column at the specified position.++This function must be used if projection pushdown is enabled to figure out+which columns to emit.++Parameters:+* @info@: The info object+* @column_index@: The index at which to get the projected column index, from+  0..duckdb_init_get_column_count(info)++Returns The column index of the projected column.+-}+foreign import ccall unsafe "duckdb_init_get_column_index"+    c_duckdb_init_get_column_index :: DuckDBInitInfo -> DuckDBIdx -> IO DuckDBIdx++{- | Sets how many threads can process this table function in parallel (default: 1)++Parameters:+* @info@: The info object+* @max_threads@: The maximum amount of threads that can process this table+  function+-}+foreign import ccall unsafe "duckdb_init_set_max_threads"+    c_duckdb_init_set_max_threads :: DuckDBInitInfo -> DuckDBIdx -> IO ()++{- | Report that an error has occurred while calling init.++Parameters:+* @info@: The info object+* @error@: The error message+-}+foreign import ccall unsafe "duckdb_init_set_error"+    c_duckdb_init_set_error :: DuckDBInitInfo -> CString -> IO ()++{- | Retrieves the extra info of the function as set in+@duckdb_table_function_set_extra_info@.++Parameters:+* @info@: The info object++Returns The extra info+-}+foreign import ccall unsafe "duckdb_function_get_extra_info"+    c_duckdb_function_get_extra_info :: DuckDBFunctionInfo -> IO (Ptr ())++{- | Gets the table function's bind data set by @duckdb_bind_set_bind_data@.++Note that the bind data is read-only. For tracking state, use the init data+instead.++Parameters:+* @info@: The function info object.++Returns The bind data object.+-}+foreign import ccall unsafe "duckdb_function_get_bind_data"+    c_duckdb_function_get_bind_data :: DuckDBFunctionInfo -> IO (Ptr ())++{- | Gets the init data set by @duckdb_init_set_init_data@ during the init.++Parameters:+* @info@: The info object++Returns The init data object+-}+foreign import ccall unsafe "duckdb_function_get_init_data"+    c_duckdb_function_get_init_data :: DuckDBFunctionInfo -> IO (Ptr ())++{- | Gets the thread-local init data set by @duckdb_init_set_init_data@ during the+local_init.++Parameters:+* @info@: The info object++Returns The init data object+-}+foreign import ccall unsafe "duckdb_function_get_local_init_data"+    c_duckdb_function_get_local_init_data :: DuckDBFunctionInfo -> IO (Ptr ())++{- | Report that an error has occurred while executing the function.++Parameters:+* @info@: The info object+* @error@: The error message+-}+foreign import ccall unsafe "duckdb_function_set_error"+    c_duckdb_function_set_error :: DuckDBFunctionInfo -> CString -> IO ()
+ src/Database/DuckDB/FFI/Threading.hs view
@@ -0,0 +1,104 @@+module Database.DuckDB.FFI.Threading (+    c_duckdb_execute_tasks,+    c_duckdb_create_task_state,+    c_duckdb_execute_tasks_state,+    c_duckdb_execute_n_tasks_state,+    c_duckdb_finish_execution,+    c_duckdb_task_state_is_finished,+    c_duckdb_destroy_task_state,+    c_duckdb_execution_is_finished,+) where++import Database.DuckDB.FFI.Types+import Foreign.C.Types (CBool (..))++{- | Execute DuckDB tasks on this thread.++Will return after @max_tasks@ have been executed, or if there are no more+tasks present.++Parameters:+* @database@: The database object to execute tasks for+* @max_tasks@: The maximum amount of tasks to execute+-}+foreign import ccall safe "duckdb_execute_tasks"+    c_duckdb_execute_tasks :: DuckDBDatabase -> DuckDBIdx -> IO ()++{- | Creates a task state that can be used with duckdb_execute_tasks_state to+execute tasks until @duckdb_finish_execution@ is called on the state.++@duckdb_destroy_state@ must be called on the result.++Parameters:+* @database@: The database object to create the task state for++Returns The task state that can be used with duckdb_execute_tasks_state.+-}+foreign import ccall unsafe "duckdb_create_task_state"+    c_duckdb_create_task_state :: DuckDBDatabase -> IO DuckDBTaskState++{- | Execute DuckDB tasks on this thread.++The thread will keep on executing tasks forever, until duckdb_finish_execution+is called on the state. Multiple threads can share the same duckdb_task_state.++Parameters:+* @state@: The task state of the executor+-}+foreign import ccall safe "duckdb_execute_tasks_state"+    c_duckdb_execute_tasks_state :: DuckDBTaskState -> IO ()++{- | Execute DuckDB tasks on this thread.++The thread will keep on executing tasks until either duckdb_finish_execution+is called on the state, max_tasks tasks have been executed or there are no+more tasks to be executed.++Multiple threads can share the same duckdb_task_state.++Parameters:+* @state@: The task state of the executor+* @max_tasks@: The maximum amount of tasks to execute++Returns The amount of tasks that have actually been executed+-}+foreign import ccall safe "duckdb_execute_n_tasks_state"+    c_duckdb_execute_n_tasks_state :: DuckDBTaskState -> DuckDBIdx -> IO DuckDBIdx++{- | Finish execution on a specific task.++Parameters:+* @state@: The task state to finish execution+-}+foreign import ccall unsafe "duckdb_finish_execution"+    c_duckdb_finish_execution :: DuckDBTaskState -> IO ()++{- | Check if the provided duckdb_task_state has finished execution++Parameters:+* @state@: The task state to inspect++Returns Whether or not duckdb_finish_execution has been called on the task+state+-}+foreign import ccall unsafe "duckdb_task_state_is_finished"+    c_duckdb_task_state_is_finished :: DuckDBTaskState -> IO CBool++{- | Destroys the task state returned from duckdb_create_task_state.++Note that this should not be called while there is an active+duckdb_execute_tasks_state running on the task state.++Parameters:+* @state@: The task state to clean up+-}+foreign import ccall unsafe "duckdb_destroy_task_state"+    c_duckdb_destroy_task_state :: DuckDBTaskState -> IO ()++{- | Returns true if the execution of the current query is finished.++Parameters:+* @con@: The connection on which to check+-}+foreign import ccall unsafe "duckdb_execution_is_finished"+    c_duckdb_execution_is_finished :: DuckDBConnection -> IO CBool
+ src/Database/DuckDB/FFI/Types.hs view
@@ -0,0 +1,1515 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE StrictData #-}++module Database.DuckDB.FFI.Types (+    -- * Enumerations+    DuckDBState (..),+    pattern DuckDBSuccess,+    pattern DuckDBError,+    DuckDBType (..),+    pattern DuckDBTypeInvalid,+    pattern DuckDBTypeBoolean,+    pattern DuckDBTypeTinyInt,+    pattern DuckDBTypeSmallInt,+    pattern DuckDBTypeInteger,+    pattern DuckDBTypeBigInt,+    pattern DuckDBTypeUTinyInt,+    pattern DuckDBTypeUSmallInt,+    pattern DuckDBTypeUInteger,+    pattern DuckDBTypeUBigInt,+    pattern DuckDBTypeFloat,+    pattern DuckDBTypeDouble,+    pattern DuckDBTypeTimestamp,+    pattern DuckDBTypeDate,+    pattern DuckDBTypeTime,+    pattern DuckDBTypeInterval,+    pattern DuckDBTypeHugeInt,+    pattern DuckDBTypeUHugeInt,+    pattern DuckDBTypeVarchar,+    pattern DuckDBTypeBlob,+    pattern DuckDBTypeDecimal,+    pattern DuckDBTypeTimestampS,+    pattern DuckDBTypeTimestampMs,+    pattern DuckDBTypeTimestampNs,+    pattern DuckDBTypeEnum,+    pattern DuckDBTypeList,+    pattern DuckDBTypeStruct,+    pattern DuckDBTypeMap,+    pattern DuckDBTypeArray,+    pattern DuckDBTypeUUID,+    pattern DuckDBTypeUnion,+    pattern DuckDBTypeBit,+    pattern DuckDBTypeTimeTz,+    pattern DuckDBTypeTimestampTz,+    pattern DuckDBTypeAny,+    pattern DuckDBTypeBigNum,+    pattern DuckDBTypeSQLNull,+    pattern DuckDBTypeStringLiteral,+    pattern DuckDBTypeIntegerLiteral,+    pattern DuckDBTypeTimeNs,+    DuckDBPendingState (..),+    pattern DuckDBPendingResultReady,+    pattern DuckDBPendingResultNotReady,+    pattern DuckDBPendingError,+    pattern DuckDBPendingNoTasksAvailable,+    DuckDBResultType (..),+    pattern DuckDBResultTypeInvalid,+    pattern DuckDBResultTypeChangedRows,+    pattern DuckDBResultTypeNothing,+    pattern DuckDBResultTypeQueryResult,+    DuckDBStatementType (..),+    pattern DuckDBStatementTypeInvalid,+    pattern DuckDBStatementTypeSelect,+    pattern DuckDBStatementTypeInsert,+    pattern DuckDBStatementTypeUpdate,+    pattern DuckDBStatementTypeExplain,+    pattern DuckDBStatementTypeDelete,+    pattern DuckDBStatementTypePrepare,+    pattern DuckDBStatementTypeCreate,+    pattern DuckDBStatementTypeExecute,+    pattern DuckDBStatementTypeAlter,+    pattern DuckDBStatementTypeTransaction,+    pattern DuckDBStatementTypeCopy,+    pattern DuckDBStatementTypeAnalyze,+    pattern DuckDBStatementTypeVariableSet,+    pattern DuckDBStatementTypeCreateFunc,+    pattern DuckDBStatementTypeDrop,+    pattern DuckDBStatementTypeExport,+    pattern DuckDBStatementTypePragma,+    pattern DuckDBStatementTypeVacuum,+    pattern DuckDBStatementTypeCall,+    pattern DuckDBStatementTypeSet,+    pattern DuckDBStatementTypeLoad,+    pattern DuckDBStatementTypeRelation,+    pattern DuckDBStatementTypeExtension,+    pattern DuckDBStatementTypeLogicalPlan,+    pattern DuckDBStatementTypeAttach,+    pattern DuckDBStatementTypeDetach,+    pattern DuckDBStatementTypeMulti,+    DuckDBErrorType (..),+    pattern DuckDBErrorInvalid,+    pattern DuckDBErrorOutOfRange,+    pattern DuckDBErrorConversion,+    pattern DuckDBErrorUnknownType,+    pattern DuckDBErrorDecimal,+    pattern DuckDBErrorMismatchType,+    pattern DuckDBErrorDivideByZero,+    pattern DuckDBErrorObjectSize,+    pattern DuckDBErrorInvalidType,+    pattern DuckDBErrorSerialization,+    pattern DuckDBErrorTransaction,+    pattern DuckDBErrorNotImplemented,+    pattern DuckDBErrorExpression,+    pattern DuckDBErrorCatalog,+    pattern DuckDBErrorParser,+    pattern DuckDBErrorPlanner,+    pattern DuckDBErrorScheduler,+    pattern DuckDBErrorExecutor,+    pattern DuckDBErrorConstraint,+    pattern DuckDBErrorIndex,+    pattern DuckDBErrorStat,+    pattern DuckDBErrorConnection,+    pattern DuckDBErrorSyntax,+    pattern DuckDBErrorSettings,+    pattern DuckDBErrorBinder,+    pattern DuckDBErrorNetwork,+    pattern DuckDBErrorOptimizer,+    pattern DuckDBErrorNullPointer,+    pattern DuckDBErrorIO,+    pattern DuckDBErrorInterrupt,+    pattern DuckDBErrorFatal,+    pattern DuckDBErrorInternal,+    pattern DuckDBErrorInvalidInput,+    pattern DuckDBErrorOutOfMemory,+    pattern DuckDBErrorPermission,+    pattern DuckDBErrorParameterNotResolved,+    pattern DuckDBErrorParameterNotAllowed,+    pattern DuckDBErrorDependency,+    pattern DuckDBErrorHTTP,+    pattern DuckDBErrorMissingExtension,+    pattern DuckDBErrorAutoload,+    pattern DuckDBErrorSequence,+    pattern DuckDBInvalidConfiguration,+    pattern DuckDBErrorInvalidConfiguration,+    DuckDBCastMode (..),+    pattern DuckDBCastNormal,+    pattern DuckDBCastTry,++    -- * Scalar Types+    DuckDBIdx,+    DuckDBSel,+    DuckDBDate (..),+    DuckDBDateStruct (..),+    DuckDBTime (..),+    DuckDBTimeStruct (..),+    DuckDBTimeNs (..),+    DuckDBTimeTz (..),+    DuckDBTimeTzStruct (..),+    DuckDBTimestamp (..),+    DuckDBTimestampStruct (..),+    DuckDBTimestampS (..),+    DuckDBTimestampMs (..),+    DuckDBTimestampNs (..),+    DuckDBInterval (..),+    DuckDBHugeInt (..),+    DuckDBUHugeInt (..),+    DuckDBDecimal (..),+    DuckDBBlob (..),+    DuckDBString (..),+    DuckDBStringT,+    DuckDBBit (..),+    DuckDBBignum (..),+    DuckDBQueryProgress (..),++    -- * Result Structures+    DuckDBResult (..),+    DuckDBColumn,++    -- * Opaque Pointer Types+    DuckDBDatabase,+    DuckDBConnection,+    DuckDBConfig,+    DuckDBInstanceCache,+    DuckDBArrowOptions,+    DuckDBArrow,+    DuckDBArrowSchema,+    DuckDBArrowArray,+    ArrowSchemaPtr (..),+    ArrowArrayPtr (..),+    ArrowStreamPtr (..),+    DuckDBArrowConvertedSchema,+    DuckDBArrowStream,+    DuckDBPreparedStatement,+    DuckDBPendingResult,+    DuckDBExtractedStatements,+    DuckDBLogicalType,+    DuckDBCreateTypeInfo,+    DuckDBVector,+    DuckDBDataChunk,+    DuckDBSelectionVector,+    DuckDBFunctionInfo,+    DuckDBBindInfo,+    DuckDBInitInfo,+    DuckDBScalarFunction,+    DuckDBScalarFunctionSet,+    DuckDBAggregateFunction,+    DuckDBAggregateFunctionSet,+    DuckDBAggregateState,+    DuckDBCastFunction,+    DuckDBExpression,+    DuckDBClientContext,+    DuckDBTableFunction,+    DuckDBValue,+    DuckDBErrorData,+    DuckDBAppender,+    DuckDBTableDescription,+    DuckDBProfilingInfo,+    DuckDBReplacementScanInfo,+    DuckDBTaskState,+    ArrowArray (..),+    ArrowSchema (..),++    -- * Opaque Struct Tags+    DuckDBDatabaseStruct,+    DuckDBConnectionStruct,+    DuckDBConfigStruct,+    DuckDBInstanceCacheStruct,+    DuckDBExtractedStatementsStruct,+    DuckDBFunctionInfoStruct,+    DuckDBBindInfoStruct,+    DuckDBScalarFunctionStruct,+    DuckDBScalarFunctionSetStruct,+    DuckDBAggregateFunctionStruct,+    DuckDBAggregateFunctionSetStruct,+    DuckDBVectorStruct,+    DuckDBDataChunkStruct,+    DuckDBSelectionVectorStruct,+    DuckDBArrowOptionsStruct,+    DuckDBArrowStruct,+    DuckDBArrowConvertedSchemaStruct,+    DuckDBArrowStreamStruct,+    DuckDBExpressionStruct,+    DuckDBClientContextStruct,+    DuckDBPreparedStatementStruct,+    DuckDBValueStruct,+    DuckDBPendingResultStruct,+    DuckDBLogicalTypeStruct,+    DuckDBCreateTypeInfoStruct,+    DuckDBErrorDataStruct,+    DuckDBInitInfoStruct,+    DuckDBCastFunctionStruct,+    DuckDBTableFunctionStruct,+    DuckDBAppenderStruct,+    DuckDBTableDescriptionStruct,+    DuckDBProfilingInfoStruct,+    DuckDBReplacementScanInfoStruct,+    DuckDBAggregateStateStruct,++    -- * Function Pointer Types+    DuckDBScalarFunctionFun,+    DuckDBScalarFunctionBindFun,+    DuckDBDeleteCallback,+    DuckDBCopyCallback,+    DuckDBCastFunctionFun,+    DuckDBAggregateStateSizeFun,+    DuckDBAggregateInitFun,+    DuckDBAggregateDestroyFun,+    DuckDBAggregateUpdateFun,+    DuckDBAggregateCombineFun,+    DuckDBAggregateFinalizeFun,+    DuckDBTableFunctionBindFun,+    DuckDBTableFunctionInitFun,+    DuckDBTableFunctionFun,+    DuckDBReplacementCallback,+) where++import Data.Int (Int32, Int64, Int8)+import Data.Word (Word32, Word64, Word8)+import Foreign.C.String (CString)+import Foreign.C.Types+import Foreign.Ptr (FunPtr, Ptr, nullPtr)+import Foreign.Storable (Storable (..), peekByteOff, pokeByteOff)++-- | Unsigned index type used by DuckDB (mirrors @idx_t@).+type DuckDBIdx = Word64++-- | Selection vector entry type (mirrors @sel_t@).+type DuckDBSel = Word32++-- | Result state returned by most DuckDB C API calls.+newtype DuckDBState = DuckDBState {unDuckDBState :: CInt}+    deriving (Eq, Ord, Show, Storable)++-- | Pattern synonyms for @duckdb_state@ constants.+pattern DuckDBSuccess, DuckDBError :: DuckDBState+pattern DuckDBSuccess = DuckDBState 0+pattern DuckDBError = DuckDBState 1++{-# COMPLETE DuckDBSuccess, DuckDBError #-}++-- | DuckDB primitive physical type identifiers (mirrors @duckdb_type@).+newtype DuckDBType = DuckDBType {unDuckDBType :: CInt}+    deriving (Eq, Ord, Show, Storable)++-- | Pattern synonyms for DuckDB's physical value type tags.+pattern+    DuckDBTypeInvalid+    , DuckDBTypeBoolean+    , DuckDBTypeTinyInt+    , DuckDBTypeSmallInt+    , DuckDBTypeInteger+    , DuckDBTypeBigInt+    , DuckDBTypeUTinyInt+    , DuckDBTypeUSmallInt+    , DuckDBTypeUInteger+    , DuckDBTypeUBigInt+    , DuckDBTypeFloat+    , DuckDBTypeDouble+    , DuckDBTypeTimestamp+    , DuckDBTypeDate+    , DuckDBTypeTime+    , DuckDBTypeInterval+    , DuckDBTypeHugeInt+    , DuckDBTypeUHugeInt+    , DuckDBTypeVarchar+    , DuckDBTypeBlob+    , DuckDBTypeDecimal+    , DuckDBTypeTimestampS+    , DuckDBTypeTimestampMs+    , DuckDBTypeTimestampNs+    , DuckDBTypeEnum+    , DuckDBTypeList+    , DuckDBTypeStruct+    , DuckDBTypeMap+    , DuckDBTypeArray+    , DuckDBTypeUUID+    , DuckDBTypeUnion+    , DuckDBTypeBit+    , DuckDBTypeTimeTz+    , DuckDBTypeTimestampTz+    , DuckDBTypeAny+    , DuckDBTypeBigNum+    , DuckDBTypeSQLNull+    , DuckDBTypeStringLiteral+    , DuckDBTypeIntegerLiteral+    , DuckDBTypeTimeNs ::+        DuckDBType+pattern DuckDBTypeInvalid = DuckDBType 0+pattern DuckDBTypeBoolean = DuckDBType 1+pattern DuckDBTypeTinyInt = DuckDBType 2+pattern DuckDBTypeSmallInt = DuckDBType 3+pattern DuckDBTypeInteger = DuckDBType 4+pattern DuckDBTypeBigInt = DuckDBType 5+pattern DuckDBTypeUTinyInt = DuckDBType 6+pattern DuckDBTypeUSmallInt = DuckDBType 7+pattern DuckDBTypeUInteger = DuckDBType 8+pattern DuckDBTypeUBigInt = DuckDBType 9+pattern DuckDBTypeFloat = DuckDBType 10+pattern DuckDBTypeDouble = DuckDBType 11+pattern DuckDBTypeTimestamp = DuckDBType 12+pattern DuckDBTypeDate = DuckDBType 13+pattern DuckDBTypeTime = DuckDBType 14+pattern DuckDBTypeInterval = DuckDBType 15+pattern DuckDBTypeHugeInt = DuckDBType 16+pattern DuckDBTypeUHugeInt = DuckDBType 32+pattern DuckDBTypeVarchar = DuckDBType 17+pattern DuckDBTypeBlob = DuckDBType 18+pattern DuckDBTypeDecimal = DuckDBType 19+pattern DuckDBTypeTimestampS = DuckDBType 20+pattern DuckDBTypeTimestampMs = DuckDBType 21+pattern DuckDBTypeTimestampNs = DuckDBType 22+pattern DuckDBTypeEnum = DuckDBType 23+pattern DuckDBTypeList = DuckDBType 24+pattern DuckDBTypeStruct = DuckDBType 25+pattern DuckDBTypeMap = DuckDBType 26+pattern DuckDBTypeArray = DuckDBType 33+pattern DuckDBTypeUUID = DuckDBType 27+pattern DuckDBTypeUnion = DuckDBType 28+pattern DuckDBTypeBit = DuckDBType 29+pattern DuckDBTypeTimeTz = DuckDBType 30+pattern DuckDBTypeTimestampTz = DuckDBType 31+pattern DuckDBTypeAny = DuckDBType 34+pattern DuckDBTypeBigNum = DuckDBType 35+pattern DuckDBTypeSQLNull = DuckDBType 36+pattern DuckDBTypeStringLiteral = DuckDBType 37+pattern DuckDBTypeIntegerLiteral = DuckDBType 38+pattern DuckDBTypeTimeNs = DuckDBType 39++-- | Pending result state returned from @duckdb_pending_*@ APIs.+newtype DuckDBPendingState = DuckDBPendingState {unDuckDBPendingState :: CInt}+    deriving (Eq, Ord, Show, Storable)++-- | Pattern synonyms for @duckdb_pending_state@ constants.+pattern+    DuckDBPendingResultReady+    , DuckDBPendingResultNotReady+    , DuckDBPendingError+    , DuckDBPendingNoTasksAvailable ::+        DuckDBPendingState+pattern DuckDBPendingResultReady = DuckDBPendingState 0+pattern DuckDBPendingResultNotReady = DuckDBPendingState 1+pattern DuckDBPendingError = DuckDBPendingState 2+pattern DuckDBPendingNoTasksAvailable = DuckDBPendingState 3++{-# COMPLETE+    DuckDBPendingResultReady+    , DuckDBPendingResultNotReady+    , DuckDBPendingError+    , DuckDBPendingNoTasksAvailable+    #-}++-- | Result payload type returned by DuckDB queries (@duckdb_result_type@).+newtype DuckDBResultType = DuckDBResultType {unDuckDBResultType :: CInt}+    deriving (Eq, Ord, Show, Storable)++-- | Pattern synonyms for @duckdb_result_type@ constants.+pattern+    DuckDBResultTypeInvalid+    , DuckDBResultTypeChangedRows+    , DuckDBResultTypeNothing+    , DuckDBResultTypeQueryResult ::+        DuckDBResultType+pattern DuckDBResultTypeInvalid = DuckDBResultType 0+pattern DuckDBResultTypeChangedRows = DuckDBResultType 1+pattern DuckDBResultTypeNothing = DuckDBResultType 2+pattern DuckDBResultTypeQueryResult = DuckDBResultType 3++{-# COMPLETE+    DuckDBResultTypeInvalid+    , DuckDBResultTypeChangedRows+    , DuckDBResultTypeNothing+    , DuckDBResultTypeQueryResult+    #-}++-- | Classifies the SQL statement executed (@duckdb_statement_type@).+newtype DuckDBStatementType = DuckDBStatementType {unDuckDBStatementType :: CInt}+    deriving (Eq, Ord, Show, Storable)++-- | Pattern synonyms for @duckdb_statement_type@ constants.+pattern+    DuckDBStatementTypeInvalid+    , DuckDBStatementTypeSelect+    , DuckDBStatementTypeInsert+    , DuckDBStatementTypeUpdate+    , DuckDBStatementTypeExplain+    , DuckDBStatementTypeDelete+    , DuckDBStatementTypePrepare+    , DuckDBStatementTypeCreate+    , DuckDBStatementTypeExecute+    , DuckDBStatementTypeAlter+    , DuckDBStatementTypeTransaction+    , DuckDBStatementTypeCopy+    , DuckDBStatementTypeAnalyze+    , DuckDBStatementTypeVariableSet+    , DuckDBStatementTypeCreateFunc+    , DuckDBStatementTypeDrop+    , DuckDBStatementTypeExport+    , DuckDBStatementTypePragma+    , DuckDBStatementTypeVacuum+    , DuckDBStatementTypeCall+    , DuckDBStatementTypeSet+    , DuckDBStatementTypeLoad+    , DuckDBStatementTypeRelation+    , DuckDBStatementTypeExtension+    , DuckDBStatementTypeLogicalPlan+    , DuckDBStatementTypeAttach+    , DuckDBStatementTypeDetach+    , DuckDBStatementTypeMulti ::+        DuckDBStatementType+pattern DuckDBStatementTypeInvalid = DuckDBStatementType 0+pattern DuckDBStatementTypeSelect = DuckDBStatementType 1+pattern DuckDBStatementTypeInsert = DuckDBStatementType 2+pattern DuckDBStatementTypeUpdate = DuckDBStatementType 3+pattern DuckDBStatementTypeExplain = DuckDBStatementType 4+pattern DuckDBStatementTypeDelete = DuckDBStatementType 5+pattern DuckDBStatementTypePrepare = DuckDBStatementType 6+pattern DuckDBStatementTypeCreate = DuckDBStatementType 7+pattern DuckDBStatementTypeExecute = DuckDBStatementType 8+pattern DuckDBStatementTypeAlter = DuckDBStatementType 9+pattern DuckDBStatementTypeTransaction = DuckDBStatementType 10+pattern DuckDBStatementTypeCopy = DuckDBStatementType 11+pattern DuckDBStatementTypeAnalyze = DuckDBStatementType 12+pattern DuckDBStatementTypeVariableSet = DuckDBStatementType 13+pattern DuckDBStatementTypeCreateFunc = DuckDBStatementType 14+pattern DuckDBStatementTypeDrop = DuckDBStatementType 15+pattern DuckDBStatementTypeExport = DuckDBStatementType 16+pattern DuckDBStatementTypePragma = DuckDBStatementType 17+pattern DuckDBStatementTypeVacuum = DuckDBStatementType 18+pattern DuckDBStatementTypeCall = DuckDBStatementType 19+pattern DuckDBStatementTypeSet = DuckDBStatementType 20+pattern DuckDBStatementTypeLoad = DuckDBStatementType 21+pattern DuckDBStatementTypeRelation = DuckDBStatementType 22+pattern DuckDBStatementTypeExtension = DuckDBStatementType 23+pattern DuckDBStatementTypeLogicalPlan = DuckDBStatementType 24+pattern DuckDBStatementTypeAttach = DuckDBStatementType 25+pattern DuckDBStatementTypeDetach = DuckDBStatementType 26+pattern DuckDBStatementTypeMulti = DuckDBStatementType 27++{-# COMPLETE+    DuckDBStatementTypeInvalid+    , DuckDBStatementTypeSelect+    , DuckDBStatementTypeInsert+    , DuckDBStatementTypeUpdate+    , DuckDBStatementTypeExplain+    , DuckDBStatementTypeDelete+    , DuckDBStatementTypePrepare+    , DuckDBStatementTypeCreate+    , DuckDBStatementTypeExecute+    , DuckDBStatementTypeAlter+    , DuckDBStatementTypeTransaction+    , DuckDBStatementTypeCopy+    , DuckDBStatementTypeAnalyze+    , DuckDBStatementTypeVariableSet+    , DuckDBStatementTypeCreateFunc+    , DuckDBStatementTypeDrop+    , DuckDBStatementTypeExport+    , DuckDBStatementTypePragma+    , DuckDBStatementTypeVacuum+    , DuckDBStatementTypeCall+    , DuckDBStatementTypeSet+    , DuckDBStatementTypeLoad+    , DuckDBStatementTypeRelation+    , DuckDBStatementTypeExtension+    , DuckDBStatementTypeLogicalPlan+    , DuckDBStatementTypeAttach+    , DuckDBStatementTypeDetach+    , DuckDBStatementTypeMulti+    #-}++-- | DuckDB error classification codes.+newtype DuckDBErrorType = DuckDBErrorType {unDuckDBErrorType :: CInt}+    deriving (Eq, Ord, Show, Storable)++-- | Pattern synonyms mirroring @duckdb_error_type@ values.+pattern+    DuckDBErrorInvalid+    , DuckDBErrorOutOfRange+    , DuckDBErrorConversion+    , DuckDBErrorUnknownType+    , DuckDBErrorDecimal+    , DuckDBErrorMismatchType+    , DuckDBErrorDivideByZero+    , DuckDBErrorObjectSize+    , DuckDBErrorInvalidType+    , DuckDBErrorSerialization+    , DuckDBErrorTransaction+    , DuckDBErrorNotImplemented+    , DuckDBErrorExpression+    , DuckDBErrorCatalog+    , DuckDBErrorParser+    , DuckDBErrorPlanner+    , DuckDBErrorScheduler+    , DuckDBErrorExecutor+    , DuckDBErrorConstraint+    , DuckDBErrorIndex+    , DuckDBErrorStat+    , DuckDBErrorConnection+    , DuckDBErrorSyntax+    , DuckDBErrorSettings+    , DuckDBErrorBinder+    , DuckDBErrorNetwork+    , DuckDBErrorOptimizer+    , DuckDBErrorNullPointer+    , DuckDBErrorIO+    , DuckDBErrorInterrupt+    , DuckDBErrorFatal+    , DuckDBErrorInternal+    , DuckDBErrorInvalidInput+    , DuckDBErrorOutOfMemory+    , DuckDBErrorPermission+    , DuckDBErrorParameterNotResolved+    , DuckDBErrorParameterNotAllowed+    , DuckDBErrorDependency+    , DuckDBErrorHTTP+    , DuckDBErrorMissingExtension+    , DuckDBErrorAutoload+    , DuckDBErrorSequence+    , DuckDBInvalidConfiguration ::+        DuckDBErrorType+pattern DuckDBErrorInvalid = DuckDBErrorType 0+pattern DuckDBErrorOutOfRange = DuckDBErrorType 1+pattern DuckDBErrorConversion = DuckDBErrorType 2+pattern DuckDBErrorUnknownType = DuckDBErrorType 3+pattern DuckDBErrorDecimal = DuckDBErrorType 4+pattern DuckDBErrorMismatchType = DuckDBErrorType 5+pattern DuckDBErrorDivideByZero = DuckDBErrorType 6+pattern DuckDBErrorObjectSize = DuckDBErrorType 7+pattern DuckDBErrorInvalidType = DuckDBErrorType 8+pattern DuckDBErrorSerialization = DuckDBErrorType 9+pattern DuckDBErrorTransaction = DuckDBErrorType 10+pattern DuckDBErrorNotImplemented = DuckDBErrorType 11+pattern DuckDBErrorExpression = DuckDBErrorType 12+pattern DuckDBErrorCatalog = DuckDBErrorType 13+pattern DuckDBErrorParser = DuckDBErrorType 14+pattern DuckDBErrorPlanner = DuckDBErrorType 15+pattern DuckDBErrorScheduler = DuckDBErrorType 16+pattern DuckDBErrorExecutor = DuckDBErrorType 17+pattern DuckDBErrorConstraint = DuckDBErrorType 18+pattern DuckDBErrorIndex = DuckDBErrorType 19+pattern DuckDBErrorStat = DuckDBErrorType 20+pattern DuckDBErrorConnection = DuckDBErrorType 21+pattern DuckDBErrorSyntax = DuckDBErrorType 22+pattern DuckDBErrorSettings = DuckDBErrorType 23+pattern DuckDBErrorBinder = DuckDBErrorType 24+pattern DuckDBErrorNetwork = DuckDBErrorType 25+pattern DuckDBErrorOptimizer = DuckDBErrorType 26+pattern DuckDBErrorNullPointer = DuckDBErrorType 27+pattern DuckDBErrorIO = DuckDBErrorType 28+pattern DuckDBErrorInterrupt = DuckDBErrorType 29+pattern DuckDBErrorFatal = DuckDBErrorType 30+pattern DuckDBErrorInternal = DuckDBErrorType 31+pattern DuckDBErrorInvalidInput = DuckDBErrorType 32+pattern DuckDBErrorOutOfMemory = DuckDBErrorType 33+pattern DuckDBErrorPermission = DuckDBErrorType 34+pattern DuckDBErrorParameterNotResolved = DuckDBErrorType 35+pattern DuckDBErrorParameterNotAllowed = DuckDBErrorType 36+pattern DuckDBErrorDependency = DuckDBErrorType 37+pattern DuckDBErrorHTTP = DuckDBErrorType 38+pattern DuckDBErrorMissingExtension = DuckDBErrorType 39+pattern DuckDBErrorAutoload = DuckDBErrorType 40+pattern DuckDBErrorSequence = DuckDBErrorType 41+pattern DuckDBInvalidConfiguration = DuckDBErrorType 42++-- | Backwards-compatible alias for 'DuckDBInvalidConfiguration'.+{-# DEPRECATED DuckDBErrorInvalidConfiguration "Use DuckDBInvalidConfiguration (matches upstream duckdb.h)" #-}+pattern DuckDBErrorInvalidConfiguration :: DuckDBErrorType+pattern DuckDBErrorInvalidConfiguration = DuckDBInvalidConfiguration++{-# COMPLETE+    DuckDBErrorInvalid+    , DuckDBErrorOutOfRange+    , DuckDBErrorConversion+    , DuckDBErrorUnknownType+    , DuckDBErrorDecimal+    , DuckDBErrorMismatchType+    , DuckDBErrorDivideByZero+    , DuckDBErrorObjectSize+    , DuckDBErrorInvalidType+    , DuckDBErrorSerialization+    , DuckDBErrorTransaction+    , DuckDBErrorNotImplemented+    , DuckDBErrorExpression+    , DuckDBErrorCatalog+    , DuckDBErrorParser+    , DuckDBErrorPlanner+    , DuckDBErrorScheduler+    , DuckDBErrorExecutor+    , DuckDBErrorConstraint+    , DuckDBErrorIndex+    , DuckDBErrorStat+    , DuckDBErrorConnection+    , DuckDBErrorSyntax+    , DuckDBErrorSettings+    , DuckDBErrorBinder+    , DuckDBErrorNetwork+    , DuckDBErrorOptimizer+    , DuckDBErrorNullPointer+    , DuckDBErrorIO+    , DuckDBErrorInterrupt+    , DuckDBErrorFatal+    , DuckDBErrorInternal+    , DuckDBErrorInvalidInput+    , DuckDBErrorOutOfMemory+    , DuckDBErrorPermission+    , DuckDBErrorParameterNotResolved+    , DuckDBErrorParameterNotAllowed+    , DuckDBErrorDependency+    , DuckDBErrorHTTP+    , DuckDBErrorMissingExtension+    , DuckDBErrorAutoload+    , DuckDBErrorSequence+    , DuckDBInvalidConfiguration+    #-}++-- | Behaviour of DuckDB's casting functions (@duckdb_cast_mode@).+newtype DuckDBCastMode = DuckDBCastMode {unDuckDBCastMode :: CInt}+    deriving (Eq, Ord, Show, Storable)++-- | Pattern synonyms for @duckdb_cast_mode@ values.+pattern DuckDBCastNormal, DuckDBCastTry :: DuckDBCastMode+pattern DuckDBCastNormal = DuckDBCastMode 0+pattern DuckDBCastTry = DuckDBCastMode 1++{-# COMPLETE DuckDBCastNormal, DuckDBCastTry #-}++-- | Represents DuckDB's @duckdb_date@.+newtype DuckDBDate = DuckDBDate {unDuckDBDate :: Int32}+    deriving (Eq, Ord, Show, Storable)++-- | Decomposed representation of a @duckdb_date@.+data DuckDBDateStruct = DuckDBDateStruct+    { duckDBDateStructYear :: !Int32+    , duckDBDateStructMonth :: !Int8+    , duckDBDateStructDay :: !Int8+    }+    deriving (Eq, Show)++instance Storable DuckDBDateStruct where+    sizeOf _ = alignedSize+      where+        rawSize = sizeOf (undefined :: Int32) + 2 * sizeOf (undefined :: Int8)+        align = alignment (undefined :: Int32)+        alignedSize = ((rawSize + align - 1) `div` align) * align+    alignment _ = alignment (undefined :: Int32)+    peek ptr = do+        year <- peekByteOff ptr 0+        month <- peekByteOff ptr (sizeOf (undefined :: Int32))+        day <- peekByteOff ptr (sizeOf (undefined :: Int32) + sizeOf (undefined :: Int8))+        pure (DuckDBDateStruct year month day)+    poke ptr DuckDBDateStruct{duckDBDateStructYear = year, duckDBDateStructMonth = month, duckDBDateStructDay = day} = do+        pokeByteOff ptr 0 year+        pokeByteOff ptr (sizeOf (undefined :: Int32)) month+        pokeByteOff ptr (sizeOf (undefined :: Int32) + sizeOf (undefined :: Int8)) day++-- | Represents DuckDB's @duckdb_time@.+newtype DuckDBTime = DuckDBTime {unDuckDBTime :: Int64}+    deriving (Eq, Ord, Show, Storable)++-- | Decomposed representation of a @duckdb_time@.+data DuckDBTimeStruct = DuckDBTimeStruct+    { duckDBTimeStructHour :: !Int8+    , duckDBTimeStructMinute :: !Int8+    , duckDBTimeStructSecond :: !Int8+    , duckDBTimeStructMicros :: !Int32+    }+    deriving (Eq, Show)++instance Storable DuckDBTimeStruct where+    sizeOf _ = 2 * alignment (undefined :: Int32)+    alignment _ = alignment (undefined :: Int32)+    peek ptr = do+        hour <- peekByteOff ptr 0+        minute <- peekByteOff ptr 1+        second <- peekByteOff ptr 2+        micros <- peekByteOff ptr 4+        pure (DuckDBTimeStruct hour minute second micros)+    poke ptr DuckDBTimeStruct{duckDBTimeStructHour = hour, duckDBTimeStructMinute = minute, duckDBTimeStructSecond = second, duckDBTimeStructMicros = micros} = do+        pokeByteOff ptr 0 hour+        pokeByteOff ptr 1 minute+        pokeByteOff ptr 2 second+        pokeByteOff ptr 4 micros++-- | Represents DuckDB's @duckdb_time_ns@.+newtype DuckDBTimeNs = DuckDBTimeNs {unDuckDBTimeNs :: Int64}+    deriving (Eq, Ord, Show, Storable)++-- | Represents DuckDB's @duckdb_time_tz@.+newtype DuckDBTimeTz = DuckDBTimeTz {unDuckDBTimeTz :: Word64}+    deriving (Eq, Ord, Show, Storable)++-- | Decomposed representation of a @duckdb_time_tz@.+data DuckDBTimeTzStruct = DuckDBTimeTzStruct+    { duckDBTimeTzStructTime :: !DuckDBTimeStruct+    , duckDBTimeTzStructOffset :: !Int32+    }+    deriving (Eq, Show)++instance Storable DuckDBTimeTzStruct where+    sizeOf _ = sizeOf (undefined :: DuckDBTimeStruct) + sizeOf (undefined :: Int32)+    alignment _ = alignment (undefined :: Int32)+    peek ptr = do+        time <- peekByteOff ptr 0+        offset <- peekByteOff ptr (sizeOf (undefined :: DuckDBTimeStruct))+        pure (DuckDBTimeTzStruct time offset)+    poke ptr DuckDBTimeTzStruct{duckDBTimeTzStructTime = time, duckDBTimeTzStructOffset = offset} = do+        pokeByteOff ptr 0 time+        pokeByteOff ptr (sizeOf (undefined :: DuckDBTimeStruct)) offset++-- | Represents DuckDB's @duckdb_timestamp@.+newtype DuckDBTimestamp = DuckDBTimestamp {unDuckDBTimestamp :: Int64}+    deriving (Eq, Ord, Show, Storable)++-- | Decomposed representation of a @duckdb_timestamp@.+data DuckDBTimestampStruct = DuckDBTimestampStruct+    { duckDBTimestampStructDate :: !DuckDBDateStruct+    , duckDBTimestampStructTime :: !DuckDBTimeStruct+    }+    deriving (Eq, Show)++instance Storable DuckDBTimestampStruct where+    sizeOf _ = sizeOf (undefined :: DuckDBDateStruct) + sizeOf (undefined :: DuckDBTimeStruct)+    alignment _ = alignment (undefined :: DuckDBTimeStruct)+    peek ptr = do+        date <- peekByteOff ptr 0+        time <- peekByteOff ptr (sizeOf (undefined :: DuckDBDateStruct))+        pure (DuckDBTimestampStruct date time)+    poke ptr DuckDBTimestampStruct{duckDBTimestampStructDate = date, duckDBTimestampStructTime = time} = do+        pokeByteOff ptr 0 date+        pokeByteOff ptr (sizeOf (undefined :: DuckDBDateStruct)) time++-- | Represents DuckDB's @duckdb_timestamp_s@.+newtype DuckDBTimestampS = DuckDBTimestampS {unDuckDBTimestampS :: Int64}+    deriving (Eq, Ord, Show, Storable)++-- | Represents DuckDB's @duckdb_timestamp_ms@.+newtype DuckDBTimestampMs = DuckDBTimestampMs {unDuckDBTimestampMs :: Int64}+    deriving (Eq, Ord, Show, Storable)++-- | Represents DuckDB's @duckdb_timestamp_ns@.+newtype DuckDBTimestampNs = DuckDBTimestampNs {unDuckDBTimestampNs :: Int64}+    deriving (Eq, Ord, Show, Storable)++-- | Represents DuckDB's @duckdb_interval@.+data DuckDBInterval = DuckDBInterval+    { duckDBIntervalMonths :: !Int32+    , duckDBIntervalDays :: !Int32+    , duckDBIntervalMicros :: !Int64+    }+    deriving (Eq, Show)++instance Storable DuckDBInterval where+    sizeOf _ = 2 * sizeOf (undefined :: Int32) + sizeOf (undefined :: Int64)+    alignment _ = alignment (undefined :: Int64)+    peek ptr = do+        months <- peekByteOff ptr 0+        days <- peekByteOff ptr (sizeOf (undefined :: Int32))+        micros <- peekByteOff ptr (2 * sizeOf (undefined :: Int32))+        pure (DuckDBInterval months days micros)+    poke ptr (DuckDBInterval months days micros) = do+        pokeByteOff ptr 0 months+        pokeByteOff ptr (sizeOf (undefined :: Int32)) days+        pokeByteOff ptr (2 * sizeOf (undefined :: Int32)) micros++-- | Represents DuckDB's @duckdb_hugeint@.+data DuckDBHugeInt = DuckDBHugeInt+    { duckDBHugeIntLower :: !Word64+    , duckDBHugeIntUpper :: !Int64+    }+    deriving (Eq, Show)++instance Storable DuckDBHugeInt where+    sizeOf _ = sizeOf (undefined :: Word64) + sizeOf (undefined :: Int64)+    alignment _ = alignment (undefined :: Word64)+    peek ptr = do+        lower <- peekByteOff ptr 0+        upper <- peekByteOff ptr (sizeOf (undefined :: Word64))+        pure (DuckDBHugeInt lower upper)+    poke ptr (DuckDBHugeInt lower upper) = do+        pokeByteOff ptr 0 lower+        pokeByteOff ptr (sizeOf (undefined :: Word64)) upper++-- | Represents DuckDB's @duckdb_uhugeint@.+data DuckDBUHugeInt = DuckDBUHugeInt+    { duckDBUHugeIntLower :: !Word64+    , duckDBUHugeIntUpper :: !Word64+    }+    deriving (Eq, Show)++instance Storable DuckDBUHugeInt where+    sizeOf _ = 2 * sizeOf (undefined :: Word64)+    alignment _ = alignment (undefined :: Word64)+    peek ptr = do+        lower <- peekByteOff ptr 0+        upper <- peekByteOff ptr (sizeOf (undefined :: Word64))+        pure (DuckDBUHugeInt lower upper)+    poke ptr (DuckDBUHugeInt lower upper) = do+        pokeByteOff ptr 0 lower+        pokeByteOff ptr (sizeOf (undefined :: Word64)) upper++-- | Represents DuckDB's @duckdb_decimal@.+data DuckDBDecimal = DuckDBDecimal+    { duckDBDecimalWidth :: !Word8+    , duckDBDecimalScale :: !Word8+    , duckDBDecimalValue :: !DuckDBHugeInt+    }+    deriving (Eq, Show)++instance Storable DuckDBDecimal where+    sizeOf _ = valueOffset + sizeOf (undefined :: DuckDBHugeInt)+      where+        alignHuge = alignment (undefined :: DuckDBHugeInt)+        valueOffset = ((2 + alignHuge - 1) `div` alignHuge) * alignHuge+    alignment _ = alignment (undefined :: DuckDBHugeInt)+    peek ptr = do+        width <- peekByteOff ptr 0+        scale <- peekByteOff ptr 1+        let alignHuge = alignment (undefined :: DuckDBHugeInt)+            valueOffset = ((2 + alignHuge - 1) `div` alignHuge) * alignHuge+        value <- peekByteOff ptr valueOffset+        pure (DuckDBDecimal width scale value)+    poke ptr (DuckDBDecimal width scale value) = do+        pokeByteOff ptr 0 width+        pokeByteOff ptr 1 scale+        let alignHuge = alignment (undefined :: DuckDBHugeInt)+            valueOffset = ((2 + alignHuge - 1) `div` alignHuge) * alignHuge+        pokeByteOff ptr valueOffset value++-- | Represents DuckDB's @duckdb_blob@.+data DuckDBBlob = DuckDBBlob+    { duckDBBlobData :: !(Ptr ())+    , duckDBBlobSize :: !DuckDBIdx+    }+    deriving (Eq, Show)++instance Storable DuckDBBlob where+    sizeOf _ = sizeOf (undefined :: Ptr ()) + sizeOf (undefined :: DuckDBIdx)+    alignment _ = alignment (undefined :: Ptr ())+    peek ptr = do+        dat <- peekByteOff ptr 0+        len <- peekByteOff ptr (sizeOf (undefined :: Ptr ()))+        pure (DuckDBBlob dat len)+    poke ptr (DuckDBBlob dat len) = do+        pokeByteOff ptr 0 dat+        pokeByteOff ptr (sizeOf (undefined :: Ptr ())) len++-- | Represents DuckDB's @duckdb_string@.+data DuckDBString = DuckDBString+    { duckDBStringData :: !(Ptr CChar)+    , duckDBStringSize :: !DuckDBIdx+    }+    deriving (Eq, Show)++instance Storable DuckDBString where+    sizeOf _ = sizeOf (undefined :: Ptr CChar) + sizeOf (undefined :: DuckDBIdx)+    alignment _ = alignment (undefined :: Ptr CChar)+    peek ptr = do+        dat <- peekByteOff ptr 0+        len <- peekByteOff ptr (sizeOf (undefined :: Ptr CChar))+        pure (DuckDBString dat len)+    poke ptr (DuckDBString dat len) = do+        pokeByteOff ptr 0 dat+        pokeByteOff ptr (sizeOf (undefined :: Ptr CChar)) len++-- | Represents DuckDB's @duckdb_string_t@.+data DuckDBStringT++-- | Represents DuckDB's @duckdb_bit@.+data DuckDBBit = DuckDBBit+    { duckDBBitData :: !(Ptr Word8)+    , duckDBBitSize :: !DuckDBIdx+    }+    deriving (Eq, Show)++instance Storable DuckDBBit where+    sizeOf _ = sizeOf (undefined :: Ptr Word8) + sizeOf (undefined :: DuckDBIdx)+    alignment _ = alignment (undefined :: Ptr Word8)+    peek ptr = do+        dat <- peekByteOff ptr 0+        len <- peekByteOff ptr (sizeOf (undefined :: Ptr Word8))+        pure (DuckDBBit dat len)+    poke ptr (DuckDBBit dat len) = do+        pokeByteOff ptr 0 dat+        pokeByteOff ptr (sizeOf (undefined :: Ptr Word8)) len++-- | Represents DuckDB's @duckdb_bignum@.+data DuckDBBignum = DuckDBBignum+    { duckDBBignumData :: !(Ptr Word8)+    , duckDBBignumSize :: !DuckDBIdx+    , duckDBBignumIsNegative :: !CBool+    }+    deriving (Eq, Show)++instance Storable DuckDBBignum where+    sizeOf _ = alignedSize+      where+        baseSize = sizeOf (undefined :: Ptr Word8) + sizeOf (undefined :: DuckDBIdx) + sizeOf (undefined :: CBool)+        align = alignment (undefined :: Ptr Word8)+        alignedSize = ((baseSize + align - 1) `div` align) * align+    alignment _ = alignment (undefined :: Ptr Word8)+    peek ptr = do+        dat <- peekByteOff ptr 0+        len <- peekByteOff ptr (sizeOf (undefined :: Ptr Word8))+        isNeg <- peekByteOff ptr (sizeOf (undefined :: Ptr Word8) + sizeOf (undefined :: DuckDBIdx))+        pure (DuckDBBignum dat len isNeg)+    poke ptr (DuckDBBignum dat len isNeg) = do+        pokeByteOff ptr 0 dat+        pokeByteOff ptr (sizeOf (undefined :: Ptr Word8)) len+        pokeByteOff ptr (sizeOf (undefined :: Ptr Word8) + sizeOf (undefined :: DuckDBIdx)) isNeg++-- | Represents DuckDB's @duckdb_query_progress_type@.+data DuckDBQueryProgress = DuckDBQueryProgress+    { duckDBQueryProgressPercentage :: !Double+    , duckDBQueryProgressRowsProcessed :: !Word64+    , duckDBQueryProgressTotalRows :: !Word64+    }+    deriving (Eq, Show)++instance Storable DuckDBQueryProgress where+    sizeOf _ = sizeOf (undefined :: CDouble) + 2 * sizeOf (undefined :: Word64)+    alignment _ = alignment (undefined :: CDouble)+    peek ptr = do+        percentage <- realToFrac <$> (peekByteOff ptr 0 :: IO CDouble)+        let offset1 = sizeOf (undefined :: CDouble)+            offset2 = offset1 + sizeOf (undefined :: Word64)+        processed <- peekByteOff ptr offset1+        total <- peekByteOff ptr offset2+        pure (DuckDBQueryProgress percentage processed total)+    poke ptr (DuckDBQueryProgress percentage processed total) = do+        pokeByteOff ptr 0 (realToFrac percentage :: CDouble)+        let offset1 = sizeOf (undefined :: CDouble)+            offset2 = offset1 + sizeOf (undefined :: Word64)+        pokeByteOff ptr offset1 processed+        pokeByteOff ptr offset2 total++-- | Opaque DuckDB column handle.+data DuckDBColumn++-- | DuckDB result structure (opaque to callers, but required for FFI marshalling).+data DuckDBResult = DuckDBResult+    { duckDBResultDeprecatedColumnCount :: !DuckDBIdx+    , duckDBResultDeprecatedRowCount :: !DuckDBIdx+    , duckDBResultDeprecatedRowsChanged :: !DuckDBIdx+    , duckDBResultDeprecatedColumns :: !(Ptr DuckDBColumn)+    , duckDBResultDeprecatedErrorMessage :: !CString+    , duckDBResultInternalData :: !(Ptr ())+    }++instance Storable DuckDBResult where+    sizeOf _ = 3 * sizeOf (undefined :: DuckDBIdx) + 3 * sizeOf (undefined :: Ptr ())+    alignment _ = alignment (undefined :: DuckDBIdx)+    peek ptr = do+        colCount <- peekByteOff ptr 0+        rowCount <- peekByteOff ptr (sizeOf (undefined :: DuckDBIdx))+        rowsChanged <- peekByteOff ptr (2 * sizeOf (undefined :: DuckDBIdx))+        let basePtr = 3 * sizeOf (undefined :: DuckDBIdx)+        columns <- peekByteOff ptr basePtr+        errMsg <- peekByteOff ptr (basePtr + sizeOf (undefined :: Ptr ()))+        internal <- peekByteOff ptr (basePtr + 2 * sizeOf (undefined :: Ptr ()))+        pure+            DuckDBResult+                { duckDBResultDeprecatedColumnCount = colCount+                , duckDBResultDeprecatedRowCount = rowCount+                , duckDBResultDeprecatedRowsChanged = rowsChanged+                , duckDBResultDeprecatedColumns = columns+                , duckDBResultDeprecatedErrorMessage = errMsg+                , duckDBResultInternalData = internal+                }+    poke ptr result = do+        let columnCount = duckDBResultDeprecatedColumnCount result+            rowCount = duckDBResultDeprecatedRowCount result+            rowsChanged = duckDBResultDeprecatedRowsChanged result+            columns = duckDBResultDeprecatedColumns result+            errorMessage = duckDBResultDeprecatedErrorMessage result+            internalData = duckDBResultInternalData result+            basePtr = 3 * sizeOf (undefined :: DuckDBIdx)+        pokeByteOff ptr 0 columnCount+        pokeByteOff ptr (sizeOf (undefined :: DuckDBIdx)) rowCount+        pokeByteOff ptr (2 * sizeOf (undefined :: DuckDBIdx)) rowsChanged+        pokeByteOff ptr basePtr columns+        pokeByteOff ptr (basePtr + sizeOf (undefined :: Ptr ())) errorMessage+        pokeByteOff ptr (basePtr + 2 * sizeOf (undefined :: Ptr ())) internalData++-- | Tag type backing @duckdb_database@ pointers.+data DuckDBDatabaseStruct++-- | Handle to a DuckDB database instance.+type DuckDBDatabase = Ptr DuckDBDatabaseStruct++-- | Tag type backing @duckdb_connection@ pointers.+data DuckDBConnectionStruct++-- | Handle to a DuckDB connection.+type DuckDBConnection = Ptr DuckDBConnectionStruct++-- | Tag type backing @duckdb_config@ pointers.+data DuckDBConfigStruct++-- | Handle to a DuckDB configuration object.+type DuckDBConfig = Ptr DuckDBConfigStruct++-- | Tag type backing @duckdb_instance_cache@ pointers.+data DuckDBInstanceCacheStruct++-- | Handle to a DuckDB instance cache.+type DuckDBInstanceCache = Ptr DuckDBInstanceCacheStruct++-- | Tag type backing @duckdb_extracted_statements@ pointers.+data DuckDBExtractedStatementsStruct++-- | Handle to extracted SQL statements.+type DuckDBExtractedStatements = Ptr DuckDBExtractedStatementsStruct++-- | Tag type backing @duckdb_function_info@ pointers.+data DuckDBFunctionInfoStruct++-- | Handle to function execution context.+type DuckDBFunctionInfo = Ptr DuckDBFunctionInfoStruct++-- | Tag type backing @duckdb_bind_info@ pointers.+data DuckDBBindInfoStruct++-- | Handle to scalar function bind context.+type DuckDBBindInfo = Ptr DuckDBBindInfoStruct++-- | Tag type backing @duckdb_scalar_function@ pointers.+data DuckDBScalarFunctionStruct++-- | Handle to a scalar function definition.+type DuckDBScalarFunction = Ptr DuckDBScalarFunctionStruct++-- | Tag type backing @duckdb_scalar_function_set@ pointers.+data DuckDBScalarFunctionSetStruct++-- | Handle to a set of scalar function overloads.+type DuckDBScalarFunctionSet = Ptr DuckDBScalarFunctionSetStruct++-- | Tag type backing @duckdb_aggregate_function@ pointers.+data DuckDBAggregateFunctionStruct++-- | Handle to an aggregate function definition.+type DuckDBAggregateFunction = Ptr DuckDBAggregateFunctionStruct++-- | Tag type backing @duckdb_aggregate_function_set@ pointers.+data DuckDBAggregateFunctionSetStruct++-- | Handle to an aggregate function set.+type DuckDBAggregateFunctionSet = Ptr DuckDBAggregateFunctionSetStruct++-- | Tag type backing @duckdb_vector@ pointers.+data DuckDBVectorStruct++-- | Handle to a DuckDB vector.+type DuckDBVector = Ptr DuckDBVectorStruct++-- | Tag type backing @duckdb_data_chunk@ pointers.+data DuckDBDataChunkStruct++-- | Handle to a DuckDB data chunk.+type DuckDBDataChunk = Ptr DuckDBDataChunkStruct++-- | Tag type backing @duckdb_selection_vector@ pointers.+data DuckDBSelectionVectorStruct++-- | Handle to a DuckDB selection vector.+type DuckDBSelectionVector = Ptr DuckDBSelectionVectorStruct++-- | Tag type backing @duckdb_arrow_options@ pointers.+data DuckDBArrowOptionsStruct++-- | Handle to DuckDB Arrow options.+type DuckDBArrowOptions = Ptr DuckDBArrowOptionsStruct++-- | Tag type backing @duckdb_arrow@ pointers.+newtype DuckDBArrowStruct = DuckDBArrowStruct+    { duckdbArrowInternalPtr :: Ptr ()+    }++-- | Handle to an Arrow query result.+type DuckDBArrow = Ptr DuckDBArrowStruct++-- | Tag type backing @duckdb_arrow_schema@ pointers.+newtype DuckDBArrowSchemaStruct = DuckDBArrowSchemaStruct+    { duckdbArrowSchemaInternalPtr :: Ptr ()+    }++-- | Handle to an Arrow schema.+type DuckDBArrowSchema = Ptr DuckDBArrowSchemaStruct++-- | Tag type backing @duckdb_arrow_array@ pointers.+newtype DuckDBArrowArrayStruct = DuckDBArrowArrayStruct+    { duckdbArrowArrayInternalPtr :: Ptr ()+    }++-- | Handle to an Arrow array.+type DuckDBArrowArray = Ptr DuckDBArrowArrayStruct++instance Storable DuckDBArrowStruct where+    sizeOf _ = pointerSize+    alignment _ = alignment (nullPtr :: Ptr ())+    peek ptr =+        DuckDBArrowStruct+            <$> peekByteOff ptr 0+    poke ptr DuckDBArrowStruct{..} =+        pokeByteOff ptr 0 duckdbArrowInternalPtr++instance Storable DuckDBArrowSchemaStruct where+    sizeOf _ = pointerSize+    alignment _ = alignment (nullPtr :: Ptr ())+    peek ptr =+        DuckDBArrowSchemaStruct+            <$> peekByteOff ptr 0+    poke ptr DuckDBArrowSchemaStruct{..} =+        pokeByteOff ptr 0 duckdbArrowSchemaInternalPtr++instance Storable DuckDBArrowArrayStruct where+    sizeOf _ = pointerSize+    alignment _ = alignment (nullPtr :: Ptr ())+    peek ptr =+        DuckDBArrowArrayStruct+            <$> peekByteOff ptr 0+    poke ptr DuckDBArrowArrayStruct{..} =+        pokeByteOff ptr 0 duckdbArrowArrayInternalPtr++-- | Tag type backing @duckdb_arrow_converted_schema@ pointers.+data DuckDBArrowConvertedSchemaStruct++-- | Handle to a converted Arrow schema.+type DuckDBArrowConvertedSchema = Ptr DuckDBArrowConvertedSchemaStruct++-- | Tag type backing @duckdb_arrow_stream@ pointers.+newtype DuckDBArrowStreamStruct = DuckDBArrowStreamStruct+    { duckdbArrowStreamInternalPtr :: Ptr ()+    }++-- | Handle to an Arrow stream.+type DuckDBArrowStream = Ptr DuckDBArrowStreamStruct++instance Storable DuckDBArrowStreamStruct where+    sizeOf _ = pointerSize+    alignment _ = alignment (nullPtr :: Ptr ())+    peek ptr =+        DuckDBArrowStreamStruct+            <$> peekByteOff ptr 0+    poke ptr DuckDBArrowStreamStruct{..} =+        pokeByteOff ptr 0 duckdbArrowStreamInternalPtr++-- | Tag type backing @duckdb_expression@ pointers.+data DuckDBExpressionStruct++-- | Handle to a DuckDB expression.+type DuckDBExpression = Ptr DuckDBExpressionStruct++-- | Tag type backing @duckdb_client_context@ pointers.+data DuckDBClientContextStruct++-- | Handle to a DuckDB client context.+type DuckDBClientContext = Ptr DuckDBClientContextStruct++-- | Tag type backing @duckdb_prepared_statement@ pointers.+data DuckDBPreparedStatementStruct++-- | Handle to a prepared statement.+type DuckDBPreparedStatement = Ptr DuckDBPreparedStatementStruct++-- | Tag type backing @duckdb_value@ pointers.+data DuckDBValueStruct++-- | Handle to a scalar DuckDB value.+type DuckDBValue = Ptr DuckDBValueStruct++-- | Tag type backing @duckdb_pending_result@ pointers.+data DuckDBPendingResultStruct++-- | Handle to a pending (incremental) query result.+type DuckDBPendingResult = Ptr DuckDBPendingResultStruct++-- | Tag type backing @duckdb_logical_type@ pointers.+data DuckDBLogicalTypeStruct++-- | Handle to a DuckDB logical type value.+type DuckDBLogicalType = Ptr DuckDBLogicalTypeStruct++-- | Tag type backing @duckdb_create_type_info@ pointers.+data DuckDBCreateTypeInfoStruct++-- | Handle to logical type registration details.+type DuckDBCreateTypeInfo = Ptr DuckDBCreateTypeInfoStruct++-- | Tag type backing @duckdb_error_data@ pointers.+data DuckDBErrorDataStruct++-- | Handle to DuckDB error data.+type DuckDBErrorData = Ptr DuckDBErrorDataStruct++-- | Tag type backing @duckdb_init_info@ pointers.+data DuckDBInitInfoStruct++-- | Handle to table function initialization state.+type DuckDBInitInfo = Ptr DuckDBInitInfoStruct++-- | Tag type backing @duckdb_cast_function@ pointers.+data DuckDBCastFunctionStruct++-- | Handle to a cast function definition.+type DuckDBCastFunction = Ptr DuckDBCastFunctionStruct++-- | Tag type backing @duckdb_table_function@ pointers.+data DuckDBTableFunctionStruct++-- | Handle to a table function definition.+type DuckDBTableFunction = Ptr DuckDBTableFunctionStruct++-- | Tag type backing @duckdb_appender@ pointers.+data DuckDBAppenderStruct++-- | Handle to an appender.+type DuckDBAppender = Ptr DuckDBAppenderStruct++-- | Tag type backing @duckdb_table_description@ pointers.+data DuckDBTableDescriptionStruct++-- | Handle to a table description.+type DuckDBTableDescription = Ptr DuckDBTableDescriptionStruct++-- | Tag type backing @duckdb_profiling_info@ pointers.+data DuckDBProfilingInfoStruct++-- | Handle to profiling information.+type DuckDBProfilingInfo = Ptr DuckDBProfilingInfoStruct++-- | Tag type backing @duckdb_replacement_scan_info@ pointers.+data DuckDBReplacementScanInfoStruct++-- | Handle to replacement scan context.+type DuckDBReplacementScanInfo = Ptr DuckDBReplacementScanInfoStruct++-- | Handle to a DuckDB aggregate state.+data DuckDBAggregateStateStruct++-- | Opaque pointer to the aggregate-function state handed to user callbacks.+type DuckDBAggregateState = Ptr DuckDBAggregateStateStruct++-- | Handle to a DuckDB task state.+type DuckDBTaskState = Ptr ()++-- | Function pointer used to represent scalar function execution callbacks.+type DuckDBScalarFunctionFun = FunPtr (DuckDBFunctionInfo -> DuckDBDataChunk -> DuckDBVector -> IO ())++-- | Function pointer used to represent scalar function bind callbacks.+type DuckDBScalarFunctionBindFun = FunPtr (DuckDBBindInfo -> IO ())++-- | Function pointer used to destroy user-provided data blobs.+type DuckDBDeleteCallback = FunPtr (Ptr () -> IO ())++-- | Function pointer used to copy user-provided data blobs.+type DuckDBCopyCallback = FunPtr (Ptr () -> IO (Ptr ()))++-- | Function pointer implementing cast functions.+type DuckDBCastFunctionFun =+    FunPtr (DuckDBFunctionInfo -> DuckDBIdx -> DuckDBVector -> DuckDBVector -> IO CBool)++-- | Function pointer returning aggregate state size.+type DuckDBAggregateStateSizeFun = FunPtr (DuckDBFunctionInfo -> IO DuckDBIdx)++-- | Function pointer initializing aggregate state.+type DuckDBAggregateInitFun = FunPtr (DuckDBFunctionInfo -> DuckDBAggregateState -> IO ())++-- | Function pointer destroying aggregate state batches.+type DuckDBAggregateDestroyFun = FunPtr (Ptr DuckDBAggregateState -> DuckDBIdx -> IO ())++-- | Function pointer updating aggregate states.+type DuckDBAggregateUpdateFun =+    FunPtr (DuckDBFunctionInfo -> DuckDBDataChunk -> Ptr DuckDBAggregateState -> IO ())++-- | Function pointer combining aggregate states.+type DuckDBAggregateCombineFun =+    FunPtr+        ( DuckDBFunctionInfo ->+          Ptr DuckDBAggregateState ->+          Ptr DuckDBAggregateState ->+          DuckDBIdx ->+          IO ()+        )++-- | Function pointer finalising aggregate states.+type DuckDBAggregateFinalizeFun =+    FunPtr+        ( DuckDBFunctionInfo ->+          Ptr DuckDBAggregateState ->+          DuckDBVector ->+          DuckDBIdx ->+          DuckDBIdx ->+          IO ()+        )++-- | Function pointer for table function bind callbacks.+type DuckDBTableFunctionBindFun = FunPtr (DuckDBBindInfo -> IO ())++-- | Function pointer for table function init callbacks.+type DuckDBTableFunctionInitFun = FunPtr (DuckDBInitInfo -> IO ())++-- | Function pointer for table function execution callbacks.+type DuckDBTableFunctionFun = FunPtr (DuckDBFunctionInfo -> DuckDBDataChunk -> IO ())++-- | Function pointer for replacement scan callbacks.+type DuckDBReplacementCallback =+    FunPtr (DuckDBReplacementScanInfo -> CString -> Ptr () -> IO ())++-- The full Arrow C Data Interface definitions are not included here to avoid+-- introducing a dependency on the Arrow C headers. Instead, we define only the+-- parts we need for testing DuckDB's Arrow integration.+-- See https://arrow.apache.org/docs/format/CDataInterface.html for the full+-- specification.+-- #ifndef ARROW_C_DATA_INTERFACE+-- #define ARROW_C_DATA_INTERFACE++-- #define ARROW_FLAG_DICTIONARY_ORDERED 1+-- #define ARROW_FLAG_NULLABLE 2+-- #define ARROW_FLAG_MAP_KEYS_SORTED 4++-- struct ArrowSchema {+--   // Array type description+--   const char* format;+--   const char* name;+--   const char* metadata;+--   int64_t flags;+--   int64_t n_children;+--   struct ArrowSchema** children;+--   struct ArrowSchema* dictionary;++--   // Release callback+--   void (*release)(struct ArrowSchema*);+--   // Opaque producer-specific data+--   void* private_data;+-- };++-- struct ArrowArray {+--   // Array data description+--   int64_t length;+--   int64_t null_count;+--   int64_t offset;+--   int64_t n_buffers;+--   int64_t n_children;+--   const void** buffers;+--   struct ArrowArray** children;+--   struct ArrowArray* dictionary;++--   // Release callback+--   void (*release)(struct ArrowArray*);+--   // Opaque producer-specific data+--   void* private_data;+-- };++-- #endif  // ARROW_C_DATA_INTERFACE++{- | Partial Arrow schema view used for tests that require inspecting DuckDB's+Arrow wrappers without depending on the full Arrow C Data Interface+definitions.+-}+data ArrowSchema = ArrowSchema+    { arrowSchemaFormat :: CString+    , arrowSchemaName :: CString+    , arrowSchemaMetadata :: CString+    , arrowSchemaFlags :: Int64+    , arrowSchemaChildCount :: Int64+    , arrowSchemaChildren :: Ptr (Ptr ArrowSchema)+    , arrowSchemaDictionary :: Ptr ArrowSchema+    , arrowSchemaRelease :: FunPtr (Ptr ArrowSchema -> IO ())+    , arrowSchemaPrivateData :: Ptr ()+    }++instance Storable ArrowSchema where+    sizeOf _ = pointerSize * 9+    alignment _ = alignment (nullPtr :: Ptr ())+    peek ptr =+        ArrowSchema+            <$> peekByteOff ptr 0+            <*> peekByteOff ptr pointerSize+            <*> peekByteOff ptr (pointerSize * 2)+            <*> peekByteOff ptr (pointerSize * 3)+            <*> peekByteOff ptr (pointerSize * 4)+            <*> peekByteOff ptr (pointerSize * 5)+            <*> peekByteOff ptr (pointerSize * 6)+            <*> peekByteOff ptr (pointerSize * 7)+            <*> peekByteOff ptr (pointerSize * 8)+    poke ptr ArrowSchema{..} = do+        pokeByteOff ptr 0 arrowSchemaFormat+        pokeByteOff ptr pointerSize arrowSchemaName+        pokeByteOff ptr (pointerSize * 2) arrowSchemaMetadata+        pokeByteOff ptr (pointerSize * 3) arrowSchemaFlags+        pokeByteOff ptr (pointerSize * 4) arrowSchemaChildCount+        pokeByteOff ptr (pointerSize * 5) arrowSchemaChildren+        pokeByteOff ptr (pointerSize * 6) arrowSchemaDictionary+        pokeByteOff ptr (pointerSize * 7) arrowSchemaRelease+        pokeByteOff ptr (pointerSize * 8) arrowSchemaPrivateData++-- | Partial Arrow array view mirroring the DuckDB C API layout.+data ArrowArray = ArrowArray+    { arrowArrayLength :: Int64+    , arrowArrayNullCount :: Int64+    , arrowArrayOffset :: Int64+    , arrowArrayBufferCount :: Int64+    , arrowArrayChildCount :: Int64+    , arrowArrayBuffers :: Ptr (Ptr ())+    , arrowArrayChildren :: Ptr (Ptr ArrowArray)+    , arrowArrayDictionary :: Ptr ArrowArray+    , arrowArrayRelease :: FunPtr (Ptr ArrowArray -> IO ())+    , arrowArrayPrivateData :: Ptr ()+    }++instance Storable ArrowArray where+    sizeOf _ = pointerSize * 5 + intFieldSize * 5+    alignment _ = alignment (nullPtr :: Ptr ())+    peek ptr =+        ArrowArray+            <$> peekByteOff ptr 0+            <*> peekByteOff ptr intFieldSize+            <*> peekByteOff ptr (intFieldSize * 2)+            <*> peekByteOff ptr (intFieldSize * 3)+            <*> peekByteOff ptr (intFieldSize * 4)+            <*> peekByteOff ptr (intFieldSize * 5)+            <*> peekByteOff ptr (intFieldSize * 5 + pointerSize)+            <*> peekByteOff ptr (intFieldSize * 5 + pointerSize * 2)+            <*> peekByteOff ptr (intFieldSize * 5 + pointerSize * 3)+            <*> peekByteOff ptr (intFieldSize * 5 + pointerSize * 4)+    poke ptr ArrowArray{..} = do+        pokeByteOff ptr 0 arrowArrayLength+        pokeByteOff ptr intFieldSize arrowArrayNullCount+        pokeByteOff ptr (intFieldSize * 2) arrowArrayOffset+        pokeByteOff ptr (intFieldSize * 3) arrowArrayBufferCount+        pokeByteOff ptr (intFieldSize * 4) arrowArrayChildCount+        pokeByteOff ptr (intFieldSize * 5) arrowArrayBuffers+        pokeByteOff ptr (intFieldSize * 5 + pointerSize) arrowArrayChildren+        pokeByteOff ptr (intFieldSize * 5 + pointerSize * 2) arrowArrayDictionary+        pokeByteOff ptr (intFieldSize * 5 + pointerSize * 3) arrowArrayRelease+        pokeByteOff ptr (intFieldSize * 5 + pointerSize * 4) arrowArrayPrivateData++{- | Pointer wrapper for the deprecated Arrow schema handle exposed by DuckDB.+The underlying memory is managed by DuckDB and must only be accessed through+the deprecated Arrow helper functions.+-}+newtype ArrowSchemaPtr = ArrowSchemaPtr {unArrowSchemaPtr :: Ptr ArrowSchema}+    deriving (Eq)++{- | Pointer wrapper for the deprecated Arrow array handle exposed by DuckDB.+DuckDB assumes exclusive ownership and coordinates buffer lifetimes via the+release callback stored in the referenced @ArrowArray@ struct.+-}+newtype ArrowArrayPtr = ArrowArrayPtr {unArrowArrayPtr :: Ptr ArrowArray}+    deriving (Eq)++{- | Pointer wrapper for the deprecated Arrow stream handle returned by DuckDB.+DuckDB owns the referenced stream; call @c_duckdb_destroy_arrow_stream@ after+invoking @duckdb_arrow_scan@.+-}+newtype ArrowStreamPtr = ArrowStreamPtr {unArrowStreamPtr :: Ptr ()}+    deriving (Eq)++pointerSize :: Int+pointerSize = sizeOf (nullPtr :: Ptr ())++intFieldSize :: Int+intFieldSize = sizeOf (0 :: Int64)
+ src/Database/DuckDB/FFI/Validity.hs view
@@ -0,0 +1,61 @@+module Database.DuckDB.FFI.Validity (+    c_duckdb_validity_row_is_valid,+    c_duckdb_validity_set_row_validity,+    c_duckdb_validity_set_row_invalid,+    c_duckdb_validity_set_row_valid,+) where++import Data.Word (Word64)+import Database.DuckDB.FFI.Types+import Foreign.C.Types (CBool (..))+import Foreign.Ptr (Ptr)++{- | Returns whether or not a row is valid (i.e., not NULL) in the given validity+mask.++Parameters:+* @validity@: The validity mask, as obtained through+  @duckdb_vector_get_validity@+* @row@: The row index++Returns true if the row is valid, false otherwise+-}+foreign import ccall unsafe "duckdb_validity_row_is_valid"+    c_duckdb_validity_row_is_valid :: Ptr Word64 -> DuckDBIdx -> IO CBool++{- | In a validity mask, sets a specific row to either valid or invalid.++Note that @duckdb_vector_ensure_validity_writable@ should be called before+calling @duckdb_vector_get_validity@, to ensure that there is a validity mask+to write to.++Parameters:+* @validity@: The validity mask, as obtained through+  @duckdb_vector_get_validity@.+* @row@: The row index+* @valid@: Whether or not to set the row to valid, or invalid+-}+foreign import ccall unsafe "duckdb_validity_set_row_validity"+    c_duckdb_validity_set_row_validity :: Ptr Word64 -> DuckDBIdx -> CBool -> IO ()++{- | In a validity mask, sets a specific row to invalid.++Equivalent to @duckdb_validity_set_row_validity@ with valid set to false.++Parameters:+* @validity@: The validity mask+* @row@: The row index+-}+foreign import ccall unsafe "duckdb_validity_set_row_invalid"+    c_duckdb_validity_set_row_invalid :: Ptr Word64 -> DuckDBIdx -> IO ()++{- | In a validity mask, sets a specific row to valid.++Equivalent to @duckdb_validity_set_row_validity@ with valid set to true.++Parameters:+* @validity@: The validity mask+* @row@: The row index+-}+foreign import ccall unsafe "duckdb_validity_set_row_valid"+    c_duckdb_validity_set_row_valid :: Ptr Word64 -> DuckDBIdx -> IO ()
+ src/Database/DuckDB/FFI/ValueInterface.hs view
@@ -0,0 +1,928 @@+module Database.DuckDB.FFI.ValueInterface (+    c_duckdb_destroy_value,+    c_duckdb_create_varchar,+    c_duckdb_create_varchar_length,+    c_duckdb_create_bool,+    c_duckdb_create_int8,+    c_duckdb_create_uint8,+    c_duckdb_create_int16,+    c_duckdb_create_uint16,+    c_duckdb_create_int32,+    c_duckdb_create_uint32,+    c_duckdb_create_uint64,+    c_duckdb_create_int64,+    c_duckdb_create_hugeint,+    c_duckdb_create_uhugeint,+    c_duckdb_create_bignum,+    c_duckdb_create_decimal,+    c_duckdb_create_float,+    c_duckdb_create_double,+    c_duckdb_create_date,+    c_duckdb_create_time,+    c_duckdb_create_time_ns,+    c_duckdb_create_time_tz_value,+    c_duckdb_create_timestamp,+    c_duckdb_create_timestamp_tz,+    c_duckdb_create_timestamp_s,+    c_duckdb_create_timestamp_ms,+    c_duckdb_create_timestamp_ns,+    c_duckdb_create_interval,+    c_duckdb_create_blob,+    c_duckdb_create_bit,+    c_duckdb_create_uuid,+    c_duckdb_get_bool,+    c_duckdb_get_int8,+    c_duckdb_get_uint8,+    c_duckdb_get_int16,+    c_duckdb_get_uint16,+    c_duckdb_get_int32,+    c_duckdb_get_uint32,+    c_duckdb_get_int64,+    c_duckdb_get_uint64,+    c_duckdb_get_hugeint,+    c_duckdb_get_uhugeint,+    c_duckdb_get_bignum,+    c_duckdb_get_decimal,+    c_duckdb_get_float,+    c_duckdb_get_double,+    c_duckdb_get_date,+    c_duckdb_get_time,+    c_duckdb_get_time_ns,+    c_duckdb_get_time_tz,+    c_duckdb_get_timestamp,+    c_duckdb_get_timestamp_tz,+    c_duckdb_get_timestamp_s,+    c_duckdb_get_timestamp_ms,+    c_duckdb_get_timestamp_ns,+    c_duckdb_get_interval,+    c_duckdb_get_value_type,+    c_duckdb_get_blob,+    c_duckdb_get_bit,+    c_duckdb_get_uuid,+    c_duckdb_get_varchar,+    c_duckdb_create_struct_value,+    c_duckdb_create_list_value,+    c_duckdb_create_array_value,+    c_duckdb_create_map_value,+    c_duckdb_create_union_value,+    c_duckdb_get_map_size,+    c_duckdb_get_map_key,+    c_duckdb_get_map_value,+    c_duckdb_is_null_value,+    c_duckdb_create_null_value,+    c_duckdb_get_list_size,+    c_duckdb_get_list_child,+    c_duckdb_create_enum_value,+    c_duckdb_get_enum_value,+    c_duckdb_get_struct_child,+    c_duckdb_value_to_string,+) where++import Data.Int (Int16, Int32, Int64, Int8)+import Data.Word (Word16, Word32, Word64, Word8)+import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CBool (..), CDouble (..), CFloat (..))+import Foreign.Ptr (Ptr)++{- | Destroys the value and de-allocates all memory allocated for that type.++Parameters:+* @value@: The value to destroy.+-}+foreign import ccall unsafe "duckdb_destroy_value"+    c_duckdb_destroy_value :: Ptr DuckDBValue -> IO ()++{- | Creates a value from a null-terminated string++Parameters:+* @text@: The null-terminated string++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_varchar"+    c_duckdb_create_varchar :: CString -> IO DuckDBValue++{- | Creates a value from a string++Parameters:+* @text@: The text+* @length@: The length of the text++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_varchar_length"+    c_duckdb_create_varchar_length :: CString -> DuckDBIdx -> IO DuckDBValue++{- | Creates a value from a boolean++Parameters:+* @input@: The boolean value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_bool"+    c_duckdb_create_bool :: CBool -> IO DuckDBValue++{- | Creates a value from an int8_t (a tinyint)++Parameters:+* @input@: The tinyint value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_int8"+    c_duckdb_create_int8 :: Int8 -> IO DuckDBValue++{- | Creates a value from a uint8_t (a utinyint)++Parameters:+* @input@: The utinyint value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_uint8"+    c_duckdb_create_uint8 :: Word8 -> IO DuckDBValue++{- | Creates a value from an int16_t (a smallint)++Parameters:+* @input@: The smallint value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_int16"+    c_duckdb_create_int16 :: Int16 -> IO DuckDBValue++{- | Creates a value from a uint16_t (a usmallint)++Parameters:+* @input@: The usmallint value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_uint16"+    c_duckdb_create_uint16 :: Word16 -> IO DuckDBValue++{- | Creates a value from an int32_t (an integer)++Parameters:+* @input@: The integer value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_int32"+    c_duckdb_create_int32 :: Int32 -> IO DuckDBValue++{- | Creates a value from a uint32_t (a uinteger)++Parameters:+* @input@: The uinteger value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_uint32"+    c_duckdb_create_uint32 :: Word32 -> IO DuckDBValue++{- | Creates a value from a uint64_t (a ubigint)++Parameters:+* @input@: The ubigint value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_uint64"+    c_duckdb_create_uint64 :: Word64 -> IO DuckDBValue++{- | Creates a value from an int64++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_int64"+    c_duckdb_create_int64 :: Int64 -> IO DuckDBValue++{- | Creates a value from a hugeint++Parameters:+* @input@: The hugeint value++Returns The value. This must be destroyed with @duckdb_destroy_value@.++These bindings call the wrapper symbol @wrapped_duckdb_create_hugeint@+but mirror the DuckDB C API semantics of @duckdb_create_hugeint@.+-}+foreign import ccall unsafe "wrapped_duckdb_create_hugeint"+    c_duckdb_create_hugeint :: Ptr DuckDBHugeInt -> IO DuckDBValue++{- | Creates a value from a uhugeint++Parameters:+* @input@: The uhugeint value++Returns The value. This must be destroyed with @duckdb_destroy_value@.++These bindings call the wrapper symbol @wrapped_duckdb_create_uhugeint@+but mirror the DuckDB C API semantics of @duckdb_create_uhugeint@.+-}+foreign import ccall unsafe "wrapped_duckdb_create_uhugeint"+    c_duckdb_create_uhugeint :: Ptr DuckDBUHugeInt -> IO DuckDBValue++{- | Creates a BIGNUM value from a duckdb_bignum++Parameters:+* @input@: The duckdb_bignum value++Returns The value. This must be destroyed with @duckdb_destroy_value@.++These bindings call the wrapper symbol @wrapped_duckdb_create_bignum@+but mirror the DuckDB C API semantics of @duckdb_create_bignum@.+-}+foreign import ccall unsafe "wrapped_duckdb_create_bignum"+    c_duckdb_create_bignum :: Ptr DuckDBBignum -> IO DuckDBValue++{- | Creates a DECIMAL value from a duckdb_decimal++Parameters:+* @input@: The duckdb_decimal value++Returns The value. This must be destroyed with @duckdb_destroy_value@.++These bindings call the wrapper symbol @wrapped_duckdb_create_decimal@+but mirror the DuckDB C API semantics of @duckdb_create_decimal@.+-}+foreign import ccall unsafe "wrapped_duckdb_create_decimal"+    c_duckdb_create_decimal :: Ptr DuckDBDecimal -> IO DuckDBValue++{- | Creates a value from a float++Parameters:+* @input@: The float value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_float"+    c_duckdb_create_float :: CFloat -> IO DuckDBValue++{- | Creates a value from a double++Parameters:+* @input@: The double value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_double"+    c_duckdb_create_double :: CDouble -> IO DuckDBValue++{- | Creates a value from a date++Parameters:+* @input@: The date value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_date"+    c_duckdb_create_date :: DuckDBDate -> IO DuckDBValue++{- | Creates a value from a time++Parameters:+* @input@: The time value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_time"+    c_duckdb_create_time :: DuckDBTime -> IO DuckDBValue++{- | Creates a value from a time_ns++Parameters:+* @input@: The time value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_time_ns"+    c_duckdb_create_time_ns :: DuckDBTimeNs -> IO DuckDBValue++{- | Creates a value from a time_tz. Not to be confused with+@duckdb_create_time_tz@, which creates a duckdb_time_tz_t.++Parameters:+* @value@: The time_tz value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_time_tz_value"+    c_duckdb_create_time_tz_value :: DuckDBTimeTz -> IO DuckDBValue++{- | Creates a TIMESTAMP value from a duckdb_timestamp++Parameters:+* @input@: The duckdb_timestamp value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_timestamp"+    c_duckdb_create_timestamp :: DuckDBTimestamp -> IO DuckDBValue++{- | Creates a TIMESTAMP_TZ value from a duckdb_timestamp++Parameters:+* @input@: The duckdb_timestamp value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_timestamp_tz"+    c_duckdb_create_timestamp_tz :: DuckDBTimestamp -> IO DuckDBValue++{- | Creates a TIMESTAMP_S value from a duckdb_timestamp_s++Parameters:+* @input@: The duckdb_timestamp_s value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_timestamp_s"+    c_duckdb_create_timestamp_s :: DuckDBTimestampS -> IO DuckDBValue++{- | Creates a TIMESTAMP_MS value from a duckdb_timestamp_ms++Parameters:+* @input@: The duckdb_timestamp_ms value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_timestamp_ms"+    c_duckdb_create_timestamp_ms :: DuckDBTimestampMs -> IO DuckDBValue++{- | Creates a TIMESTAMP_NS value from a duckdb_timestamp_ns++Parameters:+* @input@: The duckdb_timestamp_ns value++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_timestamp_ns"+    c_duckdb_create_timestamp_ns :: DuckDBTimestampNs -> IO DuckDBValue++{- | Creates a value from an interval++Parameters:+* @input@: The interval value++Returns The value. This must be destroyed with @duckdb_destroy_value@.++These bindings call the wrapper symbol @wrapped_duckdb_create_interval@+but mirror the DuckDB C API semantics of @duckdb_create_interval@.+-}+foreign import ccall unsafe "wrapped_duckdb_create_interval"+    c_duckdb_create_interval :: Ptr DuckDBInterval -> IO DuckDBValue++{- | Creates a value from a blob++Parameters:+* @data@: The blob data+* @length@: The length of the blob data++Returns The value. This must be destroyed with @duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_blob"+    c_duckdb_create_blob :: Ptr Word8 -> DuckDBIdx -> IO DuckDBValue++{- | Creates a BIT value from a duckdb_bit++Parameters:+* @input@: The duckdb_bit value++Returns The value. This must be destroyed with @duckdb_destroy_value@.++These bindings call the wrapper symbol @wrapped_duckdb_create_bit@ but+mirror the DuckDB C API semantics of @duckdb_create_bit@.+-}+foreign import ccall unsafe "wrapped_duckdb_create_bit"+    c_duckdb_create_bit :: Ptr DuckDBBit -> IO DuckDBValue++{- | Creates a UUID value from a uhugeint++Parameters:+* @input@: The duckdb_uhugeint containing the UUID++Returns The value. This must be destroyed with @duckdb_destroy_value@.++These bindings call the wrapper symbol @wrapped_duckdb_create_uuid@ but+mirror the DuckDB C API semantics of @duckdb_create_uuid@.+-}+foreign import ccall unsafe "wrapped_duckdb_create_uuid"+    c_duckdb_create_uuid :: Ptr DuckDBUHugeInt -> IO DuckDBValue++{- | Returns the boolean value of the given value.++Parameters:+* @val@: A duckdb_value containing a boolean++Returns A boolean, or false if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_bool"+    c_duckdb_get_bool :: DuckDBValue -> IO CBool++{- | Returns the int8_t value of the given value.++Parameters:+* @val@: A duckdb_value containing a tinyint++Returns A int8_t, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_int8"+    c_duckdb_get_int8 :: DuckDBValue -> IO Int8++{- | Returns the uint8_t value of the given value.++Parameters:+* @val@: A duckdb_value containing a utinyint++Returns A uint8_t, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_uint8"+    c_duckdb_get_uint8 :: DuckDBValue -> IO Word8++{- | Returns the int16_t value of the given value.++Parameters:+* @val@: A duckdb_value containing a smallint++Returns A int16_t, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_int16"+    c_duckdb_get_int16 :: DuckDBValue -> IO Int16++{- | Returns the uint16_t value of the given value.++Parameters:+* @val@: A duckdb_value containing a usmallint++Returns A uint16_t, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_uint16"+    c_duckdb_get_uint16 :: DuckDBValue -> IO Word16++{- | Returns the int32_t value of the given value.++Parameters:+* @val@: A duckdb_value containing an integer++Returns A int32_t, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_int32"+    c_duckdb_get_int32 :: DuckDBValue -> IO Int32++{- | Returns the uint32_t value of the given value.++Parameters:+* @val@: A duckdb_value containing a uinteger++Returns A uint32_t, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_uint32"+    c_duckdb_get_uint32 :: DuckDBValue -> IO Word32++{- | Returns the int64_t value of the given value.++Parameters:+* @val@: A duckdb_value containing a bigint++Returns A int64_t, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_int64"+    c_duckdb_get_int64 :: DuckDBValue -> IO Int64++{- | Returns the uint64_t value of the given value.++Parameters:+* @val@: A duckdb_value containing a ubigint++Returns A uint64_t, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_uint64"+    c_duckdb_get_uint64 :: DuckDBValue -> IO Word64++{- | Returns the hugeint value of the given value.++Parameters:+* @val@: A duckdb_value containing a hugeint++Returns A duckdb_hugeint, or MinValue if the value cannot be converted++These bindings call the wrapper symbol @wrapped_duckdb_get_hugeint@ but+mirror the DuckDB C API semantics of @duckdb_get_hugeint@.+-}+foreign import ccall unsafe "wrapped_duckdb_get_hugeint"+    c_duckdb_get_hugeint :: DuckDBValue -> Ptr DuckDBHugeInt -> IO ()++{- | Returns the uhugeint value of the given value.++Parameters:+* @val@: A duckdb_value containing a uhugeint++Returns A duckdb_uhugeint, or MinValue if the value cannot be converted++These bindings call the wrapper symbol @wrapped_duckdb_get_uhugeint@+but mirror the DuckDB C API semantics of @duckdb_get_uhugeint@.+-}+foreign import ccall unsafe "wrapped_duckdb_get_uhugeint"+    c_duckdb_get_uhugeint :: DuckDBValue -> Ptr DuckDBUHugeInt -> IO ()++{- | Returns the duckdb_bignum value of the given value. The @data@ field must be+destroyed with @duckdb_free@.++Parameters:+* @val@: A duckdb_value containing a BIGNUM++Returns A duckdb_bignum. The @data@ field must be destroyed with+@duckdb_free@.++These bindings call the wrapper symbol @wrapped_duckdb_get_bignum@ but+mirror the DuckDB C API semantics of @duckdb_get_bignum@.+-}+foreign import ccall unsafe "wrapped_duckdb_get_bignum"+    c_duckdb_get_bignum :: DuckDBValue -> Ptr DuckDBBignum -> IO ()++{- | Returns the duckdb_decimal value of the given value.++Parameters:+* @val@: A duckdb_value containing a DECIMAL++Returns A duckdb_decimal, or MinValue if the value cannot be converted++These bindings call the wrapper symbol @wrapped_duckdb_get_decimal@ but+mirror the DuckDB C API semantics of @duckdb_get_decimal@.+-}+foreign import ccall unsafe "wrapped_duckdb_get_decimal"+    c_duckdb_get_decimal :: DuckDBValue -> Ptr DuckDBDecimal -> IO ()++{- | Returns the float value of the given value.++Parameters:+* @val@: A duckdb_value containing a float++Returns A float, or NAN if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_float"+    c_duckdb_get_float :: DuckDBValue -> IO CFloat++{- | Returns the double value of the given value.++Parameters:+* @val@: A duckdb_value containing a double++Returns A double, or NAN if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_double"+    c_duckdb_get_double :: DuckDBValue -> IO CDouble++{- | Returns the date value of the given value.++Parameters:+* @val@: A duckdb_value containing a date++Returns A duckdb_date, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_date"+    c_duckdb_get_date :: DuckDBValue -> IO DuckDBDate++{- | Returns the time value of the given value.++Parameters:+* @val@: A duckdb_value containing a time++Returns A duckdb_time, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_time"+    c_duckdb_get_time :: DuckDBValue -> IO DuckDBTime++{- | Returns the time_ns value of the given value.++Parameters:+* @val@: A duckdb_value containing a time_ns++Returns A duckdb_time_ns, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_time_ns"+    c_duckdb_get_time_ns :: DuckDBValue -> IO DuckDBTimeNs++{- | Returns the time_tz value of the given value.++Parameters:+* @val@: A duckdb_value containing a time_tz++Returns A duckdb_time_tz, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_time_tz"+    c_duckdb_get_time_tz :: DuckDBValue -> IO DuckDBTimeTz++{- | Returns the TIMESTAMP value of the given value.++Parameters:+* @val@: A duckdb_value containing a TIMESTAMP++Returns A duckdb_timestamp, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_timestamp"+    c_duckdb_get_timestamp :: DuckDBValue -> IO DuckDBTimestamp++{- | Returns the TIMESTAMP_TZ value of the given value.++Parameters:+* @val@: A duckdb_value containing a TIMESTAMP_TZ++Returns A duckdb_timestamp, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_timestamp_tz"+    c_duckdb_get_timestamp_tz :: DuckDBValue -> IO DuckDBTimestamp++{- | Returns the duckdb_timestamp_s value of the given value.++Parameters:+* @val@: A duckdb_value containing a TIMESTAMP_S++Returns A duckdb_timestamp_s, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_timestamp_s"+    c_duckdb_get_timestamp_s :: DuckDBValue -> IO DuckDBTimestampS++{- | Returns the duckdb_timestamp_ms value of the given value.++Parameters:+* @val@: A duckdb_value containing a TIMESTAMP_MS++Returns A duckdb_timestamp_ms, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_timestamp_ms"+    c_duckdb_get_timestamp_ms :: DuckDBValue -> IO DuckDBTimestampMs++{- | Returns the duckdb_timestamp_ns value of the given value.++Parameters:+* @val@: A duckdb_value containing a TIMESTAMP_NS++Returns A duckdb_timestamp_ns, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_timestamp_ns"+    c_duckdb_get_timestamp_ns :: DuckDBValue -> IO DuckDBTimestampNs++{- | Returns the interval value of the given value.++Parameters:+* @val@: A duckdb_value containing a interval++Returns A duckdb_interval, or MinValue if the value cannot be converted++These bindings call the wrapper symbol @wrapped_duckdb_get_interval@+but mirror the DuckDB C API semantics of @duckdb_get_interval@.+-}+foreign import ccall unsafe "wrapped_duckdb_get_interval"+    c_duckdb_get_interval :: DuckDBValue -> Ptr DuckDBInterval -> IO ()++{- | Returns the type of the given value. The type is valid as long as the value is+not destroyed. The type itself must not be destroyed.++Parameters:+* @val@: A duckdb_value++Returns A duckdb_logical_type.+-}+foreign import ccall unsafe "duckdb_get_value_type"+    c_duckdb_get_value_type :: DuckDBValue -> IO DuckDBLogicalType++{- | Returns the blob value of the given value.++Parameters:+* @val@: A duckdb_value containing a blob++Returns A duckdb_blob++These bindings call the wrapper symbol @wrapped_duckdb_get_blob@ but+mirror the DuckDB C API semantics of @duckdb_get_blob@.+-}+foreign import ccall unsafe "wrapped_duckdb_get_blob"+    c_duckdb_get_blob :: DuckDBValue -> Ptr DuckDBBlob -> IO ()++{- | Returns the duckdb_bit value of the given value. The @data@ field must be+destroyed with @duckdb_free@.++Parameters:+* @val@: A duckdb_value containing a BIT++Returns A duckdb_bit++These bindings call the wrapper symbol @wrapped_duckdb_get_bit@ but+mirror the DuckDB C API semantics of @duckdb_get_bit@.+-}+foreign import ccall unsafe "wrapped_duckdb_get_bit"+    c_duckdb_get_bit :: DuckDBValue -> Ptr DuckDBBit -> IO ()++{- | Returns a duckdb_uhugeint representing the UUID value of the given value.++Parameters:+* @val@: A duckdb_value containing a UUID++Returns A duckdb_uhugeint representing the UUID value++These bindings call the wrapper symbol @wrapped_duckdb_get_uuid@ but+mirror the DuckDB C API semantics of @duckdb_get_uuid@.+-}+foreign import ccall unsafe "wrapped_duckdb_get_uuid"+    c_duckdb_get_uuid :: DuckDBValue -> Ptr DuckDBUHugeInt -> IO ()++{- | Obtains a string representation of the given value. The result must be+destroyed with @duckdb_free@.++Parameters:+* @value@: The value++Returns The string value. This must be destroyed with @duckdb_free@.+-}+foreign import ccall unsafe "duckdb_get_varchar"+    c_duckdb_get_varchar :: DuckDBValue -> IO CString++{- | Creates a struct value from a type and an array of values. Must be destroyed+with @duckdb_destroy_value@.++Parameters:+* @type@: The type of the struct+* @values@: The values for the struct fields++Returns The struct value, or nullptr, if any child type is @DUCKDB_TYPE_ANY@+or @DUCKDB_TYPE_INVALID@.+-}+foreign import ccall unsafe "duckdb_create_struct_value"+    c_duckdb_create_struct_value :: DuckDBLogicalType -> Ptr DuckDBValue -> IO DuckDBValue++{- | Creates a list value from a child (element) type and an array of values of+length @value_count@. Must be destroyed with @duckdb_destroy_value@.++Parameters:+* @type@: The type of the list+* @values@: The values for the list+* @value_count@: The number of values in the list++Returns The list value, or nullptr, if the child type is @DUCKDB_TYPE_ANY@ or+@DUCKDB_TYPE_INVALID@.+-}+foreign import ccall unsafe "duckdb_create_list_value"+    c_duckdb_create_list_value :: DuckDBLogicalType -> Ptr DuckDBValue -> DuckDBIdx -> IO DuckDBValue++{- | Creates an array value from a child (element) type and an array of values of+length @value_count@. Must be destroyed with @duckdb_destroy_value@.++Parameters:+* @type@: The type of the array+* @values@: The values for the array+* @value_count@: The number of values in the array++Returns The array value, or nullptr, if the child type is @DUCKDB_TYPE_ANY@ or+@DUCKDB_TYPE_INVALID@.+-}+foreign import ccall unsafe "duckdb_create_array_value"+    c_duckdb_create_array_value :: DuckDBLogicalType -> Ptr DuckDBValue -> DuckDBIdx -> IO DuckDBValue++{- | Creates a map value from a map type and two arrays, one for the keys and one+for the values, each of length @entry_count@. Must be destroyed with+@duckdb_destroy_value@.++Parameters:+* @map_type@: The map type+* @keys@: The keys of the map+* @values@: The values of the map+* @entry_count@: The number of entrys (key-value pairs) in the map++Returns The map value, or nullptr, if the parameters are invalid.+-}+foreign import ccall unsafe "duckdb_create_map_value"+    c_duckdb_create_map_value :: DuckDBLogicalType -> Ptr DuckDBValue -> Ptr DuckDBValue -> DuckDBIdx -> IO DuckDBValue++{- | Creates a union value from a union type, a tag index, and a value. Must be+destroyed with @duckdb_destroy_value@.++Parameters:+* @union_type@: The union type+* @tag_index@: The index of the tag of the union+* @value@: The value of the union for that tag++Returns The union value, or nullptr, if the parameters are invalid.+-}+foreign import ccall unsafe "duckdb_create_union_value"+    c_duckdb_create_union_value :: DuckDBLogicalType -> DuckDBIdx -> DuckDBValue -> IO DuckDBValue++{- | Returns the number of elements in a MAP value.++Parameters:+* @value@: The MAP value.++Returns The number of elements in the map.+-}+foreign import ccall unsafe "duckdb_get_map_size"+    c_duckdb_get_map_size :: DuckDBValue -> IO DuckDBIdx++{- | Returns the MAP key at index as a duckdb_value.++Parameters:+* @value@: The MAP value.+* @index@: The index of the key.++Returns The key as a duckdb_value.+-}+foreign import ccall unsafe "duckdb_get_map_key"+    c_duckdb_get_map_key :: DuckDBValue -> DuckDBIdx -> IO DuckDBValue++{- | Returns the MAP value at index as a duckdb_value.++Parameters:+* @value@: The MAP value.+* @index@: The index of the value.++Returns The value as a duckdb_value.+-}+foreign import ccall unsafe "duckdb_get_map_value"+    c_duckdb_get_map_value :: DuckDBValue -> DuckDBIdx -> IO DuckDBValue++{- | Returns whether the value's type is SQLNULL or not.++Parameters:+* @value@: The value to check.++Returns True, if the value's type is SQLNULL, otherwise false.+-}+foreign import ccall unsafe "duckdb_is_null_value"+    c_duckdb_is_null_value :: DuckDBValue -> IO CBool++{- | Creates a value of type SQLNULL.++Returns The duckdb_value representing SQLNULL. This must be destroyed with+@duckdb_destroy_value@.+-}+foreign import ccall unsafe "duckdb_create_null_value"+    c_duckdb_create_null_value :: IO DuckDBValue++{- | Returns the number of elements in a LIST value.++Parameters:+* @value@: The LIST value.++Returns The number of elements in the list.+-}+foreign import ccall unsafe "duckdb_get_list_size"+    c_duckdb_get_list_size :: DuckDBValue -> IO DuckDBIdx++{- | Returns the LIST child at index as a duckdb_value.++Parameters:+* @value@: The LIST value.+* @index@: The index of the child.++Returns The child as a duckdb_value.+-}+foreign import ccall unsafe "duckdb_get_list_child"+    c_duckdb_get_list_child :: DuckDBValue -> DuckDBIdx -> IO DuckDBValue++{- | Creates an enum value from a type and a value. Must be destroyed with+@duckdb_destroy_value@.++Parameters:+* @type@: The type of the enum+* @value@: The value for the enum++Returns The enum value, or nullptr.+-}+foreign import ccall unsafe "duckdb_create_enum_value"+    c_duckdb_create_enum_value :: DuckDBLogicalType -> Word64 -> IO DuckDBValue++{- | Returns the enum value of the given value.++Parameters:+* @value@: A duckdb_value containing an enum++Returns A uint64_t, or MinValue if the value cannot be converted+-}+foreign import ccall unsafe "duckdb_get_enum_value"+    c_duckdb_get_enum_value :: DuckDBValue -> IO Word64++{- | Returns the STRUCT child at index as a duckdb_value.++Parameters:+* @value@: The STRUCT value.+* @index@: The index of the child.++Returns The child as a duckdb_value.+-}+foreign import ccall unsafe "duckdb_get_struct_child"+    c_duckdb_get_struct_child :: DuckDBValue -> DuckDBIdx -> IO DuckDBValue++{- | Returns the SQL string representation of the given value.++Parameters:+* @value@: A duckdb_value.++Returns The SQL string representation as a null-terminated string. The result+must be freed with @duckdb_free@.+-}+foreign import ccall unsafe "duckdb_value_to_string"+    c_duckdb_value_to_string :: DuckDBValue -> IO CString
+ src/Database/DuckDB/FFI/Vector.hs view
@@ -0,0 +1,248 @@+module Database.DuckDB.FFI.Vector (+    c_duckdb_create_vector,+    c_duckdb_destroy_vector,+    c_duckdb_vector_get_column_type,+    c_duckdb_vector_get_data,+    c_duckdb_vector_get_validity,+    c_duckdb_vector_ensure_validity_writable,+    c_duckdb_vector_assign_string_element,+    c_duckdb_vector_assign_string_element_len,+    c_duckdb_list_vector_get_child,+    c_duckdb_list_vector_get_size,+    c_duckdb_list_vector_set_size,+    c_duckdb_list_vector_reserve,+    c_duckdb_struct_vector_get_child,+    c_duckdb_array_vector_get_child,+    c_duckdb_slice_vector,+    c_duckdb_vector_copy_sel,+    c_duckdb_vector_reference_value,+    c_duckdb_vector_reference_vector,+) where++import Data.Word (Word64)+import Database.DuckDB.FFI.Types+import Foreign.C.String (CString)+import Foreign.C.Types (CInt (..))+import Foreign.Ptr (Ptr)++{- | Creates a flat vector. Must be destroyed with @duckdb_destroy_vector@.++Parameters:+* @type@: The logical type of the vector.+* @capacity@: The capacity of the vector.++Returns The vector.+-}+foreign import ccall unsafe "duckdb_create_vector"+    c_duckdb_create_vector :: DuckDBLogicalType -> DuckDBIdx -> IO DuckDBVector++{- | Destroys the vector and de-allocates its memory.++Parameters:+* @vector@: A pointer to the vector.+-}+foreign import ccall unsafe "duckdb_destroy_vector"+    c_duckdb_destroy_vector :: Ptr DuckDBVector -> IO ()++{- | Retrieves the column type of the specified vector.++The result must be destroyed with @duckdb_destroy_logical_type@.++Parameters:+* @vector@: The vector get the data from++Returns The type of the vector+-}+foreign import ccall unsafe "duckdb_vector_get_column_type"+    c_duckdb_vector_get_column_type :: DuckDBVector -> IO DuckDBLogicalType++{- | Retrieves the data pointer of the vector.++The data pointer can be used to read or write values from the vector. How to+read or write values depends on the type of the vector.++Parameters:+* @vector@: The vector to get the data from++Returns The data pointer+-}+foreign import ccall unsafe "duckdb_vector_get_data"+    c_duckdb_vector_get_data :: DuckDBVector -> IO (Ptr ())++{- | Retrieves the validity mask pointer of the specified vector.++If all values are valid, this function MIGHT return NULL!++The validity mask is a bitset that signifies null-ness within the data chunk.+It is a series of uint64_t values, where each uint64_t value contains validity+for 64 tuples. The bit is set to 1 if the value is valid (i.e., not NULL) or 0+if the value is invalid (i.e., NULL).++Validity of a specific value can be obtained like this:++idx_t entry_idx = row_idx / 64; idx_t idx_in_entry = row_idx % 64; bool+is_valid = validity_mask[entry_idx] & (1 << idx_in_entry);++Alternatively, the (slower) duckdb_validity_row_is_valid function can be used.++Parameters:+* @vector@: The vector to get the data from++Returns The pointer to the validity mask, or NULL if no validity mask is+present+-}+foreign import ccall unsafe "duckdb_vector_get_validity"+    c_duckdb_vector_get_validity :: DuckDBVector -> IO (Ptr Word64)++{- | Ensures the validity mask is writable by allocating it.++After this function is called, @duckdb_vector_get_validity@ will ALWAYS return+non-NULL. This allows NULL values to be written to the vector, regardless of+whether a validity mask was present before.++Parameters:+* @vector@: The vector to alter+-}+foreign import ccall unsafe "duckdb_vector_ensure_validity_writable"+    c_duckdb_vector_ensure_validity_writable :: DuckDBVector -> IO ()++{- | Assigns a string element in the vector at the specified location.++Parameters:+* @vector@: The vector to alter+* @index@: The row position in the vector to assign the string to+* @str@: The null-terminated string+-}+foreign import ccall unsafe "duckdb_vector_assign_string_element"+    c_duckdb_vector_assign_string_element :: DuckDBVector -> DuckDBIdx -> CString -> IO ()++{- | Assigns a string element in the vector at the specified location. You may also+use this function to assign BLOBs.++Parameters:+* @vector@: The vector to alter+* @index@: The row position in the vector to assign the string to+* @str@: The string+* @str_len@: The length of the string (in bytes)+-}+foreign import ccall unsafe "duckdb_vector_assign_string_element_len"+    c_duckdb_vector_assign_string_element_len :: DuckDBVector -> DuckDBIdx -> CString -> DuckDBIdx -> IO ()++{- | Retrieves the child vector of a list vector.++The resulting vector is valid as long as the parent vector is valid.++Parameters:+* @vector@: The vector++Returns The child vector+-}+foreign import ccall unsafe "duckdb_list_vector_get_child"+    c_duckdb_list_vector_get_child :: DuckDBVector -> IO DuckDBVector++{- | Returns the size of the child vector of the list.++Parameters:+* @vector@: The vector++Returns The size of the child list+-}+foreign import ccall unsafe "duckdb_list_vector_get_size"+    c_duckdb_list_vector_get_size :: DuckDBVector -> IO DuckDBIdx++{- | Sets the total size of the underlying child-vector of a list vector.++Parameters:+* @vector@: The list vector.+* @size@: The size of the child list.++Returns The duckdb state. Returns DuckDBError if the vector is nullptr.+-}+foreign import ccall unsafe "duckdb_list_vector_set_size"+    c_duckdb_list_vector_set_size :: DuckDBVector -> DuckDBIdx -> IO DuckDBState++{- | Sets the total capacity of the underlying child-vector of a list.++After calling this method, you must call @duckdb_vector_get_validity@ and+@duckdb_vector_get_data@ to obtain current data and validity pointers++Parameters:+* @vector@: The list vector.+* @required_capacity@: the total capacity to reserve.++Returns The duckdb state. Returns DuckDBError if the vector is nullptr.+-}+foreign import ccall unsafe "duckdb_list_vector_reserve"+    c_duckdb_list_vector_reserve :: DuckDBVector -> DuckDBIdx -> IO DuckDBState++{- | Retrieves the child vector of a struct vector. The resulting vector is valid+as long as the parent vector is valid.++Parameters:+* @vector@: The vector+* @index@: The child index++Returns The child vector+-}+foreign import ccall unsafe "duckdb_struct_vector_get_child"+    c_duckdb_struct_vector_get_child :: DuckDBVector -> DuckDBIdx -> IO DuckDBVector++{- | Retrieves the child vector of an array vector. The resulting vector is valid+as long as the parent vector is valid. The resulting vector has the size of+the parent vector multiplied by the array size.++Parameters:+* @vector@: The vector++Returns The child vector+-}+foreign import ccall unsafe "duckdb_array_vector_get_child"+    c_duckdb_array_vector_get_child :: DuckDBVector -> IO DuckDBVector++{- | Slice a vector with a selection vector. The length of the selection vector+must be less than or equal to the length of the vector. Turns the vector into+a dictionary vector.++Parameters:+* @vector@: The vector to slice.+* @sel@: The selection vector.+* @len@: The length of the selection vector.+-}+foreign import ccall unsafe "duckdb_slice_vector"+    c_duckdb_slice_vector :: DuckDBVector -> DuckDBSelectionVector -> DuckDBIdx -> IO ()++{- | Copy the src vector to the dst with a selection vector that identifies which+indices to copy.++Parameters:+* @src@: The vector to copy from.+* @dst@: The vector to copy to.+* @sel@: The selection vector. The length of the selection vector should not+  be more than the length of the src vector+* @src_count@: The number of entries from selection vector to copy. Think of+  this as the effective length of the selection vector starting from index 0+* @src_offset@: The offset in the selection vector to copy from (important:+  actual number of items copied = src_count - src_offset).+* @dst_offset@: The offset in the dst vector to start copying to.+-}+foreign import ccall unsafe "duckdb_vector_copy_sel"+    c_duckdb_vector_copy_sel :: DuckDBVector -> DuckDBVector -> DuckDBSelectionVector -> DuckDBIdx -> DuckDBIdx -> DuckDBIdx -> IO ()++{- | Copies the value from @value@ to @vector@.++Parameters:+* @vector@: The receiving vector.+* @value@: The value to copy into the vector.+-}+foreign import ccall unsafe "duckdb_vector_reference_value"+    c_duckdb_vector_reference_value :: DuckDBVector -> DuckDBValue -> IO ()++{- | Changes @to_vector@ to reference `from_vector. After, the vectors share+ownership of the data.++Parameters:+* @to_vector@: The receiving vector.+* @from_vector@: The vector to reference.+-}+foreign import ccall unsafe "duckdb_vector_reference_vector"+    c_duckdb_vector_reference_vector :: DuckDBVector -> DuckDBVector -> IO ()
+ test/AggregateFunctionsTest.hs view
@@ -0,0 +1,411 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE ForeignFunctionInterface #-}++module AggregateFunctionsTest (tests) where++import Control.Concurrent (runInBoundThread)+import Control.Exception (bracket)+import Control.Monad (forM_, when)+import Data.Int (Int32)+import Data.List (isInfixOf)+import Data.Word (Word64)+import Database.DuckDB.FFI+import Foreign.C.String (CString, peekCString, withCString)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca, free, mallocBytes)+import Foreign.Ptr (Ptr, castPtr, freeHaskellFunPtr, nullFunPtr, nullPtr)+import Foreign.Storable (peek, peekElemOff, poke, pokeElemOff, sizeOf)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (withConnection, withDatabase, withLogicalType, withResultCString)++tests :: TestTree+tests =+    testGroup+        "Aggregate Functions"+        [ sumAggregate+        , extraInfoAggregate+        , aggregateFunctionSet+        , aggregateErrorPropagation+        , specialHandlingNulls+        ]++-- Test cases ----------------------------------------------------------------++sumAggregate :: TestTree+sumAggregate =+    testCase "register custom aggregate and execute" $+        runInBoundThread do+            withDatabase \db ->+                withConnection db \conn ->+                    withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+                        withAggregateFunction \aggFun ->+                            withCallbacks \cbs -> do+                                setupAggregateFunction aggFun cbs intType "haskell_sum"+                                c_duckdb_register_aggregate_function conn aggFun >>= (@?= DuckDBSuccess)+                                withCString "SELECT haskell_sum(v) FROM (VALUES (1), (2), (3)) t(v)" \sql ->+                                    withResultCString conn sql \resPtr ->+                                        c_duckdb_value_int32 resPtr 0 0 >>= (@?= 6)++extraInfoAggregate :: TestTree+extraInfoAggregate =+    testCase "extra info is visible inside callbacks" $+        runInBoundThread do+            let config = defaultAggregateConfig{cfgBonus = 2}+            withDatabase \db ->+                withConnection db \conn ->+                    withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+                        withAggregateConfig config \configPtr ->+                            withAggregateFunction \aggFun ->+                                withCallbacks \cbs -> do+                                    setupAggregateFunction aggFun cbs intType "haskell_bonus_sum"+                                    c_duckdb_aggregate_function_set_extra_info aggFun configPtr nullFunPtr++                                    c_duckdb_register_aggregate_function conn aggFun >>= (@?= DuckDBSuccess)++                                    withCString "SELECT haskell_bonus_sum(v) FROM (VALUES (1), (2), (3)) t(v)" \sql ->+                                        withResultCString conn sql \resPtr ->+                                            c_duckdb_value_int32 resPtr 0 0 >>= (@?= 12)++aggregateFunctionSet :: TestTree+aggregateFunctionSet =+    testCase "aggregate function set registers overload" $+        runInBoundThread do+            withDatabase \db ->+                withConnection db \conn ->+                    withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+                        withAggregateFunction \aggFun ->+                            withCallbacks \cbs -> do+                                setupAggregateFunction aggFun cbs intType "haskell_sum_set"+                                withCString "haskell_sum_set" \setName ->+                                    withAggregateFunctionSet setName \set -> do+                                        c_duckdb_add_aggregate_function_to_set set aggFun >>= (@?= DuckDBSuccess)+                                        c_duckdb_register_aggregate_function_set conn set >>= (@?= DuckDBSuccess)++                                        withCString "SELECT haskell_sum_set(v) FROM (VALUES (4), (5), (6)) t(v)" \sql ->+                                            withResultCString conn sql \resPtr ->+                                                c_duckdb_value_int32 resPtr 0 0 >>= (@?= 15)++aggregateErrorPropagation :: TestTree+aggregateErrorPropagation =+    testCase "callbacks can signal errors" $+        runInBoundThread do+            let config = defaultAggregateConfig{cfgFailOnNegative = 1}+            withDatabase \db ->+                withConnection db \conn ->+                    withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+                        withAggregateConfig config \configPtr ->+                            withAggregateFunction \aggFun ->+                                withCallbacks \cbs -> do+                                    setupAggregateFunction aggFun cbs intType "no_negatives"+                                    c_duckdb_aggregate_function_set_extra_info aggFun configPtr nullFunPtr+                                    c_duckdb_register_aggregate_function conn aggFun >>= (@?= DuckDBSuccess)++                                    withCString "SELECT no_negatives(v) FROM (VALUES (1), (-5)) t(v)" \sql ->+                                        alloca \resPtr -> do+                                            errState <- c_duckdb_query conn sql resPtr+                                            errState @?= DuckDBError++                                            msgPtr <- c_duckdb_result_error resPtr+                                            errMsg <- peekCString msgPtr+                                            assertBool "expected negative error message" ("negatives not allowed" `isInfixOf` errMsg)++                                            c_duckdb_destroy_result resPtr++specialHandlingNulls :: TestTree+specialHandlingNulls =+    testCase "special handling allows returning NULL when all inputs are NULL" $+        runInBoundThread do+            let config = defaultAggregateConfig{cfgNullIfAllInvalid = 1}+            withDatabase \db ->+                withConnection db \conn ->+                    withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+                        withAggregateConfig config \configPtr ->+                            withAggregateFunction \aggFun ->+                                withCallbacks \cbs -> do+                                    setupAggregateFunction aggFun cbs intType "nullable_sum"+                                    c_duckdb_aggregate_function_set_extra_info aggFun configPtr nullFunPtr+                                    c_duckdb_aggregate_function_set_special_handling aggFun+                                    c_duckdb_register_aggregate_function conn aggFun >>= (@?= DuckDBSuccess)++                                    withCString "SELECT nullable_sum(v) FROM (VALUES (CAST(NULL AS INTEGER))) t(v)" \sql ->+                                        withResultCString conn sql \resPtr -> do+                                            isNull <- c_duckdb_value_is_null resPtr 0 0+                                            cbToBool isNull @?= True++-- Aggregate state -----------------------------------------------------------++data SumState = SumState+    { ssTotal :: Int32+    , ssSeen :: Int32+    , ssNulls :: Int32+    }++initialSumState :: SumState+initialSumState = SumState 0 0 0++sumStateSize :: Int+sumStateSize = 3 * sizeOf (undefined :: Int32)++type SumStatePtr = Ptr Int32++readSumState :: SumStatePtr -> IO SumState+readSumState ptr = do+    total <- peekElemOff ptr 0+    seen <- peekElemOff ptr 1+    nulls <- peekElemOff ptr 2+    pure SumState{ssTotal = total, ssSeen = seen, ssNulls = nulls}++writeSumState :: SumStatePtr -> SumState -> IO ()+writeSumState ptr SumState{ssTotal = total, ssSeen = seen, ssNulls = nulls} = do+    pokeElemOff ptr 0 total+    pokeElemOff ptr 1 seen+    pokeElemOff ptr 2 nulls++-- Aggregate configuration ---------------------------------------------------++data AggregateConfig = AggregateConfig+    { cfgBonus :: Int32+    , cfgFailOnNegative :: Int32+    , cfgReturnNullCount :: Int32+    , cfgNullIfAllInvalid :: Int32+    }++defaultAggregateConfig :: AggregateConfig+defaultAggregateConfig = AggregateConfig 0 0 0 0++type AggregateConfigPtr = Ptr Int32++aggregateConfigSize :: Int+aggregateConfigSize = 4 * sizeOf (undefined :: Int32)++writeAggregateConfig :: AggregateConfigPtr -> AggregateConfig -> IO ()+writeAggregateConfig ptr AggregateConfig{cfgBonus = bonus, cfgFailOnNegative = failNeg, cfgReturnNullCount = returnNulls, cfgNullIfAllInvalid = nullAll} = do+    pokeElemOff ptr 0 bonus+    pokeElemOff ptr 1 failNeg+    pokeElemOff ptr 2 returnNulls+    pokeElemOff ptr 3 nullAll++peekAggregateConfig :: AggregateConfigPtr -> IO AggregateConfig+peekAggregateConfig ptr = do+    bonus <- peekElemOff ptr 0+    failNeg <- peekElemOff ptr 1+    returnNulls <- peekElemOff ptr 2+    nullAll <- peekElemOff ptr 3+    pure+        AggregateConfig+            { cfgBonus = bonus+            , cfgFailOnNegative = failNeg+            , cfgReturnNullCount = returnNulls+            , cfgNullIfAllInvalid = nullAll+            }++configFromInfo :: DuckDBFunctionInfo -> IO AggregateConfig+configFromInfo info = do+    raw <- c_duckdb_aggregate_function_get_extra_info info+    if raw == nullPtr+        then pure defaultAggregateConfig+        else peekAggregateConfig (castPtr raw)++withAggregateConfig :: AggregateConfig -> (Ptr () -> IO a) -> IO a+withAggregateConfig cfg action =+    bracket acquire freeConfig (action . castPtr)+  where+    acquire = do+        ptr <- mallocBytes aggregateConfigSize :: IO AggregateConfigPtr+        writeAggregateConfig ptr cfg+        pure ptr+    freeConfig = free++shouldFailOnNegative :: AggregateConfig -> Bool+shouldFailOnNegative AggregateConfig{cfgFailOnNegative = flag} = flag /= 0++shouldReturnNullCount :: AggregateConfig -> Bool+shouldReturnNullCount AggregateConfig{cfgReturnNullCount = flag} = flag /= 0++shouldMarkNullWhenAllInvalid :: AggregateConfig -> Bool+shouldMarkNullWhenAllInvalid AggregateConfig{cfgNullIfAllInvalid = flag} = flag /= 0++-- Callback implementations --------------------------------------------------++data Callbacks = Callbacks+    { cbStateSize :: DuckDBAggregateStateSizeFun+    , cbInit :: DuckDBAggregateInitFun+    , cbUpdate :: DuckDBAggregateUpdateFun+    , cbCombine :: DuckDBAggregateCombineFun+    , cbFinalize :: DuckDBAggregateFinalizeFun+    , cbDestroy :: DuckDBAggregateDestroyFun+    }++withCallbacks :: (Callbacks -> IO a) -> IO a+withCallbacks = bracket acquire release+  where+    acquire = do+        sizeFun <- mkStateSizeFun stateSizeFun+        initFun <- mkInitFun initCallback+        updateFun <- mkUpdateFun updateCallback+        combineFun <- mkCombineFun combineCallback+        finalizeFun <- mkFinalizeFun finalizeCallback+        destroyFun <- mkDestroyFun destroyCallback+        pure+            Callbacks+                { cbStateSize = sizeFun+                , cbInit = initFun+                , cbUpdate = updateFun+                , cbCombine = combineFun+                , cbFinalize = finalizeFun+                , cbDestroy = destroyFun+                }+    release Callbacks{cbStateSize = sizeFun, cbInit = initFun, cbUpdate = updateFun, cbCombine = combineFun, cbFinalize = finalizeFun, cbDestroy = destroyFun} = do+        freeHaskellFunPtr sizeFun+        freeHaskellFunPtr initFun+        freeHaskellFunPtr updateFun+        freeHaskellFunPtr combineFun+        freeHaskellFunPtr finalizeFun+        freeHaskellFunPtr destroyFun++setupAggregateFunction :: DuckDBAggregateFunction -> Callbacks -> DuckDBLogicalType -> String -> IO ()+setupAggregateFunction aggFun Callbacks{cbStateSize = sizeFun, cbInit = initFun, cbUpdate = updateFun, cbCombine = combineFun, cbFinalize = finalizeFun, cbDestroy = destroyFun} intType name = do+    withCString name $ \cname -> c_duckdb_aggregate_function_set_name aggFun cname+    c_duckdb_aggregate_function_add_parameter aggFun intType+    c_duckdb_aggregate_function_set_return_type aggFun intType+    c_duckdb_aggregate_function_set_functions aggFun sizeFun initFun updateFun combineFun finalizeFun+    c_duckdb_aggregate_function_set_destructor aggFun destroyFun++stateSizeFun :: DuckDBFunctionInfo -> IO DuckDBIdx+stateSizeFun _ = pure (fromIntegral (sizeOf (nullPtr :: Ptr ())))++initCallback :: DuckDBFunctionInfo -> DuckDBAggregateState -> IO ()+initCallback _ state = do+    raw <- c_duckdb_malloc (fromIntegral sumStateSize)+    let storage = castPtr raw :: SumStatePtr+    writeSumState storage initialSumState+    writeStateValuePtr state storage++updateCallback :: DuckDBFunctionInfo -> DuckDBDataChunk -> Ptr DuckDBAggregateState -> IO ()+updateCallback info chunk stateArrayPtr = do+    rowCount <- c_duckdb_data_chunk_get_size chunk+    vec <- c_duckdb_data_chunk_get_vector chunk 0+    dataPtr <- vectorDataPtr vec+    validity <- c_duckdb_vector_get_validity vec+    config <- configFromInfo info++    let rowCountInt = fromIntegral rowCount+    forM_ [0 .. rowCountInt - 1] \i -> do+        statePtr <- peekElemOff stateArrayPtr i+        storage <- readStateValuePtr statePtr+        current <- readSumState storage+        let seen' = ssSeen current + 1+            idx = fromIntegral i++        isValid <- rowIsValid validity idx+        if not isValid+            then writeSumState storage current{ssSeen = seen', ssNulls = ssNulls current + 1}+            else do+                value <- peekElemOff dataPtr i+                if shouldFailOnNegative config && value < 0+                    then do+                        withCString "negatives not allowed" $ \errMsg ->+                            c_duckdb_aggregate_function_set_error info errMsg+                        writeSumState storage current{ssSeen = seen'}+                    else+                        let total' = ssTotal current + value + cfgBonus config+                         in writeSumState storage SumState{ssTotal = total', ssSeen = seen', ssNulls = ssNulls current}++combineCallback :: DuckDBFunctionInfo -> Ptr DuckDBAggregateState -> Ptr DuckDBAggregateState -> DuckDBIdx -> IO ()+combineCallback _ sourceStates targetStates count =+    forM_ [0 .. fromIntegral count - 1] \i -> do+        sourceState <- peekElemOff sourceStates i+        targetState <- peekElemOff targetStates i+        sourcePtr <- readStateValuePtr sourceState+        targetPtr <- readStateValuePtr targetState+        source <- readSumState sourcePtr+        target <- readSumState targetPtr++        let combined =+                SumState+                    { ssTotal = ssTotal source + ssTotal target+                    , ssSeen = ssSeen source + ssSeen target+                    , ssNulls = ssNulls source + ssNulls target+                    }+        writeSumState targetPtr combined++finalizeCallback :: DuckDBFunctionInfo -> Ptr DuckDBAggregateState -> DuckDBVector -> DuckDBIdx -> DuckDBIdx -> IO ()+finalizeCallback info stateArrayPtr outVec _ offset = do+    state <- peekElemOff stateArrayPtr (fromIntegral offset)+    storage <- readStateValuePtr state+    SumState{ssTotal = total, ssSeen = seen, ssNulls = nulls} <- readSumState storage+    config <- configFromInfo info++    outPtr <- vectorDataPtr outVec+    let resultValue = if shouldReturnNullCount config then nulls else total+    pokeElemOff outPtr (fromIntegral offset) resultValue++    let allInvalid = seen > 0 && seen == nulls+    when (shouldMarkNullWhenAllInvalid config && allInvalid) do+        c_duckdb_vector_ensure_validity_writable outVec+        validity <- c_duckdb_vector_get_validity outVec+        c_duckdb_validity_set_row_invalid validity offset++destroyCallback :: Ptr DuckDBAggregateState -> DuckDBIdx -> IO ()+destroyCallback states count =+    forM_ [0 .. fromIntegral count - 1] \i -> do+        state <- peekElemOff states i+        storage <- readStateValuePtr state+        when (storage /= nullPtr) $+            c_duckdb_free (castPtr storage)++-- Helper pointer accessors --------------------------------------------------++writeStateValuePtr :: DuckDBAggregateState -> SumStatePtr -> IO ()+writeStateValuePtr state ptr =+    poke (castPtr state :: Ptr (Ptr ())) (castPtr ptr)++readStateValuePtr :: DuckDBAggregateState -> IO SumStatePtr+readStateValuePtr state = do+    raw <- peek (castPtr state :: Ptr (Ptr ()))+    pure (castPtr raw)++vectorDataPtr :: DuckDBVector -> IO (Ptr Int32)+vectorDataPtr vec = castPtr <$> c_duckdb_vector_get_data vec++rowIsValid :: Ptr Word64 -> DuckDBIdx -> IO Bool+rowIsValid validity idx+    | validity == nullPtr = pure True+    | otherwise = cbToBool <$> c_duckdb_validity_row_is_valid validity idx++cbToBool :: CBool -> Bool+cbToBool (CBool v) = v /= 0++-- Wrapper builders ----------------------------------------------------------++foreign import ccall safe "wrapper"+    mkStateSizeFun :: (DuckDBFunctionInfo -> IO DuckDBIdx) -> IO DuckDBAggregateStateSizeFun++foreign import ccall safe "wrapper"+    mkInitFun :: (DuckDBFunctionInfo -> DuckDBAggregateState -> IO ()) -> IO DuckDBAggregateInitFun++foreign import ccall safe "wrapper"+    mkUpdateFun :: (DuckDBFunctionInfo -> DuckDBDataChunk -> Ptr DuckDBAggregateState -> IO ()) -> IO DuckDBAggregateUpdateFun++foreign import ccall safe "wrapper"+    mkCombineFun :: (DuckDBFunctionInfo -> Ptr DuckDBAggregateState -> Ptr DuckDBAggregateState -> DuckDBIdx -> IO ()) -> IO DuckDBAggregateCombineFun++foreign import ccall safe "wrapper"+    mkFinalizeFun :: (DuckDBFunctionInfo -> Ptr DuckDBAggregateState -> DuckDBVector -> DuckDBIdx -> DuckDBIdx -> IO ()) -> IO DuckDBAggregateFinalizeFun++foreign import ccall safe "wrapper"+    mkDestroyFun :: (Ptr DuckDBAggregateState -> DuckDBIdx -> IO ()) -> IO DuckDBAggregateDestroyFun++-- Resource helpers ----------------------------------------------------------++withAggregateFunction :: (DuckDBAggregateFunction -> IO a) -> IO a+withAggregateFunction = bracket c_duckdb_create_aggregate_function destroy+  where+    destroy fun = alloca \ptr -> poke ptr fun >> c_duckdb_destroy_aggregate_function ptr++withAggregateFunctionSet :: CString -> (DuckDBAggregateFunctionSet -> IO a) -> IO a+withAggregateFunctionSet name = bracket acquire release+  where+    acquire = c_duckdb_create_aggregate_function_set name+    release set = alloca \ptr -> poke ptr set >> c_duckdb_destroy_aggregate_function_set ptr
+ test/AppenderTest.hs view
@@ -0,0 +1,553 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE TypeApplications #-}++module AppenderTest (tests) where++import Control.Exception (bracket, finally)+import Control.Monad (forM_, when, (>=>))+import Data.Int (Int16, Int32, Int64, Int8)+import Data.List (isInfixOf)+import Data.Word (Word16, Word32, Word64, Word8)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString, withCStringLen)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Marshal.Array (peekArray, withArray)+import Foreign.Marshal.Utils (with)+import Foreign.Ptr (Ptr, castPtr, nullPtr)+import Foreign.Storable (peek, poke, pokeElemOff)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (+    destroyErrorData,+    withConnection,+    withDatabase,+    withDuckValue,+    withLogicalType,+    withResultCString,+ )++tests :: TestTree+tests =+    testGroup+        "Appender"+        [ appenderRowwiseLifecycle+        , appenderColumnSubset+        , appenderDataChunkInsert+        , appenderQueryAppender+        , appenderNumericAndFloatScalars+        , appenderTemporalTypes+        , appenderStringAndBlob+        , appenderChunkDefaults+        , appenderErrorDataInspection+        ]++appenderRowwiseLifecycle :: TestTree+appenderRowwiseLifecycle =+    testCase "append rows using scalar APIs and inspect results" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement conn "CREATE TABLE appender_demo(id INTEGER, name VARCHAR, active BOOLEAN DEFAULT TRUE)"++                withTableAppender conn "appender_demo" \app -> do+                    c_duckdb_appender_column_count app >>= (@?= 3)+                    checkColumnType app 0 DuckDBTypeInteger+                    checkColumnType app 1 DuckDBTypeVarchar+                    checkColumnType app 2 DuckDBTypeBoolean++                    c_duckdb_appender_begin_row app >>= (@?= DuckDBSuccess)+                    c_duckdb_append_int32 app 1 >>= (@?= DuckDBSuccess)+                    withCString "alice" (c_duckdb_append_varchar app >=> (@?= DuckDBSuccess))+                    c_duckdb_append_bool app (CBool 1) >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_end_row app >>= (@?= DuckDBSuccess)++                    c_duckdb_appender_begin_row app >>= (@?= DuckDBSuccess)+                    c_duckdb_append_int32 app 2 >>= (@?= DuckDBSuccess)+                    c_duckdb_append_null app >>= (@?= DuckDBSuccess)+                    c_duckdb_append_bool app (CBool 0) >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_end_row app >>= (@?= DuckDBSuccess)++                    c_duckdb_appender_begin_row app >>= (@?= DuckDBSuccess)+                    c_duckdb_append_int32 app 3 >>= (@?= DuckDBSuccess)+                    withDuckValue (withCString "via_value" c_duckdb_create_varchar) (c_duckdb_append_value app >=> (@?= DuckDBSuccess))+                    c_duckdb_append_default app >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_end_row app >>= (@?= DuckDBSuccess)++                    errPtr0 <- c_duckdb_appender_error app+                    when (errPtr0 /= nullPtr) $ do+                        msg <- peekCString errPtr0+                        msg @?= ""++                    c_duckdb_appender_flush app >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_close app >>= (@?= DuckDBSuccess)++                    withCString "SELECT id, name, active FROM appender_demo ORDER BY id" \sql ->+                        withResultCString conn sql \resPtr -> do+                            c_duckdb_row_count resPtr >>= (@?= 3)++                            c_duckdb_value_int32 resPtr 0 0 >>= (@?= 1)+                            fetchString resPtr 1 0 >>= (@?= "alice")+                            fetchBool resPtr 2 0 >>= (@?= True)++                            c_duckdb_value_int32 resPtr 0 1 >>= (@?= 2)+                            c_duckdb_value_is_null resPtr 1 1 >>= (@?= CBool 1)+                            fetchBool resPtr 2 1 >>= (@?= False)++                            c_duckdb_value_int32 resPtr 0 2 >>= (@?= 3)+                            fetchString resPtr 1 2 >>= (@?= "via_value")+                            fetchBool resPtr 2 2 >>= (@?= True)++appenderColumnSubset :: TestTree+appenderColumnSubset =+    testCase "restrict active columns and rely on table defaults" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement+                    conn+                    "CREATE TABLE subset_demo(id INTEGER DEFAULT 100, name VARCHAR, note VARCHAR, active BOOLEAN DEFAULT FALSE)"++                withTableAppender conn "subset_demo" \app -> do+                    c_duckdb_appender_clear_columns app >>= (@?= DuckDBSuccess)+                    withCString "name" (c_duckdb_appender_add_column app >=> (@?= DuckDBSuccess))+                    withCString "note" (c_duckdb_appender_add_column app >=> (@?= DuckDBSuccess))++                    c_duckdb_appender_column_count app >>= (@?= 2)++                    c_duckdb_appender_begin_row app >>= (@?= DuckDBSuccess)+                    withCString "subset-one" (c_duckdb_append_varchar app >=> (@?= DuckDBSuccess))+                    withCString "note one" (c_duckdb_append_varchar app >=> (@?= DuckDBSuccess))+                    c_duckdb_appender_end_row app >>= (@?= DuckDBSuccess)++                    c_duckdb_appender_begin_row app >>= (@?= DuckDBSuccess)+                    withCString "subset-two" (c_duckdb_append_varchar app >=> (@?= DuckDBSuccess))+                    c_duckdb_append_null app >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_end_row app >>= (@?= DuckDBSuccess)++                    c_duckdb_appender_flush app >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_close app >>= (@?= DuckDBSuccess)++                    withCString "SELECT id, name, note, active FROM subset_demo ORDER BY rowid" \sql ->+                        withResultCString conn sql \resPtr -> do+                            c_duckdb_row_count resPtr >>= (@?= 2)++                            c_duckdb_value_int32 resPtr 0 0 >>= (@?= 100)+                            fetchString resPtr 1 0 >>= (@?= "subset-one")+                            fetchString resPtr 2 0 >>= (@?= "note one")+                            fetchBool resPtr 3 0 >>= (@?= False)++                            c_duckdb_value_int32 resPtr 0 1 >>= (@?= 100)+                            fetchString resPtr 1 1 >>= (@?= "subset-two")+                            c_duckdb_value_is_null resPtr 2 1 >>= (@?= CBool 1)+                            fetchBool resPtr 3 1 >>= (@?= False)++appenderDataChunkInsert :: TestTree+appenderDataChunkInsert =+    testCase "append via data chunk using extended constructor" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement conn "CREATE TABLE chunk_demo(id INTEGER, label VARCHAR)"++                withTableAppenderExt conn "chunk_demo" \app -> do+                    c_duckdb_appender_column_count app >>= (@?= 2)++                    withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+                        withLogicalType (c_duckdb_create_logical_type DuckDBTypeVarchar) \textType ->+                            withArray [intType, textType] \typeArray ->+                                withDataChunk (c_duckdb_create_data_chunk typeArray 2) \chunk -> do+                                    intVec <- c_duckdb_data_chunk_get_vector chunk 0+                                    fillIntVector intVec [10, 11]++                                    textVec <- c_duckdb_data_chunk_get_vector chunk 1+                                    assignStrings textVec ["ten", "eleven"]++                                    c_duckdb_data_chunk_set_size chunk 2+                                    c_duckdb_append_data_chunk app chunk >>= (@?= DuckDBSuccess)++                    c_duckdb_appender_flush app >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_close app >>= (@?= DuckDBSuccess)++                    withCString "SELECT id, label FROM chunk_demo ORDER BY id" \sql ->+                        withResultCString conn sql \resPtr -> do+                            c_duckdb_row_count resPtr >>= (@?= 2)+                            c_duckdb_value_int32 resPtr 0 0 >>= (@?= 10)+                            fetchString resPtr 1 0 >>= (@?= "ten")+                            c_duckdb_value_int32 resPtr 0 1 >>= (@?= 11)+                            fetchString resPtr 1 1 >>= (@?= "eleven")++appenderQueryAppender :: TestTree+appenderQueryAppender =+    testCase "append rows through query-based appender" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement conn "CREATE TABLE query_target(id INTEGER, label VARCHAR)"++                withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+                    withLogicalType (c_duckdb_create_logical_type DuckDBTypeVarchar) \textType -> do+                        let types = [intType, textType]+                        withQueryAppender conn "INSERT INTO query_target SELECT * FROM appended_data" types \app -> do+                            c_duckdb_appender_column_count app >>= (@?= 2)++                            c_duckdb_appender_begin_row app >>= (@?= DuckDBSuccess)+                            c_duckdb_append_int32 app 21 >>= (@?= DuckDBSuccess)+                            withCString "twenty-one" (c_duckdb_append_varchar app >=> (@?= DuckDBSuccess))+                            c_duckdb_appender_end_row app >>= (@?= DuckDBSuccess)++                            c_duckdb_appender_begin_row app >>= (@?= DuckDBSuccess)+                            c_duckdb_append_int32 app 22 >>= (@?= DuckDBSuccess)+                            withCString "twenty-two" (c_duckdb_append_varchar app >=> (@?= DuckDBSuccess))+                            c_duckdb_appender_end_row app >>= (@?= DuckDBSuccess)++                            c_duckdb_appender_flush app >>= (@?= DuckDBSuccess)+                            c_duckdb_appender_close app >>= (@?= DuckDBSuccess)++                        withCString "SELECT id, label FROM query_target ORDER BY id" \sql ->+                            withResultCString conn sql \resPtr -> do+                                c_duckdb_row_count resPtr >>= (@?= 2)+                                c_duckdb_value_int32 resPtr 0 0 >>= (@?= 21)+                                fetchString resPtr 1 0 >>= (@?= "twenty-one")+                                c_duckdb_value_int32 resPtr 0 1 >>= (@?= 22)+                                fetchString resPtr 1 1 >>= (@?= "twenty-two")++appenderNumericAndFloatScalars :: TestTree+appenderNumericAndFloatScalars =+    testCase "append numeric scalar types" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement+                    conn+                    ( unlines+                        [ "CREATE TABLE numeric_scalars_demo("+                        , "  signed8 TINYINT,"+                        , "  signed16 SMALLINT,"+                        , "  signed64 BIGINT,"+                        , "  signed128 HUGEINT,"+                        , "  unsigned8 UTINYINT,"+                        , "  unsigned16 USMALLINT,"+                        , "  unsigned32 UINTEGER,"+                        , "  unsigned64 UBIGINT,"+                        , "  unsigned128 UHUGEINT,"+                        , "  real32 FLOAT,"+                        , "  real64 DOUBLE"+                        , ")"+                        ]+                    )++                let int8Val = (-12) :: Int8+                    int16Val = (-32000) :: Int16+                    int64Val = (-9876543210) :: Int64+                    hugeVal = DuckDBHugeInt 0xFEDCBA9876543210 0+                    uint8Val = 200 :: Word8+                    uint16Val = 60000 :: Word16+                    uint32Val = 4000000000 :: Word32+                    uint64Val = 12345678901234567890 :: Word64+                    uhugeVal = DuckDBUHugeInt 0x0123456789ABCDEF 0x0011223344556677+                    floatVal = (-12.5) :: Float+                    doubleVal = 1234.5678 :: Double++                withTableAppender conn "numeric_scalars_demo" \app -> do+                    c_duckdb_appender_begin_row app >>= (@?= DuckDBSuccess)+                    c_duckdb_append_int8 app int8Val >>= (@?= DuckDBSuccess)+                    c_duckdb_append_int16 app int16Val >>= (@?= DuckDBSuccess)+                    c_duckdb_append_int64 app int64Val >>= (@?= DuckDBSuccess)+                    with hugeVal (c_duckdb_append_hugeint app >=> (@?= DuckDBSuccess))+                    c_duckdb_append_uint8 app uint8Val >>= (@?= DuckDBSuccess)+                    c_duckdb_append_uint16 app uint16Val >>= (@?= DuckDBSuccess)+                    c_duckdb_append_uint32 app uint32Val >>= (@?= DuckDBSuccess)+                    c_duckdb_append_uint64 app uint64Val >>= (@?= DuckDBSuccess)+                    with uhugeVal (c_duckdb_append_uhugeint app >=> (@?= DuckDBSuccess))+                    c_duckdb_append_float app (realToFrac floatVal) >>= (@?= DuckDBSuccess)+                    c_duckdb_append_double app (realToFrac doubleVal) >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_end_row app >>= (@?= DuckDBSuccess)++                    c_duckdb_appender_flush app >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_close app >>= (@?= DuckDBSuccess)++                let query =+                        unlines+                            [ "SELECT"+                            , "  signed8,"+                            , "  signed16,"+                            , "  signed64,"+                            , "  signed128,"+                            , "  unsigned8,"+                            , "  unsigned16,"+                            , "  unsigned32,"+                            , "  unsigned64,"+                            , "  unsigned128,"+                            , "  real32,"+                            , "  real64"+                            , "FROM numeric_scalars_demo"+                            ]++                withCString query \sql ->+                    withResultCString conn sql \resPtr -> do+                        c_duckdb_row_count resPtr >>= (@?= 1)+                        c_duckdb_value_int8 resPtr 0 0 >>= (@?= int8Val)+                        c_duckdb_value_int16 resPtr 1 0 >>= (@?= int16Val)+                        c_duckdb_value_int64 resPtr 2 0 >>= (@?= int64Val)+                        alloca \ptr -> do+                            c_duckdb_value_hugeint resPtr 3 0 ptr+                            peek ptr >>= (@?= hugeVal)+                        c_duckdb_value_uint8 resPtr 4 0 >>= (@?= uint8Val)+                        c_duckdb_value_uint16 resPtr 5 0 >>= (@?= uint16Val)+                        c_duckdb_value_uint32 resPtr 6 0 >>= (@?= uint32Val)+                        c_duckdb_value_uint64 resPtr 7 0 >>= (@?= uint64Val)+                        alloca \ptr -> do+                            c_duckdb_value_uhugeint resPtr 8 0 ptr+                            peek ptr >>= (@?= uhugeVal)+                        fv <- c_duckdb_value_float resPtr 9 0+                        realToFrac fv @?= floatVal+                        dv <- c_duckdb_value_double resPtr 10 0+                        realToFrac dv @?= doubleVal++appenderTemporalTypes :: TestTree+appenderTemporalTypes =+    testCase "append temporal values" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement+                    conn+                    "CREATE TABLE temporal_demo(d DATE, t TIME, ts TIMESTAMP, iv INTERVAL)"++                let dateStruct = DuckDBDateStruct 2024 3 31+                dateVal <- with dateStruct c_duckdb_to_date++                let timeStruct = DuckDBTimeStruct 12 34 56 987654+                timeVal <- with timeStruct c_duckdb_to_time++                let timestampStruct = DuckDBTimestampStruct dateStruct timeStruct+                timestampVal <- with timestampStruct c_duckdb_to_timestamp++                let intervalVal = DuckDBInterval 5 12 3456789++                withTableAppender conn "temporal_demo" \app -> do+                    c_duckdb_appender_begin_row app >>= (@?= DuckDBSuccess)+                    c_duckdb_append_date app dateVal >>= (@?= DuckDBSuccess)+                    c_duckdb_append_time app timeVal >>= (@?= DuckDBSuccess)+                    c_duckdb_append_timestamp app timestampVal >>= (@?= DuckDBSuccess)+                    with intervalVal (c_duckdb_append_interval app >=> (@?= DuckDBSuccess))+                    c_duckdb_appender_end_row app >>= (@?= DuckDBSuccess)++                    c_duckdb_appender_flush app >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_close app >>= (@?= DuckDBSuccess)++                withCString "SELECT d, t, ts, iv FROM temporal_demo" \sql ->+                    withResultCString conn sql \resPtr -> do+                        c_duckdb_row_count resPtr >>= (@?= 1)++                        fetchedDate <- c_duckdb_value_date resPtr 0 0+                        alloca \structPtr -> do+                            c_duckdb_from_date fetchedDate structPtr+                            peek structPtr >>= (@?= dateStruct)++                        fetchedTime <- c_duckdb_value_time resPtr 1 0+                        alloca \structPtr -> do+                            c_duckdb_from_time fetchedTime structPtr+                            peek structPtr >>= (@?= timeStruct)++                        fetchedTimestamp <- c_duckdb_value_timestamp resPtr 2 0+                        alloca \structPtr -> do+                            c_duckdb_from_timestamp fetchedTimestamp structPtr+                            peek structPtr >>= (@?= timestampStruct)++                        alloca \intervalPtr -> do+                            c_duckdb_value_interval resPtr 3 0 intervalPtr+                            peek intervalPtr >>= (@?= intervalVal)++appenderStringAndBlob :: TestTree+appenderStringAndBlob =+    testCase "append bounded varchar and blob values" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement+                    conn+                    "CREATE TABLE string_blob_demo(text_fragment VARCHAR, payload BLOB)"++                let blobBytes = [0xde, 0xad, 0xbe, 0xef] :: [Word8]++                withTableAppender conn "string_blob_demo" \app -> do+                    c_duckdb_appender_begin_row app >>= (@?= DuckDBSuccess)+                    withCStringLen "hello world" \(ptr, _len) -> do+                        c_duckdb_append_varchar_length app ptr (fromIntegral @Integer 5) >>= (@?= DuckDBSuccess)+                    withArray blobBytes \ptr -> do+                        c_duckdb_append_blob app (castPtr ptr) (fromIntegral (length blobBytes)) >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_end_row app >>= (@?= DuckDBSuccess)++                    c_duckdb_appender_flush app >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_close app >>= (@?= DuckDBSuccess)++                withCString "SELECT text_fragment, payload FROM string_blob_demo" \sql ->+                    withResultCString conn sql \resPtr -> do+                        c_duckdb_row_count resPtr >>= (@?= 1)+                        fetchString resPtr 0 0 >>= (@?= "hello")+                        fetchBlob resPtr 1 0 >>= (@?= blobBytes)++appenderChunkDefaults :: TestTree+appenderChunkDefaults =+    testCase "populate chunk entries using column defaults" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement+                    conn+                    "CREATE TABLE chunk_defaults_demo(val INTEGER DEFAULT 99, note VARCHAR DEFAULT 'fallback')"++                withTableAppender conn "chunk_defaults_demo" \app -> do+                    c_duckdb_appender_column_count app >>= (@?= 2)++                    withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+                        withLogicalType (c_duckdb_create_logical_type DuckDBTypeVarchar) \textType ->+                            withArray [intType, textType] \typeArray ->+                                withDataChunk (c_duckdb_create_data_chunk typeArray 2) \chunk -> do+                                    intVec <- c_duckdb_data_chunk_get_vector chunk 0+                                    fillIntVector intVec [10]++                                    textVec <- c_duckdb_data_chunk_get_vector chunk 1+                                    assignStrings textVec ["explicit"]++                                    c_duckdb_append_default_to_chunk app chunk 0 1 >>= (@?= DuckDBSuccess)+                                    c_duckdb_append_default_to_chunk app chunk 1 1 >>= (@?= DuckDBSuccess)++                                    c_duckdb_data_chunk_set_size chunk 2+                                    c_duckdb_append_data_chunk app chunk >>= (@?= DuckDBSuccess)++                    c_duckdb_appender_flush app >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_close app >>= (@?= DuckDBSuccess)++                withCString "SELECT val, note FROM chunk_defaults_demo ORDER BY rowid" \sql ->+                    withResultCString conn sql \resPtr -> do+                        c_duckdb_row_count resPtr >>= (@?= 2)+                        c_duckdb_value_int32 resPtr 0 0 >>= (@?= 10)+                        fetchString resPtr 1 0 >>= (@?= "explicit")+                        c_duckdb_value_int32 resPtr 0 1 >>= (@?= 99)+                        fetchString resPtr 1 1 >>= (@?= "fallback")++appenderErrorDataInspection :: TestTree+appenderErrorDataInspection =+    testCase "retrieve appender error payloads" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement+                    conn+                    "CREATE TABLE error_demo(val INTEGER CHECK (val > 0))"++                withTableAppender conn "error_demo" \app -> do+                    c_duckdb_appender_begin_row app >>= (@?= DuckDBSuccess)+                    c_duckdb_append_int32 app (-1) >>= (@?= DuckDBSuccess)+                    c_duckdb_appender_end_row app >>= (@?= DuckDBSuccess)++                    flushState <- c_duckdb_appender_flush app+                    flushState @?= DuckDBError++                    errData <- c_duckdb_appender_error_data app+                    CBool hasError <- c_duckdb_error_data_has_error errData+                    assertBool "error data indicates failure" (hasError /= 0)++                    errType <- c_duckdb_error_data_error_type errData+                    errType @?= DuckDBErrorConstraint++                    errMsgPtr <- c_duckdb_error_data_message errData+                    errMsg <- peekCString errMsgPtr+                    assertBool "constraint violation message mentions CHECK" ("CHECK" `isInfixOf` errMsg)++                    destroyErrorData errData++                withCString "SELECT COUNT(*) FROM error_demo" \sql ->+                    withResultCString conn sql \resPtr -> do+                        c_duckdb_row_count resPtr >>= (@?= 1)+                        c_duckdb_value_int64 resPtr 0 0 >>= (@?= 0)++-- Helpers -------------------------------------------------------------------++runStatement :: DuckDBConnection -> String -> IO ()+runStatement conn sql =+    withCString sql \sqlPtr ->+        alloca \resPtr -> do+            state <- c_duckdb_query conn sqlPtr resPtr+            if state == DuckDBSuccess+                then c_duckdb_destroy_result resPtr+                else do+                    errPtr <- c_duckdb_result_error resPtr+                    errMsg <-+                        if errPtr == nullPtr+                            then pure "unknown error"+                            else peekCString errPtr+                    c_duckdb_destroy_result resPtr+                    assertFailure ("duckdb_query failed: " <> errMsg)++withTableAppender :: DuckDBConnection -> String -> (DuckDBAppender -> IO a) -> IO a+withTableAppender conn tableName action =+    withCString tableName \tablePtr ->+        withAppenderAcquire+            (c_duckdb_appender_create conn nullPtr tablePtr)+            action++withTableAppenderExt :: DuckDBConnection -> String -> (DuckDBAppender -> IO a) -> IO a+withTableAppenderExt conn tableName action =+    withCString tableName \tablePtr ->+        withAppenderAcquire+            (c_duckdb_appender_create_ext conn nullPtr nullPtr tablePtr)+            action++withQueryAppender :: DuckDBConnection -> String -> [DuckDBLogicalType] -> (DuckDBAppender -> IO a) -> IO a+withQueryAppender conn query types action =+    withCString query \queryPtr ->+        withArray types \typeArray ->+            withAppenderAcquire+                (c_duckdb_appender_create_query conn queryPtr (fromIntegral (length types)) typeArray nullPtr nullPtr)+                action++withAppenderAcquire :: (Ptr DuckDBAppender -> IO DuckDBState) -> (DuckDBAppender -> IO a) -> IO a+withAppenderAcquire acquire action =+    alloca \appPtr -> do+        state <- acquire appPtr+        state @?= DuckDBSuccess+        app <- peek appPtr+        let release = do+                destroyState <- c_duckdb_appender_destroy appPtr+                assertBool "destroy returns success or error" (destroyState == DuckDBSuccess || destroyState == DuckDBError)+        action app `finally` release++withDataChunk :: IO DuckDBDataChunk -> (DuckDBDataChunk -> IO a) -> IO a+withDataChunk acquire = bracket acquire destroyChunk+  where+    destroyChunk chunk = alloca \ptr -> poke ptr chunk >> c_duckdb_destroy_data_chunk ptr++fillIntVector :: DuckDBVector -> [Int32] -> IO ()+fillIntVector vec values = do+    dataPtrRaw <- c_duckdb_vector_get_data vec+    let dataPtr = castPtr dataPtrRaw :: Ptr Int32+    forM_ (zip [0 ..] values) (uncurry (pokeElemOff dataPtr))++assignStrings :: DuckDBVector -> [String] -> IO ()+assignStrings vec values =+    forM_ (zip [0 ..] values) \(idx, val) ->+        withCString val $ \str ->+            c_duckdb_vector_assign_string_element vec (fromIntegral @Integer idx) str++checkColumnType :: DuckDBAppender -> DuckDBIdx -> DuckDBType -> IO ()+checkColumnType app idx expected =+    do+        logicalType <- c_duckdb_appender_column_type app idx+        withLogicalType (pure logicalType) (c_duckdb_get_type_id >=> (@?= expected))++fetchString :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO String+fetchString resPtr col row = do+    strPtr <- c_duckdb_value_varchar resPtr col row+    text <- peekCString strPtr+    c_duckdb_free (castPtr strPtr)+    pure text++fetchBlob :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO [Word8]+fetchBlob resPtr col row =+    alloca \blobPtr -> do+        c_duckdb_value_blob resPtr col row blobPtr+        DuckDBBlob dat len <- peek blobPtr+        let dataPtr = castPtr dat :: Ptr Word8+        bytes <- peekArray (fromIntegral len) dataPtr+        c_duckdb_free dat+        pure bytes++fetchBool :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO Bool+fetchBool resPtr col row = do+    CBool val <- c_duckdb_value_boolean resPtr col row+    pure (val /= 0)
+ test/ArrowInterfaceDeprecatedTests.hs view
@@ -0,0 +1,489 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE ForeignFunctionInterface #-}++module ArrowInterfaceDeprecatedTests (tests) where++import Control.Exception (bracket, finally)+import Control.Monad (unless, when)+import Data.Int (Int32, Int64)+import Data.List (isInfixOf)+import Data.Maybe (isNothing)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, peekCStringLen, withCString)+import Foreign.C.Types (CChar)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (FunPtr, Ptr, castPtr, nullFunPtr, nullPtr, plusPtr)+import Foreign.Storable (peek, peekElemOff, poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (withConnection, withDatabase, withResult)++tests :: TestTree+tests =+    testGroup+        "Deprecated Arrow Interface"+        [ queryArrowExposesSchemaAndArrays+        , queryArrowReportsErrors+        , preparedArrowSchemaMatchesStatement+        , executePreparedArrowProducesRows+        , resultArrowArrayMirrorsChunk+        , arrowRowsChangedReflectsMutations+        , arrowArrayScanRegistersView+        , arrowStreamScanRegistersView+        , arrowPointerHelpersClearInternalState+        ]++-- basic query ---------------------------------------------------------------++queryArrowExposesSchemaAndArrays :: TestTree+queryArrowExposesSchemaAndArrays =+    testCase "query_arrow exposes schema metadata and arrays" $+        withDatabase \db ->+            withConnection db \conn ->+                withSuccessfulArrow conn "SELECT 1::INTEGER AS id, 'duck'::VARCHAR AS label" \arrow -> do+                    columnCount <- c_duckdb_arrow_column_count arrow+                    columnCount @?= 2++                    rowCount <- c_duckdb_arrow_row_count arrow+                    rowCount @?= 1++                    rowsChanged <- c_duckdb_arrow_rows_changed arrow+                    rowsChanged @?= 0++                    errPtr <- c_duckdb_query_arrow_error arrow+                    when (errPtr /= nullPtr) $ do+                        errMsg <- peekCString errPtr+                        errMsg @?= ""++-- schema/array access validated in dedicated scan test++-- error handling ------------------------------------------------------------++queryArrowReportsErrors :: TestTree+queryArrowReportsErrors =+    testCase "query_arrow surfaces execution errors" $+        withDatabase \db ->+            withConnection db \conn ->+                withCString "SELECT * FROM missing_table" \querySql ->+                    alloca \arrowPtr -> do+                        poke arrowPtr nullPtr+                        state <- c_duckdb_query_arrow conn querySql arrowPtr+                        state @?= DuckDBError++                        arrow <- peek arrowPtr+                        assertBool "arrow result should still be allocated on error" (arrow /= nullPtr)++                        errPtr <- c_duckdb_query_arrow_error arrow+                        assertBool "error message should be present" (errPtr /= nullPtr)+                        errMsg <- peekCString errPtr+                        assertBool "error message should mention missing_table" ("missing_table" `isInfixOf` errMsg)++                        c_duckdb_destroy_arrow arrowPtr++-- prepared statements -------------------------------------------------------++preparedArrowSchemaMatchesStatement :: TestTree+preparedArrowSchemaMatchesStatement =+    testCase "prepared_arrow_schema reflects projected columns" $+        withDatabase \db ->+            withConnection db \conn ->+                withPrepared conn "SELECT id, label FROM (VALUES (1, 'a')) AS t(id, label)" \stmt -> do+                    alloca \schemaOut -> do+                        poke schemaOut nullPtr+                        stSchema <- c_duckdb_prepared_arrow_schema stmt schemaOut+                        case stSchema of+                            DuckDBSuccess -> do+                                schemaWrapper <- peek schemaOut+                                assertBool "prepared arrow schema pointer should not be null" (schemaWrapper /= nullPtr)+                            DuckDBError -> do+                                schemaWrapper <- peek schemaOut+                                schemaWrapper @?= nullPtr+                                errPtr <- c_duckdb_prepare_error stmt+                                when (errPtr /= nullPtr) $ do+                                    errMsg <- peekCString errPtr+                                    errMsg @?= ""++executePreparedArrowProducesRows :: TestTree+executePreparedArrowProducesRows =+    testCase "execute_prepared_arrow materialises a result set" $+        withDatabase \db ->+            withConnection db \conn ->+                withPrepared conn "SELECT ?::INTEGER + 5 AS computed" \stmt -> do+                    c_duckdb_bind_int32 stmt 1 (5 :: Int32) >>= (@?= DuckDBSuccess)++                    withPreparedArrow stmt \arrow -> do+                        rowCount <- c_duckdb_arrow_row_count arrow+                        rowCount @?= 1++                        colCount <- c_duckdb_arrow_column_count arrow+                        colCount @?= 1++-- result conversion ---------------------------------------------------------++resultArrowArrayMirrorsChunk :: TestTree+resultArrowArrayMirrorsChunk =+    -- NOTE: duckdb_result_arrow_array writes release callbacks into the+    -- provided ArrowArray storage. Always zero-initialise the struct+    -- (see zeroArrowArray) and honour the release function before the+    -- stack memory goes out of scope.+    testCase "result_arrow_array converts materialised chunks to Arrow arrays" $+        withDatabase \db ->+            withConnection db \conn -> do+                execStatement conn "CREATE TABLE arrow_chunks(id BIGINT, label VARCHAR);"+                execStatement conn "INSERT INTO arrow_chunks VALUES (10, 'ten'), (11, 'eleven');"++                withResult conn "SELECT id, label FROM arrow_chunks ORDER BY id" \resPtr -> do+                    chunk <- c_duckdb_result_get_chunk resPtr 0+                    assertBool "fetch_chunk returned a null chunk" (chunk /= nullPtr)+                    chunkSize <- c_duckdb_data_chunk_get_size chunk+                    chunkCols <- c_duckdb_data_chunk_get_column_count chunk++                    alloca \arrowArrayPtr -> do+                        poke arrowArrayPtr zeroArrowArray+                        let duckArray :: DuckDBArrowArray+                            duckArray = castPtr arrowArrayPtr++                        alloca \arrayOut -> do+                            poke arrayOut duckArray+                            c_duckdb_result_arrow_array resPtr chunk arrayOut++                            array <- peek arrowArrayPtr++                            arrowArrayLength array @?= fromIntegral chunkSize+                            arrowArrayChildCount array @?= fromIntegral chunkCols++                            validateChunkChildren array++                            releaseArrowArray arrowArrayPtr++                    destroyChunk chunk++-- rows changed --------------------------------------------------------------++arrowRowsChangedReflectsMutations :: TestTree+arrowRowsChangedReflectsMutations =+    testCase "arrow_rows_changed reports mutation counts" $+        withDatabase \db ->+            withConnection db \conn -> do+                execStatement conn "CREATE TABLE arrow_changes(val INTEGER);"++                withSuccessfulArrow conn "INSERT INTO arrow_changes VALUES (1), (2), (3)" \arrow -> do+                    rowCount <- c_duckdb_arrow_row_count arrow+                    assertBool "modification result should not report negative rows" (rowCount >= 0)++                    changed <- c_duckdb_arrow_rows_changed arrow+                    assertBool "rows_changed should report positive count" (changed > 0)++-- arrow scans ----------------------------------------------------------------++arrowArrayScanRegistersView :: TestTree+arrowArrayScanRegistersView =+    -- NOTE: Both the schema and array buffers must be initialised to+    -- zeroed Arrow structures before calling the query helpers. DuckDB+    -- fills in release callbacks and expects us to invoke+    -- duckdb_destroy_arrow_stream on the out stream.+    testCase "arrow_array_scan registers a view and yields a release stream" $+        withDatabase \db ->+            withConnection db \conn -> do+                execStatement conn "CREATE TABLE arrow_scan_source(i BIGINT, label VARCHAR);"+                execStatement conn "INSERT INTO arrow_scan_source VALUES (5, 'five'), (6, 'six');"++                withSuccessfulArrow conn "SELECT i, label FROM arrow_scan_source ORDER BY i" \arrow -> do+                    alloca \schemaStorage -> do+                        poke schemaStorage zeroArrowSchema+                        let schemaHandle = castPtr schemaStorage :: DuckDBArrowSchema+                        alloca \schemaOut -> do+                            poke schemaOut schemaHandle+                            schemaState <- c_duckdb_query_arrow_schema arrow schemaOut+                            schemaState @?= DuckDBSuccess++                            alloca \arrayStorage -> do+                                poke arrayStorage zeroArrowArray+                                let arrayHandle = castPtr arrayStorage :: DuckDBArrowArray+                                alloca \arrayOut -> do+                                    poke arrayOut arrayHandle+                                    arrayState <- c_duckdb_query_arrow_array arrow arrayOut+                                    arrayState @?= DuckDBSuccess++                                    withCString "arrow_array_view" \viewName ->+                                        alloca \streamOut -> do+                                            poke streamOut nullPtr+                                            scanState <- c_duckdb_arrow_array_scan conn viewName schemaHandle arrayHandle streamOut+                                            scanState @?= DuckDBSuccess++                                            streamWrapper <- peek streamOut+                                            assertBool "arrow_array_scan returned a null stream handle" (streamWrapper /= nullPtr)++                                            withResult conn "SELECT COUNT(*) FROM arrow_array_view" \resPtr -> do+                                                count <- c_duckdb_value_int64 resPtr 0 0+                                                count @?= 2++                                            c_duckdb_destroy_arrow_stream streamOut+                                    releaseArrowArray arrayStorage+                        releaseArrowSchema schemaStorage++arrowStreamScanRegistersView :: TestTree+arrowStreamScanRegistersView =+    -- NOTE: duckdb_arrow_array_scan returns DuckDBError if the target+    -- view name already exists (even if it is a table). Use a fresh+    -- view name that will be dropped implicitly when the connection+    -- closes.+    testCase "arrow_scan registers a view from an Arrow stream" $+        withDatabase \db ->+            withConnection db \conn -> do+                execStatement conn "CREATE TABLE arrow_stream_source(i BIGINT, label VARCHAR);"+                execStatement conn "INSERT INTO arrow_stream_source VALUES (7, 'seven'), (8, 'eight');"++                withSuccessfulArrow conn "SELECT i, label FROM arrow_stream_source ORDER BY i" \arrow -> do+                    alloca \schemaStorage ->+                        finally+                            ( do+                                poke schemaStorage zeroArrowSchema+                                let schemaHandle = castPtr schemaStorage :: DuckDBArrowSchema+                                alloca \schemaOut -> do+                                    poke schemaOut schemaHandle+                                    schemaState <- c_duckdb_query_arrow_schema arrow schemaOut+                                    schemaState @?= DuckDBSuccess++                                    alloca \arrayStorage ->+                                        finally+                                            ( do+                                                poke arrayStorage zeroArrowArray+                                                let arrayHandle = castPtr arrayStorage :: DuckDBArrowArray+                                                alloca \arrayOut -> do+                                                    poke arrayOut arrayHandle+                                                    arrayState <- c_duckdb_query_arrow_array arrow arrayOut+                                                    arrayState @?= DuckDBSuccess++                                                    withCString "arrow_stream_array_view" \sourceView ->+                                                        alloca \streamOut -> do+                                                            poke streamOut nullPtr+                                                            arrayScanState <- c_duckdb_arrow_array_scan conn sourceView schemaHandle arrayHandle streamOut+                                                            arrayScanState @?= DuckDBSuccess++                                                            streamHandle <- peek streamOut+                                                            assertBool "arrow_array_scan returned a null stream" (streamHandle /= nullPtr)++                                                            withCString "arrow_stream_view" \streamView -> do+                                                                streamScanState <- c_duckdb_arrow_scan conn streamView streamHandle+                                                                streamScanState @?= DuckDBSuccess++                                                            withResult conn "SELECT COUNT(*) FROM arrow_stream_view" \resPtr -> do+                                                                count <- c_duckdb_value_int64 resPtr 0 0+                                                                count @?= 2++                                                            c_duckdb_destroy_arrow_stream streamOut+                                            )+                                            (releaseArrowArray arrayStorage)+                            )+                            (releaseArrowSchema schemaStorage)++arrowPointerHelpersClearInternalState :: TestTree+arrowPointerHelpersClearInternalState =+    -- NOTE: The deprecated Arrow handles are thin wrappers whose+    -- internal_ptr fields can be inspected/reset via the exported+    -- duckdbArrow{Schema,Array,Stream}{Internal,Clear} helpers. We avoid+    -- direct foreign imports here so future changes to the shims only+    -- require updates in the Arrow module.+    testCase "arrow pointer helper functions read and clear internal_ptr fields" $+        alloca \schemaFieldPtr ->+            alloca \arrayFieldPtr ->+                alloca \streamFieldPtr -> do+                    let schemaField = schemaFieldPtr :: Ptr (Ptr ())+                        schemaHandle = castPtr schemaField :: DuckDBArrowSchema+                        schemaSentinel = ArrowSchemaPtr (castPtr schemaField)+                    poke schemaField (castPtr (unArrowSchemaPtr schemaSentinel))+                    schemaPtr <- duckdbArrowSchemaInternal schemaHandle+                    fmap unArrowSchemaPtr schemaPtr @?= Just (unArrowSchemaPtr schemaSentinel)++                    duckdbArrowSchemaClear schemaHandle+                    schemaCleared <- duckdbArrowSchemaInternal schemaHandle+                    assertBool "schema internal_ptr should be cleared" (isNothing schemaCleared)++                    let arrayField = arrayFieldPtr :: Ptr (Ptr ())+                        arrayHandle = castPtr arrayField :: DuckDBArrowArray+                        arraySentinel = ArrowArrayPtr (castPtr arrayField)+                    poke arrayField (castPtr (unArrowArrayPtr arraySentinel))+                    arrayPtr <- duckdbArrowArrayInternal arrayHandle+                    fmap unArrowArrayPtr arrayPtr @?= Just (unArrowArrayPtr arraySentinel)++                    duckdbArrowArrayClear arrayHandle+                    arrayCleared <- duckdbArrowArrayInternal arrayHandle+                    assertBool "array internal_ptr should be cleared" (isNothing arrayCleared)++                    let streamField = streamFieldPtr :: Ptr (Ptr ())+                        streamHandle = castPtr streamField :: DuckDBArrowStream+                        streamSentinel = ArrowStreamPtr (castPtr streamField)+                    poke streamField (castPtr (unArrowStreamPtr streamSentinel))+                    streamPtr <- duckdbArrowStreamInternal streamHandle+                    fmap unArrowStreamPtr streamPtr @?= Just (unArrowStreamPtr streamSentinel)++                    duckdbArrowStreamClear streamHandle+                    streamCleared <- duckdbArrowStreamInternal streamHandle+                    assertBool "stream internal_ptr should be cleared" (isNothing streamCleared)++zeroArrowArray :: ArrowArray+zeroArrowArray =+    ArrowArray+        { arrowArrayLength = 0+        , arrowArrayNullCount = 0+        , arrowArrayOffset = 0+        , arrowArrayBufferCount = 0+        , arrowArrayChildCount = 0+        , arrowArrayBuffers = nullPtr+        , arrowArrayChildren = nullPtr+        , arrowArrayDictionary = nullPtr+        , arrowArrayRelease = nullFunPtr+        , arrowArrayPrivateData = nullPtr+        }++zeroArrowSchema :: ArrowSchema+zeroArrowSchema =+    ArrowSchema+        { arrowSchemaFormat = nullPtr+        , arrowSchemaName = nullPtr+        , arrowSchemaMetadata = nullPtr+        , arrowSchemaFlags = 0+        , arrowSchemaChildCount = 0+        , arrowSchemaChildren = nullPtr+        , arrowSchemaDictionary = nullPtr+        , arrowSchemaRelease = nullFunPtr+        , arrowSchemaPrivateData = nullPtr+        }++validateChunkChildren :: ArrowArray -> IO ()+validateChunkChildren array = do+    let expectedIds = [10, 11] :: [Int64]+        expectedLabels = ["ten", "eleven"]+        childCount = fromIntegral (arrowArrayChildCount array) :: Int+        rowCount = fromIntegral (arrowArrayLength array) :: Int+    childCount @?= 2+    rowCount @?= length expectedIds++    let childrenPtr = arrowArrayChildren array+    assertBool "Arrow array did not expose child arrays" (childrenPtr /= nullPtr)++    let ensurePtr name ptrPred =+            unless ptrPred $+                assertFailure ("Arrow array " ++ name ++ " pointer is null")++    intChildPtr <- peekElemOff childrenPtr 0+    ensurePtr "integer child" (intChildPtr /= nullPtr)+    intChild <- peek intChildPtr+    arrowArrayNullCount intChild @?= 0+    let intBufferCount = fromIntegral (arrowArrayBufferCount intChild) :: Int+    assertBool "Integer child did not expose the expected buffers" (intBufferCount >= 2)++    let intBuffers = arrowArrayBuffers intChild+    ensurePtr "integer buffers" (intBuffers /= nullPtr)+    valueBufferRaw <- peekElemOff intBuffers 1+    ensurePtr "integer value buffer" (valueBufferRaw /= nullPtr)+    let valueBuffer = castPtr valueBufferRaw :: Ptr Int64+    values <- mapM (peekElemOff valueBuffer) [0 .. rowCount - 1]+    values @?= expectedIds++    strChildPtr <- peekElemOff childrenPtr 1+    ensurePtr "varchar child" (strChildPtr /= nullPtr)+    strChild <- peek strChildPtr+    arrowArrayNullCount strChild @?= 0+    let strBufferCount = fromIntegral (arrowArrayBufferCount strChild) :: Int+    assertBool "Varchar child did not expose the expected buffers" (strBufferCount >= 3)++    let strBuffers = arrowArrayBuffers strChild+    ensurePtr "varchar buffers" (strBuffers /= nullPtr)+    offsetsRaw <- peekElemOff strBuffers 1+    dataRaw <- peekElemOff strBuffers 2+    ensurePtr "varchar offsets buffer" (offsetsRaw /= nullPtr)+    ensurePtr "varchar data buffer" (dataRaw /= nullPtr)++    let offsetsPtr = castPtr offsetsRaw :: Ptr Int32+        dataPtr = castPtr dataRaw :: Ptr CChar+    labels <-+        mapM+            ( \idx -> do+                start <- fromIntegral <$> peekElemOff offsetsPtr idx+                end <- fromIntegral <$> peekElemOff offsetsPtr (idx + 1)+                peekCStringLen (dataPtr `plusPtr` start, end - start)+            )+            [0 .. rowCount - 1]+    labels @?= expectedLabels++releaseArrowArray :: Ptr ArrowArray -> IO ()+releaseArrowArray arrayPtr = do+    array <- peek arrayPtr+    let releaseFun = arrowArrayRelease array+    when (releaseFun /= nullFunPtr) $ do+        let release = mkArrowArrayRelease releaseFun+        release arrayPtr++releaseArrowSchema :: Ptr ArrowSchema -> IO ()+releaseArrowSchema schemaPtr = do+    schema <- peek schemaPtr+    let releaseFun = arrowSchemaRelease schema+    when (releaseFun /= nullFunPtr) $ do+        let release = mkArrowSchemaRelease releaseFun+        release schemaPtr++foreign import ccall "dynamic"+    mkArrowArrayRelease :: FunPtr (Ptr ArrowArray -> IO ()) -> Ptr ArrowArray -> IO ()++foreign import ccall "dynamic"+    mkArrowSchemaRelease :: FunPtr (Ptr ArrowSchema -> IO ()) -> Ptr ArrowSchema -> IO ()++withSuccessfulArrow :: DuckDBConnection -> String -> (DuckDBArrow -> IO a) -> IO a+withSuccessfulArrow conn sql action =+    withCString sql \sqlPtr ->+        alloca \arrowPtr ->+            bracket+                ( do+                    poke arrowPtr nullPtr+                    state <- c_duckdb_query_arrow conn sqlPtr arrowPtr+                    state @?= DuckDBSuccess+                    arrow <- peek arrowPtr+                    assertBool "duckdb_query_arrow returned null result" (arrow /= nullPtr)+                    pure arrow+                )+                (\_ -> c_duckdb_destroy_arrow arrowPtr)+                action++withPrepared :: DuckDBConnection -> String -> (DuckDBPreparedStatement -> IO a) -> IO a+withPrepared conn sql action =+    withCString sql \sqlPtr ->+        alloca \stmtPtr ->+            bracket+                ( do+                    state <- c_duckdb_prepare conn sqlPtr stmtPtr+                    state @?= DuckDBSuccess+                    stmt <- peek stmtPtr+                    assertBool "prepare should produce a statement" (stmt /= nullPtr)+                    pure stmt+                )+                (\_ -> c_duckdb_destroy_prepare stmtPtr)+                action++withPreparedArrow :: DuckDBPreparedStatement -> (DuckDBArrow -> IO a) -> IO a+withPreparedArrow stmt action =+    alloca \arrowPtr ->+        bracket+            ( do+                poke arrowPtr nullPtr+                state <- c_duckdb_execute_prepared_arrow stmt arrowPtr+                state @?= DuckDBSuccess+                arrow <- peek arrowPtr+                assertBool "execute_prepared_arrow returned null result" (arrow /= nullPtr)+                pure arrow+            )+            (\_ -> c_duckdb_destroy_arrow arrowPtr)+            action++destroyChunk :: DuckDBDataChunk -> IO ()+destroyChunk chunk =+    alloca \ptr -> poke ptr chunk >> c_duckdb_destroy_data_chunk ptr++execStatement :: DuckDBConnection -> String -> IO ()+execStatement conn sql =+    withCString sql \sqlPtr ->+        alloca \resPtr -> do+            st <- c_duckdb_query conn sqlPtr resPtr+            st @?= DuckDBSuccess+            c_duckdb_destroy_result resPtr
+ test/ArrowInterfaceTest.hs view
@@ -0,0 +1,259 @@+{-# LANGUAGE BlockArguments #-}++module ArrowInterfaceTest (tests) where++import Control.Exception (bracket)+import Control.Monad (when)+import Data.Bits (testBit)+import Data.Int (Int32)+import Database.DuckDB.FFI+import Foreign.C.String (CString, peekCString, withCString)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Marshal.Array (peekArray, withArray)+import Foreign.Marshal.Utils (withMany)+import Foreign.Ptr (FunPtr, Ptr, castPtr, nullFunPtr, nullPtr)+import Foreign.Storable (Storable (..), peek, peekElemOff, poke, pokeElemOff)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Arrow Interface"+        [ arrowSchemaRoundtrip+        , arrowChunkRoundtrip+        ]++arrowSchemaRoundtrip :: TestTree+arrowSchemaRoundtrip =+    testCase "to_arrow_schema exposes children and converts back" $+        withDatabase \db ->+            withConnection db \conn ->+                withArrowOptions conn \arrowOpts ->+                    withLogicalTypes [DuckDBTypeInteger, DuckDBTypeVarchar] \logicalTypes ->+                        withArray logicalTypes \logicalArray ->+                            withColumnNames ["id", "label"] \nameArray ->+                                withArrowSchema \schemaPtr -> do+                                    errData <- c_duckdb_to_arrow_schema arrowOpts logicalArray nameArray (fromIntegral (length logicalTypes)) (castPtr schemaPtr)+                                    assertNoError errData++                                    schema <- peek schemaPtr+                                    formatStr <- peekCString (arrowSchemaFormat schema)+                                    formatStr @?= "+s"+                                    arrowSchemaChildCount schema @?= fromIntegral (length logicalTypes)++                                    let childCount = fromIntegral (arrowSchemaChildCount schema)+                                    let childArrayPtr = arrowSchemaChildren schema+                                    assertBool "children pointer should not be null" (childArrayPtr /= nullPtr)+                                    [fc, sc] <- peekArray childCount childArrayPtr >>= mapM peek+                                    peekCString (arrowSchemaName fc) >>= (@?= "id")+                                    peekCString (arrowSchemaName sc) >>= (@?= "label")+                                    assertBool "schema release pointer should be set" (arrowSchemaRelease schema /= nullFunPtr)++                                    withConvertedSchema conn schemaPtr (const (pure ()))++                                    releaseArrowSchema schemaPtr++arrowChunkRoundtrip :: TestTree+arrowChunkRoundtrip =+    testCase "data_chunk_to_arrow and from_arrow preserve values and nulls" $+        withDatabase \db ->+            withConnection db \conn ->+                withArrowOptions conn \arrowOpts ->+                    withLogicalTypes [DuckDBTypeInteger] \logicalTypes ->+                        withArray logicalTypes \logicalArray ->+                            withColumnNames ["val"] \nameArray ->+                                withArrowSchema \schemaPtr -> do+                                    errSchema <- c_duckdb_to_arrow_schema arrowOpts logicalArray nameArray 1 (castPtr schemaPtr)+                                    assertNoError errSchema++                                    withConvertedSchema conn schemaPtr \convertedSchema ->+                                        do+                                            chunk <- c_duckdb_create_data_chunk logicalArray 1+                                            assertBool "create_data_chunk should return chunk" (chunk /= nullPtr)++                                            withOwnedChunk chunk \ownedChunk -> do+                                                c_duckdb_data_chunk_set_size ownedChunk 2+                                                vector <- c_duckdb_data_chunk_get_vector ownedChunk 0+                                                dataPtr <- c_duckdb_vector_get_data vector+                                                let intPtr = castPtr dataPtr :: Ptr Int32+                                                pokeElemOff intPtr 0 42+                                                pokeElemOff intPtr 1 0++                                                c_duckdb_vector_ensure_validity_writable vector+                                                maskPtr <- c_duckdb_vector_get_validity vector+                                                assertBool "validity mask pointer should not be null" (maskPtr /= nullPtr)+                                                c_duckdb_validity_set_row_valid maskPtr 0+                                                c_duckdb_validity_set_row_invalid maskPtr 1++                                                withArrowArray \arrayPtr ->+                                                    do+                                                        errArray <- c_duckdb_data_chunk_to_arrow arrowOpts ownedChunk (castPtr arrayPtr)+                                                        assertNoError errArray++                                                        array <- peek arrayPtr+                                                        arrowArrayLength array @?= 2+                                                        assertBool "release pointer should not be null before transfer" (arrowArrayRelease array /= nullFunPtr)++                                                        alloca \outChunkPtr -> do+                                                            poke outChunkPtr nullPtr+                                                            errFromArrow <- c_duckdb_data_chunk_from_arrow conn (castPtr arrayPtr) convertedSchema outChunkPtr+                                                            assertNoError errFromArrow+                                                            restoredChunk <- peek outChunkPtr+                                                            assertBool "restored chunk should not be null" (restoredChunk /= nullPtr)++                                                            withOwnedChunk restoredChunk $ \restored -> do+                                                                restoredSize <- c_duckdb_data_chunk_get_size restored+                                                                restoredSize @?= 2++                                                                restoredVector <- c_duckdb_data_chunk_get_vector restored 0+                                                                restoredDataPtr <- c_duckdb_vector_get_data restoredVector+                                                                let restoredIntPtr = castPtr restoredDataPtr :: Ptr Int32+                                                                restoredVal <- peekElemOff restoredIntPtr 0+                                                                restoredVal @?= 42++                                                                restoredMaskPtr <- c_duckdb_vector_get_validity restoredVector+                                                                assertBool "restored validity mask pointer should not be null" (restoredMaskPtr /= nullPtr)+                                                                restoredMaskWord <- peek restoredMaskPtr+                                                                assertBool "first row should be valid" (testBit restoredMaskWord 0)+                                                                assertBool "second row should be null" (not (testBit restoredMaskWord 1))++                                                            arrayAfter <- peek arrayPtr+                                                            arrowArrayRelease arrayAfter @?= nullFunPtr++                                    releaseArrowSchema schemaPtr++withArrowOptions :: DuckDBConnection -> (DuckDBArrowOptions -> IO a) -> IO a+withArrowOptions conn action =+    alloca \optsPtr -> do+        let acquire = do+                poke optsPtr nullPtr+                c_duckdb_connection_get_arrow_options conn optsPtr+                opts <- peek optsPtr+                when (opts == nullPtr) $ assertFailure "duckdb_connection_get_arrow_options returned null"+                pure opts+            release _ = c_duckdb_destroy_arrow_options optsPtr+        bracket acquire release action++withLogicalTypes :: [DuckDBType] -> ([DuckDBLogicalType] -> IO a) -> IO a+withLogicalTypes [] action = action []+withLogicalTypes (t : ts) action =+    bracket (c_duckdb_create_logical_type t) destroyLogicalType \lt ->+        withLogicalTypes ts \rest -> action (lt : rest)++withColumnNames :: [String] -> (Ptr CString -> IO a) -> IO a+withColumnNames names action =+    withMany withCString names $ \cNames -> withArray cNames action++withArrowSchema :: (Ptr ArrowSchema -> IO a) -> IO a+withArrowSchema = withStruct zeroArrowSchema++withArrowArray :: (Ptr ArrowArray -> IO a) -> IO a+withArrowArray action =+    withStruct zeroArrowArray \ptr -> do+        result <- action ptr+        releaseArrowArray ptr+        pure result++withStruct :: (Storable a) => a -> (Ptr a -> IO b) -> IO b+withStruct initial action =+    alloca \ptr -> do+        poke ptr initial+        action ptr++withOwnedChunk :: DuckDBDataChunk -> (DuckDBDataChunk -> IO a) -> IO a+withOwnedChunk chunk = bracket (pure chunk) destroyChunk++withConvertedSchema :: DuckDBConnection -> Ptr ArrowSchema -> (DuckDBArrowConvertedSchema -> IO a) -> IO a+withConvertedSchema conn schemaPtr action =+    alloca \convertedPtr -> do+        poke convertedPtr nullPtr+        err <- c_duckdb_schema_from_arrow conn (castPtr schemaPtr) convertedPtr+        assertNoError err+        converted <- peek convertedPtr+        assertBool "converted schema pointer should not be null" (converted /= nullPtr)+        bracket (pure converted) destroyArrowConvertedSchema action++destroyLogicalType :: DuckDBLogicalType -> IO ()+destroyLogicalType lt =+    alloca \ptr -> do+        poke ptr lt+        c_duckdb_destroy_logical_type ptr++destroyChunk :: DuckDBDataChunk -> IO ()+destroyChunk chunk =+    alloca \ptr -> do+        poke ptr chunk+        c_duckdb_destroy_data_chunk ptr++destroyArrowConvertedSchema :: DuckDBArrowConvertedSchema -> IO ()+destroyArrowConvertedSchema schema =+    alloca \ptr -> do+        poke ptr schema+        c_duckdb_destroy_arrow_converted_schema ptr++destroyErrorData :: DuckDBErrorData -> IO ()+destroyErrorData err =+    alloca \ptr -> do+        poke ptr err+        c_duckdb_destroy_error_data ptr++assertNoError :: DuckDBErrorData -> IO ()+assertNoError err =+    when (err /= nullPtr) $ do+        msgPtr <- c_duckdb_error_data_message err+        msg <- peekCString msgPtr+        destroyErrorData err+        assertFailure ("DuckDB reported error: " <> msg)++releaseArrowSchema :: Ptr ArrowSchema -> IO ()+releaseArrowSchema schemaPtr = do+    schema <- peek schemaPtr+    let releaseFun = arrowSchemaRelease schema+    when (releaseFun /= nullFunPtr) $ do+        let release = mkArrowSchemaRelease releaseFun+        release schemaPtr++releaseArrowArray :: Ptr ArrowArray -> IO ()+releaseArrowArray arrayPtr = do+    array <- peek arrayPtr+    let releaseFun = arrowArrayRelease array+    when (releaseFun /= nullFunPtr) $ do+        let release = mkArrowArrayRelease releaseFun+        release arrayPtr++zeroArrowSchema :: ArrowSchema+zeroArrowSchema =+    ArrowSchema+        { arrowSchemaFormat = nullPtr+        , arrowSchemaName = nullPtr+        , arrowSchemaMetadata = nullPtr+        , arrowSchemaFlags = 0+        , arrowSchemaChildCount = 0+        , arrowSchemaChildren = nullPtr+        , arrowSchemaDictionary = nullPtr+        , arrowSchemaRelease = nullFunPtr+        , arrowSchemaPrivateData = nullPtr+        }++zeroArrowArray :: ArrowArray+zeroArrowArray =+    ArrowArray+        { arrowArrayLength = 0+        , arrowArrayNullCount = 0+        , arrowArrayOffset = 0+        , arrowArrayBufferCount = 0+        , arrowArrayChildCount = 0+        , arrowArrayBuffers = nullPtr+        , arrowArrayChildren = nullPtr+        , arrowArrayDictionary = nullPtr+        , arrowArrayRelease = nullFunPtr+        , arrowArrayPrivateData = nullPtr+        }++foreign import ccall "dynamic"+    mkArrowSchemaRelease :: FunPtr (Ptr ArrowSchema -> IO ()) -> Ptr ArrowSchema -> IO ()++foreign import ccall "dynamic"+    mkArrowArrayRelease :: FunPtr (Ptr ArrowArray -> IO ()) -> Ptr ArrowArray -> IO ()
+ test/BindValuesTest.hs view
@@ -0,0 +1,275 @@+{-# LANGUAGE BlockArguments #-}++module BindValuesTest (tests) where++import Control.Monad (when)+import Data.Int (Int16, Int32, Int64, Int8)+import Data.List (intercalate)+import Data.Time.Calendar (diffDays, fromGregorian)+import Data.Word (Word16, Word32, Word64, Word8)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, peekCStringLen, withCString)+import Foreign.C.Types (CBool (..), CDouble (..), CFloat (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Marshal.Array (peekArray, withArray)+import Foreign.Ptr (Ptr, castPtr, nullPtr)+import Foreign.Storable (peek, poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Bind Values"+        [bindValuesRoundtrip]++bindValuesRoundtrip :: TestTree+bindValuesRoundtrip =+    testCase "bind every supported value type" $+        withDatabase \db ->+            withConnection db \conn -> do+                withCString createSQL \ddl ->+                    alloca \resPtr -> do+                        st <- c_duckdb_query conn ddl resPtr+                        st @?= DuckDBSuccess+                        c_duckdb_destroy_result resPtr++                -- values used for binding and verification+                let boolValue = CBool 1+                    tinyValue = (-5 :: Int8)+                    smallValue = (-300 :: Int16)+                    intValue = (-4000000 :: Int32)+                    bigValue = (-5000000000000 :: Int64)+                    u8Value = 200 :: Word8+                    u16Value = 60000 :: Word16+                    u32Value = 4000000000 :: Word32+                    u64Value = maxBound :: Word64+                    floatValue = CFloat 1.5+                    doubleValue = CDouble 2.5+                    dateDay = fromGregorian 2021 7 20+                    epoch = fromGregorian 1970 1 1+                    dateValue = DuckDBDate (fromIntegral (diffDays dateDay epoch))+                    timeMicros :: Integer+                    timeMicros = ((12 * 60 + 34) * 60 + 56) * 1000000+                    timeValue = DuckDBTime (fromIntegral timeMicros)+                    timestampValue = DuckDBTimestamp (fromIntegral (duckDBDateDays dateValue) * 86400000000 + fromIntegral timeMicros)+                    intervalValue = DuckDBInterval{duckDBIntervalMonths = 0, duckDBIntervalDays = 1, duckDBIntervalMicros = 7200000000}+                    decimalValue = DuckDBDecimal{duckDBDecimalWidth = 18, duckDBDecimalScale = 2, duckDBDecimalValue = DuckDBHugeInt{duckDBHugeIntLower = 1234567, duckDBHugeIntUpper = 0}}+                    hugeValue = DuckDBHugeInt{duckDBHugeIntLower = 9223372036854775809, duckDBHugeIntUpper = 0}+                    uhugeValue = DuckDBUHugeInt{duckDBUHugeIntLower = 123456789, duckDBUHugeIntUpper = 1}+                    varcharValue = "varchar binding"+                    varcharLenValue = "varchar length binding"+                    blobBytes :: [Word8]+                    blobBytes = map (fromIntegral . fromEnum) "abc"++                withCString insertSQL \cInsert ->+                    alloca \stmtPtr -> do+                        st <- c_duckdb_prepare conn cInsert stmtPtr+                        stmt <- peek stmtPtr+                        when (st /= DuckDBSuccess) $ do+                            msg <- if stmt == nullPtr then pure "prepare failed" else c_duckdb_prepare_error stmt >>= peekCString+                            assertFailure msg+                        st @?= DuckDBSuccess+                        assertBool "prepared statement should not be null" (stmt /= nullPtr)++                        withCString varcharValue \varcharPtr ->+                            withCString varcharLenValue \varcharLenPtr ->+                                withArray blobBytes \blobPtr ->+                                    alloca \hugePtr ->+                                        alloca \uhugePtr ->+                                            alloca \decimalPtr ->+                                                alloca \intervalPtr ->+                                                    alloca \valuePtr -> do+                                                        poke hugePtr hugeValue+                                                        poke uhugePtr uhugeValue+                                                        poke decimalPtr decimalValue+                                                        poke intervalPtr intervalValue++                                                        duckValue <- c_duckdb_create_bool boolValue+                                                        poke valuePtr duckValue+                                                        c_duckdb_bind_value stmt 1 duckValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_destroy_value valuePtr++                                                        c_duckdb_bind_boolean stmt 2 boolValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_int8 stmt 3 tinyValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_int16 stmt 4 smallValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_int32 stmt 5 intValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_int64 stmt 6 bigValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_hugeint stmt 7 hugePtr >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_uint8 stmt 8 u8Value >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_uint16 stmt 9 u16Value >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_uint32 stmt 10 u32Value >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_uint64 stmt 11 u64Value >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_uhugeint stmt 12 uhugePtr >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_float stmt 13 floatValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_double stmt 14 doubleValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_date stmt 15 dateValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_time stmt 16 timeValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_timestamp stmt 17 timestampValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_timestamp_tz stmt 18 timestampValue >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_interval stmt 19 intervalPtr >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_decimal stmt 20 decimalPtr >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_varchar stmt 21 varcharPtr >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_varchar_length stmt 22 varcharLenPtr (fromIntegral (length varcharLenValue) :: DuckDBIdx) >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_blob stmt 23 (castPtr blobPtr) (fromIntegral (length blobBytes) :: DuckDBIdx) >>= (@?= DuckDBSuccess)+                                                        c_duckdb_bind_null stmt 24 >>= (@?= DuckDBSuccess)++                                                        alloca \execResPtr -> do+                                                            stExec <- c_duckdb_execute_prepared stmt execResPtr+                                                            stExec @?= DuckDBSuccess+                                                            c_duckdb_destroy_result execResPtr++                                                        c_duckdb_destroy_prepare stmtPtr++                -- Validate inserted row+                withCString selectSQL \cSelect ->+                    alloca \resPtr -> do+                        st <- c_duckdb_query conn cSelect resPtr+                        st @?= DuckDBSuccess++                        rowCount <- c_duckdb_row_count resPtr+                        rowCount @?= 1++                        c_duckdb_value_boolean resPtr 0 0 >>= (@?= CBool 1)+                        c_duckdb_value_boolean resPtr 1 0 >>= (@?= CBool 1)+                        c_duckdb_value_int8 resPtr 2 0 >>= (@?= (-5 :: Int8))+                        c_duckdb_value_int16 resPtr 3 0 >>= (@?= (-300 :: Int16))+                        c_duckdb_value_int32 resPtr 4 0 >>= (@?= (-4000000 :: Int32))+                        c_duckdb_value_int64 resPtr 5 0 >>= (@?= (-5000000000000 :: Int64))++                        alloca \hugePtr -> do+                            c_duckdb_value_hugeint resPtr 6 0 hugePtr+                            peek hugePtr >>= (@?= DuckDBHugeInt{duckDBHugeIntLower = 9223372036854775809, duckDBHugeIntUpper = 0})++                        c_duckdb_value_uint8 resPtr 7 0 >>= (@?= (200 :: Word8))+                        c_duckdb_value_uint16 resPtr 8 0 >>= (@?= (60000 :: Word16))+                        c_duckdb_value_uint32 resPtr 9 0 >>= (@?= (4000000000 :: Word32))+                        c_duckdb_value_uint64 resPtr 10 0 >>= (@?= maxBound)++                        alloca \uhugePtr -> do+                            c_duckdb_value_uhugeint resPtr 11 0 uhugePtr+                            peek uhugePtr >>= (@?= DuckDBUHugeInt{duckDBUHugeIntLower = 123456789, duckDBUHugeIntUpper = 1})++                        valFloat <- c_duckdb_value_float resPtr 12 0+                        realToFrac valFloat @?= (1.5 :: Double)+                        valDouble <- c_duckdb_value_double resPtr 13 0+                        realToFrac valDouble @?= (2.5 :: Double)++                        DuckDBDate fetchedDate <- c_duckdb_value_date resPtr 14 0+                        fetchedDate @?= duckDBDateDays dateValue++                        DuckDBTime fetchedTime <- c_duckdb_value_time resPtr 15 0+                        fetchedTime @?= duckDBTimeMicros timeValue++                        DuckDBTimestamp fetchedTs <- c_duckdb_value_timestamp resPtr 16 0+                        fetchedTs @?= duckDBTimestampMicros timestampValue++                        DuckDBTimestamp fetchedTsTz <- c_duckdb_value_timestamp resPtr 17 0+                        let tzDifference = fetchedTsTz - duckDBTimestampMicros timestampValue+                        tzDifference @?= 7200000000++                        alloca \intervalPtr -> do+                            c_duckdb_value_interval resPtr 18 0 intervalPtr+                            peek intervalPtr >>= (@?= intervalValue)++                        alloca \decimalPtr -> do+                            c_duckdb_value_decimal resPtr 19 0 decimalPtr+                            DuckDBDecimal{duckDBDecimalWidth = width, duckDBDecimalScale = scale} <- peek decimalPtr+                            (width, scale) @?= (18, 2)++                        varchar <- c_duckdb_value_varchar resPtr 20 0+                        peekCString varchar >>= (@?= varcharValue)+                        c_duckdb_free (castPtr varchar)++                        alloca \stringPtr -> do+                            c_duckdb_value_string resPtr 21 0 stringPtr+                            DuckDBString{duckDBStringData = datPtr, duckDBStringSize = datSize} <- peek stringPtr+                            peekCStringLen (datPtr, fromIntegral datSize) >>= (@?= varcharLenValue)+                            when (datPtr /= nullPtr) $ c_duckdb_free (castPtr datPtr)++                        alloca \blobPtr -> do+                            c_duckdb_value_blob resPtr 22 0 blobPtr+                            DuckDBBlob{duckDBBlobData = blobDataPtr, duckDBBlobSize = blobSize} <- peek blobPtr+                            peekArray (fromIntegral blobSize) (castPtr blobDataPtr :: Ptr Word8) >>= (@?= blobBytes)+                            c_duckdb_free (castPtr blobDataPtr)++                        c_duckdb_value_is_null resPtr 23 0 >>= (@?= CBool 1)++                        c_duckdb_destroy_result resPtr++                -- Named parameter index lookup (separate statement)+                withCString "SELECT $named_param" \namedSQL ->+                    alloca \stmtPtr -> do+                        st <- c_duckdb_prepare conn namedSQL stmtPtr+                        stmt <- peek stmtPtr+                        when (st /= DuckDBSuccess) $ do+                            errPtr <- c_duckdb_prepare_error stmt+                            msg <- if errPtr == nullPtr then pure "prepare failed" else peekCString errPtr+                            assertFailure msg+                        st @?= DuckDBSuccess+                        assertBool "named statement" (stmt /= nullPtr)++                        alloca \idxPtr -> do+                            stIdx <- withCString "named_param" $ \name -> c_duckdb_bind_parameter_index stmt idxPtr name+                            when (stIdx /= DuckDBSuccess) $ do+                                errPtr <- c_duckdb_prepare_error stmt+                                msg <- if errPtr == nullPtr then pure "bind_parameter_index failed" else peekCString errPtr+                                assertFailure msg+                            stIdx @?= DuckDBSuccess+                            idx <- peek idxPtr+                            idx @?= 1+                            bindState <- c_duckdb_bind_int32 stmt idx 42+                            when (bindState /= DuckDBSuccess) $ do+                                errPtr <- c_duckdb_prepare_error stmt+                                msg <- if errPtr == nullPtr then pure "bind failed" else peekCString errPtr+                                assertFailure msg+                            bindState @?= DuckDBSuccess++                        alloca \execResPtr -> do+                            stExec <- c_duckdb_execute_prepared stmt execResPtr+                            stExec @?= DuckDBSuccess+                            c_duckdb_row_count execResPtr >>= (@?= 1)+                            c_duckdb_value_int32 execResPtr 0 0 >>= (@?= 42)+                            c_duckdb_destroy_result execResPtr++                        c_duckdb_destroy_prepare stmtPtr+  where+    createSQL =+        "CREATE TABLE bind_values ("+            <> "via_value BOOLEAN,"+            <> "bool_col BOOLEAN,"+            <> "tiny_col TINYINT,"+            <> "small_col SMALLINT,"+            <> "int_col INTEGER,"+            <> "big_col BIGINT,"+            <> "huge_col HUGEINT,"+            <> "uint8_col UTINYINT,"+            <> "uint16_col USMALLINT,"+            <> "uint32_col UINTEGER,"+            <> "uint64_col UBIGINT,"+            <> "uhuge_col HUGEINT,"+            <> "float_col FLOAT,"+            <> "double_col DOUBLE,"+            <> "date_col DATE,"+            <> "time_col TIME,"+            <> "ts_col TIMESTAMP,"+            <> "tstz_col TIMESTAMP,"+            <> "interval_col INTERVAL,"+            <> "decimal_col DECIMAL(18,2),"+            <> "varchar_col VARCHAR,"+            <> "varchar_len_col VARCHAR,"+            <> "blob_col BLOB,"+            <> "named_null INTEGER"+            <> ")"++    insertSQL =+        "INSERT INTO bind_values VALUES ("+            <> intercalate ", " (replicate 24 "?")+            <> ")"++    selectSQL = "SELECT * FROM bind_values"++    duckDBDateDays (DuckDBDate d) = d+    duckDBTimeMicros (DuckDBTime t) = t+    duckDBTimestampMicros (DuckDBTimestamp t) = t
+ test/CastFunctionsTest.hs view
@@ -0,0 +1,160 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE NamedFieldPuns #-}++module CastFunctionsTest (tests) where++import Control.Exception (bracket)+import Control.Monad (when)+import Data.IORef (IORef, newIORef, readIORef, writeIORef)+import Data.Int (Int32)+import Data.List (isInfixOf)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString, withCStringLen)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (Ptr, castPtr, freeHaskellFunPtr, nullFunPtr, nullPtr)+import Foreign.StablePtr (StablePtr, castPtrToStablePtr, castStablePtrToPtr, deRefStablePtr, freeStablePtr, newStablePtr)+import Foreign.Storable (peekElemOff, poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (withConnection, withDatabase, withLogicalType, withResult)++data CastHarness = CastHarness+    { chLastMode :: IO DuckDBCastMode+    , chExtraSeen :: IO Bool+    }++tests :: TestTree+tests =+    testGroup+        "Cast Functions"+        [ castFunctionOverridesBuiltin+        , castFunctionReportsErrors+        , castFunctionTryMode+        ]++castFunctionOverridesBuiltin :: TestTree+castFunctionOverridesBuiltin =+    testCase "custom cast overrides builtin conversion" $+        withDatabase \db ->+            withConnection db \conn ->+                withTestCast conn \_ -> do+                    withResult conn "SELECT CAST(v AS VARCHAR) FROM (VALUES (1), (5)) AS t(v)" \resPtr -> do+                        fetchString resPtr 0 0 >>= (@?= "value: 1")+                        fetchString resPtr 0 1 >>= (@?= "value: 5")+                        errPtr <- c_duckdb_result_error resPtr+                        errPtr @?= nullPtr++castFunctionReportsErrors :: TestTree+castFunctionReportsErrors =+    testCase "cast failure surfaces error message" $+        withDatabase \db ->+            withConnection db \conn ->+                withTestCast conn \_ ->+                    withCString "SELECT CAST(v AS VARCHAR) FROM (VALUES (-7)) AS t(v)" \sql ->+                        alloca \resPtr -> do+                            state <- c_duckdb_query conn sql resPtr+                            state @?= DuckDBError+                            errPtr <- c_duckdb_result_error resPtr+                            assertBool "expected error pointer" (errPtr /= nullPtr)+                            errMsg <- peekCString errPtr+                            assertBool "error message should mention negative" ("negative" `isInfixOf` errMsg)+                            c_duckdb_destroy_result resPtr++castFunctionTryMode :: TestTree+castFunctionTryMode =+    testCase "try_cast produces null rows and reports try mode" $+        withDatabase \db ->+            withConnection db \conn ->+                withTestCast conn \CastHarness{chLastMode, chExtraSeen} -> do+                    withResult conn "SELECT TRY_CAST(v AS VARCHAR) FROM (VALUES (2), (-3)) AS t(v)" \resPtr -> do+                        fetchString resPtr 0 0 >>= (@?= "value: 2")+                        c_duckdb_value_is_null resPtr 0 1 >>= (@?= CBool 1)+                        errPtr <- c_duckdb_result_error resPtr+                        errPtr @?= nullPtr+                    chLastMode >>= (@?= DuckDBCastTry)+                    chExtraSeen >>= (@?= True)++-- Harness ------------------------------------------------------------------++withTestCast :: DuckDBConnection -> (CastHarness -> IO a) -> IO a+withTestCast conn action = do+    modeRef <- newIORef DuckDBCastNormal+    extraSeenRef <- newIORef False+    prefixStable <- newStablePtr ("value: " :: String)+    castFunPtr <- mkCastFun (castCallback modeRef extraSeenRef prefixStable)+    result <-+        withCastFunction \castFun ->+            withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \sourceType ->+                withLogicalType (c_duckdb_create_logical_type DuckDBTypeVarchar) \targetType -> do+                    c_duckdb_cast_function_set_source_type castFun sourceType+                    c_duckdb_cast_function_set_target_type castFun targetType+                    c_duckdb_cast_function_set_implicit_cast_cost castFun 0+                    c_duckdb_cast_function_set_extra_info castFun (castStablePtrToPtr prefixStable) nullFunPtr+                    c_duckdb_cast_function_set_function castFun castFunPtr+                    c_duckdb_register_cast_function conn castFun >>= (@?= DuckDBSuccess)+                    action+                        CastHarness+                            { chLastMode = readIORef modeRef+                            , chExtraSeen = readIORef extraSeenRef+                            }+    freeHaskellFunPtr castFunPtr+    freeStablePtr prefixStable+    pure result++castCallback :: IORef DuckDBCastMode -> IORef Bool -> StablePtr String -> DuckDBFunctionInfo -> DuckDBIdx -> DuckDBVector -> DuckDBVector -> IO CBool+castCallback modeRef extraSeenRef prefixStable info count inputVec outputVec = do+    actualPtr <- c_duckdb_cast_function_get_extra_info info+    when (actualPtr == castStablePtrToPtr prefixStable) $ writeIORef extraSeenRef True+    prefix <-+        if actualPtr == nullPtr+            then pure "value: "+            else deRefStablePtr (castPtrToStablePtr actualPtr)+    mode <- c_duckdb_cast_function_get_cast_mode info+    writeIORef modeRef mode+    inputData <- c_duckdb_vector_get_data inputVec+    c_duckdb_vector_ensure_validity_writable outputVec+    let inPtr = castPtr inputData :: Ptr Int32+        rowCount = fromIntegral count :: Int+    success <- processRows prefix mode inPtr outputVec 0 rowCount+    pure (if success then CBool 1 else CBool 0)+  where+    processRows :: String -> DuckDBCastMode -> Ptr Int32 -> DuckDBVector -> Int -> Int -> IO Bool+    processRows prefix mode inPtr outVec idx total+        | idx >= total = pure True+        | otherwise = do+            val <- peekElemOff inPtr idx+            if val < 0+                then withCString "negative values not allowed" \errMsg ->+                    if mode == DuckDBCastTry+                        then do+                            c_duckdb_cast_function_set_row_error info errMsg (fromIntegral idx) outVec+                            processRows prefix mode inPtr outVec (idx + 1) total+                        else do+                            c_duckdb_cast_function_set_error info errMsg+                            pure False+                else do+                    let rendered = prefix ++ show val+                    withCStringLen rendered \(cStr, len) ->+                        c_duckdb_vector_assign_string_element_len outVec (fromIntegral idx) cStr (fromIntegral len)+                    processRows prefix mode inPtr outVec (idx + 1) total++-- Helpers ------------------------------------------------------------------++withCastFunction :: (DuckDBCastFunction -> IO a) -> IO a+withCastFunction = bracket c_duckdb_create_cast_function destroy+  where+    destroy cf = alloca \ptr -> poke ptr cf >> c_duckdb_destroy_cast_function ptr++fetchString :: Ptr DuckDBResult -> DuckDBIdx -> DuckDBIdx -> IO String+fetchString resPtr col row = do+    cStr <- c_duckdb_value_varchar resPtr col row+    value <- peekCString cStr+    c_duckdb_free (castPtr cStr)+    pure value++-- Wrapper constructors ------------------------------------------------------++foreign import ccall "wrapper"+    mkCastFun :: (DuckDBFunctionInfo -> DuckDBIdx -> DuckDBVector -> DuckDBVector -> IO CBool) -> IO DuckDBCastFunctionFun
+ test/ConfigurationTest.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE BlockArguments #-}++module ConfigurationTest (tests) where++import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (nullPtr)+import Foreign.Storable (peek)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))++tests :: TestTree+tests =+    testGroup+        "Configuration"+        [configurationLifecycle]++configurationLifecycle :: TestTree+configurationLifecycle =+    testCase "create, inspect, mutate, and destroy configuration" $+        alloca \configPtr -> do+            state <- c_duckdb_create_config configPtr+            state @?= DuckDBSuccess++            config <- peek configPtr+            assertBool "config pointer should not be null" (config /= nullPtr)++            count <- c_duckdb_config_count+            assertBool "config flag count should be > 0" (count > 0)++            alloca \namePtr -> alloca \descPtr -> do+                flagState <- c_duckdb_get_config_flag 0 namePtr descPtr+                flagState @?= DuckDBSuccess+                flagName <- peek namePtr >>= peekCString+                flagDesc <- peek descPtr >>= peekCString+                assertBool "flag name should not be empty" (not (null flagName))+                assertBool "flag description should not be empty" (not (null flagDesc))++            setState <-+                withCString "access_mode" \flag ->+                    withCString "READ_WRITE" $ \value ->+                        c_duckdb_set_config config flag value+            setState @?= DuckDBSuccess++            c_duckdb_destroy_config configPtr
+ test/DataChunkTest.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE BlockArguments #-}++module DataChunkTest (tests) where++import Control.Exception (bracket)+import Control.Monad (forM_, void, when, (>=>))+import Data.Int (Int32)+import Database.DuckDB.FFI+import Foreign.Marshal.Alloc (alloca)+import Foreign.Marshal.Array (withArray)+import Foreign.Ptr (Ptr, castPtr, nullPtr, plusPtr)+import Foreign.Storable (peek, poke, sizeOf)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (testCase, (@?=))+import Utils (setAllValid, withLogicalType)++-- | Entry point for the Data Chunk focused tests.+tests :: TestTree+tests =+    testGroup+        "Data Chunk Interface"+        [ dataChunkLifecycle+        , dataChunkReset+        ]++-- | Populate an integer chunk and verify the stored sequence.+dataChunkLifecycle :: TestTree+dataChunkLifecycle =+    testCase "create chunk, fill via vector, and read back" $ do+        withIntegerLogicalType \intType ->+            withArray [intType] \typeArray ->+                withDataChunk (c_duckdb_create_data_chunk typeArray 1) \chunk -> do+                    c_duckdb_data_chunk_get_column_count chunk >>= (@?= 1)++                    vec <- c_duckdb_data_chunk_get_vector chunk 0+                    fillVectorWithSequence vec [1 .. 4]+                    c_duckdb_data_chunk_set_size chunk 4++                    verifyChunkContents chunk [1 .. 4]++-- | Resetting should zero the size yet keep buffers reusable.+dataChunkReset :: TestTree+dataChunkReset =+    testCase "reset clears size and keeps vectors reusable" $ do+        withIntegerLogicalType \intType ->+            withArray [intType] \typeArray ->+                withDataChunk (c_duckdb_create_data_chunk typeArray 1) \chunk -> do+                    vec <- c_duckdb_data_chunk_get_vector chunk 0+                    fillVectorWithSequence vec [10, 20, 30]+                    c_duckdb_data_chunk_set_size chunk 3++                    c_duckdb_data_chunk_reset chunk+                    c_duckdb_data_chunk_get_size chunk >>= (@?= 0)++                    fillVectorWithSequence vec [7, 8]+                    c_duckdb_data_chunk_set_size chunk 2+                    verifyChunkContents chunk [7, 8]++-- Helpers -------------------------------------------------------------------++withIntegerLogicalType :: (DuckDBLogicalType -> IO a) -> IO a+withIntegerLogicalType = withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger)++withDataChunk :: IO DuckDBDataChunk -> (DuckDBDataChunk -> IO a) -> IO a+withDataChunk acquire = bracket acquire destroyChunk+  where+    destroyChunk chunk = alloca \ptr -> poke ptr chunk >> c_duckdb_destroy_data_chunk ptr++fillVectorWithSequence :: DuckDBVector -> [Int32] -> IO ()+fillVectorWithSequence vec values = do+    colType <- c_duckdb_vector_get_column_type vec+    withLogicalType (pure colType) (c_duckdb_get_type_id >=> (@?= DuckDBTypeInteger))+    void (c_duckdb_vector_ensure_validity_writable vec)+    dataPtrRaw <- c_duckdb_vector_get_data vec+    let dataPtr = castPtr dataPtrRaw :: Ptr Int32+    validity <- c_duckdb_vector_get_validity vec+    when (validity /= nullPtr) $ setAllValid validity (length values)+    forM_ (zip [0 ..] values) (uncurry (pokeElem dataPtr))++verifyChunkContents :: DuckDBDataChunk -> [Int32] -> IO ()+verifyChunkContents chunk expected = do+    sz <- c_duckdb_data_chunk_get_size chunk+    sz @?= fromIntegral (length expected)+    vec <- c_duckdb_data_chunk_get_vector chunk 0+    dataPtrRaw <- c_duckdb_vector_get_data vec+    let dataPtr = castPtr dataPtrRaw :: Ptr Int32+    forM_ (zip [0 ..] expected) \(idx, val) -> peekElem dataPtr idx >>= (@?= val)++-- validity helpers ----------------------------------------------------------++-- pointer utilities ---------------------------------------------------------++pokeElem :: Ptr Int32 -> Int -> Int32 -> IO ()+pokeElem base idx = poke (base `plusElem` idx)++peekElem :: Ptr Int32 -> Int -> IO Int32+peekElem base idx = peek (base `plusElem` idx)++plusElem :: Ptr a -> Int -> Ptr a+plusElem base idx = base `plusPtr` (idx * sizeOf (undefined :: Int32))
+ test/ErrorDataTest.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE BlockArguments #-}++module ErrorDataTest (tests) where++import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (nullPtr)+import Foreign.Storable (poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))++tests :: TestTree+tests =+    testGroup+        "Error Data"+        [createInspectDestroy]++createInspectDestroy :: TestTree+createInspectDestroy =+    testCase "create error data, inspect properties, destroy" $+        withCString "synthetic failure" \message -> do+            errData <- c_duckdb_create_error_data DuckDBErrorInvalid message+            assertBool "error data pointer should not be null" (errData /= nullPtr)++            errType <- c_duckdb_error_data_error_type errData+            errType @?= DuckDBErrorInvalid++            retrievedMessagePtr <- c_duckdb_error_data_message errData+            retrievedMessage <- peekCString retrievedMessagePtr+            retrievedMessage @?= "synthetic failure"++            hasErr <- c_duckdb_error_data_has_error errData+            hasErr @?= CBool 1++            alloca \ptr -> do+                poke ptr errData+                c_duckdb_destroy_error_data ptr
+ test/ExecutePreparedStatementsTest.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE BlockArguments #-}++module ExecutePreparedStatementsTest (tests) where++import Control.Monad (forM_)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (castPtr, nullPtr)+import Foreign.Storable (peek, poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Execute Prepared Statements"+        [ executePreparedProducesResult+        , executePreparedStreamingProducesChunks+        ]++setupTable :: DuckDBConnection -> IO ()+setupTable conn = do+    let statements =+            [ "CREATE TABLE exec_prepared (id INTEGER, name VARCHAR);"+            , "INSERT INTO exec_prepared VALUES (1, 'alpha'), (2, 'beta');"+            ]+    forM_ statements \sql ->+        withCString sql \cSql ->+            alloca \resPtr -> do+                st <- c_duckdb_query conn cSql resPtr+                st @?= DuckDBSuccess+                c_duckdb_destroy_result resPtr++executePreparedProducesResult :: TestTree+executePreparedProducesResult =+    testCase "execute_prepared returns regular result" $+        withDatabase \db ->+            withConnection db \conn -> do+                setupTable conn++                withCString "SELECT name FROM exec_prepared WHERE id = ?" \querySql ->+                    alloca \stmtPtr -> do+                        st <- c_duckdb_prepare conn querySql stmtPtr+                        st @?= DuckDBSuccess+                        stmt <- peek stmtPtr+                        assertBool "prepared statement should not be null" (stmt /= nullPtr)++                        c_duckdb_bind_int32 stmt 1 2 >>= (@?= DuckDBSuccess)++                        alloca \resPtr -> do+                            execState <- c_duckdb_execute_prepared stmt resPtr+                            execState @?= DuckDBSuccess++                            streamingFlag <- c_duckdb_result_is_streaming resPtr+                            streamingFlag @?= CBool 0++                            rowCount <- c_duckdb_row_count resPtr+                            rowCount @?= 1++                            varcharPtr <- c_duckdb_value_varchar resPtr 0 0+                            peekCString varcharPtr >>= (@?= "beta")+                            c_duckdb_free (castPtr varcharPtr)++                            c_duckdb_destroy_result resPtr++                        c_duckdb_destroy_prepare stmtPtr++executePreparedStreamingProducesChunks :: TestTree+executePreparedStreamingProducesChunks =+    testCase "execute_prepared_streaming produces streaming result" $+        withDatabase \db ->+            withConnection db \conn -> do+                setupTable conn++                withCString "SELECT id, name FROM exec_prepared ORDER BY id" \querySql ->+                    alloca \stmtPtr -> do+                        st <- c_duckdb_prepare conn querySql stmtPtr+                        st @?= DuckDBSuccess+                        stmt <- peek stmtPtr+                        assertBool "streaming prepared statement should not be null" (stmt /= nullPtr)++                        alloca \resPtr -> do+                            execState <- c_duckdb_execute_prepared_streaming stmt resPtr+                            execState @?= DuckDBSuccess++                            streamingFlag <- c_duckdb_result_is_streaming resPtr+                            streamingFlag @?= CBool 1++                            chunk <- c_duckdb_stream_fetch_chunk resPtr+                            assertBool "streaming fetch chunk returns data" (chunk /= nullPtr)++                            chunkSize <- c_duckdb_data_chunk_get_size chunk+                            assertBool "streamed chunk should contain rows" (chunkSize > 0)++                            alloca \chunkPtr -> do+                                poke chunkPtr chunk+                                c_duckdb_destroy_data_chunk chunkPtr++                            c_duckdb_destroy_result resPtr++                        c_duckdb_destroy_prepare stmtPtr
+ test/ExpressionTest.hs view
@@ -0,0 +1,168 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE NamedFieldPuns #-}++module ExpressionTest (tests) where++import Control.Exception (finally)+import Data.Char (toLower)+import Data.IORef (IORef, newIORef, readIORef, writeIORef)+import Data.List (isInfixOf)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (castPtr, freeHaskellFunPtr, nullFunPtr, nullPtr)+import Foreign.Storable (peek, poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (+    destroyDuckValue,+    destroyErrorData,+    destroyLogicalType,+    withConnection,+    withDatabase,+    withLogicalType,+    withResult,+    withScalarFunction,+ )++data ExpressionHarness = ExpressionHarness+    { ehFoldable :: IO (Maybe Bool)+    , ehReturnType :: IO (Maybe DuckDBType)+    , ehFoldedValue :: IO (Maybe String)+    , ehFoldError :: IO (Maybe String)+    }++data ExpressionState = ExpressionState+    { esFoldable :: IORef (Maybe Bool)+    , esReturnType :: IORef (Maybe DuckDBType)+    , esFoldValue :: IORef (Maybe String)+    , esFoldError :: IORef (Maybe String)+    }++tests :: TestTree+tests =+    testGroup+        "Expression Interface"+        [ expressionFoldLiteral+        , expressionNonFoldable+        ]++expressionFoldLiteral :: TestTree+expressionFoldLiteral =+    testCase "folds literal expressions to constant values" $+        withDatabase \db ->+            withConnection db \conn ->+                withExpressionFunction conn "expr_literal" \ExpressionHarness{ehFoldable, ehReturnType, ehFoldedValue, ehFoldError} -> do+                    withResult conn "SELECT expr_literal(42)" \_ -> pure ()+                    ehFoldable >>= (@?= Just True)+                    ehReturnType >>= (@?= Just DuckDBTypeInteger)+                    ehFoldedValue >>= (@?= Just "42")+                    ehFoldError >>= (@?= Nothing)++expressionNonFoldable :: TestTree+expressionNonFoldable =+    testCase "detects non-foldable column references" $+        withDatabase \db ->+            withConnection db \conn ->+                withExpressionFunction conn "expr_non_foldable" \ExpressionHarness{ehFoldable, ehReturnType, ehFoldedValue, ehFoldError} -> do+                    withResult conn "SELECT expr_non_foldable(v) FROM (VALUES (7)) t(v)" \_ -> pure ()+                    ehFoldable >>= (@?= Just False)+                    ehReturnType >>= (@?= Just DuckDBTypeInteger)+                    ehFoldedValue >>= (@?= Nothing)+                    errMsg <- ehFoldError+                    assertBool "expected fold error message" $+                        maybe False (isInfixOf "fold" . map toLower) errMsg++withExpressionFunction :: DuckDBConnection -> String -> (ExpressionHarness -> IO a) -> IO a+withExpressionFunction conn funcName action = do+    foldRef <- newIORef Nothing+    typeRef <- newIORef Nothing+    valueRef <- newIORef Nothing+    errorRef <- newIORef Nothing+    let state = ExpressionState foldRef typeRef valueRef errorRef+    bindPtr <- mkScalarBindFun (expressionBind state)+    execPtr <- mkScalarExecFun expressionExec+    result <-+        withScalarFunction $ \scalarFun -> do+            withCString funcName $ \name -> c_duckdb_scalar_function_set_name scalarFun name+            withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) $ \intType -> do+                c_duckdb_scalar_function_add_parameter scalarFun intType+                c_duckdb_scalar_function_set_return_type scalarFun intType+                c_duckdb_scalar_function_set_bind scalarFun bindPtr+                c_duckdb_scalar_function_set_function scalarFun execPtr+                c_duckdb_scalar_function_set_extra_info scalarFun nullPtr nullFunPtr+                c_duckdb_register_scalar_function conn scalarFun >>= (@?= DuckDBSuccess)+                action+                    ExpressionHarness+                        { ehFoldable = readIORef foldRef+                        , ehReturnType = readIORef typeRef+                        , ehFoldedValue = readIORef valueRef+                        , ehFoldError = readIORef errorRef+                        }+    freeHaskellFunPtr bindPtr+    freeHaskellFunPtr execPtr+    pure result++expressionBind :: ExpressionState -> DuckDBBindInfo -> IO ()+expressionBind ExpressionState{esFoldable, esReturnType, esFoldValue, esFoldError} info = do+    argCount <- c_duckdb_scalar_function_bind_get_argument_count info+    argCount @?= 1+    exprHandle <- c_duckdb_scalar_function_bind_get_argument info 0+    finally+        ( do+            foldableFlag <- c_duckdb_expression_is_foldable exprHandle+            let isFoldable = foldableFlag /= 0+            writeIORef esFoldable (Just isFoldable)+            retType <- c_duckdb_expression_return_type exprHandle+            typeId <- c_duckdb_get_type_id retType+            destroyLogicalType retType+            writeIORef esReturnType (Just typeId)+            alloca \ctxPtr -> do+                c_duckdb_scalar_function_get_client_context info ctxPtr+                ctx <- peek ctxPtr+                alloca \valuePtr -> do+                    poke valuePtr nullPtr+                    errData <- c_duckdb_expression_fold ctx exprHandle valuePtr+                    if errData == nullPtr+                        then do+                            valueHandle <- peek valuePtr+                            if valueHandle == nullPtr+                                then do+                                    writeIORef esFoldValue Nothing+                                    writeIORef esFoldError (Just "fold produced null value")+                                else do+                                    rendered <- duckValueToString valueHandle+                                    writeIORef esFoldValue (Just rendered)+                                    writeIORef esFoldError Nothing+                                    destroyDuckValue valueHandle+                        else do+                            msgPtr <- c_duckdb_error_data_message errData+                            msg <- peekCString msgPtr+                            writeIORef esFoldValue Nothing+                            writeIORef esFoldError (Just msg)+                            destroyErrorData errData+        )+        (alloca \exprPtr -> poke exprPtr exprHandle >> c_duckdb_destroy_expression exprPtr)++expressionExec :: DuckDBFunctionInfo -> DuckDBDataChunk -> DuckDBVector -> IO ()+expressionExec _ chunk outVec = do+    inVec <- c_duckdb_data_chunk_get_vector chunk 0+    c_duckdb_vector_reference_vector outVec inVec++-- Helpers ------------------------------------------------------------------++duckValueToString :: DuckDBValue -> IO String+duckValueToString value = do+    strPtr <- c_duckdb_value_to_string value+    text <- peekCString strPtr+    c_duckdb_free (castPtr strPtr)+    pure text++-- Wrapper constructors -----------------------------------------------------++foreign import ccall "wrapper"+    mkScalarBindFun :: (DuckDBBindInfo -> IO ()) -> IO DuckDBScalarFunctionBindFun++foreign import ccall "wrapper"+    mkScalarExecFun :: (DuckDBFunctionInfo -> DuckDBDataChunk -> DuckDBVector -> IO ()) -> IO DuckDBScalarFunctionFun
+ test/ExtractStatementsTest.hs view
@@ -0,0 +1,103 @@+{-# LANGUAGE BlockArguments #-}++module ExtractStatementsTest (tests) where++import Control.Monad (forM_, when)+import Data.Int (Int64)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (nullPtr)+import Foreign.Storable (peek)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Extract Statements"+        [ extractPrepareAndExecuteSequence+        , extractFailureYieldsErrorMessage+        ]++extractPrepareAndExecuteSequence :: TestTree+extractPrepareAndExecuteSequence =+    testCase "extract, prepare, and execute a multi-statement script" $+        withDatabase \db ->+            withConnection db \conn -> do+                let script =+                        "CREATE TABLE extracted_values(val INTEGER);"+                            <> "INSERT INTO extracted_values VALUES (1), (2);"+                            <> "SELECT SUM(val) FROM extracted_values;"++                withCString script \scriptPtr ->+                    alloca \exPtr -> do+                        stmtCount <- c_duckdb_extract_statements conn scriptPtr exPtr+                        stmtCount @?= 3++                        extracted <- peek exPtr+                        assertBool "extracted handle should not be null" (extracted /= nullPtr)++                        let indices = [0 :: Integer .. fromIntegral stmtCount - 1]+                        forM_ indices \idx ->+                            alloca \stmtPtr -> do+                                let duckIdx = fromIntegral idx+                                prepState <- c_duckdb_prepare_extracted_statement conn extracted duckIdx stmtPtr+                                prepState @?= DuckDBSuccess++                                stmt <- peek stmtPtr+                                assertBool "prepared statement should not be null" (stmt /= nullPtr)++                                alloca \resPtr -> do+                                    execState <- c_duckdb_execute_prepared stmt resPtr+                                    execState @?= DuckDBSuccess++                                    case idx of+                                        0 -> do+                                            resultType <- c_duckdb_result_return_type resPtr+                                            resultType @?= DuckDBResultTypeNothing+                                        1 -> do+                                            resultType <- c_duckdb_result_return_type resPtr+                                            resultType @?= DuckDBResultTypeChangedRows+                                        2 -> do+                                            rowCount <- c_duckdb_row_count resPtr+                                            rowCount @?= 1+                                            total <- c_duckdb_value_int64 resPtr 0 0+                                            (total :: Int64) @?= 3+                                        _ -> pure ()++                                    c_duckdb_destroy_result resPtr++                                c_duckdb_destroy_prepare stmtPtr++                        -- Preparing out-of-range should fail with an informative error+                        alloca \stmtPtr -> do+                            let invalidIdx = stmtCount+                            stInvalid <- c_duckdb_prepare_extracted_statement conn extracted invalidIdx stmtPtr+                            stInvalid @?= DuckDBError+                            stmt <- peek stmtPtr+                            assertBool "invalid index should not yield a statement handle" (stmt == nullPtr)++                        c_duckdb_destroy_extracted exPtr++extractFailureYieldsErrorMessage :: TestTree+extractFailureYieldsErrorMessage =+    testCase "failed extract surfaces parser error" $+        withDatabase \db ->+            withConnection db \conn -> do+                withCString "SELECT * FROM invalid_table WHERE" \badSql ->+                    alloca \exPtr -> do+                        stmtCount <- c_duckdb_extract_statements conn badSql exPtr+                        stmtCount @?= 0++                        extracted <- peek exPtr+                        assertBool "extracted handle should be available for errors" (extracted /= nullPtr)++                        errPtr <- c_duckdb_extract_statements_error extracted+                        when (errPtr == nullPtr) $+                            assertFailure "expected error message pointer from failed extract"+                        errMsg <- peekCString errPtr+                        assertBool "error message should not be empty" (not (null errMsg))++                        c_duckdb_destroy_extracted exPtr
+ test/HelpersTest.hs view
@@ -0,0 +1,161 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE TypeApplications #-}++module HelpersTest (tests) where++import Control.Exception (bracket)+import Data.Int (Int64)+import Data.Time.Calendar (diffDays, fromGregorian)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.C.Types (CBool (..), CDouble (..), CSize (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Marshal.Array (allocaArray)+import Foreign.Ptr (Ptr, castPtr, nullPtr)+import Foreign.Storable (peek, poke, pokeElemOff)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (withLogicalType)++tests :: TestTree+tests =+    testGroup+        "Helpers"+        [ testMallocFree+        , testVectorSize+        , testDateTimeHelpers+        , testStringHelpers+        , testHugeIntHelpers+        , testDecimalHelpers+        ]++testMallocFree :: TestTree+testMallocFree =+    testCase "duckdb_malloc/duckdb_free allocate and release memory" $ do+        ptr <- c_duckdb_malloc (CSize 128)+        assertBool "allocation should succeed" (ptr /= nullPtr)+        c_duckdb_free ptr++testVectorSize :: TestTree+testVectorSize =+    testCase "duckdb_vector_size returns positive number" $ do+        size <- c_duckdb_vector_size+        assertBool "vector size should be positive" (size > 0)++testDateTimeHelpers :: TestTree+testDateTimeHelpers =+    testCase "date/time/timestamp helper round-trips" $ do+        let day = fromGregorian 2023 6 1+            epoch = fromGregorian 1970 1 1+            days = diffDays day epoch+            duckDay = DuckDBDate (fromIntegral days)++        alloca \dateStructPtr -> do+            c_duckdb_from_date duckDay dateStructPtr+            DuckDBDateStruct{duckDBDateStructYear = y, duckDBDateStructMonth = m, duckDBDateStructDay = d} <- peek dateStructPtr+            (y, m, d) @?= (2023, 6, 1)+            roundTrippedDay <- c_duckdb_to_date dateStructPtr+            roundTrippedDay @?= duckDay++        c_duckdb_is_finite_date duckDay >>= (@?= CBool 1)++        let microsPerSecond = 1000000+            timeMicros = ((12 * 60 + 34) * 60 + 56) * microsPerSecond+            duckTime = DuckDBTime (fromIntegral timeMicros)++        alloca \timeStructPtr -> do+            c_duckdb_from_time duckTime timeStructPtr+            DuckDBTimeStruct{duckDBTimeStructHour = h, duckDBTimeStructMinute = mi, duckDBTimeStructSecond = s, duckDBTimeStructMicros = mu} <- peek timeStructPtr+            (h, mi, s, mu) @?= (12, 34, 56, 0)+            roundTrippedTime <- c_duckdb_to_time timeStructPtr+            roundTrippedTime @?= duckTime++        DuckDBTimeTz tz <- c_duckdb_create_time_tz (fromIntegral @Integer timeMicros) 60+        alloca \timeTzPtr -> do+            c_duckdb_from_time_tz (DuckDBTimeTz tz) timeTzPtr+            DuckDBTimeTzStruct{duckDBTimeTzStructTime = DuckDBTimeStruct{duckDBTimeStructHour = h', duckDBTimeStructMinute = mi', duckDBTimeStructSecond = s'}, duckDBTimeTzStructOffset = offset} <- peek timeTzPtr+            (h', mi', s', offset) @?= (12, 34, 56, 60)++        let tsMicros :: Int64+            tsMicros = fromIntegral days * 86400000000 + fromIntegral timeMicros+            duckTimestamp = DuckDBTimestamp tsMicros++        alloca \tsStructPtr -> do+            c_duckdb_from_timestamp duckTimestamp tsStructPtr+            DuckDBTimestampStruct{duckDBTimestampStructDate = DuckDBDateStruct{duckDBDateStructYear = y', duckDBDateStructMonth = m', duckDBDateStructDay = d'}, duckDBTimestampStructTime = DuckDBTimeStruct{duckDBTimeStructHour = hour', duckDBTimeStructMinute = minute', duckDBTimeStructSecond = sec', duckDBTimeStructMicros = micro'}} <-+                peek tsStructPtr+            (y', m', d', hour', minute', sec', micro') @?= (2023, 6, 1, 12, 34, 56, 0)+            roundTrippedTs <- c_duckdb_to_timestamp tsStructPtr+            roundTrippedTs @?= duckTimestamp++        c_duckdb_is_finite_timestamp duckTimestamp >>= (@?= CBool 1)+        c_duckdb_is_finite_timestamp_s (DuckDBTimestampS (tsMicros `div` 1000000)) >>= (@?= CBool 1)+        c_duckdb_is_finite_timestamp_ms (DuckDBTimestampMs (tsMicros `div` 1000)) >>= (@?= CBool 1)+        c_duckdb_is_finite_timestamp_ns (DuckDBTimestampNs (tsMicros * 1000)) >>= (@?= CBool 1)++testStringHelpers :: TestTree+testStringHelpers =+    testCase "string_t helpers inspect inline and heap strings" $ do+        inspectString "short" True+        inspectString (replicate 32 'x') False+  where+    inspectString text expectInline =+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeVarchar) \varcharType ->+            allocaArray 1 \typesPtr -> do+                pokeElemOff typesPtr 0 varcharType+                bracket+                    (c_duckdb_create_data_chunk typesPtr 1)+                    destroyDataChunk+                    \chunk -> do+                        vec <- c_duckdb_data_chunk_get_vector chunk 0+                        withCString text $ \cStr ->+                            c_duckdb_vector_assign_string_element vec 0 cStr+                        c_duckdb_data_chunk_set_size chunk 1+                        dataPtr <- c_duckdb_vector_get_data vec+                        let stringPtr = castPtr dataPtr :: Ptr DuckDBStringT+                        inlineFlag <- c_duckdb_string_is_inlined stringPtr+                        inlineFlag @?= if expectInline then CBool 1 else CBool 0+                        len <- c_duckdb_string_t_length stringPtr+                        len @?= fromIntegral (length text)+                        textPtr <- c_duckdb_string_t_data stringPtr+                        peekCString textPtr >>= (@?= text)++testHugeIntHelpers :: TestTree+testHugeIntHelpers =+    testCase "hugeint/u-hugeint helpers convert values" $ do+        alloca \hugePtr -> do+            let hugeVal = DuckDBHugeInt{duckDBHugeIntLower = 123456789, duckDBHugeIntUpper = 0}+            poke hugePtr hugeVal+            CDouble dbl <- c_duckdb_hugeint_to_double hugePtr+            dbl @?= 123456789+            c_duckdb_double_to_hugeint (CDouble 987654321) hugePtr+            DuckDBHugeInt{duckDBHugeIntLower = lower, duckDBHugeIntUpper = upper} <- peek hugePtr+            (upper, lower) @?= (0, 987654321)++        alloca \uhugePtr -> do+            let uhugeVal = DuckDBUHugeInt{duckDBUHugeIntLower = 987654321, duckDBUHugeIntUpper = 1}+            poke uhugePtr uhugeVal+            CDouble dbl <- c_duckdb_uhugeint_to_double uhugePtr+            dbl @?= fromIntegral @Integer (987654321 + 2 ^ (64 :: Int))+            c_duckdb_double_to_uhugeint (CDouble 123456789) uhugePtr+            DuckDBUHugeInt{duckDBUHugeIntLower = lower, duckDBUHugeIntUpper = upper} <- peek uhugePtr+            (upper, lower) @?= (0, 123456789)++testDecimalHelpers :: TestTree+testDecimalHelpers =+    testCase "decimal helper round-trip" $ do+        let input = CDouble 12345.67+        alloca \decimalPtr -> do+            c_duckdb_double_to_decimal input 18 2 decimalPtr+            DuckDBDecimal{duckDBDecimalWidth = width, duckDBDecimalScale = scale} <- peek decimalPtr+            (width, scale) @?= (18, 2)+            result <- c_duckdb_decimal_to_double decimalPtr+            result @?= input++-- Utilities ----------------------------------------------------------------++destroyDataChunk :: DuckDBDataChunk -> IO ()+destroyDataChunk chunk =+    alloca \ptr -> do+        poke ptr chunk+        c_duckdb_destroy_data_chunk ptr
+ test/LogicalTypesTest.hs view
@@ -0,0 +1,166 @@+{-# LANGUAGE BlockArguments #-}++module LogicalTypesTest (tests) where++import Control.Monad (forM_, when, (>=>))+import Data.Word (Word32, Word8)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.Marshal.Array (withArray)+import Foreign.Marshal.Utils (withMany)+import Foreign.Ptr (castPtr, nullPtr)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (withConnection, withDatabase, withLogicalType, withResult)++tests :: TestTree+tests =+    testGroup+        "Logical Type Interface"+        [ primitiveLogicalTypes+        , decimalLogicalType+        , enumLogicalType+        , compositeLogicalTypes+        , aliasRoundtrip+        , registerLogicalType+        ]++primitiveLogicalTypes :: TestTree+primitiveLogicalTypes =+    testCase "primitive logical types report their ids" $+        forM_ primitives \(duckType, expected) ->+            withLogicalType (c_duckdb_create_logical_type duckType) \lt -> do+                typeId <- c_duckdb_get_type_id lt+                typeId @?= expected+  where+    primitives =+        [ (DuckDBTypeBoolean, DuckDBTypeBoolean)+        , (DuckDBTypeInteger, DuckDBTypeInteger)+        , (DuckDBTypeVarchar, DuckDBTypeVarchar)+        , (DuckDBTypeBlob, DuckDBTypeBlob)+        ]++decimalLogicalType :: TestTree+decimalLogicalType =+    testCase "decimal logical type exposes width/scale/internal type" $+        withLogicalType (c_duckdb_create_decimal_type width scale) \lt -> do+            c_duckdb_get_type_id lt >>= (@?= DuckDBTypeDecimal)+            c_duckdb_decimal_width lt >>= (@?= width)+            c_duckdb_decimal_scale lt >>= (@?= scale)+            c_duckdb_decimal_internal_type lt >>= (@?= DuckDBTypeBigInt)+  where+    width, scale :: Word8+    width = 18+    scale = 4++enumLogicalType :: TestTree+enumLogicalType =+    testCase "enum logical type exposes dictionary" $ do+        enumType <-+            withMany withCString ["Small", "Medium", "Large"] \namePtrs ->+                withArray namePtrs (`c_duckdb_create_enum_type` 3)+        withLogicalType (pure enumType) \lt -> do+            c_duckdb_get_type_id lt >>= (@?= DuckDBTypeEnum)+            c_duckdb_enum_internal_type lt >>= (@?= DuckDBTypeUTinyInt)+            c_duckdb_enum_dictionary_size lt >>= (@?= (3 :: Word32))+            valuePtr <- c_duckdb_enum_dictionary_value lt 1+            peekCString valuePtr >>= (@?= "Medium")+            c_duckdb_free (castPtr valuePtr)++compositeLogicalTypes :: TestTree+compositeLogicalTypes =+    testCase "composite logical types expose nesting metadata" $ do+        -- List type+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \child -> do+            listType <- c_duckdb_create_list_type child+            withLogicalType (pure listType) \lt -> do+                c_duckdb_get_type_id lt >>= (@?= DuckDBTypeList)+                listChild <- c_duckdb_list_type_child_type lt+                withLogicalType (pure listChild) (c_duckdb_get_type_id >=> (@?= DuckDBTypeInteger))++        -- Array type+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \arrayChild -> do+            arrayType <- c_duckdb_create_array_type arrayChild 5+            withLogicalType (pure arrayType) \lt -> do+                c_duckdb_get_type_id lt >>= (@?= DuckDBTypeArray)+                arrayChildType <- c_duckdb_array_type_child_type lt+                withLogicalType (pure arrayChildType) (c_duckdb_get_type_id >=> (@?= DuckDBTypeInteger))+                c_duckdb_array_type_array_size lt >>= (@?= 5)++        -- Map type+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeVarchar) \keyType ->+            withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \valType -> do+                mapType <- c_duckdb_create_map_type keyType valType+                withLogicalType (pure mapType) \lt -> do+                    c_duckdb_get_type_id lt >>= (@?= DuckDBTypeMap)+                    mapKey <- c_duckdb_map_type_key_type lt+                    withLogicalType (pure mapKey) (c_duckdb_get_type_id >=> (@?= DuckDBTypeVarchar))+                    mapVal <- c_duckdb_map_type_value_type lt+                    withLogicalType (pure mapVal) (c_duckdb_get_type_id >=> (@?= DuckDBTypeInteger))++        -- Struct type+        withMany withCString ["id", "name"] \fieldNames -> withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \idType ->+            withLogicalType (c_duckdb_create_logical_type DuckDBTypeVarchar) \nameType ->+                withArray [idType, nameType] \childArray ->+                    withArray fieldNames \namesArray -> do+                        structType <- c_duckdb_create_struct_type childArray namesArray 2+                        withLogicalType (pure structType) \lt -> do+                            c_duckdb_get_type_id lt >>= (@?= DuckDBTypeStruct)+                            childCount <- c_duckdb_struct_type_child_count lt+                            childCount @?= 2+                            childName0 <- c_duckdb_struct_type_child_name lt 0+                            peekCString childName0 >>= (@?= "id")+                            c_duckdb_free (castPtr childName0)+                            structChild1 <- c_duckdb_struct_type_child_type lt 1+                            withLogicalType (pure structChild1) (c_duckdb_get_type_id >=> (@?= DuckDBTypeVarchar))++        -- Union type+        withMany withCString ["int_member", "text_member"] \memberNames -> withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intMember ->+            withLogicalType (c_duckdb_create_logical_type DuckDBTypeVarchar) \textMember ->+                withArray [intMember, textMember] \memberArray ->+                    withArray memberNames \nameArray -> do+                        unionType <- c_duckdb_create_union_type memberArray nameArray 2+                        withLogicalType (pure unionType) \lt -> do+                            c_duckdb_get_type_id lt >>= (@?= DuckDBTypeUnion)+                            memberCount <- c_duckdb_union_type_member_count lt+                            memberCount @?= 2+                            memberName0 <- c_duckdb_union_type_member_name lt 0+                            peekCString memberName0 >>= (@?= "int_member")+                            c_duckdb_free (castPtr memberName0)+                            memberChild <- c_duckdb_union_type_member_type lt 1+                            withLogicalType (pure memberChild) (c_duckdb_get_type_id >=> (@?= DuckDBTypeVarchar))++aliasRoundtrip :: TestTree+aliasRoundtrip =+    testCase "logical type aliases can be set and retrieved" $+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \lt -> do+            aliasBefore <- c_duckdb_logical_type_get_alias lt+            aliasBefore @?= nullPtr+            withCString "custom_alias" $ \alias -> c_duckdb_logical_type_set_alias lt alias+            aliasAfter <- c_duckdb_logical_type_get_alias lt+            assertBool "alias pointer should not be null" (aliasAfter /= nullPtr)+            when (aliasAfter == nullPtr) $+                assertFailure "expected alias pointer"+            peekCString aliasAfter >>= (@?= "custom_alias")+            c_duckdb_free (castPtr aliasAfter)++registerLogicalType :: TestTree+registerLogicalType =+    testCase "registered logical type alias is accepted in SQL" $+        withDatabase \db ->+            withConnection db \conn ->+                withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \lt -> do+                    let aliasName = "custom_int_alias"+                    withCString aliasName $ \aliasPtr ->+                        c_duckdb_logical_type_set_alias lt aliasPtr+                    c_duckdb_register_logical_type conn lt nullPtr >>= (@?= DuckDBSuccess)++                    let createSql = "CREATE TABLE logical_type_demo (val " ++ aliasName ++ ")"+                    withResult conn createSql \_ -> pure ()++                    withResult conn "PRAGMA table_info('logical_type_demo')" \resPtr -> do+                        c_duckdb_row_count resPtr >>= (@?= 1)+                        typePtr <- c_duckdb_value_varchar resPtr 2 0+                        typeName <- peekCString typePtr+                        c_duckdb_free (castPtr typePtr)+                        typeName @?= aliasName
+ test/Main.hs view
@@ -0,0 +1,72 @@+module Main (main) where++import qualified AggregateFunctionsTest+import qualified AppenderTest+import qualified ArrowInterfaceDeprecatedTests+import qualified ArrowInterfaceTest+import qualified BindValuesTest+import qualified CastFunctionsTest+import qualified ConfigurationTest+import qualified DataChunkTest+import qualified ErrorDataTest+import qualified ExecutePreparedStatementsTest+import qualified ExpressionTest+import qualified ExtractStatementsTest+import qualified HelpersTest+import qualified LogicalTypesTest+import qualified OpenConnectTest+import qualified PendingResultTest+import qualified PreparedStatementsTest+import qualified ProfilingInfoTest+import qualified QueryExecutionTest+import qualified ReplacementScansTest+import qualified ResultFunctionsTest+import qualified SafeFetchTest+import qualified ScalarFunctionsTest+import qualified SelectionVectorTest+import qualified StreamingResultTest+import qualified TableDescriptionTest+import qualified TableFunctionsTest+import Test.Tasty (defaultMain, testGroup)+import qualified ThreadingTest+import qualified ValidityMaskTest+import qualified ValueInterfaceTest+import qualified VectorTest++main :: IO ()+main =+    defaultMain $+        testGroup+            "DuckDB FFI"+            [ OpenConnectTest.tests+            , ErrorDataTest.tests+            , ConfigurationTest.tests+            , PreparedStatementsTest.tests+            , BindValuesTest.tests+            , ExecutePreparedStatementsTest.tests+            , ExtractStatementsTest.tests+            , LogicalTypesTest.tests+            , DataChunkTest.tests+            , VectorTest.tests+            , ValidityMaskTest.tests+            , ScalarFunctionsTest.tests+            , AggregateFunctionsTest.tests+            , TableFunctionsTest.tests+            , ProfilingInfoTest.tests+            , SelectionVectorTest.tests+            , PendingResultTest.tests+            , ValueInterfaceTest.tests+            , QueryExecutionTest.tests+            , ResultFunctionsTest.tests+            , SafeFetchTest.tests+            , HelpersTest.tests+            , TableDescriptionTest.tests+            , AppenderTest.tests+            , CastFunctionsTest.tests+            , ExpressionTest.tests+            , ReplacementScansTest.tests+            , ArrowInterfaceDeprecatedTests.tests+            , ArrowInterfaceTest.tests+            , StreamingResultTest.tests+            , ThreadingTest.tests+            ]
+ test/OpenConnectTest.hs view
@@ -0,0 +1,137 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE RecordWildCards #-}++module OpenConnectTest (tests) where++import Control.Monad (when)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (castPtr, nullPtr)+import Foreign.Storable (peek, poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))++tests :: TestTree+tests =+    testGroup+        "Open Connect"+        [ testLibraryVersion+        , testOpenAndConnect+        , testOpenExtWithConfig+        , testInstanceCache+        ]++testLibraryVersion :: TestTree+testLibraryVersion =+    testCase "duckdb_library_version returns a non-empty string" $ do+        versionPtr <- c_duckdb_library_version+        version <- peekCString versionPtr+        assertBool "library version should not be empty" (not (null version))++testOpenAndConnect :: TestTree+testOpenAndConnect =+    testCase "open, connect, and gather connection details" $+        withCString ":memory:" $ \path ->+            alloca $ \dbPtr -> do+                state <- c_duckdb_open path dbPtr+                state @?= DuckDBSuccess+                db <- peek dbPtr++                alloca $ \connPtr -> do+                    connState <- c_duckdb_connect db connPtr+                    connState @?= DuckDBSuccess+                    conn <- peek connPtr++                    -- Query progress information (should succeed even without a running query)+                    alloca $ \progressPtr -> do+                        poke progressPtr (DuckDBQueryProgress 0 0 0)+                        c_duckdb_query_progress conn progressPtr+                        DuckDBQueryProgress{..} <- peek progressPtr+                        assertBool "rows processed should be non-negative" (duckDBQueryProgressRowsProcessed >= 0 && duckDBQueryProgressTotalRows >= 0)++                    -- Interrupt is a no-op but should succeed+                    c_duckdb_interrupt conn++                    -- Retrieve client context and confirm it points back to the same connection+                    alloca $ \ctxPtr -> do+                        poke ctxPtr nullPtr+                        c_duckdb_connection_get_client_context conn ctxPtr+                        ctx <- peek ctxPtr+                        assertBool "client context should not be null" (ctx /= nullPtr)+                        cid <- c_duckdb_client_context_get_connection_id ctx+                        assertBool "connection id should be non-negative" (cid >= 0)+                        c_duckdb_destroy_client_context ctxPtr++                    -- Retrieve arrow options object+                    alloca $ \arrowPtr -> do+                        poke arrowPtr nullPtr+                        c_duckdb_connection_get_arrow_options conn arrowPtr+                        arrowOpts <- peek arrowPtr+                        assertBool "arrow options should not be null" (arrowOpts /= nullPtr)+                        c_duckdb_destroy_arrow_options arrowPtr++                    -- Fetch table names (there should be none, but the call should succeed)+                    alloca $ \valuePtr -> do+                        tablesValue <- withCString "%" $ \filterStr -> c_duckdb_get_table_names conn filterStr (CBool 0)+                        poke valuePtr tablesValue+                        c_duckdb_destroy_value valuePtr++                    -- Disconnect and close database+                    c_duckdb_disconnect connPtr++                c_duckdb_close dbPtr++testOpenExtWithConfig :: TestTree+testOpenExtWithConfig =+    testCase "open_ext with configuration" $+        withCString ":memory:" $ \path ->+            alloca $ \configPtr -> do+                cfgState <- c_duckdb_create_config configPtr+                cfgState @?= DuckDBSuccess+                config <- peek configPtr++                alloca $ \errorPtr -> do+                    poke errorPtr nullPtr+                    alloca $ \dbPtr -> do+                        state <- c_duckdb_open_ext path dbPtr config errorPtr+                        state @?= DuckDBSuccess++                        errMsgPtr <- peek errorPtr+                        when (errMsgPtr /= nullPtr) $+                            c_duckdb_free (castPtr errMsgPtr)++                        c_duckdb_close dbPtr++                c_duckdb_destroy_config configPtr++testInstanceCache :: TestTree+testInstanceCache =+    testCase "create instance cache and reuse database handle" $+        withCString ":memory:" $ \path ->+            alloca $ \configPtr -> do+                cfgState <- c_duckdb_create_config configPtr+                cfgState @?= DuckDBSuccess+                config <- peek configPtr++                cache <- c_duckdb_create_instance_cache+                assertBool "instance cache should not be null" (cache /= nullPtr)++                alloca $ \dbPtr -> do+                    alloca $ \errorPtr -> do+                        poke errorPtr nullPtr+                        state <- c_duckdb_get_or_create_from_cache cache path dbPtr config errorPtr+                        state @?= DuckDBSuccess+                        err <- peek errorPtr+                        when (err /= nullPtr) $+                            c_duckdb_free (castPtr err)++                        -- the database returned from the cache should be usable (we close it immediately)+                        c_duckdb_close dbPtr++                alloca $ \cachePtr -> do+                    poke cachePtr cache+                    c_duckdb_destroy_instance_cache cachePtr++                c_duckdb_destroy_config configPtr
+ test/PendingResultTest.hs view
@@ -0,0 +1,212 @@+{-# LANGUAGE BlockArguments #-}++module PendingResultTest (tests) where++import Control.Monad (forM_, when)+import Data.Int (Int32, Int64)+import Data.Maybe (isNothing)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (Ptr, nullPtr)+import Foreign.Storable (peek, poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Pending Result Interface"+        [ pendingPreparedRoundtrip+        , pendingPreparedStreamingRoundtrip+        , pendingPreparedReportsError+        ]++withChunk :: DuckDBDataChunk -> (Ptr DuckDBDataChunk -> IO ()) -> IO ()+withChunk chunk action =+    alloca \chunkPtr -> do+        poke chunkPtr chunk+        action chunkPtr++pendingErrorMessage :: DuckDBPendingResult -> IO (Maybe String)+pendingErrorMessage pending = do+    errPtr <- c_duckdb_pending_error pending+    if errPtr == nullPtr+        then pure Nothing+        else Just <$> peekCString errPtr++assertPendingState :: DuckDBPendingState -> IO ()+assertPendingState state =+    let valid =+            state == DuckDBPendingResultReady+                || state == DuckDBPendingResultNotReady+                || state == DuckDBPendingError+                || state == DuckDBPendingNoTasksAvailable+     in assertBool "unexpected pending state" valid++pendingPreparedRoundtrip :: TestTree+pendingPreparedRoundtrip =+    testCase "pending_prepared executes query to completion" $+        withDatabase \db ->+            withConnection db \conn -> do+                forM_+                    [ "CREATE TABLE pending_numbers(val INTEGER);"+                    , "INSERT INTO pending_numbers VALUES (1), (2), (3);"+                    ]+                    \sql ->+                        withCString sql \cSql ->+                            alloca \resPtr -> do+                                st <- c_duckdb_query conn cSql resPtr+                                st @?= DuckDBSuccess+                                c_duckdb_destroy_result resPtr++                withCString "SELECT SUM(val) FROM pending_numbers;" \querySql ->+                    alloca \stmtPtr -> do+                        st <- c_duckdb_prepare conn querySql stmtPtr+                        st @?= DuckDBSuccess+                        stmt <- peek stmtPtr+                        assertBool "prepared statement should not be null" (stmt /= nullPtr)++                        alloca \pendingPtr -> do+                            stPending <- c_duckdb_pending_prepared stmt pendingPtr+                            stPending @?= DuckDBSuccess+                            pending <- peek pendingPtr+                            assertBool "pending result should not be null" (pending /= nullPtr)++                            stateBefore <- c_duckdb_pending_execute_check_state pending+                            assertPendingState stateBefore+                            _ <- c_duckdb_pending_execution_is_finished stateBefore++                            taskState <- c_duckdb_pending_execute_task pending+                            assertPendingState taskState+                            _ <- c_duckdb_pending_execution_is_finished taskState++                            alloca \resPtr -> do+                                execState <- c_duckdb_execute_pending pending resPtr+                                execState @?= DuckDBSuccess++                                resultFinished <- c_duckdb_pending_execute_check_state pending+                                assertPendingState resultFinished++                                rowCount <- c_duckdb_row_count resPtr+                                rowCount @?= 1+                                total <- c_duckdb_value_int64 resPtr 0 0+                                (total :: Int64) @?= 6++                                c_duckdb_destroy_result resPtr++                            errMsg <- pendingErrorMessage pending+                            assertBool "no error expected for successful pending execution" (isNothing errMsg)++                            c_duckdb_destroy_pending pendingPtr++                        c_duckdb_destroy_prepare stmtPtr++pendingPreparedStreamingRoundtrip :: TestTree+pendingPreparedStreamingRoundtrip =+    testCase "pending_prepared_streaming yields streaming duckdb_result" $+        withDatabase \db ->+            withConnection db \conn -> do+                forM_+                    [ "CREATE TABLE pending_stream(id INTEGER);"+                    , "INSERT INTO pending_stream VALUES (1), (2), (3), (4);"+                    ]+                    \sql ->+                        withCString sql \cSql ->+                            alloca \resPtr -> do+                                st <- c_duckdb_query conn cSql resPtr+                                st @?= DuckDBSuccess+                                c_duckdb_destroy_result resPtr++                withCString "SELECT * FROM pending_stream ORDER BY id;" \querySql ->+                    alloca \stmtPtr -> do+                        st <- c_duckdb_prepare conn querySql stmtPtr+                        st @?= DuckDBSuccess+                        stmt <- peek stmtPtr+                        assertBool "prepared statement should not be null" (stmt /= nullPtr)++                        alloca \pendingPtr -> do+                            stPending <- c_duckdb_pending_prepared_streaming stmt pendingPtr+                            stPending @?= DuckDBSuccess+                            pending <- peek pendingPtr+                            assertBool "pending result should not be null" (pending /= nullPtr)++                            stateBefore <- c_duckdb_pending_execute_check_state pending+                            assertPendingState stateBefore+                            _ <- c_duckdb_pending_execution_is_finished stateBefore++                            taskState <- c_duckdb_pending_execute_task pending+                            assertPendingState taskState+                            _ <- c_duckdb_pending_execution_is_finished taskState++                            alloca \resPtr -> do+                                execState <- c_duckdb_execute_pending pending resPtr+                                execState @?= DuckDBSuccess++                                streamingFlag <- c_duckdb_result_is_streaming resPtr+                                streamingFlag @?= CBool 1++                                chunk <- c_duckdb_stream_fetch_chunk resPtr+                                assertBool "streaming fetch should yield a chunk" (chunk /= nullPtr)++                                chunkSize <- c_duckdb_data_chunk_get_size chunk+                                assertBool "streamed chunk should have rows" (chunkSize > 0)++                                withChunk chunk c_duckdb_destroy_data_chunk+                                c_duckdb_destroy_result resPtr++                            errMsg <- pendingErrorMessage pending+                            assertBool "no error expected for successful streaming execution" (isNothing errMsg)++                            c_duckdb_destroy_pending pendingPtr++                        c_duckdb_destroy_prepare stmtPtr++pendingPreparedReportsError :: TestTree+pendingPreparedReportsError =+    testCase "pending execution surfaces failure details" $+        withDatabase \db ->+            withConnection db \conn -> do+                forM_+                    [ "CREATE TABLE pending_unique(val INTEGER PRIMARY KEY);"+                    , "INSERT INTO pending_unique VALUES (1);"+                    ]+                    \sql ->+                        withCString sql \cSql ->+                            alloca \resPtr -> do+                                st <- c_duckdb_query conn cSql resPtr+                                st @?= DuckDBSuccess+                                c_duckdb_destroy_result resPtr++                withCString "INSERT INTO pending_unique VALUES (?);" \insertSql ->+                    alloca \stmtPtr -> do+                        st <- c_duckdb_prepare conn insertSql stmtPtr+                        st @?= DuckDBSuccess+                        stmt <- peek stmtPtr+                        assertBool "prepared insert statement should not be null" (stmt /= nullPtr)++                        c_duckdb_bind_int32 stmt 1 (1 :: Int32) >>= (@?= DuckDBSuccess)++                        alloca \pendingPtr -> do+                            stPending <- c_duckdb_pending_prepared stmt pendingPtr+                            stPending @?= DuckDBSuccess+                            pending <- peek pendingPtr+                            assertBool "pending result should not be null" (pending /= nullPtr)++                            _ <- c_duckdb_pending_execute_check_state pending+                            taskState <- c_duckdb_pending_execute_task pending+                            assertPendingState taskState++                            alloca \resPtr -> do+                                execState <- c_duckdb_execute_pending pending resPtr+                                execState @?= DuckDBError++                            errMsg <- pendingErrorMessage pending+                            when (maybe False null errMsg) $+                                assertFailure "pending error message should not be empty when present"++                            c_duckdb_destroy_pending pendingPtr++                        c_duckdb_destroy_prepare stmtPtr
+ test/PreparedStatementsTest.hs view
@@ -0,0 +1,112 @@+{-# LANGUAGE BlockArguments #-}++module PreparedStatementsTest (tests) where++import Control.Monad (when)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (nullPtr)+import Foreign.Storable (peek, poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Prepared Statements"+        [ preparedStatementMetadata+        , preparedStatementError+        ]++preparedStatementMetadata :: TestTree+preparedStatementMetadata =+    testCase "inspect prepared statement metadata" $+        withDatabase \db ->+            withConnection db \conn -> do+                -- Seed table for column metadata checks+                withCString "CREATE TABLE numbers(value INTEGER)" \ddl -> do+                    alloca \resPtr -> do+                        st <- c_duckdb_query conn ddl resPtr+                        st @?= DuckDBSuccess+                        c_duckdb_destroy_result resPtr++                let sql = "SELECT value FROM numbers WHERE value = ?"+                withCString sql \cSql ->+                    alloca \stmtPtr -> do+                        st <- c_duckdb_prepare conn cSql stmtPtr+                        st @?= DuckDBSuccess+                        stmt <- peek stmtPtr+                        assertBool "statement pointer should not be null" (stmt /= nullPtr)++                        -- Parameter metadata+                        paramCount <- c_duckdb_nparams stmt+                        paramCount @?= 1++                        paramType <- c_duckdb_param_type stmt 0+                        -- Physical type defaults to invalid until bound; logical type carries information.+                        paramType @?= DuckDBTypeInvalid++                        logicalTypePtr <- c_duckdb_param_logical_type stmt 0+                        when (logicalTypePtr /= nullPtr) $ do+                            alloca \typePtr -> do+                                poke typePtr logicalTypePtr+                                c_duckdb_destroy_logical_type typePtr++                        namePtr <- c_duckdb_parameter_name stmt 0+                        when (namePtr /= nullPtr) $ do+                            name <- peekCString namePtr+                            name @?= ""++                        -- Statement type+                        stmtType <- c_duckdb_prepared_statement_type stmt+                        stmtType @?= DuckDBStatementTypeSelect++                        -- Column metadata+                        colCount <- c_duckdb_prepared_statement_column_count stmt+                        colCount @?= 1++                        colNamePtr <- c_duckdb_prepared_statement_column_name stmt 0+                        colName <- peekCString colNamePtr+                        colName @?= "value"++                        colType <- c_duckdb_prepared_statement_column_type stmt 0+                        colType @?= DuckDBTypeInteger++                        colLogicalType <- c_duckdb_prepared_statement_column_logical_type stmt 0+                        assertBool "column logical type should not be null" (colLogicalType /= nullPtr)+                        alloca \colTypePtr -> do+                            poke colTypePtr colLogicalType+                            c_duckdb_destroy_logical_type colTypePtr++                        -- Clear bindings succeeds even before binding values+                        c_duckdb_clear_bindings stmt >>= (@?= DuckDBSuccess)++                        -- No preparation error+                        errPtr <- c_duckdb_prepare_error stmt+                        when (errPtr /= nullPtr) $ do+                            msg <- peekCString errPtr+                            msg @?= ""++                        c_duckdb_destroy_prepare stmtPtr++preparedStatementError :: TestTree+preparedStatementError =+    testCase "prepare error surfaces message" $+        withDatabase \db ->+            withConnection db \conn -> do+                let badSql = "SELECT * FROM non_existing_table WHERE value = ?"+                withCString badSql \cSql ->+                    alloca \stmtPtr -> do+                        st <- c_duckdb_prepare conn cSql stmtPtr+                        st @?= DuckDBError+                        stmt <- peek stmtPtr+                        if stmt == nullPtr+                            then assertFailure "expected prepared statement handle after failure"+                            else do+                                errMsgPtr <- c_duckdb_prepare_error stmt+                                when (errMsgPtr == nullPtr) $ assertFailure "expected error message for failed prepare"+                                msg <- peekCString errMsgPtr+                                assertBool "error message should mention table" (not (null msg))+                                c_duckdb_destroy_prepare stmtPtr
+ test/ProfilingInfoTest.hs view
@@ -0,0 +1,113 @@+{-# LANGUAGE BlockArguments #-}++module ProfilingInfoTest (tests) where++import Control.Monad (forM)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (castPtr, nullPtr)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (destroyDuckValue, withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Profiling Info"+        [ profilingDisabledByDefault+        , profilingMetricsRoundtrip+        ]++profilingDisabledByDefault :: TestTree+profilingDisabledByDefault =+    testCase "profiling info requires enabling profiling" $+        withDatabase \db ->+            withConnection db \conn -> do+                infoPtr <- c_duckdb_get_profiling_info conn+                infoPtr @?= nullPtr++profilingMetricsRoundtrip :: TestTree+profilingMetricsRoundtrip =+    testCase "collect metrics and traverse profiling tree" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement conn "PRAGMA enable_profiling='no_output'"+                runStatement conn "CREATE TABLE profiling_numbers(value INTEGER)"+                runStatement conn "INSERT INTO profiling_numbers VALUES (1), (2), (3)"+                runStatement conn "SELECT sum(value) FROM profiling_numbers"++                infoPtr <- c_duckdb_get_profiling_info conn+                assertBool "profiling info pointer should be non-null" (infoPtr /= nullPtr)++                metricsVal <- c_duckdb_profiling_info_get_metrics infoPtr+                entryCountIdx <- c_duckdb_get_map_size metricsVal+                assertBool "expected at least one metric entry" (entryCountIdx > 0)+                entries <- collectMetrics metricsVal entryCountIdx+                destroyDuckValue metricsVal++                assertBool "metrics map should contain entries" (not (null entries))+                let (firstKey, firstValue) = head entries++                withCString firstKey \keyPtr -> do+                    valueHandle <- c_duckdb_profiling_info_get_value infoPtr keyPtr+                    assertBool ("metric " <> firstKey <> " should be present") (valueHandle /= nullPtr)+                    fetchedValue <- duckValueToString valueHandle+                    destroyDuckValue valueHandle+                    fetchedValue @?= firstValue++                childCount <- c_duckdb_profiling_info_get_child_count infoPtr+                assertBool "expected at least one child node" (childCount > 0)+                let firstChildIdx = 0+                childPtr <- c_duckdb_profiling_info_get_child infoPtr firstChildIdx+                assertBool "child pointer should be non-null" (childPtr /= nullPtr)++                childMetrics <- c_duckdb_profiling_info_get_metrics childPtr+                childCountIdx <- c_duckdb_get_map_size childMetrics+                assertBool "child node should expose metrics" (childCountIdx > 0)+                destroyDuckValue childMetrics++runStatement :: DuckDBConnection -> String -> IO ()+runStatement conn sql =+    withCString sql \sqlPtr ->+        alloca \resPtr -> do+            state <- c_duckdb_query conn sqlPtr resPtr+            if state == DuckDBSuccess+                then c_duckdb_destroy_result resPtr+                else do+                    errPtr <- c_duckdb_result_error resPtr+                    errMsg <-+                        if errPtr == nullPtr+                            then pure "unknown error"+                            else peekCString errPtr+                    c_duckdb_destroy_result resPtr+                    assertFailure ("duckdb_query failed: " <> errMsg)++collectMetrics :: DuckDBValue -> DuckDBIdx -> IO [(String, String)]+collectMetrics metricsVal entryCountIdx = do+    let entryCount = fromIntegral entryCountIdx :: Int+    forM [0 .. entryCount - 1] \i -> do+        let idx = fromIntegral i :: DuckDBIdx+        keyHandle <- c_duckdb_get_map_key metricsVal idx+        keyName <- duckValueToText keyHandle+        destroyDuckValue keyHandle++        valHandle <- c_duckdb_get_map_value metricsVal idx+        valText <- duckValueToString valHandle+        destroyDuckValue valHandle++        pure (keyName, valText)++duckValueToText :: DuckDBValue -> IO String+duckValueToText valHandle = do+    strPtr <- c_duckdb_get_varchar valHandle+    text <- peekCString strPtr+    c_duckdb_free (castPtr strPtr)+    pure text++duckValueToString :: DuckDBValue -> IO String+duckValueToString valHandle = do+    strPtr <- c_duckdb_value_to_string valHandle+    text <- peekCString strPtr+    c_duckdb_free (castPtr strPtr)+    pure text
+ test/QueryExecutionTest.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE BlockArguments #-}++module QueryExecutionTest (tests) where++import Control.Monad (forM_, when)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (nullPtr)+import Foreign.Storable (poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Query Execution"+        [ queryLifecycle+        ]++queryLifecycle :: TestTree+queryLifecycle =+    testCase "run a query and inspect metadata" $+        withDatabase \db ->+            withConnection db \conn -> do+                -- Seed table+                let createSQL = "CREATE TABLE items(id INTEGER, name VARCHAR)"+                    insertSQL = "INSERT INTO items VALUES (1, 'apple'), (2, 'banana')"+                forM_ [createSQL, insertSQL] \sql ->+                    withCString sql \cSql ->+                        alloca \resPtr -> do+                            st <- c_duckdb_query conn cSql resPtr+                            st @?= DuckDBSuccess+                            c_duckdb_destroy_result resPtr++                -- Query data+                withCString "SELECT id, name FROM items ORDER BY id" \selectSql ->+                    alloca \resPtr -> do+                        st <- c_duckdb_query conn selectSql resPtr+                        st @?= DuckDBSuccess++                        -- Column metadata+                        columnCount <- c_duckdb_column_count resPtr+                        columnCount @?= 2++                        rowCount <- c_duckdb_row_count resPtr+                        rowCount @?= 2++                        rowsChanged <- c_duckdb_rows_changed resPtr+                        rowsChanged @?= 0++                        stmtType <- c_duckdb_result_statement_type resPtr+                        stmtType @?= DuckDBStatementTypeSelect++                        arrowOpts <- c_duckdb_result_get_arrow_options resPtr+                        assertBool "arrow options should not be null" (arrowOpts /= nullPtr)+                        alloca \arrowPtr -> do+                            poke arrowPtr arrowOpts+                            c_duckdb_destroy_arrow_options arrowPtr++                        forM_ [0 .. columnCount - 1] \idx -> do+                            namePtr <- c_duckdb_column_name resPtr idx+                            colName <- peekCString namePtr+                            let expected = if idx == 0 then "id" else "name"+                            colName @?= expected++                            colType <- c_duckdb_column_type resPtr idx+                            let expectedType = if idx == 0 then DuckDBTypeInteger else DuckDBTypeVarchar+                            colType @?= expectedType++                            logicalType <- c_duckdb_column_logical_type resPtr idx+                            assertBool "logical type pointer should not be null" (logicalType /= nullPtr)+                            alloca \typePtr -> do+                                poke typePtr logicalType+                                c_duckdb_destroy_logical_type typePtr++                            dataPtr <- c_duckdb_column_data resPtr idx+                            assertBool "column data pointer should not be null" (dataPtr /= nullPtr)++                            nullmaskPtr <- c_duckdb_nullmask_data resPtr idx+                            assertBool "nullmask pointer should not be null" (nullmaskPtr /= nullPtr)++                        errPtr <- c_duckdb_result_error resPtr+                        when (errPtr /= nullPtr) $ do+                            errMsg <- peekCString errPtr+                            assertBool ("unexpected error: " <> errMsg) False++                        errType <- c_duckdb_result_error_type resPtr+                        errType @?= DuckDBErrorInvalid++                        c_duckdb_destroy_result resPtr
+ test/ReplacementScansTest.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE BlockArguments #-}++module ReplacementScansTest (tests) where++import Control.Concurrent (runInBoundThread)+import Control.Exception (bracket)+import Control.Monad (forM_)+import Data.IORef (IORef, modifyIORef', newIORef, readIORef)+import Data.Int (Int64)+import Data.List (isInfixOf)+import Database.DuckDB.FFI+import Foreign.C.String (CString, peekCString, withCString)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (Ptr, freeHaskellFunPtr, nullFunPtr, nullPtr)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (withConnection, withDatabase, withResult, withValue)++tests :: TestTree+tests =+    testGroup+        "Replacement Scans"+        [ replacementScanRewritesAndErrors+        ]++replacementScanRewritesAndErrors :: TestTree+replacementScanRewritesAndErrors =+    testCase "replacement scan rewrites table name and can report errors" $+        runInBoundThread do+            seenTablesRef <- newIORef []++            let startValue = 5+                countValue = 4+                endValue = startValue + countValue++            withReplacementCallback seenTablesRef startValue endValue \callback -> do+                withDatabase \db -> do+                    c_duckdb_add_replacement_scan db callback nullPtr nullFunPtr+                    withConnection db \conn -> do+                        assertReplacementQuery conn startValue countValue+                        assertReplacementError conn++            seenTables <- readIORef seenTablesRef+            assertBool "replacement callback should run for rewrite target" ("haskell_magic" `elem` seenTables)+            assertBool "replacement callback should run for error target" ("failing_magic" `elem` seenTables)++withReplacementCallback ::+    IORef [String] ->+    Int64 ->+    Int64 ->+    (DuckDBReplacementCallback -> IO a) ->+    IO a+withReplacementCallback seenTablesRef startValue endValue =+    bracket acquire freeHaskellFunPtr+  where+    acquire =+        mkReplacementCallback (replacementCallback seenTablesRef startValue endValue)++replacementCallback ::+    IORef [String] ->+    Int64 ->+    Int64 ->+    DuckDBReplacementScanInfo ->+    CString ->+    Ptr () ->+    IO ()+replacementCallback seenTablesRef startValue endValue info tableName _extra = do+    name <- peekCString tableName+    modifyIORef' seenTablesRef (name :)+    case name of+        "haskell_magic" -> do+            withCString "range" $ \fn ->+                c_duckdb_replacement_scan_set_function_name info fn+            withValue (c_duckdb_create_int64 startValue) $ \startVal ->+                c_duckdb_replacement_scan_add_parameter info startVal+            withValue (c_duckdb_create_int64 endValue) $ \endVal ->+                c_duckdb_replacement_scan_add_parameter info endVal+        "failing_magic" ->+            withCString "replacement rejected by test callback" $ \msg ->+                c_duckdb_replacement_scan_set_error info msg+        _ ->+            pure ()++assertReplacementQuery :: DuckDBConnection -> Int64 -> Int64 -> IO ()+assertReplacementQuery conn startValue countValue =+    withResult conn "SELECT range FROM haskell_magic ORDER BY range" \resPtr -> do+        rowCount <- c_duckdb_row_count resPtr+        rowCount @?= fromIntegral countValue+        forM_ [0 .. countValue - 1] \idx -> do+            value <- c_duckdb_value_int64 resPtr 0 (fromIntegral idx)+            value @?= startValue + idx++assertReplacementError :: DuckDBConnection -> IO ()+assertReplacementError conn =+    withCString "SELECT * FROM failing_magic" \sql ->+        alloca \resPtr -> do+            state <- c_duckdb_query conn sql resPtr+            state @?= DuckDBError+            errPtr <- c_duckdb_result_error resPtr+            errMsg <- peekCString errPtr+            assertBool "replacement error message should surface" ("rejected" `isInfixOf` errMsg)+            c_duckdb_destroy_result resPtr++foreign import ccall safe "wrapper"+    mkReplacementCallback ::+        (DuckDBReplacementScanInfo -> CString -> Ptr () -> IO ()) ->+        IO DuckDBReplacementCallback
+ test/ResultFunctionsTest.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE BlockArguments #-}++module ResultFunctionsTest (tests) where++import Control.Monad (forM_, when)+import Database.DuckDB.FFI+import Foreign.C.String (withCString)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (nullPtr)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Result Functions"+        [ chunkIntrospection+        ]++chunkIntrospection :: TestTree+chunkIntrospection =+    testCase "result chunk information and streaming state" $+        withDatabase \db ->+            withConnection db \conn -> do+                let createSQL = "CREATE TABLE items(id INTEGER);"+                    insertSQL =+                        "INSERT INTO items VALUES (1), (2), (3), (4), (5);"+                forM_ [createSQL, insertSQL] \sql ->+                    withCString sql \cSql ->+                        alloca \resPtr -> do+                            st <- c_duckdb_query conn cSql resPtr+                            st @?= DuckDBSuccess+                            c_duckdb_destroy_result resPtr++                withCString "SELECT * FROM items" \selectSQL ->+                    alloca \resPtr -> do+                        st <- c_duckdb_query conn selectSQL resPtr+                        st @?= DuckDBSuccess++                        chunkCount <- c_duckdb_result_chunk_count resPtr+                        assertBool "chunk count should be positive" (chunkCount > 0)++                        returnType <- c_duckdb_result_return_type resPtr+                        returnType @?= DuckDBResultTypeQueryResult++                        streamingFlag <- c_duckdb_result_is_streaming resPtr+                        streamingFlag @?= CBool 0++                        -- Retrieve first chunk if available+                        when (chunkCount > 0) $ do+                            chunk0 <- c_duckdb_result_get_chunk resPtr 0+                            assertBool "first chunk should not be null" (chunk0 /= nullPtr)+                            -- Requesting beyond the available chunk count should return null+                            chunkInvalid <- c_duckdb_result_get_chunk resPtr chunkCount+                            assertBool "out-of-range chunk should be null" (chunkInvalid == nullPtr)++                        c_duckdb_destroy_result resPtr
+ test/SafeFetchTest.hs view
@@ -0,0 +1,172 @@+{-# LANGUAGE BlockArguments #-}++module SafeFetchTest (tests) where++import Control.Monad (when)+import Data.Int (Int16, Int32, Int64, Int8)+import Data.Time.Calendar (diffDays, fromGregorian)+import Data.Word (Word16, Word32, Word64, Word8)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, peekCStringLen, withCString)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Marshal.Array (peekArray)+import Foreign.Ptr (Ptr, castPtr, nullPtr)+import Foreign.Storable (peek)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertFailure, testCase, (@?=))+import Utils (withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Safe Fetch Functions"+        [safeFetchRoundtrip]++safeFetchRoundtrip :: TestTree+safeFetchRoundtrip =+    testCase "retrieve various types via safe fetch accessors" $+        withDatabase \db ->+            withConnection db \conn -> do+                let query =+                        "SELECT "+                            <> "TRUE AS b," -- 0+                            <> "(-5)::TINYINT AS i8," -- 1+                            <> "(-300)::SMALLINT AS i16," -- 2+                            <> "(-4000000)::INTEGER AS i32," -- 3+                            <> "(-5000000000000)::BIGINT AS i64," -- 4+                            <> "('9223372036854775809')::HUGEINT AS huge," -- 5+                            <> "200::UTINYINT AS u8," -- 6+                            <> "60000::USMALLINT AS u16," -- 7+                            <> "4000000000::UINTEGER AS u32," -- 8+                            <> "('18446744073709551615')::UBIGINT AS u64," -- 9+                            <> "('18446744073709551616')::HUGEINT AS uhuge," -- 10+                            <> "1.5::REAL AS f32," -- 11+                            <> "2.5::DOUBLE AS f64," -- 12+                            <> "DATE '2021-07-20' AS d," -- 13+                            <> "TIME '12:34:56' AS t," -- 14+                            <> "TIMESTAMP '2021-07-20 12:34:56' AS ts," -- 15+                            <> "INTERVAL '1 day 2 hours' AS iv," -- 16+                            <> "('12345.67')::DECIMAL(18,2) AS dec," -- 17+                            <> "'this string is definitely longer than inline storage'::VARCHAR AS txt," -- 18+                            <> "CAST('abc' AS BLOB) AS blob_col," -- 19+                            <> "NULL::INTEGER AS null_col" -- 20+                withCString query \cQuery ->+                    alloca \resPtr -> do+                        state <- c_duckdb_query conn cQuery resPtr+                        when (state /= DuckDBSuccess) $ do+                            errPtr <- c_duckdb_result_error resPtr+                            errMsg <- if errPtr == nullPtr then pure "<no error>" else peekCString errPtr+                            assertFailure ("duckdb_query failed: " <> errMsg)+                        state @?= DuckDBSuccess++                        -- Boolean+                        c_duckdb_value_is_null resPtr 0 0 >>= (@?= CBool 0)+                        c_duckdb_value_boolean resPtr 0 0 >>= (@?= CBool 1)++                        -- Signed integers+                        c_duckdb_value_int8 resPtr 1 0 >>= (@?= (-5 :: Int8))+                        c_duckdb_value_int16 resPtr 2 0 >>= (@?= (-300 :: Int16))+                        c_duckdb_value_int32 resPtr 3 0 >>= (@?= (-4000000 :: Int32))+                        c_duckdb_value_int64 resPtr 4 0 >>= (@?= (-5000000000000 :: Int64))++                        -- HugeInt+                        alloca \hugePtr -> do+                            c_duckdb_value_hugeint resPtr 5 0 hugePtr+                            DuckDBHugeInt{duckDBHugeIntLower = lowerHuge, duckDBHugeIntUpper = upperHuge} <- peek hugePtr+                            lowerHuge @?= 9223372036854775809+                            upperHuge @?= 0++                        -- Unsigned integers+                        c_duckdb_value_uint8 resPtr 6 0 >>= (@?= (200 :: Word8))+                        c_duckdb_value_uint16 resPtr 7 0 >>= (@?= (60000 :: Word16))+                        c_duckdb_value_uint32 resPtr 8 0 >>= (@?= (4000000000 :: Word32))+                        c_duckdb_value_uint64 resPtr 9 0 >>= (@?= (maxBound :: Word64))++                        alloca \uhugePtr -> do+                            c_duckdb_value_uhugeint resPtr 10 0 uhugePtr+                            DuckDBUHugeInt{duckDBUHugeIntLower = lowerUHuge, duckDBUHugeIntUpper = upperUHuge} <- peek uhugePtr+                            lowerUHuge @?= 0+                            upperUHuge @?= 1++                        -- Floating point+                        valFloat <- c_duckdb_value_float resPtr 11 0+                        realToFrac valFloat @?= (1.5 :: Double)+                        valDouble <- c_duckdb_value_double resPtr 12 0+                        realToFrac valDouble @?= (2.5 :: Double)++                        -- Date/time/timestamp+                        DuckDBDate dateVal <- c_duckdb_value_date resPtr 13 0+                        let expectedDaysInteger = diffDays targetDay epochDay+                            expectedDays = fromIntegral expectedDaysInteger :: Int32+                        dateVal @?= expectedDays++                        DuckDBTime timeVal <- c_duckdb_value_time resPtr 14 0+                        let expectedTimeMicros :: Int64+                            expectedTimeMicros = ((12 * 60 + 34) * 60 + 56) * 1000000+                        timeVal @?= expectedTimeMicros++                        DuckDBTimestamp tsVal <- c_duckdb_value_timestamp resPtr 15 0+                        let expectedTimestampMicros :: Int64+                            expectedTimestampMicros =+                                fromIntegral expectedDaysInteger * 86400000000 + expectedTimeMicros+                        tsVal @?= expectedTimestampMicros++                        -- Interval+                        alloca \intervalPtr -> do+                            c_duckdb_value_interval resPtr 16 0 intervalPtr+                            DuckDBInterval{duckDBIntervalMonths = months, duckDBIntervalDays = days, duckDBIntervalMicros = micros} <- peek intervalPtr+                            months @?= 0+                            days @?= 1+                            micros @?= 7200000000++                        -- Decimal+                        alloca \decimalPtr -> do+                            c_duckdb_value_decimal resPtr 17 0 decimalPtr+                            DuckDBDecimal{duckDBDecimalWidth = width, duckDBDecimalScale = scale, duckDBDecimalValue = decValue} <- peek decimalPtr+                            width @?= 18+                            scale @?= 2+                            DuckDBHugeInt{duckDBHugeIntLower = decimalLower, duckDBHugeIntUpper = decimalUpper} <- pure decValue+                            decimalLower @?= 1234567+                            decimalUpper @?= 0++                        -- Strings+                        strVarchar <- c_duckdb_value_varchar resPtr 18 0+                        varcharText <- peekCString strVarchar+                        varcharText @?= stringLiteral+                        c_duckdb_free (castPtr strVarchar)++                        alloca \stringPtr -> do+                            c_duckdb_value_string resPtr 18 0 stringPtr+                            DuckDBString{duckDBStringData = datPtr, duckDBStringSize = datSize} <- peek stringPtr+                            bytes <- peekCStringLen (datPtr, fromIntegral datSize)+                            bytes @?= stringLiteral+                            when (datPtr /= nullPtr) $ c_duckdb_free (castPtr datPtr)++                        alloca \stringInternalPtr -> do+                            c_duckdb_value_string_internal resPtr 18 0 stringInternalPtr+                            DuckDBString{duckDBStringData = datPtr, duckDBStringSize = datSize} <- peek stringInternalPtr+                            bytes <- peekCStringLen (datPtr, fromIntegral datSize)+                            bytes @?= stringLiteral++                        varcharInternal <- c_duckdb_value_varchar_internal resPtr 18 0+                        peekCString varcharInternal >>= (@?= stringLiteral)++                        -- Blob+                        alloca \blobPtr -> do+                            c_duckdb_value_blob resPtr 19 0 blobPtr+                            DuckDBBlob{duckDBBlobData = blobData, duckDBBlobSize = blobSize} <- peek blobPtr+                            let expectedBlob :: [Word8]+                                expectedBlob = map (fromIntegral . fromEnum) ("abc" :: String)+                            peekArray (fromIntegral blobSize) (castPtr blobData :: Ptr Word8) >>= (@?= expectedBlob)+                            c_duckdb_free (castPtr blobData)++                        -- Null handling+                        c_duckdb_value_is_null resPtr 20 0 >>= (@?= CBool 1)++                        c_duckdb_destroy_result resPtr+  where+    stringLiteral :: String+    stringLiteral = "this string is definitely longer than inline storage"+    targetDay = fromGregorian 2021 7 20+    epochDay = fromGregorian 1970 1 1
+ test/ScalarFunctionsTest.hs view
@@ -0,0 +1,228 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE ForeignFunctionInterface #-}++module ScalarFunctionsTest (tests) where++import Control.Exception (bracket)+import Control.Monad (forM_, unless, when)+import Data.Char (toLower)+import Data.Int (Int32)+import Data.List (isInfixOf)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, withCString)+import Foreign.Marshal.Alloc (alloca, free, mallocBytes)+import Foreign.Ptr (Ptr, castPtr, nullPtr)+import Foreign.Storable (peek, peekElemOff, poke, pokeElemOff, sizeOf)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (withConnection, withDatabase, withResultCString)++-- | Coverage of scalar function registration and bind/exec helpers.+tests :: TestTree+tests =+    testGroup+        "Scalar Functions"+        [ scalarFunctionRoundtrip+        , scalarFunctionSetFeatures+        ]++scalarFunctionRoundtrip :: TestTree+scalarFunctionRoundtrip =+    testCase "register custom scalar function and execute" $ do+        withDatabase \db ->+            withConnection db \conn -> do+                withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType -> do+                    funPtr <- mkScalarFun negateCallback+                    withScalarFunction \fun -> do+                        withCString "negate_int" $ \name -> c_duckdb_scalar_function_set_name fun name+                        c_duckdb_scalar_function_add_parameter fun intType+                        c_duckdb_scalar_function_set_return_type fun intType+                        c_duckdb_scalar_function_set_function fun funPtr++                        c_duckdb_register_scalar_function conn fun >>= (@?= DuckDBSuccess)++                        withCString "SELECT negate_int(5)" $ \sql ->+                            withResultCString conn sql $ \resPtr ->+                                c_duckdb_value_int32 resPtr 0 0 >>= (@?= (-5))++-- Callback ------------------------------------------------------------------++negateCallback :: DuckDBFunctionInfo -> DuckDBDataChunk -> DuckDBVector -> IO ()+negateCallback _ chunk outVec = do+    rows <- c_duckdb_data_chunk_get_size chunk+    inVec <- c_duckdb_data_chunk_get_vector chunk 0+    inPtr <- castToInt32Ptr <$> c_duckdb_vector_get_data inVec+    outPtr <- castToInt32Ptr <$> c_duckdb_vector_get_data outVec++    forM_ [0 .. fromIntegral rows - 1] \idx -> do+        val <- peekElemOff inPtr idx+        pokeElemOff outPtr idx (negate val)++    c_duckdb_data_chunk_set_size chunk rows++varargBind :: Ptr () -> DuckDBDeleteCallback -> DuckDBCopyCallback -> Int -> DuckDBBindInfo -> IO ()+varargBind extraPtr deleteBindCb copyCb bindDataSize info = do+    extra <- c_duckdb_scalar_function_bind_get_extra_info info+    assertBool "extra info propagated to bind phase" (extra == extraPtr)++    argCount <- c_duckdb_scalar_function_bind_get_argument_count info++    alloca \ctxPtr -> do+        c_duckdb_scalar_function_get_client_context info ctxPtr+        ctx <- peek ctxPtr+        unless (ctx == nullPtr) $+            c_duckdb_destroy_client_context ctxPtr++    if argCount == 0+        then withCString "at least one argument required" $ \msg ->+            c_duckdb_scalar_function_bind_set_error info msg+        else do+            expr <- c_duckdb_scalar_function_bind_get_argument info 0+            alloca \exprPtr -> poke exprPtr expr >> c_duckdb_destroy_expression exprPtr++            bindStorage <- mallocBytes bindDataSize+            poke (castPtr bindStorage :: Ptr Int32) (fromIntegral argCount)++            c_duckdb_scalar_function_set_bind_data info bindStorage deleteBindCb+            c_duckdb_scalar_function_set_bind_data_copy info copyCb++varargExec :: Ptr () -> Int -> DuckDBFunctionInfo -> DuckDBDataChunk -> DuckDBVector -> IO ()+varargExec extraPtr _ info chunk outVec = do+    bindData <- c_duckdb_scalar_function_get_bind_data info+    argCount <-+        if bindData == nullPtr+            then pure (0 :: Integer)+            else fromIntegral <$> peek (castPtr bindData :: Ptr Int32)++    extra <- c_duckdb_scalar_function_get_extra_info info+    assertBool "extra info visible during execution" (extra == extraPtr)++    rowCount <- fromIntegral <$> c_duckdb_data_chunk_get_size chunk+    vectors <- mapM (c_duckdb_data_chunk_get_vector chunk . fromIntegral) [0 .. argCount - 1]+    dataPtrs <- mapM (fmap castToInt32Ptr . c_duckdb_vector_get_data) vectors+    outPtr <- castToInt32Ptr <$> c_duckdb_vector_get_data outVec++    let loop idx+            | idx >= rowCount = pure False+            | otherwise = do+                values <- mapM (`peekElemOff` idx) dataPtrs+                let total = sum values+                if total < 0+                    then do+                        withCString "negative sum not allowed" $ \msg ->+                            c_duckdb_scalar_function_set_error info msg+                        c_duckdb_data_chunk_set_size chunk 0+                        pure True+                    else do+                        pokeElemOff outPtr idx total+                        loop (idx + 1)++    errored <- loop 0+    unless errored $+        c_duckdb_data_chunk_set_size chunk (fromIntegral rowCount)++scalarFunctionSetFeatures :: TestTree+scalarFunctionSetFeatures =+    testCase "scalar function bind helpers and function sets" $+        withDatabase \db ->+            withConnection db \conn ->+                withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType -> do+                    let bindDataSize = sizeOf (undefined :: Int32)++                    extraStorage <- mallocBytes bindDataSize+                    poke (castPtr extraStorage) (123 :: Int32)+                    deleteExtraCb <- mkDeleteCallback \ptr ->+                        when (ptr /= nullPtr) (free ptr)++                    deleteBindCb <- mkDeleteCallback \ptr ->+                        when (ptr /= nullPtr) (free ptr)++                    let copyBindData src+                            | src == nullPtr = pure nullPtr+                            | otherwise = do+                                newPtr <- mallocBytes bindDataSize+                                value <- peek (castPtr src :: Ptr Int32)+                                poke (castPtr newPtr) value+                                pure newPtr+                    copyCb <- mkCopyCallback copyBindData++                    bindFun <- mkScalarBindFun (varargBind (castPtr extraStorage) deleteBindCb copyCb bindDataSize)+                    execFun <- mkScalarFun (varargExec (castPtr extraStorage) bindDataSize)++                    let functionName = "haskell_vararg"++                    withScalarFunction \fun -> do+                        withCString functionName $ \cName ->+                            c_duckdb_scalar_function_set_name fun cName+                        c_duckdb_scalar_function_set_return_type fun intType+                        c_duckdb_scalar_function_set_varargs fun intType+                        c_duckdb_scalar_function_set_special_handling fun+                        c_duckdb_scalar_function_set_volatile fun+                        c_duckdb_scalar_function_set_extra_info fun (castPtr extraStorage) deleteExtraCb+                        c_duckdb_scalar_function_set_bind fun bindFun+                        c_duckdb_scalar_function_set_function fun execFun++                        withScalarFunctionSet functionName \funSet -> do+                            c_duckdb_add_scalar_function_to_set funSet fun >>= (@?= DuckDBSuccess)+                            c_duckdb_register_scalar_function_set conn funSet >>= (@?= DuckDBSuccess)++                            withCString "SELECT haskell_vararg(1, 2, 3)" \sql ->+                                withResultCString conn sql \resPtr ->+                                    c_duckdb_value_int32 resPtr 0 0 >>= (@?= 6)++                            expectQueryError conn "SELECT haskell_vararg()" "at least one argument"+                            expectQueryError conn "SELECT haskell_vararg(-5, 2)" "negative sum"++                    pure ()++-- Wrapper builder -----------------------------------------------------------++foreign import ccall "wrapper"+    mkScalarFun :: (DuckDBFunctionInfo -> DuckDBDataChunk -> DuckDBVector -> IO ()) -> IO DuckDBScalarFunctionFun++foreign import ccall "wrapper"+    mkScalarBindFun :: (DuckDBBindInfo -> IO ()) -> IO DuckDBScalarFunctionBindFun++foreign import ccall "wrapper"+    mkDeleteCallback :: (Ptr () -> IO ()) -> IO DuckDBDeleteCallback++foreign import ccall "wrapper"+    mkCopyCallback :: (Ptr () -> IO (Ptr ())) -> IO DuckDBCopyCallback++-- Resource helpers ----------------------------------------------------------++withLogicalType :: IO DuckDBLogicalType -> (DuckDBLogicalType -> IO a) -> IO a+withLogicalType acquire = bracket acquire destroyLogicalType++destroyLogicalType :: DuckDBLogicalType -> IO ()+destroyLogicalType lt = alloca \ptr -> poke ptr lt >> c_duckdb_destroy_logical_type ptr++withScalarFunction :: (DuckDBScalarFunction -> IO a) -> IO a+withScalarFunction = bracket c_duckdb_create_scalar_function destroy+  where+    destroy fun = alloca \ptr -> poke ptr fun >> c_duckdb_destroy_scalar_function ptr++withScalarFunctionSet :: String -> (DuckDBScalarFunctionSet -> IO a) -> IO a+withScalarFunctionSet name action =+    withCString name \cName ->+        bracket (c_duckdb_create_scalar_function_set cName) destroy action+  where+    destroy set = alloca \ptr -> poke ptr set >> c_duckdb_destroy_scalar_function_set ptr++expectQueryError :: DuckDBConnection -> String -> String -> IO ()+expectQueryError conn sql expectedFragment =+    withCString sql \sqlPtr ->+        alloca \resPtr -> do+            state <- c_duckdb_query conn sqlPtr resPtr+            state @?= DuckDBError+            errPtr <- c_duckdb_result_error resPtr+            errMsg <- peekCString errPtr+            let needle = map toLower expectedFragment+                haystack = map toLower errMsg+            assertBool+                ("expected fragment \"" ++ expectedFragment ++ "\" in error message:\n" ++ errMsg)+                (needle `isInfixOf` haystack)+            c_duckdb_destroy_result resPtr++castToInt32Ptr :: Ptr () -> Ptr Int32+castToInt32Ptr = castPtr
+ test/SelectionVectorTest.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE BlockArguments #-}++module SelectionVectorTest (tests) where++import Control.Monad (forM_)+import Data.Int (Int32)+import Data.Word (Word32)+import Database.DuckDB.FFI+import Foreign.Ptr (Ptr, castPtr, nullPtr)+import Foreign.Storable (peekElemOff, pokeElemOff)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (withLogicalType, withSelectionVector, withVector)++tests :: TestTree+tests =+    testGroup+        "Selection Vector Interface"+        [ selectionVectorPointerWritable+        , selectionVectorCopySelection+        ]++selectionVectorPointerWritable :: TestTree+selectionVectorPointerWritable =+    testCase "selection vector exposes writable data pointer" $ do+        withSelectionVector 4 \selVec -> do+            dataPtr <- c_duckdb_selection_vector_get_data_ptr selVec+            assertBool "data pointer should be non-null" (dataPtr /= nullPtr)+            forM_ (zip [0 ..] [0, 2, 4, 6 :: Word32]) (uncurry (pokeElemOff dataPtr))+            fetched <- mapM (peekElemOff dataPtr) [0 .. 3]+            fetched @?= [0, 2, 4, 6]++selectionVectorCopySelection :: TestTree+selectionVectorCopySelection =+    testCase "vector_copy_sel copies selected rows" $ do+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType -> do+            withVector (c_duckdb_create_vector intType 4) \srcVec -> do+                srcPtr <- vectorDataPtr srcVec+                forM_ (zip [0 ..] [10, 20, 30, 40 :: Int32]) (uncurry (pokeElemOff srcPtr))++                withSelectionVector 2 \selVec -> do+                    selPtr <- c_duckdb_selection_vector_get_data_ptr selVec+                    pokeElemOff selPtr 0 1+                    pokeElemOff selPtr 1 3++                    withVector (c_duckdb_create_vector intType 2) \dstVec -> do+                        c_duckdb_vector_copy_sel srcVec dstVec selVec 2 0 0+                        dstPtr <- vectorDataPtr dstVec+                        val0 <- peekElemOff dstPtr 0+                        val1 <- peekElemOff dstPtr 1+                        val0 @?= 20+                        val1 @?= 40++-- Helpers -------------------------------------------------------------------++vectorDataPtr :: DuckDBVector -> IO (Ptr Int32)+vectorDataPtr vec = do+    raw <- c_duckdb_vector_get_data vec+    pure (castPtr raw)
+ test/StreamingResultTest.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE BlockArguments #-}++module StreamingResultTest (tests) where++import Control.Monad (void)+import Data.Int (Int64)+import Data.List (intercalate)+import Database.DuckDB.FFI+import Foreign.C.String (CString, withCString)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (Ptr, nullPtr)+import Foreign.Storable (peek, poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (withConnection, withDatabase, withResult)++tests :: TestTree+tests =+    testGroup+        "Streaming Result Interface"+        [ streamingFetchConsumesAllChunks+        , materializedFetchChunkExhaustsResult+        ]++streamingFetchConsumesAllChunks :: TestTree+streamingFetchConsumesAllChunks =+    testCase "stream_fetch_chunk yields data until exhaustion" $+        withDatabase \db ->+            withConnection db \conn -> do+                setupTable conn "streaming_table" 6++                withCString "SELECT id FROM streaming_table ORDER BY id" \querySql ->+                    alloca \stmtPtr -> do+                        prepareOk <- c_duckdb_prepare conn querySql stmtPtr+                        prepareOk @?= DuckDBSuccess+                        stmt <- peek stmtPtr+                        assertBool "prepared statement should not be null" (stmt /= nullPtr)++                        alloca \pendingPtr -> do+                            stPending <- c_duckdb_pending_prepared_streaming stmt pendingPtr+                            stPending @?= DuckDBSuccess+                            pending <- peek pendingPtr+                            assertBool "pending result should not be null" (pending /= nullPtr)++                            void (c_duckdb_pending_execute_task pending)++                            alloca \resPtr -> do+                                execState <- c_duckdb_execute_pending pending resPtr+                                execState @?= DuckDBSuccess++                                streamingFlag <- c_duckdb_result_is_streaming resPtr+                                streamingFlag @?= CBool 1++                                totalRows <- consumeStreamingChunks resPtr 0+                                totalRows @?= 6++                                c_duckdb_destroy_result resPtr++                            c_duckdb_destroy_pending pendingPtr++                        c_duckdb_destroy_prepare stmtPtr++materializedFetchChunkExhaustsResult :: TestTree+materializedFetchChunkExhaustsResult =+    testCase "fetch_chunk provides chunk and then null for materialized result" $+        withDatabase \db ->+            withConnection db \conn -> do+                setupTable conn "materialized_table" 4++                withResult conn "SELECT id FROM materialized_table ORDER BY id" \resPtr -> do+                    chunk <- c_duckdb_fetch_chunk resPtr+                    assertBool "first fetch_chunk should yield a chunk" (chunk /= nullPtr)++                    chunkSize <- c_duckdb_data_chunk_get_size chunk+                    assertBool "materialized chunk should have rows" (chunkSize > 0)++                    destroyChunk chunk++                    chunkNext <- c_duckdb_fetch_chunk resPtr+                    chunkNext @?= nullPtr++-- helpers ------------------------------------------------------------------++consumeStreamingChunks :: Ptr DuckDBResult -> Int64 -> IO Int64+consumeStreamingChunks resPtr acc = do+    chunk <- c_duckdb_stream_fetch_chunk resPtr+    if chunk == nullPtr+        then pure acc+        else do+            chunkSize <- c_duckdb_data_chunk_get_size chunk+            assertBool "streaming chunk should not be empty" (chunkSize > 0)+            destroyChunk chunk+            consumeStreamingChunks resPtr (acc + fromIntegral chunkSize)++setupTable :: DuckDBConnection -> String -> Int -> IO ()+setupTable conn tableName totalRows = do+    withCString ("CREATE TABLE " <> tableName <> " (id INTEGER);") $ \createSql ->+        execStatement conn createSql+    let values = intercalate ", " ["(" <> show i <> ")" | i <- [1 .. totalRows]]+        insertSql = "INSERT INTO " <> tableName <> " VALUES " <> values <> ";"+    withCString insertSql $ \insertCStr ->+        execStatement conn insertCStr++execStatement :: DuckDBConnection -> CString -> IO ()+execStatement conn sql =+    alloca \resPtr -> do+        st <- c_duckdb_query conn sql resPtr+        st @?= DuckDBSuccess+        c_duckdb_destroy_result resPtr++destroyChunk :: DuckDBDataChunk -> IO ()+destroyChunk chunk =+    alloca \ptr -> do+        poke ptr chunk+        c_duckdb_destroy_data_chunk ptr
+ test/TableDescriptionTest.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE BlockArguments #-}++module TableDescriptionTest (tests) where++import Control.Exception (finally)+import Control.Monad (when)+import Database.DuckDB.FFI+import Foreign.C.String (CString, peekCString, withCString)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (Ptr, castPtr, nullPtr)+import Foreign.Storable (peek)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Table Description"+        [ tableDescriptionLifecycle+        , tableDescriptionExtended+        , tableDescriptionErrorHandling+        ]++tableDescriptionLifecycle :: TestTree+tableDescriptionLifecycle =+    testCase "inspect column names and defaults for main schema tables" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement+                    conn+                    "CREATE TABLE description_demo(id INTEGER, name VARCHAR DEFAULT 'guest', active BOOLEAN DEFAULT TRUE)"++                withTableDescription conn Nothing "description_demo" \desc -> do+                    checkColumn desc 0 "id" False+                    checkColumn desc 1 "name" True+                    checkColumn desc 2 "active" True+                    c_duckdb_table_description_error desc >>= (@?= nullPtr)++tableDescriptionExtended :: TestTree+tableDescriptionExtended =+    testCase "describe table in custom schema via extended constructor" $+        withDatabase \db ->+            withConnection db \conn -> do+                runStatement conn "CREATE SCHEMA custom_schema"+                runStatement conn "CREATE TABLE custom_schema.ext_demo(id INTEGER DEFAULT 7, note VARCHAR)"++                withTableDescriptionExt conn Nothing (Just "custom_schema") "ext_demo" \desc -> do+                    checkColumn desc 0 "id" True+                    checkColumn desc 1 "note" False+                    c_duckdb_table_description_error desc >>= (@?= nullPtr)++tableDescriptionErrorHandling :: TestTree+tableDescriptionErrorHandling =+    testCase "surface error information when describing missing tables" $+        withDatabase \db ->+            withConnection db \conn ->+                withCString "missing_table" \tablePtr ->+                    alloca \descPtr -> do+                        state <- c_duckdb_table_description_create conn nullPtr tablePtr descPtr+                        state @?= DuckDBError+                        desc <- peek descPtr+                        let cleanup = c_duckdb_table_description_destroy descPtr+                        let action =+                                if desc == nullPtr+                                    then assertFailure "table description handle should be populated on error"+                                    else do+                                        errPtr <- c_duckdb_table_description_error desc+                                        assertBool "error pointer should not be null" (errPtr /= nullPtr)+                                        errMsg <- peekCString errPtr+                                        assertBool "error message should not be empty" (not (null errMsg))+                        action `finally` cleanup++-- Helpers -------------------------------------------------------------------++runStatement :: DuckDBConnection -> String -> IO ()+runStatement conn sql =+    withCString sql \sqlPtr ->+        alloca \resPtr -> do+            state <- c_duckdb_query conn sqlPtr resPtr+            if state == DuckDBSuccess+                then c_duckdb_destroy_result resPtr+                else do+                    errPtr <- c_duckdb_result_error resPtr+                    errMsg <-+                        if errPtr == nullPtr+                            then pure "unknown error"+                            else peekCString errPtr+                    c_duckdb_destroy_result resPtr+                    assertFailure ("duckdb_query failed: " <> errMsg)++withTableDescription :: DuckDBConnection -> Maybe String -> String -> (DuckDBTableDescription -> IO a) -> IO a+withTableDescription conn schema table action =+    withMaybeCString schema \schemaPtr ->+        withCString table \tablePtr ->+            withDescriptionHandle (c_duckdb_table_description_create conn schemaPtr tablePtr) action++withTableDescriptionExt :: DuckDBConnection -> Maybe String -> Maybe String -> String -> (DuckDBTableDescription -> IO a) -> IO a+withTableDescriptionExt conn catalog schema table action =+    withMaybeCString catalog \catalogPtr ->+        withMaybeCString schema \schemaPtr ->+            withCString table \tablePtr ->+                withDescriptionHandle (c_duckdb_table_description_create_ext conn catalogPtr schemaPtr tablePtr) action++withDescriptionHandle :: (Ptr DuckDBTableDescription -> IO DuckDBState) -> (DuckDBTableDescription -> IO a) -> IO a+withDescriptionHandle acquire action =+    alloca \descPtr -> do+        state <- acquire descPtr+        state @?= DuckDBSuccess+        desc <- peek descPtr+        when (desc == nullPtr) $+            assertFailure "table description handle should not be null"+        let cleanup = c_duckdb_table_description_destroy descPtr+        action desc `finally` cleanup++withMaybeCString :: Maybe String -> (CString -> IO a) -> IO a+withMaybeCString Nothing action = action nullPtr+withMaybeCString (Just txt) action = withCString txt action++checkColumn :: DuckDBTableDescription -> DuckDBIdx -> String -> Bool -> IO ()+checkColumn desc idx expectedName expectedDefault = do+    hasDef <- columnHasDefault desc idx+    hasDef @?= expectedDefault+    name <- getColumnName desc idx+    name @?= expectedName++getColumnName :: DuckDBTableDescription -> DuckDBIdx -> IO String+getColumnName desc idx = do+    namePtr <- c_duckdb_table_description_get_column_name desc idx+    assertBool "column name pointer should not be null" (namePtr /= nullPtr)+    name <- peekCString namePtr+    c_duckdb_free (castPtr namePtr)+    pure name++columnHasDefault :: DuckDBTableDescription -> DuckDBIdx -> IO Bool+columnHasDefault desc idx =+    alloca \outPtr -> do+        state <- c_duckdb_column_has_default desc idx outPtr+        state @?= DuckDBSuccess+        CBool val <- peek outPtr+        pure (val /= 0)
+ test/TableFunctionsTest.hs view
@@ -0,0 +1,547 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE RecordWildCards #-}++module TableFunctionsTest (tests) where++import Control.Concurrent (runInBoundThread)+import Control.Exception (bracket)+import Control.Monad (forM_, when)+import Data.Char (isAsciiUpper)+import Data.IORef (IORef, modifyIORef', newIORef, readIORef, writeIORef)+import Data.Int (Int64)+import Data.List (isInfixOf)+import Data.Maybe (fromMaybe)+import Database.DuckDB.FFI+import Foreign.C.String (CString, peekCString, withCString)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca, free, mallocBytes)+import Foreign.Ptr (Ptr, castPtr, freeHaskellFunPtr, nullPtr)+import Foreign.Storable (peek, peekElemOff, poke, pokeElemOff, sizeOf)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (withConnection, withDatabase, withLogicalType, withResultCString, withValue)++tests :: TestTree+tests =+    testGroup+        "Table Functions"+        [tableFunctionLifecycle]++tableFunctionLifecycle :: TestTree+tableFunctionLifecycle =+    testCase "table function lifecycle covers core callbacks" $+        runInBoundThread do+            withDatabase \db ->+                withLogicalType (c_duckdb_create_logical_type DuckDBTypeBigInt) \bigint ->+                    withHarness bigint \harness@TableHarness{} -> do+                        (bindError, execError, initError) <-+                            withConnection db \conn ->+                                withTableFunction \tableFun -> do+                                    configureTableFunction tableFun harness+                                    c_duckdb_register_table_function conn tableFun >>= (@?= DuckDBSuccess)++                                    withCString "SELECT value, double_value FROM haskell_numbers(5, start => 2)" \sql ->+                                        withResultCString conn sql \resPtr -> do+                                            c_duckdb_column_count resPtr >>= (@?= 2)+                                            c_duckdb_row_count resPtr >>= (@?= 5)+                                            forM_ [0 .. 4] \idx -> do+                                                c_duckdb_value_int64 resPtr 0 idx >>= (@?= 2 + fromIntegral idx)+                                                c_duckdb_value_int64 resPtr 1 idx >>= (@?= 2 * (2 + fromIntegral idx))++                                    withCString "SELECT value FROM haskell_numbers(3)" \sql ->+                                        withResultCString conn sql \resPtr -> do+                                            c_duckdb_column_count resPtr >>= (@?= 1)+                                            forM_ (zip [0 ..] [0 .. 2]) \(idx, expected) ->+                                                c_duckdb_value_int64 resPtr 0 idx >>= (@?= expected)++                                    bindError <-+                                        withCString "SELECT * FROM haskell_numbers(0)" \sql ->+                                            alloca \resPtr -> do+                                                state <- c_duckdb_query conn sql resPtr+                                                errPtr <- c_duckdb_result_error resPtr+                                                msg <- peekCStringMaybe errPtr+                                                c_duckdb_destroy_result resPtr+                                                pure (state, msg)++                                    execError <-+                                        withCString "SELECT * FROM haskell_numbers(5, fail_at => 3)" \sql ->+                                            alloca \resPtr -> do+                                                state <- c_duckdb_query conn sql resPtr+                                                errPtr <- c_duckdb_result_error resPtr+                                                msg <- peekCStringMaybe errPtr+                                                c_duckdb_destroy_result resPtr+                                                pure (state, msg)++                                    initError <-+                                        withCString "SELECT * FROM haskell_numbers(2, force_init_error => 1)" \sql ->+                                            alloca \resPtr -> do+                                                state <- c_duckdb_query conn sql resPtr+                                                errPtr <- c_duckdb_result_error resPtr+                                                msg <- peekCStringMaybe errPtr+                                                c_duckdb_destroy_result resPtr+                                                pure (state, msg)++                                    pure (bindError, execError, initError)++                        stats <- snapshotStats harness+                        hsBindFreed stats @?= 3+                        hsInitFreed stats @?= 3+                        hsLocalFreed stats @?= 3+                        assertBool "client context seen" (hsClientContextSeen stats)+                        hsCounts stats @?= [5, 3, 0, 5, 2]+                        hsProjectionHistory stats+                            @?= [ ProjectionSnapshot 2 [0, 1]+                                , ProjectionSnapshot 1 [0]+                                , ProjectionSnapshot 2 [0, 1]+                                ]+                        fst bindError @?= DuckDBError+                        assertBool "bind error mentions positivity" ("positive" `contains` snd bindError)+                        fst execError @?= DuckDBError+                        assertBool "execution error mentions table function" ("execution" `contains` snd execError)+                        fst initError @?= DuckDBError+                        assertBool "init error mentions initialization" ("init" `contains` snd initError)++-- Harness --------------------------------------------------------------------++data ProjectionSnapshot = ProjectionSnapshot+    { psCount :: !Int+    , psIndices :: ![Int]+    }+    deriving (Eq, Show)++data ExecutionMode+    = ModeUnset+    | ModeNormal+    | ModeInitError+    | ModeRejected+    deriving (Eq, Show)++data TableHarness = TableHarness+    { thColumnType :: !DuckDBLogicalType+    , thExtraBuffer :: !(Ptr ())+    , thExtraCount :: !(IORef Int)+    , thBindCount :: !(IORef Int)+    , thInitCount :: !(IORef Int)+    , thLocalCount :: !(IORef Int)+    , thBindPtr :: !(IORef (Maybe (Ptr ())))+    , thInitPtr :: !(IORef (Maybe (Ptr ())))+    , thLocalPtr :: !(IORef (Maybe (Ptr ())))+    , thProjectionCurrent :: !(IORef (Maybe ProjectionSnapshot))+    , thProjectionHistory :: !(IORef [ProjectionSnapshot])+    , thClientContextSeen :: !(IORef Bool)+    , thModeRef :: !(IORef ExecutionMode)+    , thCountHistory :: !(IORef [Int64])+    , thCallbacks :: !TableCallbacks+    , thDeletes :: !DeleteCallbacks+    }++data TableCallbacks = TableCallbacks+    { tcBind :: !DuckDBTableFunctionBindFun+    , tcInit :: !DuckDBTableFunctionInitFun+    , tcLocalInit :: !DuckDBTableFunctionInitFun+    , tcExecute :: !DuckDBTableFunctionFun+    }++data DeleteCallbacks = DeleteCallbacks+    { dcExtra :: !DuckDBDeleteCallback+    , dcBind :: !DuckDBDeleteCallback+    , dcInit :: !DuckDBDeleteCallback+    , dcLocal :: !DuckDBDeleteCallback+    }++data HarnessStats = HarnessStats+    { hsExtraFreed :: !Int+    , hsBindFreed :: !Int+    , hsInitFreed :: !Int+    , hsLocalFreed :: !Int+    , hsClientContextSeen :: !Bool+    , hsProjectionHistory :: ![ProjectionSnapshot]+    , hsCounts :: ![Int64]+    }+    deriving (Eq, Show)++withHarness :: DuckDBLogicalType -> (TableHarness -> IO a) -> IO a+withHarness columnType action = do+    rawExtraBuffer <- mallocBytes sizeOfInt64+    let extraBuffer = castPtr rawExtraBuffer :: Ptr ()+    poke (castPtr extraBuffer :: Ptr Int64) extraInfoSentinel+    extraCount <- newIORef 0+    bindCount <- newIORef 0+    initCount <- newIORef 0+    localCount <- newIORef 0+    bindPtrRef <- newIORef Nothing+    initPtrRef <- newIORef Nothing+    localPtrRef <- newIORef Nothing+    projectionCurrent <- newIORef Nothing+    projectionHistory <- newIORef []+    clientContextSeen <- newIORef False+    modeRef <- newIORef ModeUnset+    countHistory <- newIORef []++    let takeInt64 ptr = peekElemOff (castPtr ptr :: Ptr Int64)+        putInt64 ptr = pokeElemOff (castPtr ptr :: Ptr Int64)++    extraDelete <-+        mkDeleteCallback \ptr -> do+            modifyIORef' extraCount (+ 1)+            when (ptr /= nullPtr) (free ptr)++    bindDelete <-+        mkDeleteCallback \ptr -> do+            modifyIORef' bindCount (+ 1)+            writeIORef bindPtrRef Nothing+            when (ptr /= nullPtr) (free ptr)++    initDelete <-+        mkDeleteCallback \ptr -> do+            modifyIORef' initCount (+ 1)+            writeIORef initPtrRef Nothing+            when (ptr /= nullPtr) (free ptr)++    localDelete <-+        mkDeleteCallback \ptr -> do+            modifyIORef' localCount (+ 1)+            writeIORef localPtrRef Nothing+            when (ptr /= nullPtr) (free ptr)++    let resetLifecycle = do+            writeIORef projectionCurrent Nothing+            writeIORef modeRef ModeUnset+            writeIORef bindPtrRef Nothing+            writeIORef initPtrRef Nothing+            writeIORef localPtrRef Nothing++        recordProjection snapshot = do+            writeIORef projectionCurrent (Just snapshot)+            modifyIORef' projectionHistory (<> [snapshot])++        bindCallback info = do+            resetLifecycle+            writeIORef modeRef ModeRejected+            extraPtr <- c_duckdb_bind_get_extra_info info+            extraPtr @?= extraBuffer+            sentinel <- peek (castPtr extraPtr :: Ptr Int64)+            sentinel @?= extraInfoSentinel++            alloca \ctxPtr -> do+                poke ctxPtr nullPtr+                c_duckdb_table_function_get_client_context info ctxPtr+                ctx <- peek ctxPtr+                assertBool "client context should not be null" (ctx /= nullPtr)+                cid <- c_duckdb_client_context_get_connection_id ctx+                assertBool "connection id should be non-negative" (cid >= 0)+                c_duckdb_destroy_client_context ctxPtr+                writeIORef clientContextSeen True++            paramCount <- c_duckdb_bind_get_parameter_count info+            paramCount @?= 1++            withValue (c_duckdb_bind_get_parameter info 0) \value -> do+                countValue <- c_duckdb_get_int64 value+                modifyIORef' countHistory (<> [countValue])+                if countValue <= 0+                    then withCString "count must be positive" $ \msg -> c_duckdb_bind_set_error info msg+                    else do+                        startValue <-+                            withCString "start" \name ->+                                fmap (fromMaybe 0) (withOptionalValue (c_duckdb_bind_get_named_parameter info name) c_duckdb_get_int64)+                        failAtValue <-+                            withCString "fail_at" \name ->+                                fmap (fromMaybe (-1)) (withOptionalValue (c_duckdb_bind_get_named_parameter info name) c_duckdb_get_int64)+                        initErrValue <-+                            withCString "force_init_error" \name ->+                                fmap (fromMaybe 0) (withOptionalValue (c_duckdb_bind_get_named_parameter info name) c_duckdb_get_int64)+                        absentValue <-+                            withCString "missing" \name ->+                                withOptionalValue (c_duckdb_bind_get_named_parameter info name) c_duckdb_get_int64+                        absentValue @?= Nothing++                        withCString "value" \col -> c_duckdb_bind_add_result_column info col columnType+                        withCString "double_value" \col -> c_duckdb_bind_add_result_column info col columnType+                        c_duckdb_bind_set_cardinality info (fromIntegral countValue) (toCBool True)++                        if initErrValue /= 0+                            then do+                                writeIORef modeRef ModeInitError+                                writeIORef bindPtrRef Nothing+                            else do+                                cfgPtr <- mallocBytes (3 * sizeOfInt64)+                                putInt64 cfgPtr 0 startValue+                                putInt64 cfgPtr 1 countValue+                                putInt64 cfgPtr 2 failAtValue+                                c_duckdb_bind_set_bind_data info (castPtr cfgPtr) bindDelete+                                writeIORef bindPtrRef (Just (castPtr cfgPtr))+                                writeIORef modeRef ModeNormal++        initCallback info = do+            extraPtr <- c_duckdb_init_get_extra_info info+            extraPtr @?= extraBuffer+            mode <- readIORef modeRef+            case mode of+                ModeInitError -> do+                    bindRaw <- c_duckdb_init_get_bind_data info+                    bindRaw @?= nullPtr+                    withCString "init rejected missing bind data" $ \msg -> c_duckdb_init_set_error info msg+                ModeNormal -> do+                    bindRaw <- c_duckdb_init_get_bind_data info+                    stored <- readIORef bindPtrRef+                    case stored of+                        Nothing -> assertFailure "bind data expected during init"+                        Just expected -> bindRaw @?= expected++                    startValue <- takeInt64 bindRaw 0+                    countValue <- takeInt64 bindRaw 1+                    failAtValue <- takeInt64 bindRaw 2++                    columnCount <- fromIntegral <$> c_duckdb_init_get_column_count info+                    indices <-+                        mapM+                            (fmap (fromIntegral :: DuckDBIdx -> Int) . c_duckdb_init_get_column_index info . fromIntegral)+                            [0 .. columnCount - 1]+                    recordProjection ProjectionSnapshot{psCount = columnCount, psIndices = indices}++                    statePtr <- mallocBytes (3 * sizeOfInt64)+                    putInt64 statePtr 0 startValue+                    putInt64 statePtr 1 (startValue + countValue)+                    putInt64 statePtr 2 failAtValue+                    c_duckdb_init_set_init_data info (castPtr statePtr) initDelete+                    writeIORef initPtrRef (Just (castPtr statePtr))+                    c_duckdb_init_set_max_threads info 1+                _ -> pure ()++        localInitCallback info = do+            extraPtr <- c_duckdb_init_get_extra_info info+            extraPtr @?= extraBuffer+            mode <- readIORef modeRef+            case mode of+                ModeNormal -> do+                    bindRaw <- c_duckdb_init_get_bind_data info+                    stored <- readIORef bindPtrRef+                    case stored of+                        Nothing -> assertFailure "bind data should be present during local init"+                        Just expected -> bindRaw @?= expected+                    localPtr <- mallocBytes sizeOfInt64+                    poke (castPtr localPtr :: Ptr Int64) localInitSentinel+                    c_duckdb_init_set_init_data info (castPtr localPtr) localDelete+                    writeIORef localPtrRef (Just (castPtr localPtr))+                ModeInitError -> withCString "local init skipped after init error" $ \msg -> c_duckdb_init_set_error info msg+                _ -> pure ()++        executeCallback info chunk = do+            extraPtr <- c_duckdb_function_get_extra_info info+            extraPtr @?= extraBuffer++            mode <- readIORef modeRef+            case mode of+                ModeNormal -> do+                    bindRaw <- c_duckdb_function_get_bind_data info+                    storedBind <- readIORef bindPtrRef+                    case storedBind of+                        Nothing -> assertFailure "bind data should be recorded before execution"+                        Just expected -> bindRaw @?= expected++                    initRaw <- c_duckdb_function_get_init_data info+                    storedInit <- readIORef initPtrRef+                    case storedInit of+                        Nothing -> assertFailure "init data should exist during execution"+                        Just expected -> initRaw @?= expected++                    localRaw <- c_duckdb_function_get_local_init_data info+                    storedLocal <- readIORef localPtrRef+                    case storedLocal of+                        Nothing -> assertFailure "local init data should exist during execution"+                        Just expected -> localRaw @?= expected+                    sentinel <- peek (castPtr localRaw :: Ptr Int64)+                    sentinel @?= localInitSentinel++                    snapshot <- readIORef projectionCurrent+                    ProjectionSnapshot{..} <-+                        case snapshot of+                            Nothing -> do+                                _ <- assertFailure "projection snapshot missing during execution"+                                pure (ProjectionSnapshot 0 [])+                            Just snap -> pure snap+                    assertBool "projection slots match indices" (length psIndices == psCount)++                    nextVal <- takeInt64 initRaw 0+                    endVal <- takeInt64 initRaw 1+                    failAtValue <- takeInt64 initRaw 2+                    if nextVal >= endVal+                        then c_duckdb_data_chunk_set_size chunk 0+                        else do+                            let remaining = endVal - nextVal+                                batch = min remaining 1024+                                chunkEnd = nextVal + batch+                            if failAtValue >= nextVal && failAtValue < chunkEnd && failAtValue >= 0+                                then do+                                    withCString "execution aborted by table function" $ \msg -> c_duckdb_function_set_error info msg+                                    c_duckdb_data_chunk_set_size chunk 0+                                    putInt64 initRaw 0 chunkEnd+                                else do+                                    forM_ (zip [0 :: Integer ..] psIndices) \(slot, schemaIdx) -> do+                                        vec <- c_duckdb_data_chunk_get_vector chunk (fromIntegral slot)+                                        dataPtr <- vectorDataPtr vec+                                        forM_ [0 :: Integer .. fromIntegral batch - 1] \offset -> do+                                            let base = nextVal + fromIntegral offset+                                                val =+                                                    case schemaIdx of+                                                        0 -> base+                                                        1 -> 2 * base+                                                        _ -> 0+                                            pokeElemOff dataPtr (fromIntegral offset) val+                                    c_duckdb_data_chunk_set_size chunk (fromIntegral batch)+                                    putInt64 initRaw 0 chunkEnd+                ModeInitError -> do+                    withCString "execution blocked by init error" $ \msg -> c_duckdb_function_set_error info msg+                    c_duckdb_data_chunk_set_size chunk 0+                ModeRejected -> do+                    withCString "execution not expected after bind error" $ \msg -> c_duckdb_function_set_error info msg+                    c_duckdb_data_chunk_set_size chunk 0+                ModeUnset -> do+                    withCString "execution invoked without prior bind" $ \msg -> c_duckdb_function_set_error info msg+                    c_duckdb_data_chunk_set_size chunk 0++    bindFun <- mkBindFun bindCallback+    initFun <- mkInitFun initCallback+    localInitFun <- mkInitFun localInitCallback+    execFun <- mkFunctionFun executeCallback++    let callbacks =+            TableCallbacks+                { tcBind = bindFun+                , tcInit = initFun+                , tcLocalInit = localInitFun+                , tcExecute = execFun+                }+        deletes =+            DeleteCallbacks+                { dcExtra = extraDelete+                , dcBind = bindDelete+                , dcInit = initDelete+                , dcLocal = localDelete+                }++    let harness =+            TableHarness+                { thColumnType = columnType+                , thExtraBuffer = extraBuffer+                , thExtraCount = extraCount+                , thBindCount = bindCount+                , thInitCount = initCount+                , thLocalCount = localCount+                , thBindPtr = bindPtrRef+                , thInitPtr = initPtrRef+                , thLocalPtr = localPtrRef+                , thProjectionCurrent = projectionCurrent+                , thProjectionHistory = projectionHistory+                , thClientContextSeen = clientContextSeen+                , thModeRef = modeRef+                , thCountHistory = countHistory+                , thCallbacks = callbacks+                , thDeletes = deletes+                }++    result <- action harness++    freeHaskellFunPtr bindFun+    freeHaskellFunPtr initFun+    freeHaskellFunPtr localInitFun+    freeHaskellFunPtr execFun+    -- DuckDB may still invoke delete callbacks while the table function tears down.+    -- Rely on process teardown to reclaim them to avoid racing with finalizers.++    pure result++withTableFunction :: (DuckDBTableFunction -> IO a) -> IO a+withTableFunction = bracket c_duckdb_create_table_function destroy+  where+    destroy fun = alloca \ptr -> poke ptr fun >> c_duckdb_destroy_table_function ptr++configureTableFunction :: DuckDBTableFunction -> TableHarness -> IO ()+configureTableFunction fun TableHarness{..} = do+    withCString "haskell_numbers" $ \name -> c_duckdb_table_function_set_name fun name+    c_duckdb_table_function_add_parameter fun thColumnType+    withCString "start" \n -> c_duckdb_table_function_add_named_parameter fun n thColumnType+    withCString "fail_at" \n -> c_duckdb_table_function_add_named_parameter fun n thColumnType+    withCString "force_init_error" \n -> c_duckdb_table_function_add_named_parameter fun n thColumnType+    -- NOTE: DuckDB expects the extra-info pointer to be populated alongside the other callbacks;+    -- leaving it unset would skip the destructor and break the lifetime assertions below.+    c_duckdb_table_function_set_extra_info fun thExtraBuffer (dcExtra thDeletes)+    c_duckdb_table_function_set_bind fun (tcBind thCallbacks)+    c_duckdb_table_function_set_init fun (tcInit thCallbacks)+    c_duckdb_table_function_set_local_init fun (tcLocalInit thCallbacks)+    c_duckdb_table_function_set_function fun (tcExecute thCallbacks)+    c_duckdb_table_function_supports_projection_pushdown fun (toCBool True)++snapshotStats :: TableHarness -> IO HarnessStats+snapshotStats TableHarness{..} = do+    hsExtraFreed <- readIORef thExtraCount+    hsBindFreed <- readIORef thBindCount+    hsInitFreed <- readIORef thInitCount+    hsLocalFreed <- readIORef thLocalCount+    hsClientContextSeen <- readIORef thClientContextSeen+    history <- readIORef thProjectionHistory+    hsCounts <- readIORef thCountHistory+    pure+        HarnessStats+            { hsProjectionHistory = history+            , ..+            }++-- Helpers --------------------------------------------------------------------++extraInfoSentinel :: Int64+extraInfoSentinel = 0xdeadbeef42++localInitSentinel :: Int64+localInitSentinel = 0x105105105++sizeOfInt64 :: Int+sizeOfInt64 = sizeOf (undefined :: Int64)++withOptionalValue :: IO DuckDBValue -> (DuckDBValue -> IO a) -> IO (Maybe a)+withOptionalValue acquire action = do+    value <- acquire+    if value == nullPtr+        then pure Nothing+        else do+            result <- action value+            destroy value+            pure (Just result)+  where+    destroy val = alloca \ptr -> poke ptr val >> c_duckdb_destroy_value ptr++vectorDataPtr :: DuckDBVector -> IO (Ptr Int64)+vectorDataPtr vec = castPtr <$> c_duckdb_vector_get_data vec++contains :: String -> String -> Bool+contains needle haystack = mapLower needle `isInfixOf` mapLower haystack+  where+    mapLower = map toLowerAscii+    toLowerAscii c+        | isAsciiUpper c = toEnum (fromEnum c + 32)+        | otherwise = c++toCBool :: Bool -> CBool+toCBool False = CBool 0+toCBool True = CBool 1++peekCStringMaybe :: CString -> IO String+peekCStringMaybe ptr+    | ptr == nullPtr = pure ""+    | otherwise = peekCString ptr++-- Wrappers -------------------------------------------------------------------++foreign import ccall safe "wrapper"+    mkBindFun :: (DuckDBBindInfo -> IO ()) -> IO DuckDBTableFunctionBindFun++foreign import ccall safe "wrapper"+    mkInitFun :: (DuckDBInitInfo -> IO ()) -> IO DuckDBTableFunctionInitFun++foreign import ccall safe "wrapper"+    mkFunctionFun :: (DuckDBFunctionInfo -> DuckDBDataChunk -> IO ()) -> IO DuckDBTableFunctionFun++foreign import ccall safe "wrapper"+    mkDeleteCallback :: (Ptr () -> IO ()) -> IO DuckDBDeleteCallback
+ test/ThreadingTest.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE BlockArguments #-}++module ThreadingTest (tests) where++import Control.Concurrent (forkFinally, newEmptyMVar, putMVar, takeMVar)+import Data.Int (Int64)+import Database.DuckDB.FFI+import Foreign.C.String (CString, withCString)+import Foreign.C.Types (CBool (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (nullPtr)+import Foreign.Storable (peek)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))+import Utils (withConnection, withDatabase)++tests :: TestTree+tests =+    testGroup+        "Threading Information"+        [ executeTasksCompletesPendingQuery+        , taskStateControlsExecutionLifecycle+        ]++executeTasksCompletesPendingQuery :: TestTree+executeTasksCompletesPendingQuery =+    testCase "execute_tasks drives pending query to completion" $+        withDatabase \db ->+            withConnection db \conn -> do+                setupAggTable conn++                withCString "SELECT SUM(val) FROM threading_numbers;" \querySql ->+                    alloca \stmtPtr -> do+                        stPrepare <- c_duckdb_prepare conn querySql stmtPtr+                        stPrepare @?= DuckDBSuccess+                        stmt <- peek stmtPtr+                        assertBool "prepared statement should not be null" (stmt /= nullPtr)++                        alloca \pendingPtr -> do+                            stPending <- c_duckdb_pending_prepared stmt pendingPtr+                            stPending @?= DuckDBSuccess+                            pending <- peek pendingPtr+                            assertBool "pending result should not be null" (pending /= nullPtr)++                            finishedBefore <- c_duckdb_execution_is_finished conn+                            finishedBefore @?= CBool 0++                            driveTasks db conn++                            finishedAfter <- c_duckdb_execution_is_finished conn+                            finishedAfter @?= CBool 1++                            alloca \resPtr -> do+                                stExec <- c_duckdb_execute_pending pending resPtr+                                stExec @?= DuckDBSuccess+                                sumVal <- c_duckdb_value_int64 resPtr 0 0+                                (sumVal :: Int64) @?= 15+                                c_duckdb_destroy_result resPtr++                            c_duckdb_destroy_pending pendingPtr++                        c_duckdb_destroy_prepare stmtPtr++taskStateControlsExecutionLifecycle :: TestTree+taskStateControlsExecutionLifecycle =+    testCase "task state executes batches and finishes on request" $+        withDatabase \db ->+            withConnection db \conn -> do+                setupAggTable conn++                taskState <- c_duckdb_create_task_state db+                assertBool "task state should not be null" (taskState /= nullPtr)++                doneVar <- newEmptyMVar+                _ <- forkFinally (c_duckdb_execute_tasks_state taskState) (const (putMVar doneVar ()))++                executed <- c_duckdb_execute_n_tasks_state taskState 0+                assertBool "execute_n_tasks_state should not report negative work" (executed >= 0)++                isFinishedBefore <- c_duckdb_task_state_is_finished taskState+                isFinishedBefore @?= CBool 0++                c_duckdb_finish_execution taskState++                isFinishedAfter <- c_duckdb_task_state_is_finished taskState+                isFinishedAfter @?= CBool 1++                takeMVar doneVar+                c_duckdb_destroy_task_state taskState++setupAggTable :: DuckDBConnection -> IO ()+setupAggTable conn = do+    withCString "CREATE TABLE threading_numbers(val INTEGER);" $ \createSql ->+        execStatement conn createSql+    withCString "INSERT INTO threading_numbers VALUES (1), (2), (3), (4), (5);" $ \insertSql ->+        execStatement conn insertSql++driveTasks :: DuckDBDatabase -> DuckDBConnection -> IO ()+driveTasks db conn = go 0+  where+    go :: Int -> IO ()+    go attempts+        | attempts > 10 = assertFailure "execute_tasks did not finish query within expected iterations"+        | otherwise = do+            c_duckdb_execute_tasks db 1000+            finished <- c_duckdb_execution_is_finished conn+            if finished == CBool 1+                then pure ()+                else go (attempts + 1)++execStatement :: DuckDBConnection -> CString -> IO ()+execStatement conn sql =+    alloca \resPtr -> do+        st <- c_duckdb_query conn sql resPtr+        st @?= DuckDBSuccess+        c_duckdb_destroy_result resPtr
+ test/Utils.hs view
@@ -0,0 +1,125 @@+{-# LANGUAGE BlockArguments #-}++module Utils (+    withDatabase,+    withConnection,+    withResult,+    withResultCString,+    withValue,+    withDuckValue,+    destroyDuckValue,+    withLogicalType,+    destroyLogicalType,+    withSelectionVector,+    withScalarFunction,+    withVector,+    withVectorOfType,+    setAllValid,+    clearValidityBit,+    plusWord,+    destroyErrorData,+) where++import Control.Exception (bracket)+import Control.Monad (forM_)+import Data.Bits (clearBit, setBit)+import Data.Word (Word64)+import Database.DuckDB.FFI+import Foreign.C.String (CString, withCString)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (Ptr, plusPtr)+import Foreign.Storable (peek, poke, sizeOf)+import Test.Tasty.HUnit ((@?=))++withDatabase :: (DuckDBDatabase -> IO a) -> IO a+withDatabase action =+    withCString ":memory:" \path ->+        alloca \dbPtr -> do+            c_duckdb_open path dbPtr >>= (@?= DuckDBSuccess)+            db <- peek dbPtr+            result <- action db+            c_duckdb_close dbPtr+            pure result++withConnection :: DuckDBDatabase -> (DuckDBConnection -> IO a) -> IO a+withConnection db = bracket acquire release+  where+    acquire =+        alloca \connPtr -> do+            c_duckdb_connect db connPtr >>= (@?= DuckDBSuccess)+            peek connPtr+    release conn =+        alloca \connPtr -> do+            poke connPtr conn+            c_duckdb_disconnect connPtr++withResult :: DuckDBConnection -> String -> (Ptr DuckDBResult -> IO a) -> IO a+withResult conn sql action =+    withCString sql \sqlPtr -> withResultCString conn sqlPtr action++withResultCString :: DuckDBConnection -> CString -> (Ptr DuckDBResult -> IO a) -> IO a+withResultCString conn sql action =+    alloca \resPtr -> do+        c_duckdb_query conn sql resPtr >>= (@?= DuckDBSuccess)+        result <- action resPtr+        c_duckdb_destroy_result resPtr+        pure result++withValue :: IO DuckDBValue -> (DuckDBValue -> IO a) -> IO a+withValue acquire = bracket acquire destroyDuckValue++withDuckValue :: IO DuckDBValue -> (DuckDBValue -> IO a) -> IO a+withDuckValue = withValue++destroyDuckValue :: DuckDBValue -> IO ()+destroyDuckValue value =+    alloca \ptr -> poke ptr value >> c_duckdb_destroy_value ptr++withLogicalType :: IO DuckDBLogicalType -> (DuckDBLogicalType -> IO a) -> IO a+withLogicalType acquire = bracket acquire destroyLogicalType++destroyLogicalType :: DuckDBLogicalType -> IO ()+destroyLogicalType lt =+    alloca \ptr -> poke ptr lt >> c_duckdb_destroy_logical_type ptr++withSelectionVector :: DuckDBIdx -> (DuckDBSelectionVector -> IO a) -> IO a+withSelectionVector n = bracket (c_duckdb_create_selection_vector n) c_duckdb_destroy_selection_vector++withScalarFunction :: (DuckDBScalarFunction -> IO a) -> IO a+withScalarFunction = bracket c_duckdb_create_scalar_function destroy+  where+    destroy fun =+        alloca \ptr -> poke ptr fun >> c_duckdb_destroy_scalar_function ptr++withVector :: IO DuckDBVector -> (DuckDBVector -> IO a) -> IO a+withVector acquire = bracket acquire destroyVector+  where+    destroyVector vec =+        alloca \ptr -> poke ptr vec >> c_duckdb_destroy_vector ptr++withVectorOfType :: DuckDBLogicalType -> DuckDBIdx -> (DuckDBVector -> IO a) -> IO a+withVectorOfType lt capacity = withVector (c_duckdb_create_vector lt capacity)++setAllValid :: Ptr Word64 -> Int -> IO ()+setAllValid mask count =+    let totalWords = max 1 ((count + 63) `div` 64)+     in forM_ [0 .. totalWords - 1] \wordIdx -> do+            let start = wordIdx * 64+                end = min count (start + 64)+                bits = foldl setBit 0 [0 .. end - start - 1]+            poke (mask `plusWord` wordIdx) bits++clearValidityBit :: Ptr Word64 -> Int -> IO ()+clearValidityBit mask idx = do+    let wordIdx = idx `div` 64+        bitIdx = idx `mod` 64+        entryPtr = mask `plusWord` wordIdx+    current <- peek entryPtr+    poke entryPtr (clearBit current bitIdx)++plusWord :: Ptr Word64 -> Int -> Ptr Word64+plusWord base idx = base `plusPtr` (idx * sizeOf (undefined :: Word64))++destroyErrorData :: DuckDBErrorData -> IO ()+destroyErrorData errData =+    alloca \ptr -> poke ptr errData >> c_duckdb_destroy_error_data ptr
+ test/ValidityMaskTest.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE BlockArguments #-}++module ValidityMaskTest (tests) where++import Control.Monad (void)+import Database.DuckDB.FFI+import Foreign.C.Types (CBool (..))+import Foreign.Ptr (nullPtr)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (setAllValid, withLogicalType, withVector)++-- | Entry point for validity-mask focused tests.+tests :: TestTree+tests =+    testGroup+        "Validity Mask Functions"+        [ validityRowHelpers+        , validitySetOperations+        ]++validityRowHelpers :: TestTree+validityRowHelpers =+    testCase "row validity helpers reflect changes" $ do+        withIntegerVector 4 \vec -> do+            void (c_duckdb_vector_ensure_validity_writable vec)+            mask <- c_duckdb_vector_get_validity vec+            assertBool "validity pointer should not be null" (mask /= nullPtr)+            setAllValid mask 4++            toBool (c_duckdb_validity_row_is_valid mask 2) >>= (@?= True)+            c_duckdb_validity_set_row_invalid mask 2+            toBool (c_duckdb_validity_row_is_valid mask 2) >>= (@?= False)+            c_duckdb_validity_set_row_valid mask 2+            toBool (c_duckdb_validity_row_is_valid mask 2) >>= (@?= True)++validitySetOperations :: TestTree+validitySetOperations =+    testCase "set_row_validity toggles state based on CBool" $ do+        withIntegerVector 3 \vec -> do+            void (c_duckdb_vector_ensure_validity_writable vec)+            mask <- c_duckdb_vector_get_validity vec+            setAllValid mask 3++            c_duckdb_validity_set_row_validity mask 1 (CBool 0)+            toBool (c_duckdb_validity_row_is_valid mask 1) >>= (@?= False)++            c_duckdb_validity_set_row_validity mask 1 (CBool 1)+            toBool (c_duckdb_validity_row_is_valid mask 1) >>= (@?= True)++-- Helpers -------------------------------------------------------------------++withIntegerVector :: DuckDBIdx -> (DuckDBVector -> IO a) -> IO a+withIntegerVector capacity action =+    withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+        withVector (c_duckdb_create_vector intType capacity) action++toBool :: IO CBool -> IO Bool+toBool action = do+    CBool v <- action+    pure (v /= 0)
+ test/ValueInterfaceTest.hs view
@@ -0,0 +1,321 @@+{-# LANGUAGE BlockArguments #-}++module ValueInterfaceTest (tests) where++import Control.Monad (when, (>=>))+import Data.Int (Int16, Int32, Int64, Int8)+import Data.Word (Word16, Word32, Word64, Word8)+import Database.DuckDB.FFI+import Foreign.C.String (peekCString, peekCStringLen, withCString)+import Foreign.C.Types (CBool (..), CDouble (..), CFloat (..))+import Foreign.Marshal.Alloc (alloca)+import Foreign.Marshal.Array (peekArray, withArray)+import Foreign.Marshal.Utils (with, withMany)+import Foreign.Ptr (Ptr, castPtr, nullPtr)+import Foreign.Storable (peek, poke)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (testCase, (@?=))+import Utils (destroyDuckValue, destroyLogicalType, withDuckValue)++tests :: TestTree+tests =+    testGroup+        "Value Interface"+        [ scalarCreatesRoundTrip+        , valueTypeReportsLogicalType+        , collectionValuesRoundTrip+        ]++scalarCreatesRoundTrip :: TestTree+scalarCreatesRoundTrip =+    testCase "scalar value constructors and accessors" $ do+        withDuckValue (c_duckdb_create_bool (CBool 1)) (c_duckdb_get_bool >=> (@?= CBool 1))++        withDuckValue (c_duckdb_create_int8 (-8)) (c_duckdb_get_int8 >=> (@?= (-8 :: Int8)))++        withDuckValue (c_duckdb_create_uint8 250) (c_duckdb_get_uint8 >=> (@?= (250 :: Word8)))++        withDuckValue (c_duckdb_create_int16 (-32000)) (c_duckdb_get_int16 >=> (@?= (-32000 :: Int16)))++        withDuckValue (c_duckdb_create_uint16 65000) (c_duckdb_get_uint16 >=> (@?= (65000 :: Word16)))++        withDuckValue+            (c_duckdb_create_int32 (-2000000000))+            ( c_duckdb_get_int32+                >=> (@?= (-2000000000 :: Int32))+            )++        withDuckValue+            (c_duckdb_create_uint32 4000000000)+            ( c_duckdb_get_uint32+                >=> (@?= (4000000000 :: Word32))+            )++        withDuckValue+            (c_duckdb_create_int64 (-9000000000000000000))+            ( c_duckdb_get_int64+                >=> (@?= (-9000000000000000000 :: Int64))+            )++        withDuckValue+            (c_duckdb_create_uint64 10000000000000000000)+            ( c_duckdb_get_uint64+                >=> (@?= (10000000000000000000 :: Word64))+            )++        alloca \hugePtr -> do+            poke hugePtr DuckDBHugeInt{duckDBHugeIntLower = 123, duckDBHugeIntUpper = -1}+            withDuckValue (c_duckdb_create_hugeint hugePtr) \val ->+                alloca \out -> do+                    c_duckdb_get_hugeint val out+                    peek out >>= (@?= DuckDBHugeInt 123 (-1))++        alloca \uhugePtr -> do+            poke uhugePtr DuckDBUHugeInt{duckDBUHugeIntLower = 321, duckDBUHugeIntUpper = 2}+            withDuckValue (c_duckdb_create_uhugeint uhugePtr) \val ->+                alloca \out -> do+                    c_duckdb_get_uhugeint val out+                    peek out >>= (@?= DuckDBUHugeInt 321 2)++        withArray (map (fromIntegral . fromEnum) "1234") \digitsPtr -> do+            let bignum = DuckDBBignum digitsPtr 4 (CBool 0)+            with bignum \bignumPtr ->+                withDuckValue (c_duckdb_create_bignum bignumPtr) \val ->+                    alloca \outPtr -> do+                        c_duckdb_get_bignum val outPtr+                        DuckDBBignum outData outLen outNeg <- peek outPtr+                        outLen @?= 4+                        outNeg @?= CBool 0+                        peekArray (fromIntegral outLen) outData >>= (@?= map (fromIntegral . fromEnum) "1234")+                        when (outData /= nullPtr) $ c_duckdb_free (castPtr outData)++        let hugeValue = DuckDBHugeInt{duckDBHugeIntLower = 42, duckDBHugeIntUpper = 0}+            decimalValue = DuckDBDecimal{duckDBDecimalWidth = 10, duckDBDecimalScale = 2, duckDBDecimalValue = hugeValue}+        with decimalValue \decimalPtr ->+            withDuckValue (c_duckdb_create_decimal decimalPtr) \val ->+                alloca \out -> do+                    c_duckdb_get_decimal val out+                    DuckDBDecimal{duckDBDecimalWidth = w, duckDBDecimalScale = s, duckDBDecimalValue = v} <- peek out+                    (w, s, v) @?= (10, 2, hugeValue)++        withDuckValue (c_duckdb_create_float (CFloat 1.25)) (c_duckdb_get_float >=> (@?= CFloat 1.25))++        withDuckValue (c_duckdb_create_double (CDouble 2.75)) (c_duckdb_get_double >=> (@?= CDouble 2.75))++        let sampleDate = DuckDBDate 12345+        withDuckValue (c_duckdb_create_date sampleDate) (c_duckdb_get_date >=> (@?= sampleDate))++        let sampleTime = DuckDBTime 987654321+        withDuckValue (c_duckdb_create_time sampleTime) (c_duckdb_get_time >=> (@?= sampleTime))++        let sampleTimeNs = DuckDBTimeNs 876543210+        withDuckValue (c_duckdb_create_time_ns sampleTimeNs) (c_duckdb_get_time_ns >=> (@?= sampleTimeNs))++        let sampleTimeTz = DuckDBTimeTz 5555+        withDuckValue (c_duckdb_create_time_tz_value sampleTimeTz) (c_duckdb_get_time_tz >=> (@?= sampleTimeTz))++        let sampleTimestamp = DuckDBTimestamp 444444+        withDuckValue (c_duckdb_create_timestamp sampleTimestamp) (c_duckdb_get_timestamp >=> (@?= sampleTimestamp))++        withDuckValue (c_duckdb_create_timestamp_tz sampleTimestamp) (c_duckdb_get_timestamp_tz >=> (@?= sampleTimestamp))++        let tsSeconds = DuckDBTimestampS 12+        withDuckValue (c_duckdb_create_timestamp_s tsSeconds) (c_duckdb_get_timestamp_s >=> (@?= tsSeconds))++        let tsMillis = DuckDBTimestampMs 12000+        withDuckValue (c_duckdb_create_timestamp_ms tsMillis) (c_duckdb_get_timestamp_ms >=> (@?= tsMillis))++        let tsNanos = DuckDBTimestampNs 12000000+        withDuckValue (c_duckdb_create_timestamp_ns tsNanos) (c_duckdb_get_timestamp_ns >=> (@?= tsNanos))++        let intervalVal = DuckDBInterval{duckDBIntervalMonths = 1, duckDBIntervalDays = 2, duckDBIntervalMicros = 3000}+        with intervalVal \intervalPtr ->+            withDuckValue (c_duckdb_create_interval intervalPtr) \val ->+                alloca \out -> do+                    c_duckdb_get_interval val out+                    peek out >>= (@?= intervalVal)++        let blobBytes = map (fromIntegral . fromEnum) "duckdb-blob"+        withArray blobBytes \blobPtr ->+            withDuckValue (c_duckdb_create_blob blobPtr (fromIntegral (length blobBytes))) \val ->+                alloca \blobOut -> do+                    c_duckdb_get_blob val blobOut+                    DuckDBBlob{duckDBBlobData = datPtr, duckDBBlobSize = size} <- peek blobOut+                    size @?= fromIntegral (length blobBytes)+                    peekArray (fromIntegral size) (castPtr datPtr :: Ptr Word8) >>= (@?= blobBytes)+                    when (datPtr /= nullPtr) $ c_duckdb_free (castPtr datPtr)++        let bitBytes = [0xAA :: Word8]+        withArray bitBytes \bitDataPtr -> do+            let bitVal = DuckDBBit{duckDBBitData = bitDataPtr, duckDBBitSize = 8}+            with bitVal \bitPtr ->+                withDuckValue (c_duckdb_create_bit bitPtr) \val ->+                    alloca \bitOut -> do+                        c_duckdb_get_bit val bitOut+                        DuckDBBit{duckDBBitData = datPtr, duckDBBitSize = size} <- peek bitOut+                        size @?= 8+                        peekArray (fromIntegral ((size + 7) `div` 8)) datPtr >>= (@?= bitBytes)+                        when (datPtr /= nullPtr) $ c_duckdb_free (castPtr datPtr)++        let uuidValue = DuckDBUHugeInt{duckDBUHugeIntLower = 0x0011223344556677, duckDBUHugeIntUpper = 0x8899aabbccddeeff}+        with uuidValue \uuidPtr ->+            withDuckValue (c_duckdb_create_uuid uuidPtr) \val ->+                alloca \out -> do+                    c_duckdb_get_uuid val out+                    peek out >>= (@?= uuidValue)++        withCString "varchar literal" \str ->+            withDuckValue (c_duckdb_create_varchar str) \val -> do+                cPtr <- c_duckdb_get_varchar val+                peekCString cPtr >>= (@?= "varchar literal")+                c_duckdb_free (castPtr cPtr)++        let lenString = "hello\0world"+        withCString lenString \cStr -> do+            let byteLen = fromIntegral (length lenString)+            withDuckValue (c_duckdb_create_varchar_length cStr byteLen) \val -> do+                cPtr <- c_duckdb_get_varchar val+                peekCStringLen (cPtr, length "hello") >>= (@?= "hello")+                c_duckdb_free (castPtr cPtr)++        withDuckValue c_duckdb_create_null_value \val -> do+            c_duckdb_is_null_value val >>= (@?= CBool 1)+            strPtr <- c_duckdb_value_to_string val+            peekCString strPtr >>= (@?= "NULL")+            c_duckdb_free (castPtr strPtr)++valueTypeReportsLogicalType :: TestTree+valueTypeReportsLogicalType =+    testCase "value type identifiers track constructors" $ do+        withDuckValue (c_duckdb_create_int32 42) \intVal -> do+            intType <- c_duckdb_get_value_type intVal+            c_duckdb_get_type_id intType >>= (@?= DuckDBTypeInteger)++        withDuckValue (withCString "duckdb" c_duckdb_create_varchar) \strVal -> do+            strType <- c_duckdb_get_value_type strVal+            c_duckdb_get_type_id strType >>= (@?= DuckDBTypeVarchar)++        listChild <- c_duckdb_create_logical_type DuckDBTypeInteger+        listLogical <- c_duckdb_create_list_type listChild+        elemVal <- c_duckdb_create_int32 7+        withArray [elemVal] \valuesArray -> do+            let count = fromIntegral (1 :: Int)+            withDuckValue (c_duckdb_create_list_value listChild valuesArray count) \listVal -> do+                listType <- c_duckdb_get_value_type listVal+                c_duckdb_get_type_id listType >>= (@?= DuckDBTypeList)+        destroyDuckValue elemVal+        destroyLogicalType listLogical+        destroyLogicalType listChild++collectionValuesRoundTrip :: TestTree+collectionValuesRoundTrip =+    testCase "list/array/map/struct/enum/union constructors" $ do+        -- List value+        listChild <- c_duckdb_create_logical_type DuckDBTypeInteger+        listLogical <- c_duckdb_create_list_type listChild+        listVal1 <- c_duckdb_create_int32 1+        listVal2 <- c_duckdb_create_int32 2+        withArray [listVal1, listVal2] \listArray -> do+            let entryCount = fromIntegral (2 :: Int) :: DuckDBIdx+            withDuckValue (c_duckdb_create_list_value listChild listArray entryCount) \listVal -> do+                _ <- c_duckdb_get_list_size listVal+                child0 <- c_duckdb_get_list_child listVal 0+                c_duckdb_get_int32 child0 >>= (@?= 1)+                destroyDuckValue child0+                child1 <- c_duckdb_get_list_child listVal 1+                c_duckdb_get_int32 child1 >>= (@?= 2)+                destroyDuckValue child1+                listStr <- c_duckdb_value_to_string listVal+                peekCString listStr >>= (@?= "[1, 2]")+                c_duckdb_free (castPtr listStr)+        destroyDuckValue listVal1+        destroyDuckValue listVal2+        destroyLogicalType listLogical+        destroyLogicalType listChild++        -- Array value+        arrayChild <- c_duckdb_create_logical_type DuckDBTypeInteger+        arrayLogical <- c_duckdb_create_array_type arrayChild 2+        arrVal1 <- c_duckdb_create_int32 7+        arrVal2 <- c_duckdb_create_int32 8+        withArray [arrVal1, arrVal2] \arrArray -> do+            let entryCount = fromIntegral (2 :: Int) :: DuckDBIdx+            withDuckValue (c_duckdb_create_array_value arrayChild arrArray entryCount) \arrVal -> do+                _ <- c_duckdb_get_list_size arrVal+                arrStr <- c_duckdb_value_to_string arrVal+                peekCString arrStr >>= (@?= "[7, 8]")+                c_duckdb_free (castPtr arrStr)+        destroyDuckValue arrVal1+        destroyDuckValue arrVal2+        destroyLogicalType arrayLogical+        destroyLogicalType arrayChild++        -- Map value+        keyType <- c_duckdb_create_logical_type DuckDBTypeVarchar+        valType <- c_duckdb_create_logical_type DuckDBTypeInteger+        mapLogical <- c_duckdb_create_map_type keyType valType+        keyValue <- withCString "key" c_duckdb_create_varchar+        valValue <- c_duckdb_create_int32 99+        withArray [keyValue] \keyArray ->+            withArray [valValue] \valArray -> do+                let entryCount = fromIntegral (1 :: Int) :: DuckDBIdx+                withDuckValue (c_duckdb_create_map_value mapLogical keyArray valArray entryCount) \mapVal -> do+                    c_duckdb_get_map_size mapVal >>= (@?= 1)+                    keyHandle <- c_duckdb_get_map_key mapVal 0+                    keyStrPtr <- c_duckdb_get_varchar keyHandle+                    peekCString keyStrPtr >>= (@?= "key")+                    c_duckdb_free (castPtr keyStrPtr)+                    destroyDuckValue keyHandle+                    valHandle <- c_duckdb_get_map_value mapVal 0+                    c_duckdb_get_int32 valHandle >>= (@?= 99)+                    destroyDuckValue valHandle+        destroyDuckValue keyValue+        destroyDuckValue valValue+        destroyLogicalType mapLogical+        destroyLogicalType keyType+        destroyLogicalType valType++        -- Struct value+        structInt <- c_duckdb_create_logical_type DuckDBTypeInteger+        structText <- c_duckdb_create_logical_type DuckDBTypeVarchar+        structLogical <-+            withMany withCString ["id", "name"] \namePtrs ->+                withArray [structInt, structText] \typeArray ->+                    withArray namePtrs \nameArray ->+                        c_duckdb_create_struct_type typeArray nameArray 2+        withDuckValue (c_duckdb_create_int32 1) \idVal ->+            withDuckValue (withCString "Alice" c_duckdb_create_varchar) \nameVal ->+                withArray [idVal, nameVal] \structValues ->+                    withDuckValue (c_duckdb_create_struct_value structLogical structValues) \structVal -> do+                        child <- c_duckdb_get_struct_child structVal 1+                        namePtr <- c_duckdb_get_varchar child+                        peekCString namePtr >>= (@?= "Alice")+                        c_duckdb_free (castPtr namePtr)+                        destroyDuckValue child+        destroyLogicalType structLogical+        destroyLogicalType structInt+        destroyLogicalType structText++        -- Enum value+        enumLogical <-+            withMany withCString ["Red", "Green", "Blue"] \namePtrs ->+                withArray namePtrs (`c_duckdb_create_enum_type` 3)+        withDuckValue (c_duckdb_create_enum_value enumLogical 1) (c_duckdb_get_enum_value >=> (@?= 1))+        destroyLogicalType enumLogical++        -- Union value+        unionInt <- c_duckdb_create_logical_type DuckDBTypeInteger+        unionText <- c_duckdb_create_logical_type DuckDBTypeVarchar+        unionLogical <-+            withMany withCString ["int_member", "text_member"] \namePtrs ->+                withArray [unionInt, unionText] \typeArray ->+                    withArray namePtrs \nameArray ->+                        c_duckdb_create_union_type typeArray nameArray 2+        withDuckValue (c_duckdb_create_int32 42) \unionPayload ->+            withDuckValue (c_duckdb_create_union_value unionLogical 0 unionPayload) \unionVal -> do+                strPtr <- c_duckdb_value_to_string unionVal+                peekCString strPtr >>= (@?= "union_value(int_member := 42)")+                c_duckdb_free (castPtr strPtr)+        destroyLogicalType unionLogical+        destroyLogicalType unionInt+        destroyLogicalType unionText
+ test/VectorTest.hs view
@@ -0,0 +1,190 @@+{-# LANGUAGE BlockArguments #-}++module VectorTest (tests) where++import Control.Monad (forM_, void, (>=>))+import Data.Bits (setBit)+import Data.Int (Int32)+import Data.Word (Word64)+import Database.DuckDB.FFI+import Foreign.C.String (withCString)+import Foreign.Marshal.Array (allocaArray, withArray)+import Foreign.Ptr (Ptr, castPtr, nullPtr, plusPtr)+import Foreign.Storable (peek, peekElemOff, poke, pokeElemOff, sizeOf)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertBool, testCase, (@?=))+import Utils (clearValidityBit, setAllValid, withLogicalType, withSelectionVector, withValue, withVectorOfType)++-- | Entry point for vector-centric tests.+tests :: TestTree+tests =+    testGroup+        "Vector Interface"+        [ vectorDataAccess+        , vectorValidityMask+        , listVectorChildManagement+        , arrayVectorChildAccess+        , vectorSliceWithSelection+        , structVectorChildAccess+        , vectorReferenceValue+        ]++-- | Create a numeric vector, write elements, and observe the stored values.+vectorDataAccess :: TestTree+vectorDataAccess =+    testCase "write and read integer vector data" $ do+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+            withVectorOfType intType 4 \vec -> do+                colType <- c_duckdb_vector_get_column_type vec+                withLogicalType (pure colType) (c_duckdb_get_type_id >=> (@?= DuckDBTypeInteger))++                rawPtr <- c_duckdb_vector_get_data vec+                let dataPtr = castPtr rawPtr :: Ptr Int32+                forM_ (zip [0 ..] [10, 20, 30, 40]) \(idx, val) -> poke (dataPtr `plusElem` idx) val++                forM_ (zip [0 ..] [10, 20, 30, 40]) \(idx, val) -> peek (dataPtr `plusElem` idx) >>= (@?= val)++-- | Ensure validity mask allocation and manipulate individual bits.+vectorValidityMask :: TestTree+vectorValidityMask =+    testCase "ensure validity mask and clear single entry" $ do+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+            withVectorOfType intType 4 \vec -> do+                void (c_duckdb_vector_ensure_validity_writable vec)+                validity <- c_duckdb_vector_get_validity vec+                assertBool "validity mask should exist" (validity /= nullPtr)++                setAllValid validity 4+                clearValidityBit validity 2++                current <- peek validity+                let expected = foldl setBit (0 :: Word64) [0, 1, 3]+                current @?= expected++-- | Exercise list vector child management APIs.+listVectorChildManagement :: TestTree+listVectorChildManagement =+    testCase "list vector reserves space and reports size" $ do+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+            withLogicalType (c_duckdb_create_list_type intType) \listType ->+                withVectorOfType listType 2 \listVec -> do+                    childVec0 <- c_duckdb_list_vector_get_child listVec+                    assertBool "list child vector should be non-null" (childVec0 /= nullPtr)+                    initialSize <- c_duckdb_list_vector_get_size listVec+                    initialSize @?= 0++                    reserveState <- c_duckdb_list_vector_reserve listVec 5+                    reserveState @?= DuckDBSuccess+                    sizeState <- c_duckdb_list_vector_set_size listVec 5+                    sizeState @?= DuckDBSuccess+                    c_duckdb_list_vector_get_size listVec >>= (@?= 5)++                    childVec <- c_duckdb_list_vector_get_child listVec+                    metaRaw <- c_duckdb_vector_get_data listVec+                    let metaWords = castPtr metaRaw :: Ptr Word64+                    -- row 0 -> offset 0 length 3, row 1 -> offset 3 length 2+                    pokeElemOff metaWords 0 0+                    pokeElemOff metaWords 1 3+                    pokeElemOff metaWords 2 3+                    pokeElemOff metaWords 3 2++                    childRaw <- c_duckdb_vector_get_data childVec+                    let childInts = castPtr childRaw :: Ptr Int32+                        payload = [11, 12, 13, 21, 22 :: Int32]+                    forM_ (zip [0 ..] payload) (uncurry (pokeElemOff childInts))+                    fetched <- mapM (peekElemOff childInts) [0 .. length payload - 1]+                    fetched @?= payload++                    peekElemOff metaWords 1 >>= (@?= 3)+                    peekElemOff metaWords 3 >>= (@?= 2)++-- | Verify array child vector exposes a flat buffer sized by row * array length.+arrayVectorChildAccess :: TestTree+arrayVectorChildAccess =+    testCase "array vector child flattens elements" $ do+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+            withLogicalType (c_duckdb_create_array_type intType 3) \arrayType ->+                withVectorOfType arrayType 2 \arrayVec -> do+                    childVec <- c_duckdb_array_vector_get_child arrayVec+                    childType <- c_duckdb_vector_get_column_type childVec+                    withLogicalType (pure childType) (c_duckdb_get_type_id >=> (@?= DuckDBTypeInteger))++                    childRaw <- c_duckdb_vector_get_data childVec+                    let childInts = castPtr childRaw :: Ptr Int32+                        payload = [1, 2, 3, 4, 5, 6 :: Int32]+                    forM_ (zip [0 ..] payload) (uncurry (pokeElemOff childInts))+                    mapM (peekElemOff childInts) [0 .. length payload - 1] >>= (@?= payload)++-- | Slice a vector with a selection vector and materialize the dictionary.+vectorSliceWithSelection :: TestTree+vectorSliceWithSelection =+    testCase "slice vector materializes dictionary order" $ do+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+            withVectorOfType intType 4 \vec -> do+                dataRaw <- c_duckdb_vector_get_data vec+                let vecData = castPtr dataRaw :: Ptr Int32+                forM_ (zip [0 ..] [10, 20, 30, 40 :: Int32]) (uncurry (pokeElemOff vecData))++                withSelectionVector 2 \sel -> do+                    selPtr <- c_duckdb_selection_vector_get_data_ptr sel+                    pokeElemOff selPtr 0 (fromIntegral (3 :: Int))+                    pokeElemOff selPtr 1 (fromIntegral (1 :: Int))+                    c_duckdb_slice_vector vec sel 2++                    withVectorOfType intType 2 \materialized ->+                        withSelectionVector 2 \copySel -> do+                            copyPtr <- c_duckdb_selection_vector_get_data_ptr copySel+                            pokeElemOff copyPtr 0 0+                            pokeElemOff copyPtr 1 1+                            c_duckdb_vector_copy_sel vec materialized copySel 2 0 0+                            matRaw <- c_duckdb_vector_get_data materialized+                            let matPtr = castPtr matRaw :: Ptr Int32+                            mapM (peekElemOff matPtr) [0, 1] >>= (@?= [40, 20])++-- | Ensure struct vectors expose individual child vectors with matching types.+structVectorChildAccess :: TestTree+structVectorChildAccess =+    testCase "struct vector exposes typed child vectors" $ do+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+            withLogicalType (c_duckdb_create_logical_type DuckDBTypeDouble) \doubleType ->+                allocaArray 2 \typesPtr -> do+                    pokeElemOff typesPtr 0 intType+                    pokeElemOff typesPtr 1 doubleType+                    withCString "ints" \name0 ->+                        withCString "doubles" \name1 ->+                            withArray [name0, name1] \namesPtr ->+                                withLogicalType (c_duckdb_create_struct_type typesPtr namesPtr 2) \structType ->+                                    withVectorOfType structType 1 \structVec -> do+                                        intChild <- c_duckdb_struct_vector_get_child structVec 0+                                        dblChild <- c_duckdb_struct_vector_get_child structVec 1++                                        intChildType <- c_duckdb_vector_get_column_type intChild+                                        withLogicalType (pure intChildType) (c_duckdb_get_type_id >=> (@?= DuckDBTypeInteger))+                                        dblChildType <- c_duckdb_vector_get_column_type dblChild+                                        withLogicalType (pure dblChildType) (c_duckdb_get_type_id >=> (@?= DuckDBTypeDouble))++                                        intRaw <- c_duckdb_vector_get_data intChild+                                        dblRaw <- c_duckdb_vector_get_data dblChild+                                        let intPtr = castPtr intRaw :: Ptr Int32+                                            dblPtr = castPtr dblRaw :: Ptr Double+                                        pokeElemOff intPtr 0 7+                                        pokeElemOff dblPtr 0 3.5+                                        peekElemOff intPtr 0 >>= (@?= 7)+                                        peekElemOff dblPtr 0 >>= (@?= 3.5)++-- | Copy a scalar duckdb_value into a vector without materializing a chunk.+vectorReferenceValue :: TestTree+vectorReferenceValue =+    testCase "vector_reference_value writes scalar contents" $ do+        withLogicalType (c_duckdb_create_logical_type DuckDBTypeInteger) \intType ->+            withVectorOfType intType 1 \vec ->+                withValue (c_duckdb_create_int32 123) \value -> do+                    c_duckdb_vector_reference_value vec value+                    raw <- c_duckdb_vector_get_data vec+                    let dataPtr = castPtr raw :: Ptr Int32+                    peekElemOff dataPtr 0 >>= (@?= 123)++-- helpers -------------------------------------------------------------------++plusElem :: Ptr Int32 -> Int -> Ptr Int32+plusElem base idx = base `plusPtr` (idx * sizeOf (undefined :: Int32))