-
Notifications
You must be signed in to change notification settings - Fork 0
Add Arrow Flight SQL client #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
4266bb0
start work on arrow flight sql client
IMax153 d80ab1b
get basic version of arrow flight ipc to json working
IMax153 a0bd24c
fix import extension
IMax153 6b7c632
remove old readers directory
IMax153 fa4026b
refactor schema identifiers
IMax153 424b3db
remove tsx
IMax153 0883423
remove go from flake
IMax153 d756abc
remove arrow-flight-ipc package
IMax153 f60192d
cleanup remaining identifiers
IMax153 dd7daf7
cleanup and enhance internal arrow-flight-ipc modules
IMax153 dd701a1
test(harness): setup vitest test harness for generated arrow FlightDa…
cmwhited 0648821
exclude internal from publish config exports
IMax153 39f2cc3
add check workflow to CI
IMax153 459e18f
add clean script and fix issues with JSONC and TypeScript
IMax153 36ce961
fix bug in flatbuffer writer
IMax153 40ef2cb
fix test sharding
IMax153 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,4 +14,6 @@ node_modules/ | |
| .DS_Store | ||
|
|
||
| # Scratchpad Files | ||
| scratchpad/**/*.md | ||
| scratchpad/**/*.ts | ||
| !scratchpad/index.ts | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,8 @@ | |
| corepack | ||
| nodejs_24 | ||
| python3 | ||
|
|
||
| go | ||
| ]; | ||
| }; | ||
| }); | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| version: v2 | ||
|
|
||
| inputs: | ||
| - git_repo: https://github.com/apache/arrow | ||
| subdir: format | ||
|
|
||
| plugins: | ||
| - local: protoc-gen-es | ||
| out: src/proto | ||
| opt: | ||
| - target=ts | ||
| - import_extension=ts |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| import { create, toBinary } from "@bufbuild/protobuf" | ||
| import { anyPack, AnySchema } from "@bufbuild/protobuf/wkt" | ||
| import { type Client, createClient, type Transport as ConnectTransport } from "@connectrpc/connect" | ||
| import { getMessageType, MessageHeaderType } from "@edgeandnode/arrow-flight-ipc/core/types" | ||
| import { decodeRecordBatch } from "@edgeandnode/arrow-flight-ipc/record-batch/decoder" | ||
| import { recordBatchToJson } from "@edgeandnode/arrow-flight-ipc/record-batch/json" | ||
| import { parseRecordBatch } from "@edgeandnode/arrow-flight-ipc/record-batch/parser" | ||
| import { parseSchema } from "@edgeandnode/arrow-flight-ipc/schema/parser" | ||
| import type { ArrowSchema } from "@edgeandnode/arrow-flight-ipc/schema/types" | ||
| import * as Console from "effect/Console" | ||
| import * as Context from "effect/Context" | ||
| import * as Effect from "effect/Effect" | ||
| import * as Layer from "effect/Layer" | ||
| import * as Schema from "effect/Schema" | ||
| import * as Stream from "effect/Stream" | ||
| import { FlightDescriptor_DescriptorType, FlightDescriptorSchema, FlightService } from "./proto/Flight_pb.ts" | ||
| import { CommandStatementQuerySchema } from "./proto/FlightSql_pb.ts" | ||
|
|
||
| /** | ||
| * A service which abstracts the underlying transport for a given client. | ||
| * | ||
| * A transport implements a protocol, such as Connect or gRPC-web, and allows | ||
| * for the concrete clients to be independent of the protocol. | ||
| */ | ||
| export class Transport extends Context.Tag("@edgeandnode/amp/Transport")< | ||
| Transport, | ||
| ConnectTransport | ||
| >() {} | ||
|
|
||
| /** | ||
| * Represents the possible errors that can occur when executing an Arrow Flight | ||
| * query. | ||
| */ | ||
| export type ArrowFlightQueryError = | ||
| | RpcError | ||
| | NoEndpointsError | ||
| | MultipleEndpointsError | ||
| | TicketNotFoundError | ||
|
|
||
| /** | ||
| * Represents an Arrow Flight RPC request that failed. | ||
| */ | ||
| export class RpcError extends Schema.TaggedError<RpcError>( | ||
| "@edgeandnode/amp/RpcError" | ||
| )("RpcError", { | ||
| method: Schema.String, | ||
| /** | ||
| * The underlying reason for the failed RPC request. | ||
| */ | ||
| cause: Schema.Defect | ||
| }) {} | ||
|
|
||
| /** | ||
| * Represents an error that occurred as a result of a `FlightInfo` request | ||
| * returning an empty list of endpoints from which data can be acquired. | ||
| */ | ||
| export class NoEndpointsError extends Schema.TaggedError<NoEndpointsError>( | ||
| "@edgeandnode/amp/NoEndpointsError" | ||
| )("NoEndpointsError", { | ||
| /** | ||
| * The SQL query that was requested. | ||
| */ | ||
| query: Schema.String | ||
| }) {} | ||
|
|
||
| // TODO: determine if this is _really_ a logical error case | ||
| /** | ||
| * Represents an error that occured as a result of a `FlightInfo` request | ||
| * returning multiple endpoints from which data can be acquired. | ||
| * | ||
| * For Amp queries, there should only ever be **one** authoritative source | ||
| * of data. | ||
| */ | ||
| export class MultipleEndpointsError extends Schema.TaggedError<MultipleEndpointsError>( | ||
| "@edgeandnode/amp/MultipleEndpointsError" | ||
| )("MultipleEndpointsError", { | ||
| /** | ||
| * The SQL query that was requested. | ||
| */ | ||
| query: Schema.String | ||
| }) {} | ||
|
|
||
| /** | ||
| * Represents an error that occurred as a result of a `FlightInfo` request | ||
| * whose endpoint did not have a ticket. | ||
| */ | ||
| export class TicketNotFoundError extends Schema.TaggedError<TicketNotFoundError>( | ||
| "@edgeandnode/amp/TicketNotFoundError" | ||
| )("TicketNotFoundError", { | ||
| /** | ||
| * The SQL query that was requested. | ||
| */ | ||
| query: Schema.String | ||
| }) {} | ||
|
|
||
| /** | ||
| * A service which can be used to execute queries against an Arrow Flight API. | ||
| */ | ||
| export class ArrowFlight extends Context.Tag("@edgeandnode/amp/ArrowFlight")<ArrowFlight, { | ||
| /** | ||
| * The Connect `Client` that will be used to execute Arrow Flight queries. | ||
| */ | ||
| readonly client: Client<typeof FlightService> | ||
|
|
||
| readonly query: (query: string) => Effect.Effect<any> | ||
| }>() {} | ||
|
|
||
| const make = Effect.gen(function*() { | ||
| const transport = yield* Transport | ||
| const client = createClient(FlightService, transport) | ||
|
|
||
| /** | ||
| * Execute a SQL query and return a stream of rows. | ||
| */ | ||
| const request = Effect.fn("ArrowFlight.request")(function*(query: string) { | ||
| const cmd = create(CommandStatementQuerySchema, { query }) | ||
| const any = anyPack(CommandStatementQuerySchema, cmd) | ||
| const desc = create(FlightDescriptorSchema, { | ||
| type: FlightDescriptor_DescriptorType.CMD, | ||
| cmd: toBinary(AnySchema, any) | ||
| }) | ||
|
|
||
| const flightInfo = yield* Effect.tryPromise({ | ||
| try: (signal) => client.getFlightInfo(desc, { signal }), | ||
| catch: (cause) => new RpcError({ cause, method: "getFlightInfo" }) | ||
| }) | ||
|
|
||
| if (flightInfo.endpoint.length !== 1) { | ||
| return yield* flightInfo.endpoint.length <= 0 | ||
| ? new NoEndpointsError({ query }) | ||
| : new MultipleEndpointsError({ query }) | ||
| } | ||
|
|
||
| const { ticket } = flightInfo.endpoint[0]! | ||
|
|
||
| if (ticket === undefined) { | ||
| return yield* new TicketNotFoundError({ query }) | ||
| } | ||
|
|
||
| const flightDataStream = Stream.unwrapScoped(Effect.gen(function*() { | ||
| const controller = yield* Effect.acquireRelease( | ||
| Effect.sync(() => new AbortController()), | ||
| (controller) => Effect.sync(() => controller.abort()) | ||
| ) | ||
| return Stream.fromAsyncIterable( | ||
| client.doGet(ticket, { signal: controller.signal }), | ||
| (cause) => new RpcError({ cause, method: "doGet" }) | ||
| ) | ||
| })) | ||
|
|
||
| let schema: ArrowSchema | undefined | ||
|
|
||
| // Convert FlightData stream to a stream of rows | ||
| return yield* flightDataStream.pipe( | ||
| Stream.runForEach(Effect.fnUntraced(function*(flightData) { | ||
| const messageType = yield* getMessageType(flightData) | ||
|
|
||
| switch (messageType) { | ||
| case MessageHeaderType.SCHEMA: { | ||
| schema = yield* parseSchema(flightData) | ||
| break | ||
| } | ||
| case MessageHeaderType.RECORD_BATCH: { | ||
| const recordBatch = yield* parseRecordBatch(flightData) | ||
| const decodedRecordBatch = decodeRecordBatch(recordBatch, flightData.dataBody, schema!) | ||
| const json = recordBatchToJson(decodedRecordBatch) | ||
| yield* Console.dir(json, { depth: null, colors: true }) | ||
| break | ||
| } | ||
| } | ||
|
|
||
| return yield* Effect.void | ||
| })) | ||
| ) | ||
| }) | ||
|
|
||
| return { | ||
| client, | ||
| /** | ||
| * Execute a SQL query and return a stream of rows. | ||
| */ | ||
| query: request | ||
| } as const | ||
| }) | ||
|
|
||
| /** | ||
| * A layer which constructs a concrete implementation of an `ArrowFlight` | ||
| * service and depends upon some implementation of a `Transport`. | ||
| */ | ||
| export const layer: Layer.Layer<ArrowFlight, ArrowFlightQueryError, Transport> = Layer.effect(ArrowFlight, make) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { createGrpcTransport, type GrpcTransportOptions } from "@connectrpc/connect-node" | ||
| import * as Layer from "effect/Layer" | ||
| import { Transport } from "../ArrowFlight.ts" | ||
|
|
||
| /** | ||
| * Create a `Transport` for the gRPC protocol using the Node.js `http2` module. | ||
| */ | ||
| export const layerTransportGrpc = (options: GrpcTransportOptions): Layer.Layer<Transport> => | ||
| Layer.sync(Transport, () => createGrpcTransport(options)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import * as Effect from "effect/Effect" | ||
|
|
||
| export const program = Effect.void | ||
| /** | ||
| * An implementation of the Arrow Flight protocol. | ||
| */ | ||
| export * as ArrowFlight from "./ArrowFlight.ts" |
Large diffs are not rendered by default.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.