This repository has been archived by the owner on Aug 2, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 186
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support to parse backticks quoted identifiers (#240)
* Added an unquoter utils class to extract string text from the back-ticks * Modified the semantic analyzer to visit the index names and field names that have been unquoted from the back-ticks * Added an UnquoteIdentifierRule rewrite the AST that are constructed from the druid parser * Added UT for the backticksUnquoter utils class * Added UT for the updated ANTLR parser * Added UT for UnquoteIdentifierRule * Added IT for the unquote rule * Added IT to test the field alias in JDBC response
- Loading branch information
Showing
10 changed files
with
384 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 105 additions & 0 deletions
105
.../com/amazon/opendistroforelasticsearch/sql/rewriter/identifier/UnquoteIdentifierRule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* A copy of the License is located at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed | ||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package com.amazon.opendistroforelasticsearch.sql.rewriter.identifier; | ||
|
||
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; | ||
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; | ||
import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; | ||
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; | ||
import com.alibaba.druid.sql.ast.statement.SQLSelectItem; | ||
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter; | ||
import com.amazon.opendistroforelasticsearch.sql.rewriter.RewriteRule; | ||
import com.amazon.opendistroforelasticsearch.sql.utils.StringUtils; | ||
|
||
import static com.amazon.opendistroforelasticsearch.sql.utils.StringUtils.unquoteFullColumn; | ||
import static com.amazon.opendistroforelasticsearch.sql.utils.StringUtils.unquoteSingleField; | ||
|
||
/** | ||
* Quoted Identifiers Rewriter Rule | ||
*/ | ||
public class UnquoteIdentifierRule extends MySqlASTVisitorAdapter implements RewriteRule<SQLQueryExpr> { | ||
|
||
/** | ||
* | ||
* This method is to adjust the AST in the cases where the field is quoted, | ||
* and the full name in the SELECT field is in the format of indexAlias.fieldName | ||
* (e.g. SELECT b.`lastname` FROM bank AS b). | ||
* | ||
* In this case, the druid parser constructs a SQLSelectItem for the field "b.`lastname`", with SQLIdentifierExpr of | ||
* "b." and alias of "`lastname`". | ||
* | ||
* This method corrects the SQLSelectItem object to have SQLIdentifier of "b.lastname" and alias of null. | ||
*/ | ||
@Override | ||
public boolean visit(SQLSelectItem selectItem) { | ||
if (selectItem.getExpr() instanceof SQLIdentifierExpr) { | ||
String identifier = ((SQLIdentifierExpr) selectItem.getExpr()).getName(); | ||
if (identifier.endsWith(".")) { | ||
String correctedIdentifier = identifier + unquoteSingleField(selectItem.getAlias(), "`"); | ||
selectItem.setExpr(new SQLIdentifierExpr(correctedIdentifier)); | ||
selectItem.setAlias(null); | ||
} | ||
} | ||
selectItem.setAlias(unquoteSingleField(selectItem.getAlias(), "`")); | ||
return true; | ||
} | ||
|
||
/** | ||
* | ||
* This method is to adjust the AST in the cases where the alias of index is quoted | ||
* (e.g. SELECT `b`.lastname FROM bank AS `b`). | ||
* | ||
* In this case, the druid parser constructs a SQLPropertyExpr for the field "`b`.lastname", with owner of a | ||
* SQLIdentifierExpr "`b`" and name of "lastname". | ||
* | ||
* This method prevent the visitor from visitin the SQLPropertyExpr in this case, | ||
* and corrects AST with a SQLSelectItem object to have SQLIdentifier of "b.lastname". | ||
* | ||
* Used in the case where alias of index and the field name are both quoted | ||
* (e.g. SELECT `b`.`lastname` FROM bank AS `b`). | ||
*/ | ||
@Override | ||
public boolean visit(SQLPropertyExpr propertyExpr) { | ||
String fieldName = ((SQLIdentifierExpr) propertyExpr.getOwner()).getName(); | ||
if (!StringUtils.isQuoted(fieldName, "`")) { | ||
return true; | ||
} | ||
String correctedIdentifier = unquoteSingleField(fieldName) + "." + unquoteSingleField(propertyExpr.getName()); | ||
SQLSelectItem selectItem = (SQLSelectItem) propertyExpr.getParent(); | ||
selectItem.setExpr(new SQLIdentifierExpr(correctedIdentifier)); | ||
return false; | ||
} | ||
|
||
@Override | ||
public void endVisit(SQLIdentifierExpr identifierExpr) { | ||
identifierExpr.setName(unquoteFullColumn(identifierExpr.getName())); | ||
} | ||
|
||
@Override | ||
public void endVisit(SQLExprTableSource tableSource) { | ||
tableSource.setAlias(unquoteSingleField(tableSource.getAlias())); | ||
} | ||
|
||
@Override | ||
public boolean match(SQLQueryExpr root) { | ||
return true; | ||
} | ||
|
||
@Override | ||
public void rewrite(SQLQueryExpr root) { | ||
root.accept(new UnquoteIdentifierRule()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
...pendistroforelasticsearch/sql/unittest/rewriter/identifier/UnquoteIdentifierRuleTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/* | ||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* A copy of the License is located at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed | ||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package com.amazon.opendistroforelasticsearch.sql.unittest.rewriter.identifier; | ||
|
||
import com.alibaba.druid.sql.SQLUtils; | ||
import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; | ||
import com.amazon.opendistroforelasticsearch.sql.rewriter.identifier.UnquoteIdentifierRule; | ||
import com.amazon.opendistroforelasticsearch.sql.util.SqlParserUtils; | ||
import org.junit.Assert; | ||
import org.junit.Test; | ||
|
||
|
||
/** | ||
* Test cases for backticks quoted identifiers | ||
*/ | ||
public class UnquoteIdentifierRuleTest { | ||
|
||
@Test | ||
public void queryWithQuotedIndex() { | ||
query("SELECT lastname FROM `bank` WHERE balance > 1000 ORDER BY age" | ||
).shouldBeAfterRewrite("SELECT lastname FROM bank WHERE balance > 1000 ORDER BY age"); | ||
} | ||
|
||
@Test | ||
public void queryWithQuotedField() { | ||
query("SELECT `lastname` FROM bank ORDER BY age" | ||
).shouldBeAfterRewrite("SELECT lastname FROM bank ORDER BY age"); | ||
|
||
query("SELECT b.`lastname` FROM bank AS b ORDER BY age" | ||
).shouldBeAfterRewrite("SELECT b.lastname FROM bank AS b ORDER BY age"); | ||
} | ||
|
||
@Test | ||
public void queryWithQuotedAlias() { | ||
query("SELECT `b`.lastname FROM bank AS `b` ORDER BY age" | ||
).shouldBeAfterRewrite("SELECT b.lastname FROM bank AS b ORDER BY age"); | ||
|
||
query("SELECT `b`.`lastname` FROM bank AS `b` ORDER BY age" | ||
).shouldBeAfterRewrite("SELECT b.lastname FROM bank AS b ORDER BY age"); | ||
|
||
query("SELECT `b`.`lastname` AS `name` FROM bank AS `b` ORDER BY age" | ||
).shouldBeAfterRewrite("SELECT b.lastname AS name FROM bank AS b ORDER BY age"); | ||
} | ||
|
||
@Test | ||
public void selectSpecificFieldsUsingQuotedTableNamePrefix() { | ||
query("SELECT `bank`.`lastname` FROM `bank`" | ||
).shouldBeAfterRewrite("SELECT bank.lastname FROM bank"); | ||
} | ||
|
||
private QueryAssertion query(String sql) { | ||
return new QueryAssertion(sql); | ||
} | ||
|
||
private static class QueryAssertion { | ||
|
||
private UnquoteIdentifierRule rule = new UnquoteIdentifierRule(); | ||
private SQLQueryExpr expr; | ||
|
||
QueryAssertion(String sql) { | ||
this.expr = SqlParserUtils.parse(sql); | ||
} | ||
|
||
void shouldBeAfterRewrite(String expected) { | ||
rule.rewrite(expr); | ||
Assert.assertEquals( | ||
SQLUtils.toMySqlString(SqlParserUtils.parse(expected)), | ||
SQLUtils.toMySqlString(expr) | ||
); | ||
} | ||
} | ||
} |
Oops, something went wrong.