diff --git a/README.md b/README.md index 1e30212..3cebc6f 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ public class Example { try (SpiceClient client = SpiceClient.builder() .build()) { - FlightStream stream = client.query("SELECT * FROM taxi_trips LIMIT 10;"); + FlightStream stream = client.sql("SELECT * FROM taxi_trips LIMIT 10;"); while (stream.next()) { try (VectorSchemaRoot batches = stream.getRoot()) { @@ -91,7 +91,7 @@ public class Example { .withSpiceCloud() .build()) { - FlightStream stream = client.query("SELECT * FROM eth.recent_blocks LIMIT 10;"); + FlightStream stream = client.sql("SELECT * FROM eth.recent_blocks LIMIT 10;"); while (stream.next()) { try (VectorSchemaRoot batches = stream.getRoot()) { @@ -179,7 +179,7 @@ public class Example { try (SpiceClient client = SpiceClient.builder().build()) { // Query with automatic type inference - ArrowReader reader = client.queryWithParams( + ArrowReader reader = client.sqlWithParams( "SELECT * FROM taxi_trips WHERE trip_distance > $1 LIMIT 10", 5.0); // Double is inferred as Float64 @@ -201,7 +201,7 @@ public class Example { Use positional placeholders ($1, $2, etc.) for multiple parameters: ```java -ArrowReader reader = client.queryWithParams( +ArrowReader reader = client.sqlWithParams( "SELECT * FROM taxi_trips WHERE trip_distance > $1 AND fare_amount > $2 LIMIT 10", 5.0, 20.0); ``` @@ -214,7 +214,7 @@ For precise control over Arrow types, use the `Param` factory methods: import ai.spice.Param; // Explicit type specification -ArrowReader reader = client.queryWithParams( +ArrowReader reader = client.sqlWithParams( "SELECT * FROM orders WHERE order_id = $1 AND amount >= $2", Param.int64(12345), Param.decimal128(new BigDecimal("99.99"), 10, 2)); @@ -274,13 +274,13 @@ SpiceClient client = SpiceClient.builder() // io.grpc.StatusRuntimeException with Status.UNAVAILABLE, // SSLHandshakeException, or similar transport-level errors. try { - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { // process results... } } catch (ExecutionException e) { if (isTransportFailure(e.getCause())) { client.reset(); // discard bad transport, reconnect immediately - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { // process results with fresh connection... } } else { @@ -308,7 +308,7 @@ import org.apache.arrow.vector.VectorSchemaRoot; import org.apache.arrow.vector.types.pojo.Field; try (SpiceClient client = SpiceClient.builder().build()) { - FlightStream stream = client.query("SELECT * FROM taxi_trips LIMIT 10;"); + FlightStream stream = client.sql("SELECT * FROM taxi_trips LIMIT 10;"); while (stream.next()) { try (VectorSchemaRoot root = stream.getRoot()) { @@ -360,11 +360,11 @@ See [ExampleIteratingResults.java](/src/main/java/ai/spice/example/ExampleIterat ### Async Queries -`queryAsync`/`queryAsyncWithParams` submit a query for asynchronous execution and return an `AsyncQuery` handle instead of streaming results directly. This requires the Spice runtime to be running in distributed/scheduler mode; for the normal synchronous, streaming path use [`query`](#with-locally-running-spiceai-oss)/[`queryWithParams`](#parameterized-queries-recommended). +`query`/`queryWithParams` submit a query for asynchronous execution and return an `AsyncQuery` handle instead of streaming results directly. This requires the Spice runtime to be running in distributed/scheduler mode; for the normal synchronous, streaming path use [`sql`](#with-locally-running-spiceai-oss)/[`sqlWithParams`](#parameterized-queries-recommended). ```java try (SpiceClient client = SpiceClient.builder().build()) { - AsyncQuery asyncQuery = client.queryAsync("SELECT * FROM taxi_trips LIMIT 10;"); + AsyncQuery asyncQuery = client.query("SELECT * FROM taxi_trips LIMIT 10;"); try (ArrowReader reader = asyncQuery.results()) { // blocks until the query completes while (reader.loadNextBatch()) { diff --git a/docs/parameterized_queries.md b/docs/parameterized_queries.md index 84937bb..ea3e408 100644 --- a/docs/parameterized_queries.md +++ b/docs/parameterized_queries.md @@ -18,7 +18,7 @@ The parameterized query system supports three modes of parameter usage: ```java // Types are automatically inferred from Java values -ArrowReader reader = client.queryWithParams( +ArrowReader reader = client.sqlWithParams( "SELECT * FROM table WHERE id = $1 AND name = $2", 42, // Inferred as Int32 "test" // Inferred as Utf8 @@ -29,7 +29,7 @@ ArrowReader reader = client.queryWithParams( ```java // Explicitly specify Arrow types for precise control -ArrowReader reader = client.queryWithParams( +ArrowReader reader = client.sqlWithParams( "SELECT * FROM table WHERE id = $1 AND created = $2", Param.int32(42), Param.timestamp(LocalDateTime.now(), TimeUnit.MICROSECOND, "UTC") @@ -101,12 +101,12 @@ ArrowReader reader = client.queryWithParams( ## API Methods -### queryWithParams +### sqlWithParams Primary method for parameterized queries: ```java -public ArrowReader queryWithParams(String sql, Object... params) throws ExecutionException +public ArrowReader sqlWithParams(String sql, Object... params) throws ExecutionException ``` **Parameters:** @@ -136,7 +136,7 @@ public class Example { try (SpiceClient client = SpiceClient.builder().build()) { // Simple query with inferred types - ArrowReader reader = client.queryWithParams( + ArrowReader reader = client.sqlWithParams( "SELECT * FROM customers WHERE age > $1 AND country = $2 LIMIT $3", 18, // int -> Int32 "USA", // String -> Utf8 @@ -164,7 +164,7 @@ public class Example { try (SpiceClient client = SpiceClient.builder().build()) { // Use explicit types when precision matters - ArrowReader reader = client.queryWithParams( + ArrowReader reader = client.sqlWithParams( "SELECT * FROM orders WHERE order_id = $1 AND quantity = $2", Param.int32(12345), // Explicitly Int32 Param.int16((short) 10) // Explicitly Int16 @@ -192,7 +192,7 @@ public class Example { // Query with timestamp LocalDateTime startTime = LocalDateTime.of(2024, 1, 1, 0, 0, 0); - ArrowReader reader = client.queryWithParams( + ArrowReader reader = client.sqlWithParams( "SELECT * FROM events WHERE created_at > $1", Param.timestamp(startTime, TimeUnit.MICROSECOND, "UTC") ); @@ -207,7 +207,7 @@ public class Example { ### Example 4: Mixed Inferred and Explicit Types ```java -ArrowReader reader = client.queryWithParams( +ArrowReader reader = client.sqlWithParams( "SELECT * FROM table WHERE id = $1 AND name = $2 AND created = $3 AND active = $4", 42, // Inferred as Int32 Param.string("test"), // Explicit Utf8 @@ -224,7 +224,7 @@ import java.math.BigDecimal; // Working with high-precision decimal numbers BigDecimal amount = new BigDecimal("12345.67"); -ArrowReader reader = client.queryWithParams( +ArrowReader reader = client.sqlWithParams( "SELECT * FROM transactions WHERE amount >= $1", Param.decimal128(amount, 10, 2) // 10 precision, 2 scale ); @@ -244,7 +244,7 @@ Param customParam = Param.of( new ArrowType.Timestamp(TimeUnit.NANOSECOND, "America/New_York") ); -ArrowReader reader = client.queryWithParams( +ArrowReader reader = client.sqlWithParams( "SELECT * FROM table WHERE ts = $1", customParam ); @@ -252,7 +252,7 @@ ArrowReader reader = client.queryWithParams( ## Type Inference Rules -When a plain Java value is passed to `queryWithParams()`, the SDK applies these inference rules: +When a plain Java value is passed to `sqlWithParams()`, the SDK applies these inference rules: 1. **Integers**: Based on the Java type (`byte` → Int8, `short` → Int16, `int` → Int32, `long` → Int64) 2. **Floating Point**: `float` → Float32, `double` → Float64 @@ -281,7 +281,7 @@ The system provides clear error messages for type mismatches: ```java try { - ArrowReader reader = client.queryWithParams("SELECT $1", unsupportedType); + ArrowReader reader = client.sqlWithParams("SELECT $1", unsupportedType); } catch (ExecutionException e) { // Error will indicate: "Unsupported parameter type: " System.err.println("Query failed: " + e.getMessage()); @@ -309,10 +309,10 @@ Parameterized queries protect against SQL injection: // ❌ Vulnerable to SQL injection String userId = getUserInput(); // Could be: "1 OR 1=1" String sql = "SELECT * FROM users WHERE id = " + userId; -FlightStream stream = client.query(sql); +FlightStream stream = client.sql(sql); // ✅ Safe from SQL injection -ArrowReader reader = client.queryWithParams( +ArrowReader reader = client.sqlWithParams( "SELECT * FROM users WHERE id = $1", userId ); @@ -326,10 +326,10 @@ If you get type mismatch errors, use explicit types: ```java // If inference picks wrong type -ArrowReader reader = client.queryWithParams("SELECT $1", 42); // Might infer as Int32 +ArrowReader reader = client.sqlWithParams("SELECT $1", 42); // Might infer as Int32 // Use explicit type instead -ArrowReader reader = client.queryWithParams("SELECT $1", Param.int64(42)); +ArrowReader reader = client.sqlWithParams("SELECT $1", Param.int64(42)); ``` ### Unsupported Type Error @@ -338,6 +338,6 @@ If you encounter "unsupported parameter type", use `Param.of` with explicit Arro ```java Param param = Param.of(value, myCustomArrowType); -ArrowReader reader = client.queryWithParams("SELECT $1", param); +ArrowReader reader = client.sqlWithParams("SELECT $1", param); ``` diff --git a/docs/release_notes/v0.8.0.md b/docs/release_notes/v0.8.0.md index bc4a59f..78c2022 100644 --- a/docs/release_notes/v0.8.0.md +++ b/docs/release_notes/v0.8.0.md @@ -19,7 +19,7 @@ for (SearchMatch match : response.getResults()) { ### 🗣️ Natural Language to SQL (Nsql) -`nsql()` translates a natural-language query into SQL via the runtime's configured LLM and runs it, returning the rows alongside the generated SQL. `nsqlGenerateSql()` generates the SQL without running it — inspect or edit it, or run it through `query()`/`queryWithParams()` for Arrow-typed results instead of `nsql()`'s decoded JSON rows. +`nsql()` translates a natural-language query into SQL via the runtime's configured LLM and runs it, returning the rows alongside the generated SQL. `nsqlGenerateSql()` generates the SQL without running it — inspect or edit it, or run it through `sql()`/`sqlWithParams()` for Arrow-typed results instead of `nsql()`'s decoded JSON rows. ```java NsqlResponse response = client.nsql(new NsqlRequest("how many trips were over 10 miles?")); @@ -29,8 +29,55 @@ System.out.println(response.getData()); String sql = client.nsqlGenerateSql(new NsqlRequest("how many trips were over 10 miles?")); ``` +### ⏳ Async Queries + +`query()`/`queryWithParams()` submit a query for asynchronous execution and return an `AsyncQuery` handle instead of streaming results directly. This requires the Spice runtime to be running in distributed/scheduler mode; for the normal synchronous, streaming path use `sql()`/`sqlWithParams()`. + +```java +AsyncQuery asyncQuery = client.query("SELECT * FROM taxi_trips LIMIT 10;"); + +try (ArrowReader reader = asyncQuery.results()) { // blocks until the query completes + while (reader.loadNextBatch()) { + System.out.println(reader.getVectorSchemaRoot().contentToTSVString()); + } +} +``` + +The `AsyncQuery` handle also exposes `status()` for a single poll, `waitForCompletion(Duration)` to bound how long a wait can take, and `cancel()` to request cancellation. + +### 📋 Active Query Management + +`listActiveQueries()` reports the synchronous queries currently running on the runtime, and `cancelActiveQuery(queryId)` cancels one. The runtime doesn't hand a query's ID back to the client that submitted it, so listing is the only way to find the ID that cancelling needs. + +```java +List queries = client.listActiveQueries(); +for (ActiveQuery query : queries) { + System.out.printf("%s %s %s%n", + query.getQueryId(), query.getProtocol(), query.getSqlPreview()); +} + +if (!queries.isEmpty()) { + client.cancelActiveQuery(queries.get(0).getQueryId()); +} +``` + +## Breaking Changes + +**`query()` and `queryWithParams()` now submit SQL for asynchronous execution and return an `AsyncQuery` handle**, instead of streaming results directly. The previous synchronous, streaming behavior is now `sql()`/`sqlWithParams()`: + +```java +// Before (v0.7.0 and earlier) +FlightStream stream = client.query("SELECT * FROM taxi_trips"); + +// After (v0.8.0) +FlightStream stream = client.sql("SELECT * FROM taxi_trips"); +``` + +Async queries additionally require the runtime to be running in distributed/scheduler mode; calling `query()`/`queryWithParams()` against a single-node runtime now returns an error explaining that, rather than the query results. + +Everything else in this release is additive: `search()`, `nsql()`/`nsqlGenerateSql()`, and `listActiveQueries()`/`cancelActiveQuery()` are all new methods; nothing else existing was renamed, removed, or retyped. + ## Compatibility -- Public API unchanged: both features are new methods; nothing existing was renamed, removed, or retyped. - No new dependencies. diff --git a/pom.xml b/pom.xml index 88d2bee..69f790c 100644 --- a/pom.xml +++ b/pom.xml @@ -265,6 +265,13 @@ ai.spice.example + + ai.spice.SpiceClient#query(java.lang.String) + ai.spice.SpiceClient#queryWithParams(java.lang.String, java.lang.Object[]) diff --git a/src/main/java/ai/spice/AsyncQuery.java b/src/main/java/ai/spice/AsyncQuery.java index b54ff9f..69fdd40 100644 --- a/src/main/java/ai/spice/AsyncQuery.java +++ b/src/main/java/ai/spice/AsyncQuery.java @@ -34,8 +34,8 @@ of this software and associated documentation files (the "Software"), to deal /** * A handle to a query submitted for asynchronous execution via - * {@link SpiceClient#queryAsync(String)} or - * {@link SpiceClient#queryAsyncWithParams(String, Object...)}. + * {@link SpiceClient#query(String)} or + * {@link SpiceClient#queryWithParams(String, Object...)}. * *

* Async queries require the Spice runtime to be running in diff --git a/src/main/java/ai/spice/Param.java b/src/main/java/ai/spice/Param.java index 12bf5e5..ee7b2a8 100644 --- a/src/main/java/ai/spice/Param.java +++ b/src/main/java/ai/spice/Param.java @@ -39,20 +39,20 @@ of this software and associated documentation files (the "Software"), to deal * *

* Use the static factory methods to create parameters with explicit types, - * or pass simple Java values directly to queryWithParams for automatic type + * or pass simple Java values directly to sqlWithParams for automatic type * inference. *

- * + * *

* Example usage: *

- * + * *
  * // With type inference
- * client.queryWithParams("SELECT * FROM table WHERE id = $1", 123);
- * 
+ * client.sqlWithParams("SELECT * FROM table WHERE id = $1", 123);
+ *
  * // With explicit type
- * client.queryWithParams("SELECT * FROM table WHERE id = $1", Param.int32(123));
+ * client.sqlWithParams("SELECT * FROM table WHERE id = $1", Param.int32(123));
  * 
*/ public class Param { diff --git a/src/main/java/ai/spice/QueryStatus.java b/src/main/java/ai/spice/QueryStatus.java index 5ca7d9e..a872bfe 100644 --- a/src/main/java/ai/spice/QueryStatus.java +++ b/src/main/java/ai/spice/QueryStatus.java @@ -24,8 +24,8 @@ of this software and associated documentation files (the "Software"), to deal /** * The lifecycle status of an async query submitted via - * {@link SpiceClient#queryAsync(String)} or - * {@link SpiceClient#queryAsyncWithParams(String, Object...)}. + * {@link SpiceClient#query(String)} or + * {@link SpiceClient#queryWithParams(String, Object...)}. * *

* The runtime serializes these as plain strings such as {@code "SUCCEEDED"}. A diff --git a/src/main/java/ai/spice/SpiceClient.java b/src/main/java/ai/spice/SpiceClient.java index 03b53e5..98081e5 100644 --- a/src/main/java/ai/spice/SpiceClient.java +++ b/src/main/java/ai/spice/SpiceClient.java @@ -617,7 +617,7 @@ private FlightChannel selectChannel(FlightChannel[] snapshot) { * Resets the underlying gRPC transport by closing the current Flight channels and * cached prepared statements, then immediately establishes fresh connections with * a new DNS lookup and TLS handshake. - * This ensures the next {@link #query(String)} or {@link #queryWithParams(String, Object...)} + * This ensures the next {@link #sql(String)} or {@link #sqlWithParams(String, Object...)} * call does not incur connection setup overhead. * *

Use this method to recover from unrecoverable transport failures such as:

@@ -630,11 +630,11 @@ private FlightChannel selectChannel(FlightChannel[] snapshot) { *

Example usage for long-lived clients:

*
{@code
      * try {
-     *     return client.query(sql);
+     *     return client.sql(sql);
      * } catch (ExecutionException e) {
      *     if (isTransportFailure(e.getCause())) {
      *         client.reset();
-     *         return client.query(sql); // retry with fresh connection
+     *         return client.sql(sql); // retry with fresh connection
      *     }
      *     throw e;
      * }
@@ -807,13 +807,19 @@ && shouldRetry(((FlightRuntimeException) throwable).status()))
     }
 
     /**
-     * Executes a sql query
+     * Runs sql against the Flight endpoint and streams the results back
+     * synchronously.
+     *
+     * 

+ * Use {@link #query(String)} instead to submit sql for asynchronous + * execution on the runtime and poll for completion, which requires the + * runtime to be running in distributed/scheduler mode. * * @param sql the SQL query to execute * @return a FlightStream with the query results * @throws ExecutionException if there is an error executing the query */ - public FlightStream query(String sql) throws ExecutionException { + public FlightStream sql(String sql) throws ExecutionException { if (Strings.isNullOrEmpty(sql)) { throw new IllegalArgumentException("No SQL query provided"); } @@ -862,16 +868,21 @@ public FlightStream query(String sql) throws ExecutionException { * *

      * // With automatic type inference
-     * ArrowReader reader = client.queryWithParams(
+     * ArrowReader reader = client.sqlWithParams(
      *     "SELECT * FROM table WHERE id = $1 AND name = $2",
      *     123, "test");
      *
      * // With explicit types
-     * ArrowReader reader = client.queryWithParams(
+     * ArrowReader reader = client.sqlWithParams(
      *     "SELECT * FROM table WHERE id = $1 AND amount = $2",
      *     Param.int32(123), Param.float64(99.99));
      * 
* + *

+ * Use {@link #queryWithParams(String, Object...)} instead to submit the + * parameterized query for asynchronous execution on the runtime, which + * requires distributed/scheduler mode. + * * @param sql the SQL query with positional parameter placeholders ($1, $2, * etc.) * @param params the parameter values (can be plain values or Param instances) @@ -879,7 +890,7 @@ public FlightStream query(String sql) throws ExecutionException { * closing the reader. * @throws ExecutionException if there is an error executing the query */ - public ArrowReader queryWithParams(String sql, Object... params) throws ExecutionException { + public ArrowReader sqlWithParams(String sql, Object... params) throws ExecutionException { if (Strings.isNullOrEmpty(sql)) { throw new IllegalArgumentException("No SQL query provided"); } @@ -1696,14 +1707,14 @@ public void refreshDataset(String dataset, RefreshOptions refreshOptions) throws * cluster mode. * *

- * Use {@link #query(String)} for the normal synchronous, streaming query + * Use {@link #sql(String)} for the normal synchronous, streaming query * path. * * @param sql the SQL query to submit * @return a handle to the submitted query * @throws ExecutionException if the query could not be submitted */ - public AsyncQuery queryAsync(String sql) throws ExecutionException { + public AsyncQuery query(String sql) throws ExecutionException { return submitAsyncQuery(sql, null); } @@ -1712,11 +1723,11 @@ public AsyncQuery queryAsync(String sql) throws ExecutionException { * bound positionally ($1, $2, ...) and sent to the runtime as a JSON array, * so each parameter must be a value Gson can encode meaningfully as JSON * (numbers, strings, booleans, lists) — this bypasses the Arrow-typed - * parameter binding {@link #queryWithParams(String, Object...)} uses, so + * parameter binding {@link #sqlWithParams(String, Object...)} uses, so * temporal and decimal types are not given special handling here. * *

- * Use {@link #queryWithParams(String, Object...)} for the normal + * Use {@link #sqlWithParams(String, Object...)} for the normal * synchronous, streaming parameterized query path. * * @param sql the SQL query with positional parameter placeholders ($1, $2, @@ -1725,7 +1736,7 @@ public AsyncQuery queryAsync(String sql) throws ExecutionException { * @return a handle to the submitted query * @throws ExecutionException if the query could not be submitted */ - public AsyncQuery queryAsyncWithParams(String sql, Object... params) throws ExecutionException { + public AsyncQuery queryWithParams(String sql, Object... params) throws ExecutionException { return submitAsyncQuery(sql, (params != null && params.length > 0) ? params : null); } @@ -1828,8 +1839,8 @@ private static JsonObject parseAsyncActionResponse(byte[] body, String descripti * {@code GET /v1/sql/active}. * *

- * Synchronous queries are the ones started by {@link #query(String)}, - * {@link #queryWithParams(String, Object...)}, or issued directly over Flight + * Synchronous queries are the ones started by {@link #sql(String)}, + * {@link #sqlWithParams(String, Object...)}, or issued directly over Flight * SQL, HTTP, NSQL, or Search. The runtime does not return a query's ID to the * client that submitted it, so this is the only way to discover the ID that * {@link #cancelActiveQuery(String)} needs. @@ -2200,7 +2211,7 @@ public NsqlResponse nsql(NsqlRequest request) throws ExecutionException { * *

* Use it to inspect or edit the query before running it, or to run it - * through {@link #query(String)} or {@link #queryWithParams(String, Object...)} + * through {@link #sql(String)} or {@link #sqlWithParams(String, Object...)} * so the results arrive as Arrow rather than decoded JSON. * * @param request the natural-language query @@ -2278,8 +2289,8 @@ private FlightStream queryInternal(String sql) { .toRuntimeException(); } if (endpoints.size() > 1) { - logger.warn("Server returned {} endpoints; query() consumes only the first. " - + "Use queryWithParams() (ArrowReader) to consume all endpoints.", endpoints.size()); + logger.warn("Server returned {} endpoints; sql() consumes only the first. " + + "Use sqlWithParams() (ArrowReader) to consume all endpoints.", endpoints.size()); } Ticket ticket = endpoints.get(0).getTicket(); return channel.client.getStream(ticket, channel.streamOptions); diff --git a/src/main/java/ai/spice/example/ExampleDatasetRefreshSpiceOSS.java b/src/main/java/ai/spice/example/ExampleDatasetRefreshSpiceOSS.java index 7df6483..49fbdbd 100644 --- a/src/main/java/ai/spice/example/ExampleDatasetRefreshSpiceOSS.java +++ b/src/main/java/ai/spice/example/ExampleDatasetRefreshSpiceOSS.java @@ -49,7 +49,7 @@ public static void main(String[] args) { System.out.println("Dataset refresh triggered for taxi_trips"); System.out.println("Query taxi_trips dataset"); - FlightStream stream = client.query("SELECT * FROM taxi_trips LIMIT 1;"); + FlightStream stream = client.sql("SELECT * FROM taxi_trips LIMIT 1;"); while (stream.next()) { try (VectorSchemaRoot batches = stream.getRoot()) { diff --git a/src/main/java/ai/spice/example/ExampleIteratingResults.java b/src/main/java/ai/spice/example/ExampleIteratingResults.java index dc8cc52..c8c9d39 100644 --- a/src/main/java/ai/spice/example/ExampleIteratingResults.java +++ b/src/main/java/ai/spice/example/ExampleIteratingResults.java @@ -50,7 +50,7 @@ public class ExampleIteratingResults { public static void main(String[] args) { try (SpiceClient client = SpiceClient.builder().build()) { - FlightStream stream = client.query("SELECT * FROM taxi_trips LIMIT 5;"); + FlightStream stream = client.sql("SELECT * FROM taxi_trips LIMIT 5;"); // Process each batch of results while (stream.next()) { diff --git a/src/main/java/ai/spice/example/ExampleParameterizedQueries.java b/src/main/java/ai/spice/example/ExampleParameterizedQueries.java index a80c386..f9af699 100644 --- a/src/main/java/ai/spice/example/ExampleParameterizedQueries.java +++ b/src/main/java/ai/spice/example/ExampleParameterizedQueries.java @@ -78,7 +78,7 @@ private static void simpleParameterizedQuery(SpiceClient client) throws Exceptio // - 10.0 (Double) -> Float64 String sql = "SELECT trip_distance, fare_amount FROM taxi_trips WHERE trip_distance > $1 ORDER BY trip_distance LIMIT 5"; - try (ArrowReader reader = client.queryWithParams(sql, 10.0)) { + try (ArrowReader reader = client.sqlWithParams(sql, 10.0)) { printResults(reader); } } @@ -93,7 +93,7 @@ private static void multipleParameters(SpiceClient client) throws Exception { + "WHERE trip_distance > $1 AND fare_amount > $2 " + "ORDER BY trip_distance LIMIT 5"; - try (ArrowReader reader = client.queryWithParams(sql, 5.0, 20.0)) { + try (ArrowReader reader = client.sqlWithParams(sql, 5.0, 20.0)) { printResults(reader); } } @@ -108,7 +108,7 @@ private static void explicitParameterTypes(SpiceClient client) throws Exception + "WHERE payment_type = $1 ORDER BY trip_distance LIMIT 5"; // Use Param.int64() to explicitly specify Int64 type - try (ArrowReader reader = client.queryWithParams(sql, Param.int64(1))) { + try (ArrowReader reader = client.sqlWithParams(sql, Param.int64(1))) { printResults(reader); } } @@ -122,7 +122,7 @@ private static void mixedTypes(SpiceClient client) throws Exception { + "ORDER BY trip_distance LIMIT 5"; // Mix automatic inference (5.0) with explicit type (Param.string()) - try (ArrowReader reader = client.queryWithParams(sql, + try (ArrowReader reader = client.sqlWithParams(sql, 5.0, // Inferred as Float64 Param.string("N") // Explicit String type )) { diff --git a/src/main/java/ai/spice/example/ExampleSpiceCloudPlatform.java b/src/main/java/ai/spice/example/ExampleSpiceCloudPlatform.java index b43d3e4..525718f 100644 --- a/src/main/java/ai/spice/example/ExampleSpiceCloudPlatform.java +++ b/src/main/java/ai/spice/example/ExampleSpiceCloudPlatform.java @@ -42,7 +42,7 @@ public static void main(String[] args) { .withSpiceCloud() .build()) { - FlightStream stream = client.query("SELECT * FROM eth.recent_blocks LIMIT 10;"); + FlightStream stream = client.sql("SELECT * FROM eth.recent_blocks LIMIT 10;"); while (stream.next()) { try (VectorSchemaRoot batches = stream.getRoot()) { diff --git a/src/main/java/ai/spice/example/ExampleSpiceOSS.java b/src/main/java/ai/spice/example/ExampleSpiceOSS.java index e8953a8..88e8fe7 100644 --- a/src/main/java/ai/spice/example/ExampleSpiceOSS.java +++ b/src/main/java/ai/spice/example/ExampleSpiceOSS.java @@ -40,7 +40,7 @@ public static void main(String[] args) { try (SpiceClient client = SpiceClient.builder() .build()) { - FlightStream stream = client.query("SELECT * FROM taxi_trips LIMIT 10;"); + FlightStream stream = client.sql("SELECT * FROM taxi_trips LIMIT 10;"); while (stream.next()) { try (VectorSchemaRoot batches = stream.getRoot()) { diff --git a/src/test/java/ai/spice/AsyncQueryTest.java b/src/test/java/ai/spice/AsyncQueryTest.java index 9f16bb8..5d56588 100644 --- a/src/test/java/ai/spice/AsyncQueryTest.java +++ b/src/test/java/ai/spice/AsyncQueryTest.java @@ -40,8 +40,8 @@ of this software and associated documentation files (the "Software"), to deal import junit.framework.TestCase; /** - * Tests for {@link SpiceClient#queryAsync(String)}, - * {@link SpiceClient#queryAsyncWithParams(String, Object...)}, and + * Tests for {@link SpiceClient#query(String)}, + * {@link SpiceClient#queryWithParams(String, Object...)}, and * {@link AsyncQuery} against the in-process {@link TestFlightSqlServer}. */ public class AsyncQueryTest extends TestCase { @@ -86,7 +86,7 @@ private static byte[] serializeChunk(long idStart, String... names) throws Excep } public void testQueryAsyncSubmitsAndReturnsHandle() throws Exception { - AsyncQuery query = client.queryAsync("SELECT * FROM test"); + AsyncQuery query = client.query("SELECT * FROM test"); assertNotNull(query.getQueryId()); assertFalse("a fresh query id should not be blank", query.getQueryId().isEmpty()); assertEquals(QueryStatus.SUCCEEDED, query.status()); @@ -97,7 +97,7 @@ public void testQueryAsyncSubmitsAndReturnsHandle() throws Exception { } public void testQueryAsyncWithParamsSubmitsSqlAndParametersInOrder() throws Exception { - AsyncQuery query = client.queryAsyncWithParams( + AsyncQuery query = client.queryWithParams( "SELECT * FROM test WHERE id = $1 AND name = $2 AND note = $3", 1, "alice", null); assertNotNull(query.getQueryId()); @@ -112,7 +112,7 @@ public void testQueryAsyncWithParamsSubmitsSqlAndParametersInOrder() throws Exce } public void testStatusReflectsChangeBetweenPolls() throws Exception { - AsyncQuery query = client.queryAsync("SELECT * FROM test"); + AsyncQuery query = client.query("SELECT * FROM test"); server.setAsyncQueryStatusSequence(query.getQueryId(), Arrays.asList(QueryStatus.PENDING, QueryStatus.RUNNING, QueryStatus.SUCCEEDED)); @@ -126,7 +126,7 @@ public void testStatusReflectsChangeBetweenPolls() throws Exception { } public void testWaitForCompletionBlocksThroughPendingRunningSucceeded() throws Exception { - AsyncQuery query = client.queryAsyncWithParams("SELECT * FROM test WHERE id = $1", 1); + AsyncQuery query = client.queryWithParams("SELECT * FROM test WHERE id = $1", 1); server.setAsyncQueryStatusSequence(query.getQueryId(), Arrays.asList(QueryStatus.PENDING, QueryStatus.RUNNING, QueryStatus.RUNNING, QueryStatus.SUCCEEDED)); @@ -136,7 +136,7 @@ public void testWaitForCompletionBlocksThroughPendingRunningSucceeded() throws E } public void testWaitForCompletionTimesOut() throws Exception { - AsyncQuery query = client.queryAsync("SELECT * FROM test"); + AsyncQuery query = client.query("SELECT * FROM test"); // RUNNING repeated forever (the sequence holds its last entry) never reaches a terminal status. server.setAsyncQueryStatusSequence(query.getQueryId(), Arrays.asList(QueryStatus.PENDING, QueryStatus.RUNNING)); @@ -157,7 +157,7 @@ public void testWaitForCompletionTimesOut() throws Exception { } public void testResultsMultiChunkReconstructsData() throws Exception { - AsyncQuery query = client.queryAsync("SELECT * FROM test"); + AsyncQuery query = client.query("SELECT * FROM test"); byte[] chunk0 = serializeChunk(0, "a", "b"); byte[] chunk1 = serializeChunk(2, "c"); server.setAsyncQueryChunks(query.getQueryId(), Arrays.asList(chunk0, chunk1), 3); @@ -180,7 +180,7 @@ public void testResultsMultiChunkReconstructsData() throws Exception { } public void testResultsEmptyResultReturnsEmptySchema() throws Exception { - AsyncQuery query = client.queryAsync("SELECT * FROM test WHERE 1 = 0"); + AsyncQuery query = client.query("SELECT * FROM test WHERE 1 = 0"); server.setAsyncQueryChunks(query.getQueryId(), Collections.emptyList(), 0); try (ArrowReader reader = query.results()) { @@ -190,7 +190,7 @@ public void testResultsEmptyResultReturnsEmptySchema() throws Exception { } public void testResultsPropagatesChunkZeroFetchFailureForDeclaredNonEmptyResult() throws Exception { - AsyncQuery query = client.queryAsync("SELECT * FROM test"); + AsyncQuery query = client.query("SELECT * FROM test"); byte[] chunk0 = serializeChunk(0, "a"); server.setAsyncQueryChunks(query.getQueryId(), Collections.singletonList(chunk0), 1); // A genuine one-chunk result whose chunk-0 fetch fails at the RPC level @@ -208,7 +208,7 @@ public void testResultsPropagatesChunkZeroFetchFailureForDeclaredNonEmptyResult( } public void testResultsFailedThrowsWithErrorMessage() throws Exception { - AsyncQuery query = client.queryAsync("SELECT * FROM test"); + AsyncQuery query = client.query("SELECT * FROM test"); server.setAsyncQueryStatusSequence(query.getQueryId(), Arrays.asList(QueryStatus.RUNNING, QueryStatus.FAILED)); server.setAsyncQueryError(query.getQueryId(), "TABLE_NOT_FOUND", "table 'test' does not exist"); @@ -223,7 +223,7 @@ public void testResultsFailedThrowsWithErrorMessage() throws Exception { } public void testCancelUpdatesStatus() throws Exception { - AsyncQuery query = client.queryAsync("SELECT * FROM test"); + AsyncQuery query = client.query("SELECT * FROM test"); server.setAsyncQueryStatusSequence(query.getQueryId(), Arrays.asList(QueryStatus.PENDING, QueryStatus.RUNNING)); @@ -234,13 +234,13 @@ public void testCancelUpdatesStatus() throws Exception { public void testQueryAsyncRejectsEmptySql() throws Exception { try { - client.queryAsync(""); + client.query(""); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { // expected } try { - client.queryAsync(null); + client.query(null); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { // expected @@ -250,7 +250,7 @@ public void testQueryAsyncRejectsEmptySql() throws Exception { public void testQueryAsyncWithParamsRejectsEmptySql() throws Exception { try { - client.queryAsyncWithParams("", 1); + client.queryWithParams("", 1); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { // expected diff --git a/src/test/java/ai/spice/ChaosE2ETest.java b/src/test/java/ai/spice/ChaosE2ETest.java index ed8f3fa..8c4d0d4 100644 --- a/src/test/java/ai/spice/ChaosE2ETest.java +++ b/src/test/java/ai/spice/ChaosE2ETest.java @@ -99,7 +99,7 @@ private SpiceClient newClient(int maxRetries) throws Exception { } private static long countRows(SpiceClient client, String sql) throws Exception { - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { return LocalFlightServerTest.countRows(stream); } } @@ -136,7 +136,7 @@ public void testSurvivesRuntimeRestart() throws Exception { try (SpiceClient client = newClient(3)) { assertEquals(1, countRows(client, "SELECT 1")); // Prime the prepared-statement cache so the restart invalidates a live handle. - try (ArrowReader reader = client.queryWithParams("SELECT $1", 42L)) { + try (ArrowReader reader = client.sqlWithParams("SELECT $1", 42L)) { assertTrue(reader.loadNextBatch()); } @@ -155,7 +155,7 @@ public void testSurvivesRuntimeRestart() throws Exception { // Same client, no reset(): reconnect + re-prepare must be automatic. assertEquals(1, (long) guarded(() -> countRows(client, "SELECT 1"))); - try (ArrowReader reader = guarded(() -> client.queryWithParams("SELECT $1", 43L))) { + try (ArrowReader reader = guarded(() -> client.sqlWithParams("SELECT $1", 43L))) { assertTrue("cached statement must transparently re-prepare after restart", reader.loadNextBatch()); } @@ -227,7 +227,7 @@ public void testKillMidStreamFailsCleanlyAndRecovers() throws Exception { try { guarded(() -> { - try (FlightStream stream = client.query(bigSql)) { + try (FlightStream stream = client.sql(bigSql)) { assertTrue("stream should produce at least one batch", stream.next()); spiced.kill(); return LocalFlightServerTest.countRows(stream); diff --git a/src/test/java/ai/spice/FlightInfoReaderTest.java b/src/test/java/ai/spice/FlightInfoReaderTest.java index 1b66157..198921f 100644 --- a/src/test/java/ai/spice/FlightInfoReaderTest.java +++ b/src/test/java/ai/spice/FlightInfoReaderTest.java @@ -72,7 +72,7 @@ public void testBytesReadAccumulatesAcrossEndpoints() throws Exception { server.endpointCount = 2; server.batchesPerEndpoint = 2; try (SpiceClient client = SpiceClient.builder().withFlightAddress(server.flightUri()).build(); - org.apache.arrow.vector.ipc.ArrowReader reader = client.queryWithParams("SELECT 1", 1)) { + org.apache.arrow.vector.ipc.ArrowReader reader = client.sqlWithParams("SELECT 1", 1)) { long previous = 0; int batches = 0; while (reader.loadNextBatch()) { diff --git a/src/test/java/ai/spice/FlightQueryTest.java b/src/test/java/ai/spice/FlightQueryTest.java index 56197a5..b896a5f 100644 --- a/src/test/java/ai/spice/FlightQueryTest.java +++ b/src/test/java/ai/spice/FlightQueryTest.java @@ -52,7 +52,7 @@ public void testQuerySpiceCloudPlatform() throws ExecutionException, Interrupted int totalRows = 0; int columnCount = 0; - try (FlightStream res = spiceClient.query(sql)) { + try (FlightStream res = spiceClient.sql(sql)) { while (res.next()) { VectorSchemaRoot root = res.getRoot(); if (totalRows == 0) { @@ -85,7 +85,7 @@ public void testQuerySpiceOSS() throws ExecutionException, InterruptedException int totalRows = 0; int columnCount = 0; - try (FlightStream res = spiceClient.query(sql)) { + try (FlightStream res = spiceClient.sql(sql)) { while (res.next()) { VectorSchemaRoot root = res.getRoot(); if (totalRows == 0) { @@ -180,7 +180,7 @@ public void testRefreshWithOptionsSpiceOSS() throws ExecutionException, Interrup } private static long countRows(SpiceClient client, String sql) throws Exception { - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { long rows = 0; while (stream.next()) { rows += stream.getRoot().getRowCount(); diff --git a/src/test/java/ai/spice/LocalFlightServerTest.java b/src/test/java/ai/spice/LocalFlightServerTest.java index 6bfa6bd..609c8d5 100644 --- a/src/test/java/ai/spice/LocalFlightServerTest.java +++ b/src/test/java/ai/spice/LocalFlightServerTest.java @@ -75,7 +75,7 @@ static long countRows(ArrowReader reader) throws Exception { } public void testPlainQueryReturnsAllRows() throws Exception { - try (FlightStream stream = client.query("SELECT * FROM test")) { + try (FlightStream stream = client.sql("SELECT * FROM test")) { assertNotNull(stream.getSchema().findField("id")); assertNotNull(stream.getSchema().findField("name")); assertEquals(server.expectedTotalRows(), countRows(stream)); @@ -85,7 +85,7 @@ public void testPlainQueryReturnsAllRows() throws Exception { } public void testQueryWithParamsReturnsAllRows() throws Exception { - try (ArrowReader reader = client.queryWithParams("SELECT * FROM test WHERE id > $1", 5L)) { + try (ArrowReader reader = client.sqlWithParams("SELECT * FROM test WHERE id > $1", 5L)) { assertNotNull(reader.getVectorSchemaRoot().getSchema().findField("id")); assertEquals(server.expectedTotalRows(), countRows(reader)); assertTrue("bytesRead should be positive", reader.bytesRead() > 0); @@ -96,7 +96,7 @@ public void testQueryWithParamsReturnsAllRows() throws Exception { } public void testQueryWithParamsWithoutParameters() throws Exception { - try (ArrowReader reader = client.queryWithParams("SELECT 1")) { + try (ArrowReader reader = client.sqlWithParams("SELECT 1")) { assertEquals(server.expectedTotalRows(), countRows(reader)); } // No parameters bound: no DoPut should have happened. @@ -111,26 +111,26 @@ public void testQueryWithParamsReadsAllEndpoints() throws Exception { server.endpointCount = 3; server.batchesPerEndpoint = 2; server.rowsPerBatch = 7; - try (ArrowReader reader = client.queryWithParams("SELECT * FROM test", 1)) { + try (ArrowReader reader = client.sqlWithParams("SELECT * FROM test", 1)) { assertEquals(3 * 2 * 7, countRows(reader)); } assertEquals("one DoGet per endpoint", 3, server.doGetCalls.get()); } /** - * Documents the known limitation of the FlightStream-returning query() + * Documents the known limitation of the FlightStream-returning sql() * API: only the first endpoint of a partitioned result is consumed. */ public void testPlainQueryConsumesOnlyFirstEndpoint() throws Exception { server.endpointCount = 3; - try (FlightStream stream = client.query("SELECT * FROM test")) { + try (FlightStream stream = client.sql("SELECT * FROM test")) { assertEquals((long) server.batchesPerEndpoint * server.rowsPerBatch, countRows(stream)); } assertEquals(1, server.doGetCalls.get()); } public void testParameterValuesArriveAtServer() throws Exception { - try (ArrowReader reader = client.queryWithParams( + try (ArrowReader reader = client.sqlWithParams( "SELECT * FROM test WHERE a=$1 AND b=$2 AND c=$3 AND d=$4 AND e=$5 AND f=$6 AND g=$7 AND h=$8", 42, 42L, "hello", 3.5, true, new byte[] { 1, 2, 3 }, LocalDate.of(2026, 7, 30), new BigDecimal("12.34"))) { @@ -152,7 +152,7 @@ public void testParameterValuesArriveAtServer() throws Exception { } public void testExplicitParamTypesArriveAtServer() throws Exception { - try (ArrowReader reader = client.queryWithParams( + try (ArrowReader reader = client.sqlWithParams( "SELECT * FROM test WHERE a=$1 AND b=$2 AND c=$3", Param.int32(7), Param.string("typed"), Param.float64(2.25))) { countRows(reader); @@ -167,10 +167,10 @@ public void testExplicitParamTypesArriveAtServer() throws Exception { public void testRepeatedQueriesReturnConsistentResults() throws Exception { for (int i = 0; i < 5; i++) { - try (ArrowReader reader = client.queryWithParams("SELECT * FROM test WHERE id = $1", (long) i)) { + try (ArrowReader reader = client.sqlWithParams("SELECT * FROM test WHERE id = $1", (long) i)) { assertEquals(server.expectedTotalRows(), countRows(reader)); } - try (FlightStream stream = client.query("SELECT " + i)) { + try (FlightStream stream = client.sql("SELECT " + i)) { assertEquals(server.expectedTotalRows(), countRows(stream)); } } @@ -178,13 +178,13 @@ public void testRepeatedQueriesReturnConsistentResults() throws Exception { public void testEmptySqlThrowsIllegalArgument() throws Exception { try { - client.query(""); + client.sql(""); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { // expected } try { - client.queryWithParams("", 1); + client.sqlWithParams("", 1); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { // expected @@ -195,13 +195,13 @@ public void testQueryAfterCloseThrowsIllegalState() throws Exception { SpiceClient shortLived = SpiceClient.builder().withFlightAddress(server.flightUri()).build(); shortLived.close(); try { - shortLived.query("SELECT 1"); + shortLived.sql("SELECT 1"); fail("Expected IllegalStateException"); } catch (IllegalStateException expected) { assertTrue(expected.getMessage().contains("closed")); } try { - shortLived.queryWithParams("SELECT $1", 1); + shortLived.sqlWithParams("SELECT $1", 1); fail("Expected IllegalStateException"); } catch (IllegalStateException expected) { assertTrue(expected.getMessage().contains("closed")); @@ -211,7 +211,7 @@ public void testQueryAfterCloseThrowsIllegalState() throws Exception { public void testUnsupportedParameterTypeFailsWithoutRpc() throws Exception { long infoCallsBefore = server.getFlightInfoCalls.get(); try { - client.queryWithParams("SELECT $1", new Object()); + client.sqlWithParams("SELECT $1", new Object()); fail("Expected ExecutionException"); } catch (ExecutionException e) { assertTrue("cause should be IllegalArgumentException, got: " + e.getCause(), diff --git a/src/test/java/ai/spice/MtlsTest.java b/src/test/java/ai/spice/MtlsTest.java index a3fc7e6..a205343 100644 --- a/src/test/java/ai/spice/MtlsTest.java +++ b/src/test/java/ai/spice/MtlsTest.java @@ -61,12 +61,12 @@ private static SpiceClientBuilder clientFor(TestFlightSqlServer server) throws E } private static void assertQueriesWork(SpiceClient client, TestFlightSqlServer server) throws Exception { - try (FlightStream stream = client.query("SELECT * FROM test")) { + try (FlightStream stream = client.sql("SELECT * FROM test")) { assertEquals(server.expectedTotalRows(), LocalFlightServerTest.countRows(stream)); } // Parameterized queries run on the same TLS channel (prepared // statements inherit the transport configuration). - try (ArrowReader reader = client.queryWithParams("SELECT * FROM test WHERE id > $1", 1L)) { + try (ArrowReader reader = client.sqlWithParams("SELECT * FROM test WHERE id > $1", 1L)) { assertEquals(server.expectedTotalRows(), LocalFlightServerTest.countRows(reader)); } } @@ -100,7 +100,7 @@ public void testMissingClientCertificateRejected() throws Exception { .withTlsRootCertFile(certs.caCert.toString()) .build()) { try { - client.query("SELECT 1"); + client.sql("SELECT 1"); fail("Expected the TLS handshake to be rejected without a client certificate"); } catch (ExecutionException e) { assertTlsFailure(e); @@ -115,7 +115,7 @@ public void testUntrustedServerCaRejected() throws Exception { .withTlsRootCertFile(certs.otherCaCert.toString()) .build()) { try { - client.query("SELECT 1"); + client.sql("SELECT 1"); fail("Expected certificate verification to fail against an untrusted CA"); } catch (ExecutionException e) { assertTlsFailure(e); diff --git a/src/test/java/ai/spice/ParameterizedQueryTest.java b/src/test/java/ai/spice/ParameterizedQueryTest.java index be65400..dd30f37 100644 --- a/src/test/java/ai/spice/ParameterizedQueryTest.java +++ b/src/test/java/ai/spice/ParameterizedQueryTest.java @@ -60,7 +60,7 @@ public void testParameterizedQuerySpiceCloud() throws Exception { // Test with float parameter - taxi_trips available in Spice Cloud String sql = "SELECT tpep_pickup_datetime, total_amount FROM taxi_trips WHERE total_amount > $1 ORDER BY total_amount LIMIT 5"; - try (ArrowReader reader = spiceClient.queryWithParams(sql, 10.0)) { + try (ArrowReader reader = spiceClient.sqlWithParams(sql, 10.0)) { int totalRows = 0; while (reader.loadNextBatch()) { @@ -86,7 +86,7 @@ public void testParameterizedQuerySpiceOSS() throws Exception { // Test with float parameter on tpch.orders String sql = "SELECT o_orderkey, o_totalprice FROM tpch.orders WHERE o_totalprice > $1 ORDER BY o_totalprice LIMIT 5"; - try (ArrowReader reader = spiceClient.queryWithParams(sql, 10000.0)) { + try (ArrowReader reader = spiceClient.sqlWithParams(sql, 10000.0)) { int totalRows = 0; while (reader.loadNextBatch()) { @@ -116,7 +116,7 @@ public void testMultipleParameters() throws Exception { try (SpiceClient spiceClient = SpiceClient.builder().withMaxRetries(1).build()) { String sql = "SELECT o_orderkey, o_totalprice FROM tpch.orders WHERE o_totalprice > $1 AND o_custkey > $2 LIMIT 5"; - try (ArrowReader reader = spiceClient.queryWithParams(sql, 5000.0, 100)) { + try (ArrowReader reader = spiceClient.sqlWithParams(sql, 5000.0, 100)) { int totalRows = 0; while (reader.loadNextBatch()) { @@ -144,7 +144,7 @@ public void testStringParameter() throws Exception { // Use c_mktsegment which is a string column in tpch.customer String sql = "SELECT c_custkey, c_mktsegment FROM tpch.customer WHERE c_mktsegment = $1 LIMIT 5"; - try (ArrowReader reader = spiceClient.queryWithParams(sql, "BUILDING")) { + try (ArrowReader reader = spiceClient.sqlWithParams(sql, "BUILDING")) { int totalRows = 0; while (reader.loadNextBatch()) { @@ -172,7 +172,7 @@ public void testExplicitParamTypes() throws Exception { // Use explicit int64 type on tpch.customer String sql = "SELECT c_custkey, c_name, c_nationkey FROM tpch.customer WHERE c_nationkey = $1 LIMIT 5"; - try (ArrowReader reader = spiceClient.queryWithParams(sql, Param.int64(1))) { + try (ArrowReader reader = spiceClient.sqlWithParams(sql, Param.int64(1))) { int totalRows = 0; while (reader.loadNextBatch()) { @@ -198,7 +198,7 @@ public void testMixedParameterTypes() throws Exception { try (SpiceClient spiceClient = SpiceClient.builder().withMaxRetries(1).build()) { String sql = "SELECT o_orderkey, o_totalprice FROM tpch.orders WHERE o_totalprice > $1 AND o_orderstatus = $2 LIMIT 5"; - try (ArrowReader reader = spiceClient.queryWithParams(sql, + try (ArrowReader reader = spiceClient.sqlWithParams(sql, Param.float64(5000.0), Param.string("O"))) { int totalRows = 0; @@ -292,7 +292,7 @@ public void testParamFactoryMethods() { public void testNullSqlThrows() throws Exception { try (SpiceClient spiceClient = SpiceClient.builder().withMaxRetries(1).build()) { try { - spiceClient.queryWithParams(null, 1); + spiceClient.sqlWithParams(null, 1); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { assertTrue(e.getMessage().contains("No SQL query provided")); @@ -312,7 +312,7 @@ public void testNullSqlThrows() throws Exception { public void testEmptySqlThrows() throws Exception { try (SpiceClient spiceClient = SpiceClient.builder().withMaxRetries(1).build()) { try { - spiceClient.queryWithParams("", 1); + spiceClient.sqlWithParams("", 1); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { assertTrue(e.getMessage().contains("No SQL query provided")); diff --git a/src/test/java/ai/spice/PerfBenchmarkTest.java b/src/test/java/ai/spice/PerfBenchmarkTest.java index d69876e..1a07c33 100644 --- a/src/test/java/ai/spice/PerfBenchmarkTest.java +++ b/src/test/java/ai/spice/PerfBenchmarkTest.java @@ -128,7 +128,7 @@ private static long checkedCountRows(ArrowReader reader, long expectedRows) thro private static Callable paramsOp(SpiceClient client, long expectedRows) { return () -> { - try (ArrowReader reader = client.queryWithParams(SQL, 5L)) { + try (ArrowReader reader = client.sqlWithParams(SQL, 5L)) { return checkedCountRows(reader, expectedRows); } }; @@ -138,7 +138,7 @@ public void testBenchmarkPlainQuery() throws Exception { try (SpiceClient client = SpiceClient.builder().withFlightAddress(server.flightUri()).build()) { final long expectedRows = server.expectedTotalRows(); Callable op = () -> { - try (FlightStream stream = client.query("SELECT * FROM bench")) { + try (FlightStream stream = client.sql("SELECT * FROM bench")) { return checkedCountRows(stream, expectedRows); } }; @@ -146,8 +146,8 @@ public void testBenchmarkPlainQuery() throws Exception { long getFlightInfoBefore = server.getFlightInfoCalls.get(); long doGetBefore = server.doGetCalls.get(); long[] samples = measure(MEASURED_ITERATIONS, op); - System.out.println("[bench] " + stats("query()", samples)); - recordBench("query() p50", "us", p50Micros(samples)); + System.out.println("[bench] " + stats("sql()", samples)); + recordBench("sql() p50", "us", p50Micros(samples)); // The plain query path is exactly 2 RPCs: GetFlightInfo + DoGet. assertEquals(MEASURED_ITERATIONS, server.getFlightInfoCalls.get() - getFlightInfoBefore); diff --git a/src/test/java/ai/spice/ResetTest.java b/src/test/java/ai/spice/ResetTest.java index 6127f77..cc1daf6 100644 --- a/src/test/java/ai/spice/ResetTest.java +++ b/src/test/java/ai/spice/ResetTest.java @@ -57,7 +57,7 @@ protected void setUp() throws Exception { // Probe with taxi_trips (not SELECT 1) to ensure // the dataset is loaded and ready, not just that // the server is up. - try (FlightStream stream = probe.query( + try (FlightStream stream = probe.sql( "SELECT total_amount FROM taxi_trips LIMIT 1")) { stream.next(); } @@ -126,7 +126,7 @@ public void testQueryAfterResetRebuildsClient() throws Exception { client.reset(); try { - try (FlightStream stream = client.query("SELECT 1")) { + try (FlightStream stream = client.sql("SELECT 1")) { // If a local Spice runtime is running, this succeeds stream.next(); } @@ -154,7 +154,7 @@ public void testQueryWithParamsAfterResetRebuildsClient() throws Exception { client.reset(); try { - try (ArrowReader reader = client.queryWithParams("SELECT $1", 42)) { + try (ArrowReader reader = client.sqlWithParams("SELECT $1", 42)) { while (reader.loadNextBatch()) { // consume } @@ -190,7 +190,7 @@ public void testResetQueryResetQueryCycle() throws Exception { for (int i = 0; i < 3; i++) { client.reset(); try { - FlightStream stream = client.query("SELECT 1"); + FlightStream stream = client.sql("SELECT 1"); stream.close(); } catch (Exception e) { // Connection errors are fine — we're testing the reset/rebuild cycle, @@ -266,8 +266,8 @@ public void testConcurrentResetDoesNotThrow() throws Exception { } /** - * Concurrent reset() and query() should not throw unexpected errors. - * (query may fail with connection errors, but not NPE or IllegalStateException.) + * Concurrent reset() and sql() should not throw unexpected errors. + * (sql may fail with connection errors, but not NPE or IllegalStateException.) */ public void testConcurrentResetAndQuery() throws Exception { final SpiceClient client = SpiceClient.builder().withMaxRetries(0).build(); @@ -295,7 +295,7 @@ public void testConcurrentResetAndQuery() throws Exception { startLatch.await(); for (int i = 0; i < iterations; i++) { try { - FlightStream stream = client.query("SELECT 1"); + FlightStream stream = client.sql("SELECT 1"); stream.close(); } catch (Exception e) { // Unwrap ExecutionException to inspect the real cause @@ -440,7 +440,7 @@ public void testResetWithCustomConfig() throws Exception { client.reset(); try { - FlightStream stream = client.query("SELECT 1"); + FlightStream stream = client.sql("SELECT 1"); stream.close(); } catch (Exception e) { assertFalse("NPE after reset with custom config", @@ -454,7 +454,7 @@ public void testResetWithCustomConfig() throws Exception { /** * If a local Spice runtime is running, verify that - * reset() followed by query() actually returns data. + * reset() followed by sql() actually returns data. * Uses taxi_trips which is available in the CI quickstart dataset. */ public void testResetThenQueryIntegration() throws Exception { @@ -462,7 +462,7 @@ public void testResetThenQueryIntegration() throws Exception { try (SpiceClient client = SpiceClient.builder().build()) { // First query (establishes connection) - try (FlightStream stream1 = client.query( + try (FlightStream stream1 = client.sql( "SELECT total_amount FROM taxi_trips LIMIT 1")) { int rows1 = 0; while (stream1.next()) { @@ -475,7 +475,7 @@ public void testResetThenQueryIntegration() throws Exception { client.reset(); // Second query (lazy rebuild) - try (FlightStream stream2 = client.query( + try (FlightStream stream2 = client.sql( "SELECT total_amount FROM taxi_trips LIMIT 2")) { int rows2 = 0; while (stream2.next()) { @@ -488,7 +488,7 @@ public void testResetThenQueryIntegration() throws Exception { /** * If a local Spice runtime is running, verify that - * reset() followed by queryWithParams() actually returns data. + * reset() followed by sqlWithParams() actually returns data. * Uses taxi_trips which is available in the CI quickstart dataset. */ public void testResetThenQueryWithParamsIntegration() throws Exception { @@ -496,7 +496,7 @@ public void testResetThenQueryWithParamsIntegration() throws Exception { try (SpiceClient client = SpiceClient.builder().build()) { // First query - try (ArrowReader reader1 = client.queryWithParams( + try (ArrowReader reader1 = client.sqlWithParams( "SELECT total_amount FROM taxi_trips WHERE total_amount > $1 LIMIT 1", 0.0)) { int rows1 = 0; @@ -510,7 +510,7 @@ public void testResetThenQueryWithParamsIntegration() throws Exception { client.reset(); // Second query (rebuilds channels and re-prepares the statement) - try (ArrowReader reader2 = client.queryWithParams( + try (ArrowReader reader2 = client.sqlWithParams( "SELECT total_amount FROM taxi_trips WHERE total_amount > $1 LIMIT 2", 0.0)) { int rows2 = 0; diff --git a/src/test/java/ai/spice/ResilienceTest.java b/src/test/java/ai/spice/ResilienceTest.java index 8e87c3a..8cdde3b 100644 --- a/src/test/java/ai/spice/ResilienceTest.java +++ b/src/test/java/ai/spice/ResilienceTest.java @@ -57,7 +57,7 @@ public void testTransientUnavailableIsRetriedWithBackoff() throws Exception { server.failNextGetFlightInfo(1, CallStatus.UNAVAILABLE); long startNanos = System.nanoTime(); - try (FlightStream stream = client.query("SELECT 1")) { + try (FlightStream stream = client.sql("SELECT 1")) { assertEquals(server.expectedTotalRows(), LocalFlightServerTest.countRows(stream)); } long elapsedMs = (System.nanoTime() - startNanos) / 1_000_000; @@ -76,7 +76,7 @@ public void testRetriesExhaustedSurfaceLastError() throws Exception { .build()) { server.failNextGetFlightInfo(10, CallStatus.UNAVAILABLE); try { - client.query("SELECT 1"); + client.sql("SELECT 1"); fail("Expected ExecutionException"); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FlightRuntimeException); @@ -98,7 +98,7 @@ public void testNonRetryableErrorFailsFast() throws Exception { long startNanos = System.nanoTime(); try { - client.query("SELECT invalid"); + client.sql("SELECT invalid"); fail("Expected ExecutionException"); } catch (ExecutionException e) { assertEquals(FlightStatusCode.INVALID_ARGUMENT, @@ -126,7 +126,7 @@ public void testQueryTimeoutBoundsPlanning() throws Exception { long startNanos = System.nanoTime(); try { - client.query("SELECT 1"); + client.sql("SELECT 1"); fail("Expected ExecutionException"); } catch (ExecutionException e) { assertEquals(FlightStatusCode.TIMED_OUT, @@ -151,13 +151,13 @@ public void testExpiredBearerTokenRecoversAutomatically() throws Exception { .build()) { assertEquals("constructor performs the initial handshake", 1, server.basicAuthValidations.get()); - try (FlightStream stream = client.query("SELECT 1")) { + try (FlightStream stream = client.sql("SELECT 1")) { assertEquals(server.expectedTotalRows(), LocalFlightServerTest.countRows(stream)); } server.rejectNextBearerToken(); - try (FlightStream stream = client.query("SELECT 2")) { + try (FlightStream stream = client.sql("SELECT 2")) { assertEquals(server.expectedTotalRows(), LocalFlightServerTest.countRows(stream)); } assertEquals("expired token must trigger exactly one re-handshake", 2, @@ -176,7 +176,7 @@ public void testQueryWithParamsOnAuthenticatedClient() throws Exception { .withFlightAddress(server.flightUri()) .withApiKey("testapp|secret") .build()) { - try (ArrowReader reader = client.queryWithParams("SELECT * FROM t WHERE id=$1", 7L)) { + try (ArrowReader reader = client.sqlWithParams("SELECT * FROM t WHERE id=$1", 7L)) { assertEquals(server.expectedTotalRows(), LocalFlightServerTest.countRows(reader)); } assertEquals("no extra handshake beyond the constructor's", 1, @@ -191,18 +191,18 @@ public void testChannelPoolServesQueries() throws Exception { .withChannelCount(4) .build()) { for (int i = 0; i < 8; i++) { - try (FlightStream stream = client.query("SELECT " + i)) { + try (FlightStream stream = client.sql("SELECT " + i)) { assertEquals(server.expectedTotalRows(), LocalFlightServerTest.countRows(stream)); } } - try (ArrowReader reader = client.queryWithParams("SELECT * FROM t WHERE id=$1", 1L)) { + try (ArrowReader reader = client.sqlWithParams("SELECT * FROM t WHERE id=$1", 1L)) { assertEquals(server.expectedTotalRows(), LocalFlightServerTest.countRows(reader)); } assertEquals(9, server.getFlightInfoCalls.get()); // reset() rebuilds all channels and queries still work. client.reset(); - try (FlightStream stream = client.query("SELECT after_reset")) { + try (FlightStream stream = client.sql("SELECT after_reset")) { assertEquals(server.expectedTotalRows(), LocalFlightServerTest.countRows(stream)); } } @@ -238,7 +238,7 @@ public void testConcurrentResetAndQueryWithParams() throws Exception { try { start.await(); for (int i = 0; i < queries; i++) { - try (ArrowReader reader = client.queryWithParams( + try (ArrowReader reader = client.sqlWithParams( "SELECT * FROM t WHERE id=$1", (long) i)) { LocalFlightServerTest.countRows(reader); } diff --git a/src/test/java/ai/spice/SoakTest.java b/src/test/java/ai/spice/SoakTest.java index d73d606..fa2bac0 100644 --- a/src/test/java/ai/spice/SoakTest.java +++ b/src/test/java/ai/spice/SoakTest.java @@ -86,7 +86,7 @@ public void testSoak() throws Exception { try (SpiceClient client = SpiceClient.builder().withMaxRetries(3).build()) { // Fail fast (before the long run) if the runtime isn't serving. assertTrue("runtime must be ready before soaking", client.isReady()); - try (FlightStream warm = client.query(querySql)) { + try (FlightStream warm = client.sql(querySql)) { LocalFlightServerTest.countRows(warm); } // Baseline AFTER warm-up: gRPC/Netty event loops and the JDK HTTP @@ -105,12 +105,12 @@ public void testSoak() throws Exception { try { int kind = roll++ % 20; if (kind < 16) { - try (FlightStream stream = client.query(querySql)) { + try (FlightStream stream = client.sql(querySql)) { rowsRead.addAndGet(LocalFlightServerTest.countRows(stream)); } dataOperations.incrementAndGet(); } else if (kind < 19) { - try (ArrowReader reader = client.queryWithParams(paramSql, 1L)) { + try (ArrowReader reader = client.sqlWithParams(paramSql, 1L)) { rowsRead.addAndGet(LocalFlightServerTest.countRows(reader)); } dataOperations.incrementAndGet(); diff --git a/src/test/java/ai/spice/StatementCacheTest.java b/src/test/java/ai/spice/StatementCacheTest.java index 14a521d..9027fb6 100644 --- a/src/test/java/ai/spice/StatementCacheTest.java +++ b/src/test/java/ai/spice/StatementCacheTest.java @@ -61,7 +61,7 @@ private SpiceClient newClient() throws Exception { } private static void runQuery(SpiceClient client, String sql, Object... params) throws Exception { - try (ArrowReader reader = client.queryWithParams(sql, params)) { + try (ArrowReader reader = client.sqlWithParams(sql, params)) { long rows = LocalFlightServerTest.countRows(reader); assertTrue("query should return rows", rows > 0); } @@ -171,7 +171,7 @@ public void testConcurrentSameSqlQueries() throws Exception { try { start.await(); for (int i = 0; i < queriesPerThread; i++) { - try (ArrowReader reader = client.queryWithParams(SQL, (long) i)) { + try (ArrowReader reader = client.sqlWithParams(SQL, (long) i)) { totalRows.addAndGet(LocalFlightServerTest.countRows(reader)); } } diff --git a/src/test/java/ai/spice/TpchIntegrationTest.java b/src/test/java/ai/spice/TpchIntegrationTest.java index 52f3b85..9eaf41c 100644 --- a/src/test/java/ai/spice/TpchIntegrationTest.java +++ b/src/test/java/ai/spice/TpchIntegrationTest.java @@ -59,7 +59,7 @@ protected void setUp() throws Exception { synchronized (TpchIntegrationTest.class) { if (tpchAvailableCached == null) { try (SpiceClient probe = SpiceClient.builder().withMaxRetries(1).build(); - FlightStream stream = probe.query("SELECT c_custkey FROM tpch.customer LIMIT 1")) { + FlightStream stream = probe.sql("SELECT c_custkey FROM tpch.customer LIMIT 1")) { stream.next(); tpchAvailableCached = Boolean.TRUE; } catch (Exception e) { @@ -88,7 +88,7 @@ protected void tearDown() throws Exception { public void testShowTables() throws Exception { if (!tpchAvailable) return; - try (FlightStream stream = client.query("SHOW TABLES")) { + try (FlightStream stream = client.sql("SHOW TABLES")) { Set tableNames = new HashSet<>(); int columnCount = 0; @@ -121,7 +121,7 @@ public void testCustomerQuery() throws Exception { if (!tpchAvailable) return; String sql = "SELECT c_custkey, c_name, c_nationkey FROM tpch.customer LIMIT 10"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { int totalRows = 0; boolean hasExpectedColumns = false; @@ -156,7 +156,7 @@ public void testCustomerWithFilter() throws Exception { if (!tpchAvailable) return; String sql = "SELECT c_custkey, c_nationkey FROM tpch.customer WHERE c_nationkey = 1 LIMIT 5"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { int totalRows = 0; while (stream.next()) { @@ -181,7 +181,7 @@ public void testOrdersQuery() throws Exception { if (!tpchAvailable) return; String sql = "SELECT o_orderkey, o_custkey, o_totalprice FROM tpch.orders LIMIT 10"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { int totalRows = 0; while (stream.next()) { @@ -208,7 +208,7 @@ public void testOrdersWithPriceFilter() throws Exception { // Use a lower threshold that works with any scale factor String sql = "SELECT o_orderkey, o_totalprice FROM tpch.orders WHERE o_totalprice > 1000 LIMIT 5"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { int totalRows = 0; while (stream.next()) { @@ -233,7 +233,7 @@ public void testCountQuery() throws Exception { if (!tpchAvailable) return; String sql = "SELECT COUNT(*) as cnt FROM tpch.customer"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { long count = 0; while (stream.next()) { @@ -252,7 +252,7 @@ public void testSumQuery() throws Exception { // Use direct SUM without LIMIT (LIMIT doesn't work on aggregation) String sql = "SELECT SUM(o_totalprice) as total FROM tpch.orders"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { Double sum = null; while (stream.next()) { @@ -274,7 +274,7 @@ public void testGroupByQuery() throws Exception { if (!tpchAvailable) return; String sql = "SELECT o_orderstatus, COUNT(*) as cnt FROM tpch.orders GROUP BY o_orderstatus"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { int statusCount = 0; while (stream.next()) { @@ -300,7 +300,7 @@ public void testSimpleJoin() throws Exception { "FROM tpch.customer c " + "JOIN tpch.orders o ON c.c_custkey = o.o_custkey " + "LIMIT 5"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { int totalRows = 0; while (stream.next()) { @@ -321,7 +321,7 @@ public void testDescribeTable() throws Exception { if (!tpchAvailable) return; String sql = "DESCRIBE tpch.customer"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { int columnCount = 0; Set columnNames = new HashSet<>(); @@ -353,7 +353,7 @@ public void testOrderByAsc() throws Exception { if (!tpchAvailable) return; String sql = "SELECT c_custkey FROM tpch.customer ORDER BY c_custkey ASC LIMIT 5"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { long previousKey = -1; while (stream.next()) { @@ -373,7 +373,7 @@ public void testOrderByDesc() throws Exception { if (!tpchAvailable) return; String sql = "SELECT c_custkey FROM tpch.customer ORDER BY c_custkey DESC LIMIT 5"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { long previousKey = Long.MAX_VALUE; while (stream.next()) { @@ -396,7 +396,7 @@ public void testNullHandling() throws Exception { // Query that might return nulls String sql = "SELECT c_custkey, c_phone FROM tpch.customer LIMIT 10"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { int totalRows = 0; while (stream.next()) { @@ -426,7 +426,7 @@ public void testLargeResultSet() throws Exception { if (!tpchAvailable) return; String sql = "SELECT c_custkey, c_name FROM tpch.customer LIMIT 1000"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { int totalRows = 0; while (stream.next()) { @@ -445,7 +445,7 @@ public void testEmptyResult() throws Exception { // Query that should return no results String sql = "SELECT c_custkey FROM tpch.customer WHERE c_custkey < 0"; - try (FlightStream stream = client.query(sql)) { + try (FlightStream stream = client.sql(sql)) { int totalRows = 0; while (stream.next()) { @@ -463,7 +463,7 @@ public void testInvalidTableName() { if (!tpchAvailable) return; try { - try (FlightStream stream = client.query("SELECT * FROM nonexistent_table")) { + try (FlightStream stream = client.sql("SELECT * FROM nonexistent_table")) { while (stream.next()) { // Should not get here } @@ -482,7 +482,7 @@ public void testInvalidColumnName() { if (!tpchAvailable) return; try { - try (FlightStream stream = client.query("SELECT nonexistent_column FROM tpch.customer")) { + try (FlightStream stream = client.sql("SELECT nonexistent_column FROM tpch.customer")) { while (stream.next()) { // Should not get here } @@ -501,7 +501,7 @@ public void testSyntaxError() { if (!tpchAvailable) return; try { - try (FlightStream stream = client.query("SELEC * FROM tpch.customer")) { + try (FlightStream stream = client.sql("SELEC * FROM tpch.customer")) { while (stream.next()) { // Should not get here }