1515import org .elasticsearch .core .Tuple ;
1616import org .elasticsearch .indices .InvalidIndexNameException ;
1717import org .elasticsearch .transport .RemoteClusterService ;
18+ import org .elasticsearch .xpack .esql .core .util .Holder ;
1819import org .elasticsearch .xpack .esql .parser .EsqlBaseParser .IdentifierContext ;
1920import org .elasticsearch .xpack .esql .parser .EsqlBaseParser .IndexStringContext ;
2021
2122import java .util .ArrayList ;
22- import java .util .Arrays ;
2323import java .util .List ;
2424
2525import static org .elasticsearch .cluster .metadata .IndexNameExpressionResolver .SelectorResolver .SELECTOR_SEPARATOR ;
@@ -88,12 +88,14 @@ public String visitSelectorString(EsqlBaseParser.SelectorStringContext ctx) {
8888
8989 public String visitIndexPattern (List <EsqlBaseParser .IndexPatternContext > ctx ) {
9090 List <String > patterns = new ArrayList <>(ctx .size ());
91+ Holder <Boolean > hasSeenStar = new Holder <>(false );
9192 ctx .forEach (c -> {
9293 String indexPattern = visitIndexString (c .indexString ());
9394 String clusterString = visitClusterString (c .clusterString ());
9495 String selectorString = visitSelectorString (c .selectorString ());
9596
96- validateClusterAndIndexPatterns (indexPattern , c , clusterString , selectorString );
97+ hasSeenStar .set (hasSeenStar .get () || indexPattern .contains (WILDCARD ));
98+ validateClusterAndIndexPatterns (indexPattern , c , clusterString , selectorString , hasSeenStar .get ());
9799 patterns .add (reassembleIndexName (clusterString , indexPattern , selectorString ));
98100 });
99101 return Strings .collectionToDelimitedString (patterns , "," );
@@ -132,15 +134,15 @@ private static void validateClusterAndIndexPatterns(
132134 String indexPattern ,
133135 EsqlBaseParser .IndexPatternContext ctx ,
134136 String clusterString ,
135- String selectorString
137+ String selectorString ,
138+ boolean hasSeenStar
136139 ) {
137140 // multiple index names can be in the same double quote, e.g. indexPattern = "idx1, *, -idx2"
138141 String [] patterns = indexPattern .split ("," );
139142 boolean isFirstPattern = true ;
140143
141144 for (String pattern : patterns ) {
142145 pattern = pattern .strip ();
143- String [] indices = new String [] { pattern };
144146
145147 /*
146148 * Just because there was no clusterString before this index pattern does not mean that the indices
@@ -162,30 +164,21 @@ private static void validateClusterAndIndexPatterns(
162164 }
163165
164166 // {cluster_alias, indexName}
165- String [] clusterAliasAndIndex = splitIndexName (pattern );
166- clusterString = clusterAliasAndIndex [0 ];
167- indices [0 ] = clusterAliasAndIndex [1 ];
167+ String [] clusterAliasAndIndex ;
168+ try {
169+ clusterAliasAndIndex = splitIndexName (pattern );
170+ } catch (IllegalArgumentException e ) {
171+ throw new ParsingException (e , source (ctx ), e .getMessage ());
172+ }
168173
169- /*
170- * What if the pattern is index1|index2? We cannot split at the pipe char blindly as that'd mess with
171- * logstash-like examples. We only split this way if an index is associated with a remote cluster.
172- */
173- indices = Arrays .stream (indices )
174- .map (IdentifierBuilder ::breakPatternIntoIndices )
175- .flatMap (Arrays ::stream )
176- .toArray (String []::new );
174+ clusterString = clusterAliasAndIndex [0 ];
175+ pattern = clusterAliasAndIndex [1 ];
177176 } else if (clusterString != null ) {
178177 // This is not a remote index pattern and the cluster string preceding this quoted pattern
179178 // cannot be associated with it.
180179 if (isFirstPattern == false ) {
181180 clusterString = null ;
182181 }
183-
184- // Cluster alias was prefixed to the pattern and did not occur within the pattern.
185- indices = Arrays .stream (indices )
186- .map (IdentifierBuilder ::breakPatternIntoIndices )
187- .flatMap (Arrays ::stream )
188- .toArray (String []::new );
189182 }
190183
191184 if (clusterString != null ) {
@@ -195,7 +188,7 @@ private static void validateClusterAndIndexPatterns(
195188 validateClusterString (clusterString , ctx );
196189 }
197190
198- validateIndicesForCluster (clusterString , indices , ctx );
191+ validateIndexForCluster (clusterString , pattern , ctx , hasSeenStar );
199192 if (selectorString != null ) {
200193 try {
201194 // Ensures that the selector provided is one of the valid kinds
@@ -209,85 +202,53 @@ private static void validateClusterAndIndexPatterns(
209202 }
210203 }
211204
212- private static void validateIndicesForCluster (String clusterString , String [] indices , EsqlBaseParser .IndexPatternContext ctx ) {
213- for (String index : indices ) {
214- // Strip spaces off first because validation checks are not written to handle them
215- index = index .strip ();
216-
217- try {
218- Tuple <String , String > splitPattern = IndexNameExpressionResolver .splitSelectorExpression (index );
219- if (splitPattern .v2 () != null && clusterString != null ) {
220- throwOnMixingSelectorWithCluster (reassembleIndexName (clusterString , splitPattern .v1 (), splitPattern .v2 ()), ctx );
221- }
205+ private static void validateIndexForCluster (
206+ String clusterString ,
207+ String index ,
208+ EsqlBaseParser .IndexPatternContext ctx ,
209+ boolean hasSeenStar
210+ ) {
211+ // Strip spaces off first because validation checks are not written to handle them
212+ index = index .strip ();
222213
223- index = splitPattern .v1 ();
224- } catch (InvalidIndexNameException e ) {
225- // throws exception if the selector expression is invalid. Selector resolution does not complain about exclusions
226- throw new ParsingException (e , source (ctx ), e .getMessage ());
227- }
228- var hasSeenStar = index .contains (WILDCARD );
229- index = index .replace (WILDCARD , "" ).strip ();
230- if (index .isBlank ()) {
231- continue ;
232- }
233- var hasExclusion = index .startsWith (EXCLUSION );
234- index = removeExclusion (index );
235- String tempName ;
236- try {
237- // remove the exclusion outside of <>, from index names with DateMath expression,
238- // e.g. -<-logstash-{now/d}> becomes <-logstash-{now/d}> before calling resolveDateMathExpression
239- tempName = IndexNameExpressionResolver .resolveDateMathExpression (index );
240- } catch (ElasticsearchParseException e ) {
241- // throws exception if the DateMath expression is invalid, resolveDateMathExpression does not complain about exclusions
242- throw new ParsingException (e , source (ctx ), e .getMessage ());
243- }
244- hasExclusion = tempName .startsWith (EXCLUSION ) || hasExclusion ;
245- index = tempName .equals (index ) ? index : removeExclusion (tempName );
246- try {
247- MetadataCreateIndexService .validateIndexOrAliasName (index , InvalidIndexNameException ::new );
248- } catch (InvalidIndexNameException e ) {
249- // ignore invalid index name if it has exclusions and there is an index with wildcard before it
250- if (hasSeenStar && hasExclusion ) {
251- continue ;
252- }
253- throw new ParsingException (e , source (ctx ), e .getMessage ());
214+ try {
215+ Tuple <String , String > splitPattern = IndexNameExpressionResolver .splitSelectorExpression (index );
216+ if (splitPattern .v2 () != null && clusterString != null ) {
217+ throwOnMixingSelectorWithCluster (reassembleIndexName (clusterString , splitPattern .v1 (), splitPattern .v2 ()), ctx );
254218 }
255219
220+ index = splitPattern .v1 ();
221+ } catch (InvalidIndexNameException e ) {
222+ // throws exception if the selector expression is invalid. Selector resolution does not complain about exclusions
223+ throw new ParsingException (e , source (ctx ), e .getMessage ());
256224 }
257- }
258-
259- private static String [] breakPatternIntoIndices (String pattern ) {
260- if (pattern .codePoints ().anyMatch (ch -> ch == ',' )) {
261- throw new IllegalArgumentException ("Found grouped index patterns, expecting a single pattern" );
225+ hasSeenStar = hasSeenStar || index .contains (WILDCARD );
226+ index = index .replace (WILDCARD , "" ).strip ();
227+ if (index .isBlank ()) {
228+ return ;
262229 }
263-
264- // Fast path: if there's no pipe char, no point in attempting to break down the string.
265- if (pattern .contains ("|" ) == false ) {
266- return new String [] { pattern };
230+ var hasExclusion = index .startsWith (EXCLUSION );
231+ index = removeExclusion (index );
232+ String tempName ;
233+ try {
234+ // remove the exclusion outside of <>, from index names with DateMath expression,
235+ // e.g. -<-logstash-{now/d}> becomes <-logstash-{now/d}> before calling resolveDateMathExpression
236+ tempName = IndexNameExpressionResolver .resolveDateMathExpression (index );
237+ } catch (ElasticsearchParseException e ) {
238+ // throws exception if the DateMath expression is invalid, resolveDateMathExpression does not complain about exclusions
239+ throw new ParsingException (e , source (ctx ), e .getMessage ());
267240 }
268-
269- var indices = new ArrayList <String >();
270- var sb = new StringBuilder ();
271- var inDateMathExpr = false ;
272- for (int i = 0 ; i < pattern .length (); i ++) {
273- char c = pattern .charAt (i );
274- sb .append (c );
275- if (c == '<' ) {
276- inDateMathExpr = true ;
277- } else if (c == '>' ) {
278- inDateMathExpr = false ;
279- } else if (c == '|' && inDateMathExpr == false ) {
280- sb .deleteCharAt (sb .length () - 1 );
281- indices .add (sb .toString ());
282- sb .setLength (0 );
241+ hasExclusion = tempName .startsWith (EXCLUSION ) || hasExclusion ;
242+ index = tempName .equals (index ) ? index : removeExclusion (tempName );
243+ try {
244+ MetadataCreateIndexService .validateIndexOrAliasName (index , InvalidIndexNameException ::new );
245+ } catch (InvalidIndexNameException e ) {
246+ // ignore invalid index name if it has exclusions and there is an index with wildcard before it
247+ if (hasSeenStar && hasExclusion ) {
248+ return ;
283249 }
250+ throw new ParsingException (e , source (ctx ), e .getMessage ());
284251 }
285-
286- if (sb .isEmpty () == false ) {
287- indices .add (sb .toString ());
288- }
289-
290- return indices .toArray (new String [0 ]);
291252 }
292253
293254 private static String removeExclusion (String indexPattern ) {
0 commit comments