Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/client-common/src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ export interface BaseResultSet<Stream, Format extends DataFormat | unknown> {
*/
stream(): ResultStream<Format, Stream>

/**
* Returns the raw underlying stream without any row parsing or transformation.
*
* Useful when you need the raw bytes (e.g., CSV or TSV data)
* and want to pipe them directly to a file or another stream
* without the overhead of row-by-row parsing.
*
* The stream is already decompressed if HTTP compression is enabled.
*
* Should be called only once.
*
* The method should throw if the underlying stream was already consumed
* by calling the other methods.
*/
rawStream(): Stream

Comment thread
renatocron marked this conversation as resolved.
/** Close the underlying stream. */
close(): void

Expand Down
26 changes: 26 additions & 0 deletions packages/client-node/__tests__/unit/node_result_set.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,32 @@ describe('[Node.js] ResultSet', () => {
await expect(rs.text()).rejects.toEqual(err)
})

it('should return the raw stream without row parsing', async () => {
const rs = makeResultSet(getDataStream(), 'CSVWithNames')
const raw = rs.rawStream()
const chunks: Buffer[] = []
for await (const chunk of raw) {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))
}
expect(Buffer.concat(chunks).toString()).toEqual(expectedText)
})

it('should throw if rawStream is called after text', async () => {
const rs = makeResultSet(getDataStream())
await rs.text()
expect(() => rs.rawStream()).toThrow(errMsg)
})

it('should throw if text is called after rawStream', async () => {
const rs = makeResultSet(getDataStream())
const raw = rs.rawStream()
// consume it
for await (const _ of raw) {
/* noop */
}
await expect(rs.text()).rejects.toEqual(err)
})
Comment thread
renatocron marked this conversation as resolved.
Outdated

it('should be able to call Row.text and Row.json multiple times', async () => {
const rs = makeResultSet(
Stream.Readable.from([Buffer.from('{"foo":"bar"}\n')]),
Expand Down
8 changes: 8 additions & 0 deletions packages/client-node/src/result_set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ export class ResultSet<
return pipeline as any
}

/** See {@link BaseResultSet.rawStream}. */
rawStream(): Stream.Readable {
if (this._stream.readableEnded) {
throw Error(streamAlreadyConsumedMessage)
}
return this._stream
}
Comment thread
renatocron marked this conversation as resolved.

/** See {@link BaseResultSet.close}. */
close() {
this._stream.destroy(new Error(resultSetClosedMessage))
Expand Down
6 changes: 6 additions & 0 deletions packages/client-web/src/result_set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ export class ResultSet<
return pipeline as any
}

/** See {@link BaseResultSet.rawStream}. */
rawStream(): ReadableStream {
this.markAsConsumed()
return this._stream
}

async close(): Promise<void> {
this.markAsConsumed()
await this._stream.cancel()
Comment thread
renatocron marked this conversation as resolved.
Expand Down
Loading