1616import org .elasticsearch .client .RequestOptions ;
1717import org .elasticsearch .client .Response ;
1818import org .elasticsearch .client .ResponseException ;
19- import org .elasticsearch .client .RestClient ;
2019import org .elasticsearch .client .WarningsHandler ;
2120import org .elasticsearch .common .bytes .BytesArray ;
2221import org .elasticsearch .common .io .Streams ;
4241import java .io .IOException ;
4342import java .io .InputStreamReader ;
4443import java .io .OutputStream ;
45- import java .io .UncheckedIOException ;
4644import java .nio .charset .StandardCharsets ;
4745import java .time .ZoneId ;
4846import java .util .ArrayList ;
5351import java .util .Locale ;
5452import java .util .Map ;
5553import java .util .Set ;
56- import java .util .concurrent .ConcurrentHashMap ;
57- import java .util .concurrent .ConcurrentMap ;
5854import java .util .function .IntFunction ;
5955
6056import static java .util .Collections .emptySet ;
6460import static org .elasticsearch .test .MapMatcher .assertMap ;
6561import static org .elasticsearch .test .MapMatcher .matchesMap ;
6662import static org .elasticsearch .xpack .esql .EsqlTestUtils .as ;
63+ import static org .elasticsearch .xpack .esql .qa .rest .EsqlSpecTestCase .assertNotPartial ;
6764import static org .elasticsearch .xpack .esql .qa .rest .RestEsqlTestCase .Mode .ASYNC ;
6865import static org .elasticsearch .xpack .esql .qa .rest .RestEsqlTestCase .Mode .SYNC ;
6966import static org .elasticsearch .xpack .esql .type .EsqlDataTypeConverter .dateTimeToString ;
7067import static org .hamcrest .Matchers .any ;
71- import static org .hamcrest .Matchers .anyOf ;
7268import static org .hamcrest .Matchers .containsString ;
7369import static org .hamcrest .Matchers .either ;
7470import static org .hamcrest .Matchers .emptyOrNullString ;
@@ -400,9 +396,7 @@ public void testCSVNoHeaderMode() throws IOException {
400396 options .addHeader ("Content-Type" , mediaType );
401397 options .addHeader ("Accept" , "text/csv; header=absent" );
402398 request .setOptions (options );
403- Response response = performRequest (request );
404- assertWarnings (response , new AssertWarnings .NoWarnings ());
405- HttpEntity entity = response .getEntity ();
399+ HttpEntity entity = performRequest (request , new AssertWarnings .NoWarnings ());
406400 String actual = Streams .copyToString (new InputStreamReader (entity .getContent (), StandardCharsets .UTF_8 ));
407401 assertEquals ("keyword0,0\r \n " , actual );
408402 }
@@ -698,12 +692,12 @@ public void testNamedParamsForIdentifierAndIdentifierPatterns() throws IOExcepti
698692 bulkLoadTestData (10 );
699693 // positive
700694 var query = requestObjectBuilder ().query (
701- format (
702- null ,
703- "from {} | eval x1 = ?n1 | where ?n2 == x1 | stats xx2 = ?fn1(?n3) by ?n4 | keep ?n4, ?n5 | sort ?n4" ,
704- testIndexName ()
705- )
695+ format (
696+ null ,
697+ "from {} | eval x1 = ?n1 | where ?n2 == x1 | stats xx2 = ?fn1(?n3) by ?n4 | keep ?n4, ?n5 | sort ?n4" ,
698+ testIndexName ()
706699 )
700+ )
707701 .params (
708702 "[{\" n1\" : {\" identifier\" : \" integer\" }}, {\" n2\" : {\" identifier\" : \" short\" }}, "
709703 + "{\" n3\" : {\" identifier\" : \" double\" }}, {\" n4\" : {\" identifier\" : \" boolean\" }}, "
@@ -838,12 +832,12 @@ public void testDoubleParamsForIdentifiers() throws IOException {
838832 // positive
839833 // named double parameters
840834 var query = requestObjectBuilder ().query (
841- format (
842- null ,
843- "from {} | eval x1 = ??n1 | where ??n2 == x1 | stats xx2 = ??fn1(??n3) by ??n4 | keep ??n4, ??n5 | sort ??n4" ,
844- testIndexName ()
845- )
835+ format (
836+ null ,
837+ "from {} | eval x1 = ??n1 | where ??n2 == x1 | stats xx2 = ??fn1(??n3) by ??n4 | keep ??n4, ??n5 | sort ??n4" ,
838+ testIndexName ()
846839 )
840+ )
847841 .params (
848842 "[{\" n1\" : \" integer\" }, {\" n2\" : \" short\" }, {\" n3\" : \" double\" }, {\" n4\" : \" boolean\" }, "
849843 + "{\" n5\" : \" xx2\" }, {\" fn1\" : \" max\" }]"
@@ -852,12 +846,12 @@ public void testDoubleParamsForIdentifiers() throws IOException {
852846
853847 // positional double parameters
854848 query = requestObjectBuilder ().query (
855- format (
856- null ,
857- "from {} | eval x1 = ??1 | where ??2 == x1 | stats xx2 = ??6(??3) by ??4 | keep ??4, ??5 | sort ??4" ,
858- testIndexName ()
859- )
849+ format (
850+ null ,
851+ "from {} | eval x1 = ??1 | where ??2 == x1 | stats xx2 = ??6(??3) by ??4 | keep ??4, ??5 | sort ??4" ,
852+ testIndexName ()
860853 )
854+ )
861855 .params (
862856 "[{\" n1\" : \" integer\" }, {\" n2\" : \" short\" }, {\" n3\" : \" double\" }, {\" n4\" : \" boolean\" }, "
863857 + "{\" n5\" : \" xx2\" }, {\" fn1\" : \" max\" }]"
@@ -875,8 +869,8 @@ public void testDoubleParamsForIdentifiers() throws IOException {
875869
876870 // anonymous double parameters
877871 query = requestObjectBuilder ().query (
878- format (null , "from {} | eval x1 = ?? | where ?? == x1 | stats xx2 = ??(??) by ?? | keep ??, ?? | sort ??" , testIndexName ())
879- )
872+ format (null , "from {} | eval x1 = ?? | where ?? == x1 | stats xx2 = ??(??) by ?? | keep ??, ?? | sort ??" , testIndexName ())
873+ )
880874 .params (
881875 "[{\" n1\" : \" integer\" }, {\" n2\" : \" short\" }, {\" fn1\" : \" max\" }, {\" n3\" : \" double\" }, {\" n4\" : \" boolean\" }, "
882876 + "{\" n4\" : \" boolean\" }, {\" n5\" : \" xx2\" }, {\" n4\" : \" boolean\" }]"
@@ -1097,7 +1091,7 @@ public void testComplexFieldNames() throws IOException {
10971091 }
10981092
10991093 /**
1100- * INLINE STATS <strong>can</strong> group on {@code NOW()}. It's a little silly, but
1094+ * INLINESTATS <strong>can</strong> group on {@code NOW()}. It's a little silly, but
11011095 * doing something like {@code DATE_TRUNC(1 YEAR, NOW() - 1970-01-01T00:00:00Z)} is
11021096 * much more sensible. But just grouping on {@code NOW()} is enough to test this.
11031097 * <p>
@@ -1107,11 +1101,11 @@ public void testComplexFieldNames() throws IOException {
11071101 */
11081102 @ AwaitsFix (bugUrl = "Disabled temporarily until JOIN implementation is completed" )
11091103 public void testInlineStatsNow () throws IOException {
1110- assumeTrue ("INLINE STATS only available on snapshots" , Build .current ().isSnapshot ());
1104+ assumeTrue ("INLINESTATS only available on snapshots" , Build .current ().isSnapshot ());
11111105 indexTimestampData (1 );
11121106
11131107 RequestObjectBuilder builder = requestObjectBuilder ().query (
1114- fromIndex () + " | EVAL now=NOW() | INLINE STATS AVG(value) BY now | SORT value ASC"
1108+ fromIndex () + " | EVAL now=NOW() | INLINESTATS AVG(value) BY now | SORT value ASC"
11151109 );
11161110 Map <String , Object > result = runEsql (builder );
11171111 ListMatcher values = matchesList ();
@@ -1121,8 +1115,8 @@ public void testInlineStatsNow() throws IOException {
11211115 .item ("value" + i )
11221116 .item ("value" + i )
11231117 .item (i )
1124- .item (499.5 )
11251118 .item (any (String .class ))
1119+ .item (499.5 )
11261120 );
11271121 }
11281122 assertResultMap (
@@ -1131,8 +1125,8 @@ public void testInlineStatsNow() throws IOException {
11311125 .item (matchesMap ().entry ("name" , "test" ).entry ("type" , "text" ))
11321126 .item (matchesMap ().entry ("name" , "test.keyword" ).entry ("type" , "keyword" ))
11331127 .item (matchesMap ().entry ("name" , "value" ).entry ("type" , "long" ))
1134- .item (matchesMap ().entry ("name" , "AVG(value) " ).entry ("type" , "double " ))
1135- .item (matchesMap ().entry ("name" , "now " ).entry ("type" , "date " )),
1128+ .item (matchesMap ().entry ("name" , "now " ).entry ("type" , "date " ))
1129+ .item (matchesMap ().entry ("name" , "AVG(value) " ).entry ("type" , "double " )),
11361130 values
11371131 );
11381132 }
@@ -1264,40 +1258,22 @@ public static Map<String, Object> runEsql(
12641258 var results = mode == ASYNC
12651259 ? runEsqlAsync (requestObject , randomBoolean (), assertWarnings )
12661260 : runEsqlSync (requestObject , assertWarnings );
1267- if (checkPartialResults ) {
1268- assertNotPartial (results );
1269- }
1270- return results ;
1261+ return checkPartialResults ? assertNotPartial (results ) : results ;
12711262 }
12721263
1273- public static Map <String , Object > runEsql (
1274- RequestObjectBuilder requestObject ,
1275- AssertWarnings assertWarnings ,
1276- Mode mode
1277- ) throws IOException {
1264+ public static Map <String , Object > runEsql (RequestObjectBuilder requestObject , AssertWarnings assertWarnings , Mode mode )
1265+ throws IOException {
12781266 return runEsql (requestObject , assertWarnings , mode , true );
12791267 }
12801268
1281- public static Map <String , Object > runEsqlSync (
1282- RequestObjectBuilder requestObject ,
1283- AssertWarnings assertWarnings
1284- ) throws IOException {
1285- Boolean profileEnabled = requestObject .profile ;
1269+ public static Map <String , Object > runEsqlSync (RequestObjectBuilder requestObject , AssertWarnings assertWarnings ) throws IOException {
12861270 Request request = prepareRequestWithOptions (requestObject , SYNC );
12871271
1288- Response response = performRequest (request );
1289- HttpEntity entity = response .getEntity ();
1290- Map <String , Object > json = entityToMap (entity , requestObject .contentType ());
1291-
1292- assertWarnings (response , assertWarnings );
1293-
1294- return json ;
1272+ HttpEntity entity = performRequest (request , assertWarnings );
1273+ return entityToMap (entity , requestObject .contentType ());
12951274 }
12961275
1297- public static Map <String , Object > runEsqlAsync (
1298- RequestObjectBuilder requestObject ,
1299- AssertWarnings assertWarnings
1300- ) throws IOException {
1276+ public static Map <String , Object > runEsqlAsync (RequestObjectBuilder requestObject , AssertWarnings assertWarnings ) throws IOException {
13011277 return runEsqlAsync (requestObject , randomBoolean (), assertWarnings );
13021278 }
13031279
@@ -1306,7 +1282,6 @@ public static Map<String, Object> runEsqlAsync(
13061282 boolean keepOnCompletion ,
13071283 AssertWarnings assertWarnings
13081284 ) throws IOException {
1309- Boolean profileEnabled = requestObject .profile ;
13101285 addAsyncParameters (requestObject , keepOnCompletion );
13111286 Request request = prepareRequestWithOptions (requestObject , ASYNC );
13121287
@@ -1323,8 +1298,8 @@ public static Map<String, Object> runEsqlAsync(
13231298 checkKeepOnCompletion (requestObject , json , keepOnCompletion );
13241299 String id = (String ) json .get ("id" );
13251300
1326- var supportsAsyncHeaders = hasCapabilities ( adminClient (), List .of ("async_query_status_headers" ));
1327- var supportsSuggestedCast = hasCapabilities ( adminClient (), List .of ("suggested_cast" ));
1301+ var supportsAsyncHeaders = clusterHasCapability ( "POST" , "/_query" , List . of (), List .of ("async_query_status_headers" )). orElse ( false );
1302+ var supportsSuggestedCast = clusterHasCapability ( "POST" , "/_query" , List . of (), List .of ("suggested_cast" )). orElse ( false );
13281303
13291304 if (id == null ) {
13301305 // no id returned from an async call, must have completed immediately and without keep_on_completion
@@ -1386,26 +1361,6 @@ public static Map<String, Object> runEsqlAsync(
13861361 return removeAsyncProperties (result );
13871362 }
13881363
1389- record CapabilitesCacheKey (RestClient client , List <String > capabilities ) {}
1390-
1391- /**
1392- * Cache of capabilities.
1393- */
1394- private static final ConcurrentMap <CapabilitesCacheKey , Boolean > capabilities = new ConcurrentHashMap <>();
1395-
1396- public static boolean hasCapabilities (RestClient client , List <String > requiredCapabilities ) {
1397- if (requiredCapabilities .isEmpty ()) {
1398- return true ;
1399- }
1400- return capabilities .computeIfAbsent (new CapabilitesCacheKey (client , requiredCapabilities ), r -> {
1401- try {
1402- return clusterHasCapability (client , "POST" , "/_query" , List .of (), requiredCapabilities ).orElse (false );
1403- } catch (IOException e ) {
1404- throw new UncheckedIOException (e );
1405- }
1406- });
1407- }
1408-
14091364 private static Object removeOriginalTypesAndSuggestedCast (Object response ) {
14101365 if (response instanceof ArrayList <?> columns ) {
14111366 var newColumns = new ArrayList <>();
@@ -1634,8 +1589,7 @@ static String runEsqlAsTextWithFormat(RequestObjectBuilder builder, String forma
16341589 }
16351590
16361591 Response response = performRequest (request );
1637- assertWarnings (response , new AssertWarnings .NoWarnings ());
1638- HttpEntity entity = response .getEntity ();
1592+ HttpEntity entity = assertWarnings (response , new AssertWarnings .NoWarnings ());
16391593
16401594 // get the content, it could be empty because the request might have not completed
16411595 String initialValue = Streams .copyToString (new InputStreamReader (entity .getContent (), StandardCharsets .UTF_8 ));
@@ -1688,8 +1642,7 @@ static String runEsqlAsTextWithFormat(RequestObjectBuilder builder, String forma
16881642 // if `addParam` is false, `options` will already have an `Accept` header
16891643 getRequest .setOptions (options );
16901644 response = performRequest (getRequest );
1691- assertWarnings (response , new AssertWarnings .NoWarnings ());
1692- entity = response .getEntity ();
1645+ entity = assertWarnings (response , new AssertWarnings .NoWarnings ());
16931646 }
16941647 String newValue = Streams .copyToString (new InputStreamReader (entity .getContent (), StandardCharsets .UTF_8 ));
16951648
@@ -1728,6 +1681,10 @@ private static String attachBody(RequestObjectBuilder requestObject, Request req
17281681 return mediaType ;
17291682 }
17301683
1684+ private static HttpEntity performRequest (Request request , AssertWarnings assertWarnings ) throws IOException {
1685+ return assertWarnings (performRequest (request ), assertWarnings );
1686+ }
1687+
17311688 protected static Response performRequest (Request request ) throws IOException {
17321689 Response response = client ().performRequest (request );
17331690 if (shouldLog ()) {
@@ -1738,19 +1695,14 @@ protected static Response performRequest(Request request) throws IOException {
17381695 return response ;
17391696 }
17401697
1741- static void assertNotPartial (Map <String , Object > answer ) {
1742- var clusters = answer .get ("_clusters" );
1743- var reason = "unexpected partial results" + (clusters != null ? ": _clusters=" + clusters : "" );
1744- assertThat (reason , answer .get ("is_partial" ), anyOf (nullValue (), is (false )));
1745- }
1746-
1747- private static void assertWarnings (Response response , AssertWarnings assertWarnings ) {
1698+ private static HttpEntity assertWarnings (Response response , AssertWarnings assertWarnings ) {
17481699 List <String > warnings = new ArrayList <>(response .getWarnings ());
17491700 warnings .removeAll (mutedWarnings ());
17501701 if (shouldLog ()) {
17511702 LOGGER .info ("RESPONSE warnings (after muted)={}" , warnings );
17521703 }
17531704 assertWarnings .assertWarnings (warnings );
1705+ return response .getEntity ();
17541706 }
17551707
17561708 private static Set <String > mutedWarnings () {
0 commit comments