diff --git a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/analyzer/PreAnalyzerTests.java b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/analyzer/PreAnalyzerTests.java index 460d8f4576354..87050c84aeb72 100644 --- a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/analyzer/PreAnalyzerTests.java +++ b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/analyzer/PreAnalyzerTests.java @@ -42,6 +42,15 @@ public void testBasicIndexWithCatalog() { assertThat(result.indices.get(0).id().index(), is("index")); } + public void testBasicIndexWithSelector() { + LogicalPlan plan = new UnresolvedRelation(EMPTY, new TableIdentifier(EMPTY, null, "index::failures"), null, false); + PreAnalysis result = preAnalyzer.preAnalyze(plan); + assertThat(plan.preAnalyzed(), is(true)); + assertThat(result.indices, hasSize(1)); + assertThat(result.indices.get(0).id().cluster(), nullValue()); + assertThat(result.indices.get(0).id().index(), is("index::failures")); + } + public void testComplicatedQuery() { LogicalPlan plan = new Limit( EMPTY, diff --git a/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 b/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 index d2e925b2fa5d3..302542874dcbc 100644 --- a/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 +++ b/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 @@ -340,8 +340,8 @@ identifier ; tableIdentifier - : (catalog=identifier ':')? TABLE_IDENTIFIER - | (catalog=identifier ':')? name=identifier + : (catalog=identifier ':')? TABLE_IDENTIFIER ('::' selector=identifier)? + | (catalog=identifier ':')? name=identifier ('::' selector=identifier)? ; quoteIdentifier diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/IdentifierBuilder.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/IdentifierBuilder.java index bcb69ac06fb9d..ca74588ccf2a0 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/IdentifierBuilder.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/IdentifierBuilder.java @@ -7,7 +7,9 @@ package org.elasticsearch.xpack.sql.parser; import org.antlr.v4.runtime.tree.ParseTree; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.common.Strings; +import org.elasticsearch.core.Tuple; import org.elasticsearch.xpack.ql.plan.TableIdentifier; import org.elasticsearch.xpack.ql.tree.Source; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.IdentifierContext; @@ -29,7 +31,56 @@ public TableIdentifier visitTableIdentifier(TableIdentifierContext ctx) { ParseTree tree = ctx.name != null ? ctx.name : ctx.TABLE_IDENTIFIER(); String index = tree.getText(); - return new TableIdentifier(source, visitIdentifier(ctx.catalog), unquoteIdentifier(index)); + String cluster = visitIdentifier(ctx.catalog); + String indexName = unquoteIdentifier(index); + String selector = visitIdentifier(ctx.selector); + + if (cluster != null && selector != null) { + throw new ParsingException( + source, + "Invalid index name [{}:{}::{}], Selectors are not yet supported on remote cluster patterns", + cluster, + indexName, + selector + ); + } + + if (selector != null) { + try { + IndexNameExpressionResolver.SelectorResolver.validateIndexSelectorString(indexName, selector); + } catch (Exception e) { + throw new ParsingException(source, e.getMessage()); + } + } + + if (indexName.contains(IndexNameExpressionResolver.SelectorResolver.SELECTOR_SEPARATOR)) { + if (selector != null) { + throw new ParsingException( + source, + "Invalid index name [{}::{}], Invalid usage of :: separator, only one :: separator is allowed per expression", + indexName, + selector + ); + } + try { + Tuple split = IndexNameExpressionResolver.splitSelectorExpression(indexName); + indexName = split.v1(); + selector = split.v2(); + } catch (Exception e) { + throw new ParsingException(source, e.getMessage()); + } + if (selector != null) { + try { + IndexNameExpressionResolver.SelectorResolver.validateIndexSelectorString(indexName, selector); + } catch (Exception e) { + throw new ParsingException(source, "Invalid index name [{}::{}], {}", indexName, selector, e.getMessage()); + } + } + } + + indexName = IndexNameExpressionResolver.combineSelectorExpression(indexName, selector); + + return new TableIdentifier(source, cluster, indexName); } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java index 3f21b4e024e2e..c697dc53948b5 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java @@ -8147,12 +8147,17 @@ public final IdentifierContext identifier() throws RecognitionException { @SuppressWarnings("CheckReturnValue") public static class TableIdentifierContext extends ParserRuleContext { public IdentifierContext catalog; + public IdentifierContext selector; public IdentifierContext name; public TerminalNode TABLE_IDENTIFIER() { return getToken(SqlBaseParser.TABLE_IDENTIFIER, 0); } + public TerminalNode CAST_OP() { + return getToken(SqlBaseParser.CAST_OP, 0); + } + public List identifier() { return getRuleContexts(IdentifierContext.class); } @@ -8192,9 +8197,9 @@ public final TableIdentifierContext tableIdentifier() throws RecognitionExceptio enterRule(_localctx, 106, RULE_tableIdentifier); int _la; try { - setState(844); + setState(852); _errHandler.sync(this); - switch (getInterpreter().adaptivePredict(_input, 114, _ctx)) { + switch (getInterpreter().adaptivePredict(_input, 116, _ctx)) { case 1: enterOuterAlt(_localctx, 1); { setState(835); @@ -8213,23 +8218,47 @@ public final TableIdentifierContext tableIdentifier() throws RecognitionExceptio setState(837); match(TABLE_IDENTIFIER); + setState(840); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la == CAST_OP) { + { + setState(838); + match(CAST_OP); + setState(839); + ((TableIdentifierContext) _localctx).selector = identifier(); + } + } + } break; case 2: enterOuterAlt(_localctx, 2); { - setState(841); + setState(845); _errHandler.sync(this); - switch (getInterpreter().adaptivePredict(_input, 113, _ctx)) { + switch (getInterpreter().adaptivePredict(_input, 114, _ctx)) { case 1: { - setState(838); + setState(842); ((TableIdentifierContext) _localctx).catalog = identifier(); - setState(839); + setState(843); match(T__3); } break; } - setState(843); + setState(847); ((TableIdentifierContext) _localctx).name = identifier(); + setState(850); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la == CAST_OP) { + { + setState(848); + match(CAST_OP); + setState(849); + ((TableIdentifierContext) _localctx).selector = identifier(); + } + } + } break; } @@ -8319,20 +8348,20 @@ public final QuoteIdentifierContext quoteIdentifier() throws RecognitionExceptio QuoteIdentifierContext _localctx = new QuoteIdentifierContext(_ctx, getState()); enterRule(_localctx, 108, RULE_quoteIdentifier); try { - setState(848); + setState(856); _errHandler.sync(this); switch (_input.LA(1)) { case QUOTED_IDENTIFIER: _localctx = new QuotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(846); + setState(854); match(QUOTED_IDENTIFIER); } break; case BACKQUOTED_IDENTIFIER: _localctx = new BackQuotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(847); + setState(855); match(BACKQUOTED_IDENTIFIER); } break; @@ -8429,13 +8458,13 @@ public final UnquoteIdentifierContext unquoteIdentifier() throws RecognitionExce UnquoteIdentifierContext _localctx = new UnquoteIdentifierContext(_ctx, getState()); enterRule(_localctx, 110, RULE_unquoteIdentifier); try { - setState(853); + setState(861); _errHandler.sync(this); switch (_input.LA(1)) { case IDENTIFIER: _localctx = new UnquotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(850); + setState(858); match(IDENTIFIER); } break; @@ -8482,14 +8511,14 @@ public final UnquoteIdentifierContext unquoteIdentifier() throws RecognitionExce case YEAR: _localctx = new UnquotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(851); + setState(859); nonReserved(); } break; case DIGIT_IDENTIFIER: _localctx = new DigitIdentifierContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(852); + setState(860); match(DIGIT_IDENTIFIER); } break; @@ -8582,20 +8611,20 @@ public final NumberContext number() throws RecognitionException { NumberContext _localctx = new NumberContext(_ctx, getState()); enterRule(_localctx, 112, RULE_number); try { - setState(857); + setState(865); _errHandler.sync(this); switch (_input.LA(1)) { case DECIMAL_VALUE: _localctx = new DecimalLiteralContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(855); + setState(863); match(DECIMAL_VALUE); } break; case INTEGER_VALUE: _localctx = new IntegerLiteralContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(856); + setState(864); match(INTEGER_VALUE); } break; @@ -8655,7 +8684,7 @@ public final StringContext string() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(859); + setState(867); _la = _input.LA(1); if (!(_la == PARAM || _la == STRING)) { _errHandler.recoverInline(this); @@ -8728,13 +8757,13 @@ public final WhenClauseContext whenClause() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(861); + setState(869); match(WHEN); - setState(862); + setState(870); ((WhenClauseContext) _localctx).condition = expression(); - setState(863); + setState(871); match(THEN); - setState(864); + setState(872); ((WhenClauseContext) _localctx).result = expression(); } } catch (RecognitionException re) { @@ -8946,7 +8975,7 @@ public final NonReservedContext nonReserved() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(866); + setState(874); _la = _input.LA(1); if (!((((_la) & ~0x3f) == 0 && ((1L << _la) & -6012133270006398784L) != 0) || ((((_la - 70)) & ~0x3f) == 0 && ((1L << (_la - 70)) & 2341314289L) != 0))) { @@ -9009,7 +9038,7 @@ private boolean primaryExpression_sempred(PrimaryExpressionContext _localctx, in return true; } - public static final String _serializedATN = "\u0004\u0001\u008b\u0365\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001" + public static final String _serializedATN = "\u0004\u0001\u008b\u036d\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001" + "\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004" + "\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007" + "\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b" @@ -9127,45 +9156,46 @@ private boolean primaryExpression_sempred(PrimaryExpressionContext _localctx, in + "\b0\u00010\u00010\u00030\u0328\b0\u00010\u00010\u00010\u00030\u032d\b" + "0\u00011\u00011\u00012\u00012\u00013\u00013\u00013\u00053\u0336\b3\n3" + "\f3\u0339\t3\u00013\u00013\u00014\u00014\u00034\u033f\b4\u00015\u0001" - + "5\u00015\u00035\u0344\b5\u00015\u00015\u00015\u00015\u00035\u034a\b5\u0001" - + "5\u00035\u034d\b5\u00016\u00016\u00036\u0351\b6\u00017\u00017\u00017\u0003" - + "7\u0356\b7\u00018\u00018\u00038\u035a\b8\u00019\u00019\u0001:\u0001:\u0001" - + ":\u0001:\u0001:\u0001;\u0001;\u0001;\u0000\u00036DF<\u0000\u0002\u0004" - + "\u0006\b\n\f\u000e\u0010\u0012\u0014\u0016\u0018\u001a\u001c\u001e \"" - + "$&(*,.02468:<>@BDFHJLNPRTVXZ\\^`bdfhjlnprtv\u0000\u0010\u0006\u0000\u0005" - + "\u0005\u0007\u0007 ;;FFJJ\u0002\u0000,,YY\u0002\u0000\u0007\u0007FF\u0002" - + "\u0000((11\u0001\u0000\u001a\u001b\u0001\u0000wx\u0002\u0000\u0005\u0005" - + "\u0080\u0080\u0002\u0000\u000b\u000b\u001a\u001a\u0002\u0000%%77\u0002" - + "\u0000\u0005\u0005\u001c\u001c\u0001\u0000y{\u0001\u0000pv\u0002\u0000" - + "$$[[\u0005\u0000\u0017\u0018/0=@RSef\u0001\u0000~\u007f\u0017\u0000\u0006" - + "\u0007\u0011\u0012\u0014\u0017\u0019\u0019 \"\"%%\'\'*,//4477:;==??F" - + "FJMORUVXY]_aaee\u03cd\u0000x\u0001\u0000\u0000\u0000\u0002{\u0001\u0000" - + "\u0000\u0000\u0004\u0100\u0001\u0000\u0000\u0000\u0006\u010b\u0001\u0000" - + "\u0000\u0000\b\u010f\u0001\u0000\u0000\u0000\n\u0124\u0001\u0000\u0000" - + "\u0000\f\u012b\u0001\u0000\u0000\u0000\u000e\u012d\u0001\u0000\u0000\u0000" - + "\u0010\u0135\u0001\u0000\u0000\u0000\u0012\u014d\u0001\u0000\u0000\u0000" - + "\u0014\u015a\u0001\u0000\u0000\u0000\u0016\u0164\u0001\u0000\u0000\u0000" - + "\u0018\u0173\u0001\u0000\u0000\u0000\u001a\u0175\u0001\u0000\u0000\u0000" - + "\u001c\u017b\u0001\u0000\u0000\u0000\u001e\u017e\u0001\u0000\u0000\u0000" - + " \u0180\u0001\u0000\u0000\u0000\"\u0188\u0001\u0000\u0000\u0000$\u018f" - + "\u0001\u0000\u0000\u0000&\u01a1\u0001\u0000\u0000\u0000(\u01b2\u0001\u0000" - + "\u0000\u0000*\u01c2\u0001\u0000\u0000\u0000,\u01e0\u0001\u0000\u0000\u0000" - + ".\u01e2\u0001\u0000\u0000\u00000\u01ed\u0001\u0000\u0000\u00002\u01f5" - + "\u0001\u0000\u0000\u00004\u01fc\u0001\u0000\u0000\u00006\u021d\u0001\u0000" - + "\u0000\u00008\u022e\u0001\u0000\u0000\u0000:\u0231\u0001\u0000\u0000\u0000" - + "<\u0263\u0001\u0000\u0000\u0000>\u0265\u0001\u0000\u0000\u0000@\u0268" - + "\u0001\u0000\u0000\u0000B\u0272\u0001\u0000\u0000\u0000D\u0278\u0001\u0000" - + "\u0000\u0000F\u02ad\u0001\u0000\u0000\u0000H\u02ba\u0001\u0000\u0000\u0000" - + "J\u02c6\u0001\u0000\u0000\u0000L\u02c8\u0001\u0000\u0000\u0000N\u02cf" - + "\u0001\u0000\u0000\u0000P\u02db\u0001\u0000\u0000\u0000R\u02dd\u0001\u0000" - + "\u0000\u0000T\u02e9\u0001\u0000\u0000\u0000V\u02eb\u0001\u0000\u0000\u0000" - + "X\u02ff\u0001\u0000\u0000\u0000Z\u031b\u0001\u0000\u0000\u0000\\\u031d" + + "5\u00015\u00035\u0344\b5\u00015\u00015\u00015\u00035\u0349\b5\u00015\u0001" + + "5\u00015\u00035\u034e\b5\u00015\u00015\u00015\u00035\u0353\b5\u00035\u0355" + + "\b5\u00016\u00016\u00036\u0359\b6\u00017\u00017\u00017\u00037\u035e\b" + + "7\u00018\u00018\u00038\u0362\b8\u00019\u00019\u0001:\u0001:\u0001:\u0001" + + ":\u0001:\u0001;\u0001;\u0001;\u0000\u00036DF<\u0000\u0002\u0004\u0006" + + "\b\n\f\u000e\u0010\u0012\u0014\u0016\u0018\u001a\u001c\u001e \"$&(*,." + + "02468:<>@BDFHJLNPRTVXZ\\^`bdfhjlnprtv\u0000\u0010\u0006\u0000\u0005\u0005" + + "\u0007\u0007 ;;FFJJ\u0002\u0000,,YY\u0002\u0000\u0007\u0007FF\u0002\u0000" + + "((11\u0001\u0000\u001a\u001b\u0001\u0000wx\u0002\u0000\u0005\u0005\u0080" + + "\u0080\u0002\u0000\u000b\u000b\u001a\u001a\u0002\u0000%%77\u0002\u0000" + + "\u0005\u0005\u001c\u001c\u0001\u0000y{\u0001\u0000pv\u0002\u0000$$[[\u0005" + + "\u0000\u0017\u0018/0=@RSef\u0001\u0000~\u007f\u0017\u0000\u0006\u0007" + + "\u0011\u0012\u0014\u0017\u0019\u0019 \"\"%%\'\'*,//4477:;==??FFJMORU" + + "VXY]_aaee\u03d7\u0000x\u0001\u0000\u0000\u0000\u0002{\u0001\u0000\u0000" + + "\u0000\u0004\u0100\u0001\u0000\u0000\u0000\u0006\u010b\u0001\u0000\u0000" + + "\u0000\b\u010f\u0001\u0000\u0000\u0000\n\u0124\u0001\u0000\u0000\u0000" + + "\f\u012b\u0001\u0000\u0000\u0000\u000e\u012d\u0001\u0000\u0000\u0000\u0010" + + "\u0135\u0001\u0000\u0000\u0000\u0012\u014d\u0001\u0000\u0000\u0000\u0014" + + "\u015a\u0001\u0000\u0000\u0000\u0016\u0164\u0001\u0000\u0000\u0000\u0018" + + "\u0173\u0001\u0000\u0000\u0000\u001a\u0175\u0001\u0000\u0000\u0000\u001c" + + "\u017b\u0001\u0000\u0000\u0000\u001e\u017e\u0001\u0000\u0000\u0000 \u0180" + + "\u0001\u0000\u0000\u0000\"\u0188\u0001\u0000\u0000\u0000$\u018f\u0001" + + "\u0000\u0000\u0000&\u01a1\u0001\u0000\u0000\u0000(\u01b2\u0001\u0000\u0000" + + "\u0000*\u01c2\u0001\u0000\u0000\u0000,\u01e0\u0001\u0000\u0000\u0000." + + "\u01e2\u0001\u0000\u0000\u00000\u01ed\u0001\u0000\u0000\u00002\u01f5\u0001" + + "\u0000\u0000\u00004\u01fc\u0001\u0000\u0000\u00006\u021d\u0001\u0000\u0000" + + "\u00008\u022e\u0001\u0000\u0000\u0000:\u0231\u0001\u0000\u0000\u0000<" + + "\u0263\u0001\u0000\u0000\u0000>\u0265\u0001\u0000\u0000\u0000@\u0268\u0001" + + "\u0000\u0000\u0000B\u0272\u0001\u0000\u0000\u0000D\u0278\u0001\u0000\u0000" + + "\u0000F\u02ad\u0001\u0000\u0000\u0000H\u02ba\u0001\u0000\u0000\u0000J" + + "\u02c6\u0001\u0000\u0000\u0000L\u02c8\u0001\u0000\u0000\u0000N\u02cf\u0001" + + "\u0000\u0000\u0000P\u02db\u0001\u0000\u0000\u0000R\u02dd\u0001\u0000\u0000" + + "\u0000T\u02e9\u0001\u0000\u0000\u0000V\u02eb\u0001\u0000\u0000\u0000X" + + "\u02ff\u0001\u0000\u0000\u0000Z\u031b\u0001\u0000\u0000\u0000\\\u031d" + "\u0001\u0000\u0000\u0000^\u031f\u0001\u0000\u0000\u0000`\u0321\u0001\u0000" + "\u0000\u0000b\u032e\u0001\u0000\u0000\u0000d\u0330\u0001\u0000\u0000\u0000" - + "f\u0337\u0001\u0000\u0000\u0000h\u033e\u0001\u0000\u0000\u0000j\u034c" - + "\u0001\u0000\u0000\u0000l\u0350\u0001\u0000\u0000\u0000n\u0355\u0001\u0000" - + "\u0000\u0000p\u0359\u0001\u0000\u0000\u0000r\u035b\u0001\u0000\u0000\u0000" - + "t\u035d\u0001\u0000\u0000\u0000v\u0362\u0001\u0000\u0000\u0000xy\u0003" + + "f\u0337\u0001\u0000\u0000\u0000h\u033e\u0001\u0000\u0000\u0000j\u0354" + + "\u0001\u0000\u0000\u0000l\u0358\u0001\u0000\u0000\u0000n\u035d\u0001\u0000" + + "\u0000\u0000p\u0361\u0001\u0000\u0000\u0000r\u0363\u0001\u0000\u0000\u0000" + + "t\u0365\u0001\u0000\u0000\u0000v\u036a\u0001\u0000\u0000\u0000xy\u0003" + "\u0004\u0002\u0000yz\u0005\u0000\u0000\u0001z\u0001\u0001\u0000\u0000" + "\u0000{|\u00034\u001a\u0000|}\u0005\u0000\u0000\u0001}\u0003\u0001\u0000" + "\u0000\u0000~\u0101\u0003\u0006\u0003\u0000\u007f\u008d\u0005\"\u0000" @@ -9574,34 +9604,39 @@ private boolean primaryExpression_sempred(PrimaryExpressionContext _localctx, in + "\u0000\u0000\u0000\u033fi\u0001\u0000\u0000\u0000\u0340\u0341\u0003h4" + "\u0000\u0341\u0342\u0005\u0004\u0000\u0000\u0342\u0344\u0001\u0000\u0000" + "\u0000\u0343\u0340\u0001\u0000\u0000\u0000\u0343\u0344\u0001\u0000\u0000" - + "\u0000\u0344\u0345\u0001\u0000\u0000\u0000\u0345\u034d\u0005\u0084\u0000" - + "\u0000\u0346\u0347\u0003h4\u0000\u0347\u0348\u0005\u0004\u0000\u0000\u0348" - + "\u034a\u0001\u0000\u0000\u0000\u0349\u0346\u0001\u0000\u0000\u0000\u0349" - + "\u034a\u0001\u0000\u0000\u0000\u034a\u034b\u0001\u0000\u0000\u0000\u034b" - + "\u034d\u0003h4\u0000\u034c\u0343\u0001\u0000\u0000\u0000\u034c\u0349\u0001" - + "\u0000\u0000\u0000\u034dk\u0001\u0000\u0000\u0000\u034e\u0351\u0005\u0085" - + "\u0000\u0000\u034f\u0351\u0005\u0086\u0000\u0000\u0350\u034e\u0001\u0000" - + "\u0000\u0000\u0350\u034f\u0001\u0000\u0000\u0000\u0351m\u0001\u0000\u0000" - + "\u0000\u0352\u0356\u0005\u0082\u0000\u0000\u0353\u0356\u0003v;\u0000\u0354" - + "\u0356\u0005\u0083\u0000\u0000\u0355\u0352\u0001\u0000\u0000\u0000\u0355" - + "\u0353\u0001\u0000\u0000\u0000\u0355\u0354\u0001\u0000\u0000\u0000\u0356" - + "o\u0001\u0000\u0000\u0000\u0357\u035a\u0005\u0081\u0000\u0000\u0358\u035a" - + "\u0005\u0080\u0000\u0000\u0359\u0357\u0001\u0000\u0000\u0000\u0359\u0358" - + "\u0001\u0000\u0000\u0000\u035aq\u0001\u0000\u0000\u0000\u035b\u035c\u0007" - + "\u000e\u0000\u0000\u035cs\u0001\u0000\u0000\u0000\u035d\u035e\u0005b\u0000" - + "\u0000\u035e\u035f\u00034\u001a\u0000\u035f\u0360\u0005Z\u0000\u0000\u0360" - + "\u0361\u00034\u001a\u0000\u0361u\u0001\u0000\u0000\u0000\u0362\u0363\u0007" - + "\u000f\u0000\u0000\u0363w\u0001\u0000\u0000\u0000v\u0087\u0089\u008d\u0096" - + "\u0098\u009c\u00a4\u00a6\u00aa\u00ae\u00b4\u00b8\u00bd\u00c2\u00c6\u00ca" - + "\u00cf\u00d9\u00dd\u00e5\u00e8\u00ee\u00f3\u00f6\u00fb\u00fe\u0100\u0108" - + "\u010b\u0117\u011a\u011d\u0124\u012b\u012f\u0133\u0137\u013a\u013e\u0142" - + "\u0147\u014b\u0153\u0157\u015a\u0161\u016c\u016f\u0173\u0185\u018a\u018d" - + "\u0193\u019a\u01a1\u01a4\u01a8\u01ac\u01b0\u01b2\u01bd\u01c2\u01c5\u01c9" - + "\u01cc\u01d2\u01d5\u01db\u01de\u01e0\u01f2\u01f7\u01fa\u021d\u0225\u0227" - + "\u022e\u0233\u0236\u023e\u0247\u024d\u0255\u025a\u0260\u0263\u026a\u0272" - + "\u0278\u0284\u0286\u0291\u02a0\u02a5\u02a9\u02ad\u02b4\u02ba\u02c6\u02db" - + "\u02e9\u02ee\u02f5\u02f8\u02ff\u0308\u031b\u0323\u0327\u032c\u0337\u033e" - + "\u0343\u0349\u034c\u0350\u0355\u0359"; + + "\u0000\u0344\u0345\u0001\u0000\u0000\u0000\u0345\u0348\u0005\u0084\u0000" + + "\u0000\u0346\u0347\u0005|\u0000\u0000\u0347\u0349\u0003h4\u0000\u0348" + + "\u0346\u0001\u0000\u0000\u0000\u0348\u0349\u0001\u0000\u0000\u0000\u0349" + + "\u0355\u0001\u0000\u0000\u0000\u034a\u034b\u0003h4\u0000\u034b\u034c\u0005" + + "\u0004\u0000\u0000\u034c\u034e\u0001\u0000\u0000\u0000\u034d\u034a\u0001" + + "\u0000\u0000\u0000\u034d\u034e\u0001\u0000\u0000\u0000\u034e\u034f\u0001" + + "\u0000\u0000\u0000\u034f\u0352\u0003h4\u0000\u0350\u0351\u0005|\u0000" + + "\u0000\u0351\u0353\u0003h4\u0000\u0352\u0350\u0001\u0000\u0000\u0000\u0352" + + "\u0353\u0001\u0000\u0000\u0000\u0353\u0355\u0001\u0000\u0000\u0000\u0354" + + "\u0343\u0001\u0000\u0000\u0000\u0354\u034d\u0001\u0000\u0000\u0000\u0355" + + "k\u0001\u0000\u0000\u0000\u0356\u0359\u0005\u0085\u0000\u0000\u0357\u0359" + + "\u0005\u0086\u0000\u0000\u0358\u0356\u0001\u0000\u0000\u0000\u0358\u0357" + + "\u0001\u0000\u0000\u0000\u0359m\u0001\u0000\u0000\u0000\u035a\u035e\u0005" + + "\u0082\u0000\u0000\u035b\u035e\u0003v;\u0000\u035c\u035e\u0005\u0083\u0000" + + "\u0000\u035d\u035a\u0001\u0000\u0000\u0000\u035d\u035b\u0001\u0000\u0000" + + "\u0000\u035d\u035c\u0001\u0000\u0000\u0000\u035eo\u0001\u0000\u0000\u0000" + + "\u035f\u0362\u0005\u0081\u0000\u0000\u0360\u0362\u0005\u0080\u0000\u0000" + + "\u0361\u035f\u0001\u0000\u0000\u0000\u0361\u0360\u0001\u0000\u0000\u0000" + + "\u0362q\u0001\u0000\u0000\u0000\u0363\u0364\u0007\u000e\u0000\u0000\u0364" + + "s\u0001\u0000\u0000\u0000\u0365\u0366\u0005b\u0000\u0000\u0366\u0367\u0003" + + "4\u001a\u0000\u0367\u0368\u0005Z\u0000\u0000\u0368\u0369\u00034\u001a" + + "\u0000\u0369u\u0001\u0000\u0000\u0000\u036a\u036b\u0007\u000f\u0000\u0000" + + "\u036bw\u0001\u0000\u0000\u0000x\u0087\u0089\u008d\u0096\u0098\u009c\u00a4" + + "\u00a6\u00aa\u00ae\u00b4\u00b8\u00bd\u00c2\u00c6\u00ca\u00cf\u00d9\u00dd" + + "\u00e5\u00e8\u00ee\u00f3\u00f6\u00fb\u00fe\u0100\u0108\u010b\u0117\u011a" + + "\u011d\u0124\u012b\u012f\u0133\u0137\u013a\u013e\u0142\u0147\u014b\u0153" + + "\u0157\u015a\u0161\u016c\u016f\u0173\u0185\u018a\u018d\u0193\u019a\u01a1" + + "\u01a4\u01a8\u01ac\u01b0\u01b2\u01bd\u01c2\u01c5\u01c9\u01cc\u01d2\u01d5" + + "\u01db\u01de\u01e0\u01f2\u01f7\u01fa\u021d\u0225\u0227\u022e\u0233\u0236" + + "\u023e\u0247\u024d\u0255\u025a\u0260\u0263\u026a\u0272\u0278\u0284\u0286" + + "\u0291\u02a0\u02a5\u02a9\u02ad\u02b4\u02ba\u02c6\u02db\u02e9\u02ee\u02f5" + + "\u02f8\u02ff\u0308\u031b\u0323\u0327\u032c\u0337\u033e\u0343\u0348\u034d" + + "\u0352\u0354\u0358\u035d\u0361"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); static { _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java index 2679772c97b85..f102ad6a4fc0e 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java @@ -32,6 +32,7 @@ import static java.util.Collections.nCopies; import static java.util.stream.Collectors.toList; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; @@ -391,6 +392,85 @@ public void testQuotedIndexNameWithQuotedCluster() { assertEquals("elastic", relation.table().cluster()); } + public void testIndexNameDataSelector() { + Project plan = project(parseStatement("SELECT * FROM foo::data")); + + // data is effectively redundant, and so it is simplified to just the index name when executed + assertThat(plan.child(), instanceOf(UnresolvedRelation.class)); + UnresolvedRelation relation = (UnresolvedRelation) plan.child(); + assertEquals("foo", relation.table().index()); + assertNull(relation.table().cluster()); + } + + public void testIndexNameFailuresSelector() { + Project plan = project(parseStatement("SELECT * FROM foo::failures")); + + assertThat(plan.child(), instanceOf(UnresolvedRelation.class)); + UnresolvedRelation relation = (UnresolvedRelation) plan.child(); + assertEquals("foo::failures", relation.table().index()); + assertNull(relation.table().cluster()); + } + + public void testIndexNameClusterSelectorCombined() { + ParsingException e = expectThrows(ParsingException.class, () -> parseStatement("SELECT * FROM cluster:foo::failures")); + assertThat( + e.getMessage(), + containsString("Invalid index name [cluster:foo::failures], Selectors are not yet supported on remote cluster patterns") + ); + } + + public void testIndexNameInvalidSelector() { + ParsingException e = expectThrows(ParsingException.class, () -> parseStatement("SELECT * FROM foo::bar")); + assertThat( + e.getMessage(), + containsString("Invalid index name [foo::bar], invalid usage of :: separator, [bar] is not a recognized selector") + ); + } + + public void testIndexNameInvalidQuotedSelector() { + ParsingException e = expectThrows(ParsingException.class, () -> parseStatement("SELECT * FROM \"foo::bar\"")); + assertThat( + e.getMessage(), + containsString("Invalid index name [foo::bar], invalid usage of :: separator, [bar] is not a recognized selector") + ); + } + + public void testIndexNameInvalidSelectors() { + ParsingException e = expectThrows(ParsingException.class, () -> parseStatement("SELECT * FROM foo::bar::data")); + assertThat(e.getMessage(), containsString("mismatched input '::' expecting {")); + } + + public void testIndexNameInvalidMixedQuotedSelectors() { + ParsingException e = expectThrows(ParsingException.class, () -> parseStatement("SELECT * FROM \"foo::bar\"::data")); + assertThat( + e.getMessage(), + containsString( + "Invalid index name [foo::bar::data], Invalid usage of :: separator, only one :: separator is allowed per expression" + ) + ); + } + + public void testIndexNameInvalidInconsistentQuotedSelectors() { + // We disallow this case because splicing escape quotes leads to too many corner cases + ParsingException e = expectThrows(ParsingException.class, () -> parseStatement("SELECT * FROM \"foo::data,bar\"::data")); + assertThat( + e.getMessage(), + containsString( + "Invalid index name [foo::data,bar::data], Invalid usage of :: separator, only one :: separator is allowed per expression" + ) + ); + } + + public void testIndexNameInvalidQuotedSelectors() { + ParsingException e = expectThrows(ParsingException.class, () -> parseStatement("SELECT * FROM \"foo::bar::data\"")); + assertThat( + e.getMessage(), + containsString( + "Invalid index name [foo::bar::data], Invalid usage of :: separator, only one :: separator is allowed per expression" + ) + ); + } + private LogicalPlan parseStatement(String sql) { return new SqlParser().createStatement(sql); }