From 8ec47ccec9e3899df3d80527ceb7506e5e3b9c5e Mon Sep 17 00:00:00 2001 From: Jeff Raymakers Date: Wed, 25 Jun 2025 12:09:40 -0700 Subject: [PATCH] add run options to ui client (#15) --- .../classes/DuckDBUIClientConnection.ts | 22 ++++----- .../src/client/types/DuckDBUIRunOptions.ts | 12 +++++ .../makeDuckDBUIHttpRequestHeaders.ts | 46 ++++++++++++++++--- 3 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 ts/pkgs/duckdb-ui-client/src/client/types/DuckDBUIRunOptions.ts diff --git a/ts/pkgs/duckdb-ui-client/src/client/classes/DuckDBUIClientConnection.ts b/ts/pkgs/duckdb-ui-client/src/client/classes/DuckDBUIClientConnection.ts index 4a53048..8aea4a7 100644 --- a/ts/pkgs/duckdb-ui-client/src/client/classes/DuckDBUIClientConnection.ts +++ b/ts/pkgs/duckdb-ui-client/src/client/classes/DuckDBUIClientConnection.ts @@ -1,11 +1,9 @@ import { DuckDBUIHttpRequestQueue } from '../../http/classes/DuckDBUIHttpRequestQueue.js'; -import { - DuckDBUIHttpRequestHeaderOptions, - makeDuckDBUIHttpRequestHeaders, -} from '../../http/functions/makeDuckDBUIHttpRequestHeaders.js'; +import { makeDuckDBUIHttpRequestHeaders } from '../../http/functions/makeDuckDBUIHttpRequestHeaders.js'; import { sendDuckDBUIHttpRequest } from '../../http/functions/sendDuckDBUIHttpRequest.js'; import { randomString } from '../../util/functions/randomString.js'; import { materializedRunResultFromQueueResult } from '../functions/materializedRunResultFromQueueResult.js'; +import { DuckDBUIRunOptions } from '../types/DuckDBUIRunOptions.js'; import { MaterializedRunResult } from '../types/MaterializedRunResult.js'; export class DuckDBUIClientConnection { @@ -16,21 +14,21 @@ export class DuckDBUIClientConnection { public async run( sql: string, - args?: unknown[], + options?: DuckDBUIRunOptions, ): Promise { const queueResult = await this.requestQueue.enqueueAndWait( '/ddb/run', sql, - this.makeHeaders({ parameters: args }), + this.makeHeaders(options), ); return materializedRunResultFromQueueResult(queueResult); } - public enqueue(sql: string, args?: unknown[]): string { + public enqueue(sql: string, options?: DuckDBUIRunOptions): string { return this.requestQueue.enqueue( '/ddb/run', sql, - this.makeHeaders({ parameters: args }), + this.makeHeaders(options), ); } @@ -52,18 +50,16 @@ export class DuckDBUIClientConnection { return true; } - public async runQueued(id: string): Promise { + public async enqueuedResult(id: string): Promise { const queueResult = await this.requestQueue.enqueuedResult(id); return materializedRunResultFromQueueResult(queueResult); } - public get queuedCount(): number { + public get enqueuedCount(): number { return this.requestQueue.length; } - private makeHeaders( - options: Omit = {}, - ): Headers { + private makeHeaders(options: DuckDBUIRunOptions = {}): Headers { return makeDuckDBUIHttpRequestHeaders({ ...options, connectionName: this.connectionName, diff --git a/ts/pkgs/duckdb-ui-client/src/client/types/DuckDBUIRunOptions.ts b/ts/pkgs/duckdb-ui-client/src/client/types/DuckDBUIRunOptions.ts new file mode 100644 index 0000000..511c3a0 --- /dev/null +++ b/ts/pkgs/duckdb-ui-client/src/client/types/DuckDBUIRunOptions.ts @@ -0,0 +1,12 @@ +export interface DuckDBUIRunOptions { + description?: string; + databaseName?: string; + schemaName?: string; + errorsAsJson?: boolean; + parameters?: unknown[]; + resultChunkLimit?: number; + resultDatabaseName?: string; + resultSchemaName?: string; + resultTableName?: string; + resultTableChunkLimit?: number; +} diff --git a/ts/pkgs/duckdb-ui-client/src/http/functions/makeDuckDBUIHttpRequestHeaders.ts b/ts/pkgs/duckdb-ui-client/src/http/functions/makeDuckDBUIHttpRequestHeaders.ts index 53ced44..88abef8 100644 --- a/ts/pkgs/duckdb-ui-client/src/http/functions/makeDuckDBUIHttpRequestHeaders.ts +++ b/ts/pkgs/duckdb-ui-client/src/http/functions/makeDuckDBUIHttpRequestHeaders.ts @@ -1,19 +1,25 @@ +import { DuckDBUIRunOptions } from '../../client/types/DuckDBUIRunOptions.js'; import { toBase64 } from '../../util/functions/toBase64.js'; -export interface DuckDBUIHttpRequestHeaderOptions { - description?: string; +export interface DuckDBUIHttpRequestHeaderOptions extends DuckDBUIRunOptions { connectionName?: string; - databaseName?: string; - parameters?: unknown[]; } export function makeDuckDBUIHttpRequestHeaders({ description, connectionName, databaseName, + schemaName, + errorsAsJson, parameters, + resultChunkLimit, + resultDatabaseName, + resultSchemaName, + resultTableName, + resultTableChunkLimit, }: DuckDBUIHttpRequestHeaderOptions): Headers { const headers = new Headers(); + // We base64 encode some values because they can contain characters invalid in an HTTP header. if (description) { headers.append('X-DuckDB-UI-Request-Description', description); } @@ -21,13 +27,14 @@ export function makeDuckDBUIHttpRequestHeaders({ headers.append('X-DuckDB-UI-Connection-Name', connectionName); } if (databaseName) { - // base64 encode the value because it can contain characters invalid in an HTTP header headers.append('X-DuckDB-UI-Database-Name', toBase64(databaseName)); } + if (schemaName) { + headers.append('X-DuckDB-UI-Schema-Name', toBase64(schemaName)); + } if (parameters) { headers.append('X-DuckDB-UI-Parameter-Count', String(parameters.length)); for (let i = 0; i < parameters.length; i++) { - // base64 encode the value because it can contain characters invalid in an HTTP header // TODO: support non-string parameters? headers.append( `X-DuckDB-UI-Parameter-Value-${i}`, @@ -35,5 +42,32 @@ export function makeDuckDBUIHttpRequestHeaders({ ); } } + if (resultChunkLimit !== undefined) { + headers.append('X-DuckDB-UI-Result-Chunk-Limit', String(resultChunkLimit)); + } + if (resultDatabaseName) { + headers.append( + 'X-DuckDB-UI-Result-Database-Name', + toBase64(resultDatabaseName), + ); + } + if (resultSchemaName) { + headers.append( + 'X-DuckDB-UI-Result-Schema-Name', + toBase64(resultSchemaName), + ); + } + if (resultTableName) { + headers.append('X-DuckDB-UI-Result-Table-Name', toBase64(resultTableName)); + } + if (resultTableChunkLimit !== undefined) { + headers.append( + 'X-DuckDB-UI-Result-Table-Chunk-Limit', + String(resultTableChunkLimit), + ); + } + if (errorsAsJson) { + headers.append('X-DuckDB-UI-Errors-As-JSON', 'true'); + } return headers; }