From 9d441cc59c949cb5c2527e12b38c8b3c04d59240 Mon Sep 17 00:00:00 2001 From: Carlo Piovesan Date: Wed, 7 May 2025 11:25:36 +0200 Subject: [PATCH] Wrap results of fetchQueryResults via DuckDBWasmResultsWrapper This is currently just adding infrastructure, there should be no relevant changes Note that enum added to lib/include/duckdb/web/webdb.h would evetntually need to be dupliciated, adding 256, to packages/duckdb-wasm/src/status.ts --- lib/include/duckdb/web/utils/wasm_response.h | 4 ++++ lib/include/duckdb/web/webdb.h | 16 +++++++++++++++- lib/src/utils/wasm_response.cc | 10 ++++++++++ lib/src/webdb.cc | 6 +++--- lib/src/webdb_api.cc | 2 +- .../duckdb-wasm/src/bindings/bindings_base.ts | 5 ++++- packages/duckdb-wasm/src/status.ts | 7 ++++++- 7 files changed, 43 insertions(+), 7 deletions(-) diff --git a/lib/include/duckdb/web/utils/wasm_response.h b/lib/include/duckdb/web/utils/wasm_response.h index 2cff60931..9edbca39e 100644 --- a/lib/include/duckdb/web/utils/wasm_response.h +++ b/lib/include/duckdb/web/utils/wasm_response.h @@ -8,6 +8,8 @@ namespace duckdb { namespace web { +struct DuckDBWasmResultsWrapper; + struct WASMResponse { /// The status code double statusCode = 1; @@ -35,6 +37,8 @@ class WASMResponseBuffer { /// Store the arrow status. /// Returns wheather the result was OK bool Store(WASMResponse& response, arrow::Status status); + /// Store a DuckDBWasmResultsWrapper + void Store(WASMResponse& response, DuckDBWasmResultsWrapper& value); /// Store a string void Store(WASMResponse& response, std::string value); /// Store a string view diff --git a/lib/include/duckdb/web/webdb.h b/lib/include/duckdb/web/webdb.h index f7d15841a..5421435e7 100644 --- a/lib/include/duckdb/web/webdb.h +++ b/lib/include/duckdb/web/webdb.h @@ -29,6 +29,20 @@ namespace web { struct BufferingArrowIPCStreamDecoder; +struct DuckDBWasmResultsWrapper { + // Additional ResponseStatuses to be >= 256, and mirrored to packages/duckdb-wasm/src/status.ts + // Missing mapping result in a throw, but they should eventually align (it's fine if typescript side only has a + // subset) + enum ResponseStatus : uint32_t { ARROW_BUFFER = 0, MAX_ARROW_ERROR = 255 }; + DuckDBWasmResultsWrapper(arrow::Result> res, + ResponseStatus status = ResponseStatus::ARROW_BUFFER) + : arrow_buffer(res), status(status) {} + DuckDBWasmResultsWrapper(arrow::Status res, ResponseStatus status = ResponseStatus::ARROW_BUFFER) + : arrow_buffer(res), status(status) {} + arrow::Result> arrow_buffer; + ResponseStatus status; +}; + class WebDB { public: /// A connection @@ -93,7 +107,7 @@ class WebDB { /// Cancel a pending query bool CancelPendingQuery(); /// Fetch a data chunk from a pending query - arrow::Result> FetchQueryResults(); + DuckDBWasmResultsWrapper FetchQueryResults(); /// Get table names arrow::Result GetTableNames(std::string_view text); diff --git a/lib/src/utils/wasm_response.cc b/lib/src/utils/wasm_response.cc index d5645314e..61a062cea 100644 --- a/lib/src/utils/wasm_response.cc +++ b/lib/src/utils/wasm_response.cc @@ -3,6 +3,7 @@ #include #include "arrow/buffer.h" +#include "duckdb/web/webdb.h" namespace duckdb { namespace web { @@ -26,6 +27,15 @@ bool WASMResponseBuffer::Store(WASMResponse& response, arrow::Status status) { return true; } +void WASMResponseBuffer::Store(WASMResponse& response, DuckDBWasmResultsWrapper& value) { + if (value.status == DuckDBWasmResultsWrapper::ResponseStatus::ARROW_BUFFER) { + Store(response, std::move(value.arrow_buffer)); + } else { + Clear(); + response.statusCode = value.status; + } +} + void WASMResponseBuffer::Store(WASMResponse& response, std::string value) { result_str_ = std::move(value); response.statusCode = 0; diff --git a/lib/src/webdb.cc b/lib/src/webdb.cc index 345c9ac67..8d026948e 100644 --- a/lib/src/webdb.cc +++ b/lib/src/webdb.cc @@ -231,12 +231,12 @@ bool WebDB::Connection::CancelPendingQuery() { } } -arrow::Result> WebDB::Connection::FetchQueryResults() { +DuckDBWasmResultsWrapper WebDB::Connection::FetchQueryResults() { try { // Fetch data if a query is active duckdb::unique_ptr chunk; if (current_query_result_ == nullptr) { - return nullptr; + return DuckDBWasmResultsWrapper{nullptr}; } // Fetch next result chunk chunk = current_query_result_->Fetch(); @@ -248,7 +248,7 @@ arrow::Result> WebDB::Connection::FetchQueryResul current_query_result_.reset(); current_schema_.reset(); current_schema_patched_.reset(); - return nullptr; + return DuckDBWasmResultsWrapper{nullptr}; } // Serialize the record batch diff --git a/lib/src/webdb_api.cc b/lib/src/webdb_api.cc index bad9c7f9e..bf3521333 100644 --- a/lib/src/webdb_api.cc +++ b/lib/src/webdb_api.cc @@ -241,7 +241,7 @@ bool duckdb_web_pending_query_cancel(ConnectionHdl connHdl, const char* script) void duckdb_web_query_fetch_results(WASMResponse* packed, ConnectionHdl connHdl) { auto c = reinterpret_cast(connHdl); auto r = c->FetchQueryResults(); - WASMResponseBuffer::Get().Store(*packed, std::move(r)); + WASMResponseBuffer::Get().Store(*packed, r); } /// Get table names void duckdb_web_get_tablenames(WASMResponse* packed, ConnectionHdl connHdl, const char* query) { diff --git a/packages/duckdb-wasm/src/bindings/bindings_base.ts b/packages/duckdb-wasm/src/bindings/bindings_base.ts index f395bdb10..08cbc2bbe 100644 --- a/packages/duckdb-wasm/src/bindings/bindings_base.ts +++ b/packages/duckdb-wasm/src/bindings/bindings_base.ts @@ -4,7 +4,7 @@ import { Logger } from '../log'; import { InstantiationProgress } from './progress'; import { DuckDBBindings } from './bindings_interface'; import { DuckDBConnection } from './connection'; -import { StatusCode } from '../status'; +import { StatusCode, IsArrowBuffer } from '../status'; import { dropResponseBuffers, DuckDBRuntime, readString, callSRet, copyBuffer, DuckDBDataProtocol } from './runtime'; import { CSVInsertOptions, JSONInsertOptions, ArrowInsertOptions } from './insert_options'; import { ScriptTokens } from './tokens'; @@ -224,6 +224,9 @@ export abstract class DuckDBBindingsBase implements DuckDBBindings { /** Fetch query results */ public fetchQueryResults(conn: number): Uint8Array { const [s, d, n] = callSRet(this.mod, 'duckdb_web_query_fetch_results', ['number'], [conn]); + if (!IsArrowBuffer(s)) { + throw new Error("Unexpected StatusCode from duckdb_web_query_fetch_results (" + s + ") and with self reported error as" + readString(this.mod, d, n)); + } if (s !== StatusCode.SUCCESS) { throw new Error(readString(this.mod, d, n)); } diff --git a/packages/duckdb-wasm/src/status.ts b/packages/duckdb-wasm/src/status.ts index 6390a5ec1..a308e1e56 100644 --- a/packages/duckdb-wasm/src/status.ts +++ b/packages/duckdb-wasm/src/status.ts @@ -1,3 +1,8 @@ export enum StatusCode { - SUCCESS = 0, + SUCCESS = 0, + MAX_ARROW_ERROR = 255, +} + +export function IsArrowBuffer(status: StatusCode): boolean { + return (status <= StatusCode.MAX_ARROW_ERROR); }