Skip to content

Commit c4cda49

Browse files
authored
feat(qwp): add QuestDB facade with pooled Sender and Query APIs (#28)
1 parent b2ac9e5 commit c4cda49

41 files changed

Lines changed: 5060 additions & 329 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ci/validate-pr-title/validate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const allowedSubTypes = [
1616
"log",
1717
"core",
1818
"ilp",
19+
"qwp",
1920
"http",
2021
"conf",
2122
"utils",
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*+*****************************************************************************
2+
* ___ _ ____ ____
3+
* / _ \ _ _ ___ ___| |_| _ \| __ )
4+
* | | | | | | |/ _ \/ __| __| | | | _ \
5+
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
6+
* \__\_\\__,_|\___||___/\__|____/|____/
7+
*
8+
* Copyright (c) 2014-2019 Appsicle
9+
* Copyright (c) 2019-2026 QuestDB
10+
*
11+
* Licensed under the Apache License, Version 2.0 (the "License");
12+
* you may not use this file except in compliance with the License.
13+
* You may obtain a copy of the License at
14+
*
15+
* http://www.apache.org/licenses/LICENSE-2.0
16+
*
17+
* Unless required by applicable law or agreed to in writing, software
18+
* distributed under the License is distributed on an "AS IS" BASIS,
19+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
* See the License for the specific language governing permissions and
21+
* limitations under the License.
22+
*
23+
******************************************************************************/
24+
25+
package io.questdb.client;
26+
27+
import java.util.concurrent.TimeUnit;
28+
29+
/**
30+
* Async handle for a submitted {@link Query}. Returned by {@link Query#submit()}.
31+
* <p>
32+
* Lifecycle: the Completion is allocated once as a field on the per-thread
33+
* {@link Query} instance and is reused on every {@code submit()}. It is
34+
* single-flight: a new {@code submit()} cannot be issued on the same {@link Query}
35+
* until the previous Completion resolves (via {@link #await()},
36+
* {@link #await(long, TimeUnit)} returning {@code true}, or an explicit
37+
* {@link #cancel()} that races to terminal).
38+
* <p>
39+
* Signaling: the Completion is signaled from the I/O thread of the pooled
40+
* query client when the handler's terminal callback ({@code onEnd},
41+
* {@code onError}, or {@code onExecDone}) returns.
42+
*/
43+
public interface Completion {
44+
45+
/**
46+
* Blocks until the query completes. Rethrows any server-reported failure
47+
* as a {@link QueryException}. Returns normally on success.
48+
*
49+
* @throws QueryException if the server reported an error or
50+
* {@link #cancel()} won the race
51+
* @throws InterruptedException if the calling thread is interrupted
52+
* while waiting
53+
*/
54+
void await() throws InterruptedException;
55+
56+
/**
57+
* Blocks up to the given timeout. Returns {@code true} if the query
58+
* completed, {@code false} on timeout.
59+
*
60+
* @throws QueryException if the server reported an error or
61+
* {@link #cancel()} won the race
62+
* @throws InterruptedException if the calling thread is interrupted
63+
* while waiting
64+
*/
65+
boolean await(long timeout, TimeUnit unit) throws InterruptedException;
66+
67+
/**
68+
* Requests cancellation of the in-flight query. The handler's
69+
* {@code onError} fires with a cancellation status. No-op if the query
70+
* has already completed.
71+
*/
72+
void cancel();
73+
74+
/**
75+
* Returns true once the query has terminated (success, error, or cancel
76+
* acknowledged).
77+
*/
78+
boolean isDone();
79+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*+*****************************************************************************
2+
* ___ _ ____ ____
3+
* / _ \ _ _ ___ ___| |_| _ \| __ )
4+
* | | | | | | |/ _ \/ __| __| | | | _ \
5+
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
6+
* \__\_\\__,_|\___||___/\__|____/|____/
7+
*
8+
* Copyright (c) 2014-2019 Appsicle
9+
* Copyright (c) 2019-2026 QuestDB
10+
*
11+
* Licensed under the Apache License, Version 2.0 (the "License");
12+
* you may not use this file except in compliance with the License.
13+
* You may obtain a copy of the License at
14+
*
15+
* http://www.apache.org/licenses/LICENSE-2.0
16+
*
17+
* Unless required by applicable law or agreed to in writing, software
18+
* distributed under the License is distributed on an "AS IS" BASIS,
19+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
* See the License for the specific language governing permissions and
21+
* limitations under the License.
22+
*
23+
******************************************************************************/
24+
25+
package io.questdb.client;
26+
27+
import io.questdb.client.cutlass.qwp.client.QwpBindSetter;
28+
import io.questdb.client.cutlass.qwp.client.QwpColumnBatchHandler;
29+
30+
/**
31+
* Per-thread, reusable builder for one query. Obtained from
32+
* {@link QuestDB#query()}: every call on the same thread returns the same
33+
* instance, reset to empty.
34+
* <p>
35+
* Lifecycle: configure with {@link #sql}, optional {@link #binds}, and
36+
* {@link #handler}, then call {@link #submit()} to obtain a {@link Completion}.
37+
* After the Completion terminates, the next {@code QuestDB.query()} call on
38+
* the same thread returns this same instance with its state reset.
39+
* <p>
40+
* Thread safety: not thread-safe. One in-flight query per thread.
41+
*/
42+
public interface Query {
43+
44+
/** Discards the current configuration without submitting. */
45+
void abandon();
46+
47+
/**
48+
* Sets the bind-value setter, invoked by the pooled query client when the
49+
* QUERY_REQUEST frame is being prepared. Pass a reusable
50+
* {@link QwpBindSetter} instance (or a stateless lambda hoisted to a
51+
* field) to keep submission zero-allocation.
52+
*/
53+
Query binds(QwpBindSetter binds);
54+
55+
/**
56+
* Sets the result-batch handler. The handler is invoked on the pooled
57+
* query client's I/O thread; if it touches caller state, it is
58+
* responsible for its own synchronization.
59+
*/
60+
Query handler(QwpColumnBatchHandler handler);
61+
62+
/**
63+
* Sets the SQL text. The buffer is not retained past {@link #submit()}.
64+
*/
65+
Query sql(CharSequence sql);
66+
67+
/**
68+
* Submits the query for execution. Returns the {@link Completion} field
69+
* cached on this instance; never allocates. Blocks up to the builder's
70+
* configured acquire timeout if the query pool is exhausted.
71+
*
72+
* @return the single-flight Completion bound to this Query instance
73+
*/
74+
Completion submit();
75+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*+*****************************************************************************
2+
* ___ _ ____ ____
3+
* / _ \ _ _ ___ ___| |_| _ \| __ )
4+
* | | | | | | |/ _ \/ __| __| | | | _ \
5+
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
6+
* \__\_\\__,_|\___||___/\__|____/|____/
7+
*
8+
* Copyright (c) 2014-2019 Appsicle
9+
* Copyright (c) 2019-2026 QuestDB
10+
*
11+
* Licensed under the Apache License, Version 2.0 (the "License");
12+
* you may not use this file except in compliance with the License.
13+
* You may obtain a copy of the License at
14+
*
15+
* http://www.apache.org/licenses/LICENSE-2.0
16+
*
17+
* Unless required by applicable law or agreed to in writing, software
18+
* distributed under the License is distributed on an "AS IS" BASIS,
19+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
* See the License for the specific language governing permissions and
21+
* limitations under the License.
22+
*
23+
******************************************************************************/
24+
25+
package io.questdb.client;
26+
27+
/**
28+
* Thrown from {@link Completion#await()} / {@link Completion#await(long, java.util.concurrent.TimeUnit)}
29+
* when the server reported an error for the corresponding {@link Query},
30+
* when {@link Completion#cancel()} won the race, or when an unrecoverable
31+
* transport failure occurred during submission.
32+
* <p>
33+
* The original wire-level status byte is exposed via {@link #getStatus()} so
34+
* callers can distinguish cancellation from schema errors etc. without
35+
* string-matching the message.
36+
*/
37+
public class QueryException extends RuntimeException {
38+
39+
private final byte status;
40+
41+
public QueryException(byte status, String message) {
42+
super(message);
43+
this.status = status;
44+
}
45+
46+
public QueryException(byte status, String message, Throwable cause) {
47+
super(message, cause);
48+
this.status = status;
49+
}
50+
51+
/**
52+
* Returns the server-reported wire status byte (see QWP protocol
53+
* definitions), or {@code 0} if this exception was raised by the client
54+
* (for example, transport failure before any server response).
55+
*/
56+
public byte getStatus() {
57+
return status;
58+
}
59+
}

0 commit comments

Comments
 (0)