From f0e40e749739de03de08a98965b58d667c3bf87d Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Thu, 2 Nov 2023 10:56:47 +0100 Subject: [PATCH] feat: Expose LogicalType(s) for columns in QueryResult, fix #24 --- lib/duckdb.d.ts | 6 ++++++ lib/duckdb.js | 14 +++++++++----- src/duckdb_node.hpp | 1 + src/statement.cpp | 23 ++++++++++++++++++++++- test/query_result.test.ts | 10 +++++++++- test/typescript_decls.test.ts | 2 +- 6 files changed, 48 insertions(+), 8 deletions(-) diff --git a/lib/duckdb.d.ts b/lib/duckdb.d.ts index c613c097..93c2304f 100644 --- a/lib/duckdb.d.ts +++ b/lib/duckdb.d.ts @@ -108,7 +108,13 @@ export class Connection { unregister_buffer(name: string, callback?: Callback): void; } +export type LogicalType = { + id: number, + name: string, +} + export class QueryResult implements AsyncIterable { + getColumns(): Record; [Symbol.asyncIterator](): AsyncIterator; } diff --git a/lib/duckdb.js b/lib/duckdb.js index fc760d2f..c08b04ca 100644 --- a/lib/duckdb.js +++ b/lib/duckdb.js @@ -84,6 +84,13 @@ QueryResult.prototype.nextChunk; */ QueryResult.prototype.nextIpcBuffer; +/** + * Function to return logical types for columns + * + * @method + */ +QueryResult.prototype.getColumns; + /** * @name asyncIterator * @memberof module:duckdb~QueryResult @@ -218,12 +225,9 @@ Connection.prototype.each = function (sql) { * @param {...*} params * @yields row chunks */ -Connection.prototype.stream = async function* (sql) { +Connection.prototype.stream = async function (sql) { const statement = new Statement(this, sql); - const queryResult = await statement.stream.apply(statement, arguments); - for await (const result of queryResult) { - yield result; - } + return statement.stream.apply(statement, arguments); } /** diff --git a/src/duckdb_node.hpp b/src/duckdb_node.hpp index 614f32cb..4fe8e3fe 100644 --- a/src/duckdb_node.hpp +++ b/src/duckdb_node.hpp @@ -208,6 +208,7 @@ class QueryResult : public Napi::ObjectWrap { public: Napi::Value NextChunk(const Napi::CallbackInfo &info); Napi::Value NextIpcBuffer(const Napi::CallbackInfo &info); + Napi::Value GetColumns(const Napi::CallbackInfo &info); duckdb::shared_ptr cschema; private: diff --git a/src/statement.cpp b/src/statement.cpp index 3d813f8a..3bbeae27 100644 --- a/src/statement.cpp +++ b/src/statement.cpp @@ -636,7 +636,8 @@ Napi::FunctionReference QueryResult::Init(Napi::Env env, Napi::Object exports) { Napi::Function t = DefineClass(env, "QueryResult", {InstanceMethod("nextChunk", &QueryResult::NextChunk), - InstanceMethod("nextIpcBuffer", &QueryResult::NextIpcBuffer)}); + InstanceMethod("nextIpcBuffer", &QueryResult::NextIpcBuffer), + InstanceMethod("getColumns", &QueryResult::GetColumns)}); exports.Set("QueryResult", t); @@ -742,6 +743,26 @@ Napi::Value QueryResult::NextIpcBuffer(const Napi::CallbackInfo &info) { return deferred.Promise(); } +Napi::Value QueryResult::GetColumns(const Napi::CallbackInfo &info) +{ + auto env = info.Env(); + auto result = Napi::Object::New(env); + + for (duckdb::idx_t column_idx = 0; column_idx < this->result->ColumnCount(); column_idx++) + { + auto column_name = this->result->ColumnName(column_idx); + auto column_type = this->result->types[column_idx]; + + auto logic_type = Napi::Object::New(env); + logic_type.Set("id", Napi::Number::New(env, (double)column_type.id())); + logic_type.Set("name", Napi::String::New(env, column_type.ToString())); + + result.Set(column_name, logic_type); + } + + return result; +} + Napi::Object QueryResult::NewInstance(const Napi::Object &db) { return NodeDuckDB::GetData(db.Env())->query_result_constructor.New({db}); } diff --git a/test/query_result.test.ts b/test/query_result.test.ts index 16eb231c..dc4ebccb 100644 --- a/test/query_result.test.ts +++ b/test/query_result.test.ts @@ -14,7 +14,15 @@ describe('QueryResult', () => { it('streams results', async () => { let retrieved = 0; - const stream = conn.stream('SELECT * FROM range(0, ?)', total); + + const stream = await conn.stream("SELECT * FROM range(0, ?)", total); + assert.deepEqual(stream.getColumns(), { + range: { + id: 14, + name: "1", + }, + }); + for await (const row of stream) { retrieved++; } diff --git a/test/typescript_decls.test.ts b/test/typescript_decls.test.ts index ffdb6125..d0fe3167 100644 --- a/test/typescript_decls.test.ts +++ b/test/typescript_decls.test.ts @@ -222,7 +222,7 @@ describe("typescript: stream and QueryResult", function () { it("streams results", async () => { let retrieved = 0; - const stream = conn.stream("SELECT * FROM range(0, ?)", total); + const stream = await conn.stream("SELECT * FROM range(0, ?)", total); for await (const row of stream) { retrieved++; }