Skip to content

Commit 2d118a1

Browse files
Merge pull request #154 from Bit-Quill/dev-unqouted-percent-in-like-clause
Disallow unquoted literals in `LIKE` clause in `DESCRIBE` statement
2 parents edce878 + 0ec464a commit 2d118a1

File tree

7 files changed

+125
-115
lines changed

7 files changed

+125
-115
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: 51 additions & 60 deletions
Large diffs are not rendered by default.

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
@@ -82,13 +82,8 @@ tableFilter
8282
;
8383

8484
showDescribePattern
85-
: oldID=compatibleID | stringLiteral
86-
;
87-
88-
compatibleID
89-
: (MODULE | ID)+?
85+
: stringLiteral
9086
;
91-
9287
// Select Statement's Details
9388

9489
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
@@ -165,11 +165,7 @@ public UnresolvedExpression visitColumnFilter(ColumnFilterContext ctx) {
165165
@Override
166166
public UnresolvedExpression visitShowDescribePattern(
167167
ShowDescribePatternContext ctx) {
168-
if (ctx.compatibleID() != null) {
169-
return stringLiteral(ctx.compatibleID().getText());
170-
} else {
171-
return visit(ctx.stringLiteral());
172-
}
168+
return visit(ctx.stringLiteral());
173169
}
174170

175171
@Override

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

Lines changed: 33 additions & 1 deletion
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

@@ -15,7 +16,6 @@
1516
import java.util.Collections;
1617
import java.util.HashMap;
1718
import java.util.Iterator;
18-
import java.util.List;
1919
import java.util.Map;
2020
import java.util.Random;
2121
import java.util.stream.Stream;
@@ -386,6 +386,38 @@ public void can_parse_match_relevance_function() {
386386
assertNotNull(parser.parse("SELECT * FROM test WHERE match_phrase(column, 100500)"));
387387
}
388388

389+
@Test
390+
public void describe_request_accepts_only_quoted_string_literals() {
391+
assertAll(
392+
() -> assertThrows(SyntaxCheckException.class,
393+
() -> parser.parse("DESCRIBE TABLES LIKE bank")),
394+
() -> assertThrows(SyntaxCheckException.class,
395+
() -> parser.parse("DESCRIBE TABLES LIKE %bank%")),
396+
() -> assertThrows(SyntaxCheckException.class,
397+
() -> parser.parse("DESCRIBE TABLES LIKE `bank`")),
398+
() -> assertThrows(SyntaxCheckException.class,
399+
() -> parser.parse("DESCRIBE TABLES LIKE %bank% COLUMNS LIKE %status%")),
400+
() -> assertThrows(SyntaxCheckException.class,
401+
() -> parser.parse("DESCRIBE TABLES LIKE 'bank' COLUMNS LIKE status")),
402+
() -> assertNotNull(parser.parse("DESCRIBE TABLES LIKE 'bank' COLUMNS LIKE \"status\"")),
403+
() -> assertNotNull(parser.parse("DESCRIBE TABLES LIKE \"bank\" COLUMNS LIKE 'status'"))
404+
);
405+
}
406+
407+
@Test
408+
public void show_request_accepts_only_quoted_string_literals() {
409+
assertAll(
410+
() -> assertThrows(SyntaxCheckException.class,
411+
() -> parser.parse("SHOW TABLES LIKE bank")),
412+
() -> assertThrows(SyntaxCheckException.class,
413+
() -> parser.parse("SHOW TABLES LIKE %bank%")),
414+
() -> assertThrows(SyntaxCheckException.class,
415+
() -> parser.parse("SHOW TABLES LIKE `bank`")),
416+
() -> assertNotNull(parser.parse("SHOW TABLES LIKE 'bank'")),
417+
() -> assertNotNull(parser.parse("SHOW TABLES LIKE \"bank\""))
418+
);
419+
}
420+
389421
@ParameterizedTest
390422
@MethodSource({
391423
"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
@@ -552,10 +552,6 @@ public void can_build_show_selected_tables() {
552552
);
553553
}
554554

555-
/**
556-
* Todo, ideally the identifier (%) couldn't be used in LIKE operator, only the string literal
557-
* is allowed.
558-
*/
559555
@Test
560556
public void show_compatible_with_old_engine_syntax() {
561557
assertEquals(
@@ -566,18 +562,7 @@ public void show_compatible_with_old_engine_syntax() {
566562
),
567563
AllFields.of()
568564
),
569-
buildAST("SHOW TABLES LIKE %")
570-
);
571-
}
572-
573-
@Test
574-
public void describe_compatible_with_old_engine_syntax() {
575-
assertEquals(
576-
project(
577-
relation(mappingTable("a_c%")),
578-
AllFields.of()
579-
),
580-
buildAST("DESCRIBE TABLES LIKE a_c%")
565+
buildAST("SHOW TABLES LIKE '%'")
581566
);
582567
}
583568

@@ -606,24 +591,6 @@ public void can_build_describe_selected_tables_field_filter() {
606591
);
607592
}
608593

609-
/**
610-
* Todo, ideally the identifier (%) couldn't be used in LIKE operator, only the string literal
611-
* is allowed.
612-
*/
613-
@Test
614-
public void describe_and_column_compatible_with_old_engine_syntax() {
615-
assertEquals(
616-
project(
617-
filter(
618-
relation(mappingTable("a_c%")),
619-
function("like", qualifiedName("COLUMN_NAME"), stringLiteral("name%"))
620-
),
621-
AllFields.of()
622-
),
623-
buildAST("DESCRIBE TABLES LIKE a_c% COLUMNS LIKE name%")
624-
);
625-
}
626-
627594
@Test
628595
public void can_build_alias_by_keywords() {
629596
assertEquals(

0 commit comments

Comments
 (0)