Skip to content

Commit 53beb26

Browse files
authored
feat: add support for setting the default database (#9)
Some flight SQL servers support the metadata field `database` to indicate the default database that queries should use (influx and lancedb enterprise for example). This PR adds support for that option. Regrettably, we cannot test this with integration tests because the voltron flight server does not support this field.
1 parent b514a6b commit 53beb26

File tree

3 files changed

+34
-17
lines changed

3 files changed

+34
-17
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ Currently all testing is done on Node.
1212
You can install the client using npm:
1313

1414
```bash
15-
npm install @lancedb/flightsql-client
15+
npm install @lancedb/arrow-flight-sql-client
1616
```
1717

1818
## Usage
1919

2020
To use the client, you first need to connect to your database:
2121

2222
```javascript
23-
import { Client } from "@lancedb/flightsql-client";
23+
import { Client } from "@lancedb/arrow-flight-sql-client";
2424

2525
const client = await Client.connect({
2626
host: "mydb.com:10025",

src/client.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ export interface ClientOptions {
2020
* The password to login with
2121
*/
2222
password: string;
23+
/**
24+
* The default database to use (non-standard, only supported on some servers)
25+
*/
26+
defaultDatabase?: string;
2327
/**
2428
* Is the server using TLS? If not, this must be set to true.
2529
*/
@@ -118,7 +122,12 @@ export class Client {
118122
* @returns A client that can be used to execute queries
119123
*/
120124
public static async connect(options: ClientOptions): Promise<Client> {
121-
const sql = await FlightSqlClient.connect(options.host, options.username, options.password);
125+
const sql = await FlightSqlClient.connect(
126+
options.host,
127+
options.username,
128+
options.password,
129+
options.defaultDatabase,
130+
);
122131
return new Client(sql);
123132
}
124133

src/flightsql.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class FlightSqlClient {
2929
this.flight = flight;
3030
}
3131

32-
private async login(username: string, password: string): Promise<void> {
32+
private async login(username: string, password: string, defaultDatabase?: string): Promise<void> {
3333
// Most servers seem to use Basic auth for the handshake to get a token.
3434
// The GRPC headers (and not the handshake request / response) are used to
3535
// transmit the username, password, and token.
@@ -50,23 +50,26 @@ export class FlightSqlClient {
5050
// The logic here is maybe not ideal. If a server someone sends an empty metadata message and then
5151
// a populated data payload message, then we will see the empty payload message first and fail to
5252
// wait for the next message. We can optimize this later if we see it in the wild.
53-
const hello_rsp = await firstValueFrom(call.responses);
53+
const helloRsp = await firstValueFrom(call.responses);
5454

55-
if (hello_rsp?.data?.payload) {
55+
const defaultMetadata = {};
56+
if (defaultDatabase) {
57+
defaultMetadata["database"] = defaultDatabase;
58+
}
59+
60+
if (helloRsp?.data?.payload) {
5661
// If we get a payload prefer that as a token
57-
const payload = new TextDecoder().decode(hello_rsp.data.payload);
58-
this.flight.set_default_metadata({
59-
authorization: "Bearer " + payload,
60-
});
61-
} else if (hello_rsp?.metadata) {
62+
const payload = new TextDecoder().decode(helloRsp.data.payload);
63+
defaultMetadata["authorization"] = "Bearer " + payload;
64+
this.flight.set_default_metadata(defaultMetadata);
65+
} else if (helloRsp?.metadata) {
6266
// Otherwise if we get metadata then use that as the token
63-
const authorization = hello_rsp.metadata.get_first_string("authorization");
67+
const authorization = helloRsp.metadata.get_first_string("authorization");
6468
if (!authorization) {
6569
throw new Error("Handshake failed, metadata received but no authorization header present");
6670
}
67-
this.flight.set_default_metadata({
68-
authorization,
69-
});
71+
defaultMetadata["authorization"] = authorization;
72+
this.flight.set_default_metadata(defaultMetadata);
7073
} else {
7174
throw new Error("Handshake failed, no metadata or data received from server before call completed");
7275
}
@@ -91,10 +94,15 @@ export class FlightSqlClient {
9194
* @param password The password to use for the handshake
9295
* @returns A client that can be used to execute queries
9396
*/
94-
public static async connect(host: string, username: string, password: string): Promise<FlightSqlClient> {
97+
public static async connect(
98+
host: string,
99+
username: string,
100+
password: string,
101+
defaultDatabase?: string,
102+
): Promise<FlightSqlClient> {
95103
const sql = new FlightClient(host);
96104
const client = new FlightSqlClient(sql);
97-
await client.login(username, password);
105+
await client.login(username, password, defaultDatabase);
98106
return client;
99107
}
100108

0 commit comments

Comments
 (0)