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 +4/−0
- LICENSE +373/−0
- Setup.hs +109/−0
- cbits/duckdb.h +5168/−0
- cbits/duckdb_stub.c +546/−0
- duckdb-ffi.cabal +148/−0
- src/Database/DuckDB/FFI.hs +73/−0
- src/Database/DuckDB/FFI/AggregateFunctions.hs +182/−0
- src/Database/DuckDB/FFI/Appender.hs +382/−0
- src/Database/DuckDB/FFI/Arrow.hs +395/−0
- src/Database/DuckDB/FFI/BindValues.hs +160/−0
- src/Database/DuckDB/FFI/CastFunctions.hs +136/−0
- src/Database/DuckDB/FFI/Configuration.hs +85/−0
- src/Database/DuckDB/FFI/DataChunk.hs +86/−0
- src/Database/DuckDB/FFI/ErrorData.hs +61/−0
- src/Database/DuckDB/FFI/ExecutePrepared.hs +46/−0
- src/Database/DuckDB/FFI/Expression.hs +51/−0
- src/Database/DuckDB/FFI/ExtractStatements.hs +67/−0
- src/Database/DuckDB/FFI/Helpers.hs +351/−0
- src/Database/DuckDB/FFI/LogicalTypes.hs +385/−0
- src/Database/DuckDB/FFI/OpenConnect.hs +215/−0
- src/Database/DuckDB/FFI/PendingResult.hs +143/−0
- src/Database/DuckDB/FFI/PreparedStatements.hs +184/−0
- src/Database/DuckDB/FFI/ProfilingInfo.hs +67/−0
- src/Database/DuckDB/FFI/QueryExecution.hs +229/−0
- src/Database/DuckDB/FFI/ReplacementScans.hs +50/−0
- src/Database/DuckDB/FFI/ResultFunctions.hs +92/−0
- src/Database/DuckDB/FFI/SafeFetch.hs +280/−0
- src/Database/DuckDB/FFI/ScalarFunctions.hs +299/−0
- src/Database/DuckDB/FFI/SelectionVector.hs +37/−0
- src/Database/DuckDB/FFI/StreamingResult.hs +54/−0
- src/Database/DuckDB/FFI/TableDescription.hs +90/−0
- src/Database/DuckDB/FFI/TableFunctions.hs +405/−0
- src/Database/DuckDB/FFI/Threading.hs +104/−0
- src/Database/DuckDB/FFI/Types.hs +1515/−0
- src/Database/DuckDB/FFI/Validity.hs +61/−0
- src/Database/DuckDB/FFI/ValueInterface.hs +928/−0
- src/Database/DuckDB/FFI/Vector.hs +248/−0
- test/AggregateFunctionsTest.hs +411/−0
- test/AppenderTest.hs +553/−0
- test/ArrowInterfaceDeprecatedTests.hs +489/−0
- test/ArrowInterfaceTest.hs +259/−0
- test/BindValuesTest.hs +275/−0
- test/CastFunctionsTest.hs +160/−0
- test/ConfigurationTest.hs +46/−0
- test/DataChunkTest.hs +100/−0
- test/ErrorDataTest.hs +39/−0
- test/ExecutePreparedStatementsTest.hs +104/−0
- test/ExpressionTest.hs +168/−0
- test/ExtractStatementsTest.hs +103/−0
- test/HelpersTest.hs +161/−0
- test/LogicalTypesTest.hs +166/−0
- test/Main.hs +72/−0
- test/OpenConnectTest.hs +137/−0
- test/PendingResultTest.hs +212/−0
- test/PreparedStatementsTest.hs +112/−0
- test/ProfilingInfoTest.hs +113/−0
- test/QueryExecutionTest.hs +92/−0
- test/ReplacementScansTest.hs +107/−0
- test/ResultFunctionsTest.hs +59/−0
- test/SafeFetchTest.hs +172/−0
- test/ScalarFunctionsTest.hs +228/−0
- test/SelectionVectorTest.hs +59/−0
- test/StreamingResultTest.hs +116/−0
- test/TableDescriptionTest.hs +142/−0
- test/TableFunctionsTest.hs +547/−0
- test/ThreadingTest.hs +116/−0
- test/Utils.hs +125/−0
- test/ValidityMaskTest.hs +61/−0
- test/ValueInterfaceTest.hs +321/−0
- test/VectorTest.hs +190/−0
+ 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))