Skip to content

Commit 1b58f7d

Browse files
Disallow unquoted literals in LIKE clause in DESCRIBE statement (#1181)
* Allow quoted literals only in `DESCRIBE` and `SHOW` clauses. Tests. Signed-off-by: Yury-Fridlyand <[email protected]> * Fix doctest after rebase. Signed-off-by: Yury-Fridlyand <[email protected]> * Fix doctest after rebase. - Typo fix. Signed-off-by: Yury-Fridlyand <[email protected]> * Update syntax section. Signed-off-by: Yury-Fridlyand <[email protected]> Signed-off-by: Yury-Fridlyand <[email protected]> Signed-off-by: Yury-Fridlyand <[email protected]>
1 parent 2d34d9e commit 1b58f7d

File tree

9 files changed

+129
-119
lines changed

9 files changed

+129
-119
lines changed

docs/category.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"user/dql/window.rst",
4747
"user/beyond/partiql.rst",
4848
"user/dql/aggregations.rst",
49-
"user/dql/complex.rst"
49+
"user/dql/complex.rst",
50+
"user/dql/metadata.rst"
5051
]
5152
}

docs/user/dql/metadata.rst

Lines changed: 55 additions & 65 deletions
Large diffs are not rendered by default.

docs/user/img/rdd/showFilter.png

-6.21 KB
Binary file not shown.

docs/user/img/rdd/showStatement.png

-8.34 KB
Binary file not shown.

integ-test/src/test/java/org/opensearch/sql/sql/AdminIT.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,23 @@
77
package org.opensearch.sql.sql;
88

99
import static org.hamcrest.Matchers.equalTo;
10+
import static org.opensearch.sql.legacy.plugin.RestSqlAction.QUERY_API_ENDPOINT;
1011
import static org.opensearch.sql.util.MatcherUtils.assertJsonEquals;
12+
import static org.opensearch.sql.util.TestUtils.getResponseBody;
1113

1214
import com.google.common.io.Resources;
1315
import java.io.IOException;
1416
import java.net.URI;
1517
import java.nio.file.Files;
1618
import java.nio.file.Paths;
19+
import java.util.Locale;
20+
1721
import org.json.JSONArray;
1822
import org.json.JSONObject;
1923
import org.junit.Test;
2024
import org.opensearch.client.Request;
25+
import org.opensearch.client.RequestOptions;
26+
import org.opensearch.client.Response;
2127
import org.opensearch.sql.common.utils.StringUtils;
2228
import org.opensearch.sql.legacy.SQLIntegTestCase;
2329
import org.opensearch.sql.legacy.TestsConstants;
@@ -34,7 +40,7 @@ public void init() throws Exception {
3440
public void showSingleIndexAlias() throws IOException {
3541
String alias = "acc";
3642
addAlias(TestsConstants.TEST_INDEX_ACCOUNT, alias);
37-
JSONObject response = new JSONObject(executeQuery("SHOW TABLES LIKE acc", "jdbc"));
43+
JSONObject response = new JSONObject(executeQuery("SHOW TABLES LIKE 'acc'", "jdbc"));
3844

3945
/*
4046
* Assumed indices of fields in dataRows based on "schema" output for SHOW given above:
@@ -48,7 +54,7 @@ public void showSingleIndexAlias() throws IOException {
4854
public void describeSingleIndexAlias() throws IOException {
4955
String alias = "acc";
5056
addAlias(TestsConstants.TEST_INDEX_ACCOUNT, alias);
51-
JSONObject response = new JSONObject(executeQuery("DESCRIBE TABLES LIKE acc", "jdbc"));
57+
JSONObject response = new JSONObject(executeQuery("DESCRIBE TABLES LIKE 'acc'", "jdbc"));
5258

5359
/*
5460
* Assumed indices of fields in dataRows based on "schema" output for DESCRIBE given above:
@@ -58,15 +64,25 @@ public void describeSingleIndexAlias() throws IOException {
5864
assertThat(row.get(2), equalTo(alias));
5965
}
6066

67+
@Test
68+
public void describeSingleIndexWildcard() throws IOException {
69+
JSONObject response1 = executeQuery("DESCRIBE TABLES LIKE \\\"%account\\\"");
70+
JSONObject response2 = executeQuery("DESCRIBE TABLES LIKE '%account'");
71+
JSONObject response3 = executeQuery("DESCRIBE TABLES LIKE '%account' COLUMNS LIKE \\\"%name\\\"");
72+
JSONObject response4 = executeQuery("DESCRIBE TABLES LIKE \\\"%account\\\" COLUMNS LIKE '%name'");
73+
// 11 rows in the output, each corresponds to a column in the table
74+
assertEquals(11, response1.getJSONArray("datarows").length());
75+
assertTrue(response1.similar(response2));
76+
// 2 columns should match the wildcard
77+
assertEquals(2, response3.getJSONArray("datarows").length());
78+
assertTrue(response3.similar(response4));
79+
}
80+
6181
@Test
6282
public void explainShow() throws Exception {
6383
String expected = loadFromFile("expectedOutput/sql/explain_show.json");
64-
65-
final String actual = explainQuery("SHOW TABLES LIKE %");
66-
assertJsonEquals(
67-
expected,
68-
explainQuery("SHOW TABLES LIKE %")
69-
);
84+
String actual = explainQuery("SHOW TABLES LIKE '%'");
85+
assertTrue(new JSONObject(expected).similar(new JSONObject(actual)));
7086
}
7187

7288
private void addAlias(String index, String alias) throws IOException {
@@ -77,4 +93,16 @@ private String loadFromFile(String filename) throws Exception {
7793
URI uri = Resources.getResource(filename).toURI();
7894
return new String(Files.readAllBytes(Paths.get(uri)));
7995
}
96+
97+
protected JSONObject executeQuery(String query) throws IOException {
98+
Request request = new Request("POST", QUERY_API_ENDPOINT);
99+
request.setJsonEntity(String.format(Locale.ROOT, "{\n" + " \"query\": \"%s\"\n" + "}", query));
100+
101+
RequestOptions.Builder restOptionsBuilder = RequestOptions.DEFAULT.toBuilder();
102+
restOptionsBuilder.addHeader("Content-Type", "application/json");
103+
request.setOptions(restOptionsBuilder);
104+
105+
Response response = client().performRequest(request);
106+
return new JSONObject(getResponseBody(response));
107+
}
80108
}

sql/src/main/antlr/OpenSearchSQLParser.g4

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,8 @@ tableFilter
8080
;
8181

8282
showDescribePattern
83-
: oldID=compatibleID | stringLiteral
84-
;
85-
86-
compatibleID
87-
: (MODULE | ID)+?
83+
: stringLiteral
8884
;
89-
9085
// Select Statement's Details
9186

9287
querySpecification

sql/src/main/java/org/opensearch/sql/sql/parser/AstExpressionBuilder.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,7 @@ public UnresolvedExpression visitColumnFilter(ColumnFilterContext ctx) {
188188
@Override
189189
public UnresolvedExpression visitShowDescribePattern(
190190
ShowDescribePatternContext ctx) {
191-
if (ctx.compatibleID() != null) {
192-
return stringLiteral(ctx.compatibleID().getText());
193-
} else {
194-
return visit(ctx.stringLiteral());
195-
}
191+
return visit(ctx.stringLiteral());
196192
}
197193

198194
@Override

sql/src/test/java/org/opensearch/sql/sql/antlr/SQLSyntaxParserTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
package org.opensearch.sql.sql.antlr;
88

9+
import static org.junit.jupiter.api.Assertions.assertAll;
910
import static org.junit.jupiter.api.Assertions.assertNotNull;
1011
import static org.junit.jupiter.api.Assertions.assertThrows;
1112

@@ -474,6 +475,38 @@ public void can_parse_wildcard_query_relevance_function() {
474475
+ "boost=1.5, case_insensitive=true, rewrite=\"scoring_boolean\")"));
475476
}
476477

478+
@Test
479+
public void describe_request_accepts_only_quoted_string_literals() {
480+
assertAll(
481+
() -> assertThrows(SyntaxCheckException.class,
482+
() -> parser.parse("DESCRIBE TABLES LIKE bank")),
483+
() -> assertThrows(SyntaxCheckException.class,
484+
() -> parser.parse("DESCRIBE TABLES LIKE %bank%")),
485+
() -> assertThrows(SyntaxCheckException.class,
486+
() -> parser.parse("DESCRIBE TABLES LIKE `bank`")),
487+
() -> assertThrows(SyntaxCheckException.class,
488+
() -> parser.parse("DESCRIBE TABLES LIKE %bank% COLUMNS LIKE %status%")),
489+
() -> assertThrows(SyntaxCheckException.class,
490+
() -> parser.parse("DESCRIBE TABLES LIKE 'bank' COLUMNS LIKE status")),
491+
() -> assertNotNull(parser.parse("DESCRIBE TABLES LIKE 'bank' COLUMNS LIKE \"status\"")),
492+
() -> assertNotNull(parser.parse("DESCRIBE TABLES LIKE \"bank\" COLUMNS LIKE 'status'"))
493+
);
494+
}
495+
496+
@Test
497+
public void show_request_accepts_only_quoted_string_literals() {
498+
assertAll(
499+
() -> assertThrows(SyntaxCheckException.class,
500+
() -> parser.parse("SHOW TABLES LIKE bank")),
501+
() -> assertThrows(SyntaxCheckException.class,
502+
() -> parser.parse("SHOW TABLES LIKE %bank%")),
503+
() -> assertThrows(SyntaxCheckException.class,
504+
() -> parser.parse("SHOW TABLES LIKE `bank`")),
505+
() -> assertNotNull(parser.parse("SHOW TABLES LIKE 'bank'")),
506+
() -> assertNotNull(parser.parse("SHOW TABLES LIKE \"bank\""))
507+
);
508+
}
509+
477510
@ParameterizedTest
478511
@MethodSource({
479512
"matchPhraseComplexQueries",

sql/src/test/java/org/opensearch/sql/sql/parser/AstBuilderTest.java

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -567,10 +567,6 @@ public void can_build_show_selected_tables() {
567567
);
568568
}
569569

570-
/**
571-
* Todo, ideally the identifier (%) couldn't be used in LIKE operator, only the string literal
572-
* is allowed.
573-
*/
574570
@Test
575571
public void show_compatible_with_old_engine_syntax() {
576572
assertEquals(
@@ -581,18 +577,7 @@ public void show_compatible_with_old_engine_syntax() {
581577
),
582578
AllFields.of()
583579
),
584-
buildAST("SHOW TABLES LIKE %")
585-
);
586-
}
587-
588-
@Test
589-
public void describe_compatible_with_old_engine_syntax() {
590-
assertEquals(
591-
project(
592-
relation(mappingTable("a_c%")),
593-
AllFields.of()
594-
),
595-
buildAST("DESCRIBE TABLES LIKE a_c%")
580+
buildAST("SHOW TABLES LIKE '%'")
596581
);
597582
}
598583

@@ -621,24 +606,6 @@ public void can_build_describe_selected_tables_field_filter() {
621606
);
622607
}
623608

624-
/**
625-
* Todo, ideally the identifier (%) couldn't be used in LIKE operator, only the string literal
626-
* is allowed.
627-
*/
628-
@Test
629-
public void describe_and_column_compatible_with_old_engine_syntax() {
630-
assertEquals(
631-
project(
632-
filter(
633-
relation(mappingTable("a_c%")),
634-
function("like", qualifiedName("COLUMN_NAME"), stringLiteral("name%"))
635-
),
636-
AllFields.of()
637-
),
638-
buildAST("DESCRIBE TABLES LIKE a_c% COLUMNS LIKE name%")
639-
);
640-
}
641-
642609
@Test
643610
public void can_build_alias_by_keywords() {
644611
assertEquals(

0 commit comments

Comments
 (0)