SQL: double quotes escaping bug fix (#43829)
(cherry picked from commit d589dcad18c3708913e13c757b91c846aeb35bb4)
This commit is contained in:
parent
1ae0db7053
commit
bb3e5351b5
|
@ -121,6 +121,9 @@ SELECT "first_name" <1>
|
||||||
<1> Double quotes `"` used for column and table identifiers
|
<1> Double quotes `"` used for column and table identifiers
|
||||||
<2> Single quotes `'` used for a string literal
|
<2> Single quotes `'` used for a string literal
|
||||||
|
|
||||||
|
NOTE:: to escape single or double quotes, one needs to use that specific quote one more time. For example, the literal `John's` can be escaped like
|
||||||
|
`SELECT 'John''s' AS name`. The same goes for double quotes escaping - `SELECT 123 AS "test""number"` will display as a result a column with the name `test"number`.
|
||||||
|
|
||||||
[[sql-syntax-special-chars]]
|
[[sql-syntax-special-chars]]
|
||||||
==== Special characters
|
==== Special characters
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,12 @@ abstract class IdentifierBuilder extends AbstractBuilder {
|
||||||
ParseTree tree = ctx.name != null ? ctx.name : ctx.TABLE_IDENTIFIER();
|
ParseTree tree = ctx.name != null ? ctx.name : ctx.TABLE_IDENTIFIER();
|
||||||
String index = tree.getText();
|
String index = tree.getText();
|
||||||
|
|
||||||
return new TableIdentifier(source, visitIdentifier(ctx.catalog), index);
|
return new TableIdentifier(source, visitIdentifier(ctx.catalog), unquoteIdentifier(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String visitIdentifier(IdentifierContext ctx) {
|
public String visitIdentifier(IdentifierContext ctx) {
|
||||||
return ctx == null ? null : ctx.getText();
|
return ctx == null ? null : unquoteIdentifier(ctx.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -41,4 +41,8 @@ abstract class IdentifierBuilder extends AbstractBuilder {
|
||||||
|
|
||||||
return Strings.collectionToDelimitedString(visitList(ctx.identifier(), String.class), ".");
|
return Strings.collectionToDelimitedString(visitList(ctx.identifier(), String.class), ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String unquoteIdentifier(String identifier) {
|
||||||
|
return identifier.replace("\"\"", "\"");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,10 @@
|
||||||
package org.elasticsearch.xpack.sql.parser;
|
package org.elasticsearch.xpack.sql.parser;
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
|
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
import org.elasticsearch.xpack.sql.expression.Alias;
|
||||||
|
import org.elasticsearch.xpack.sql.expression.Literal;
|
||||||
import org.elasticsearch.xpack.sql.expression.NamedExpression;
|
import org.elasticsearch.xpack.sql.expression.NamedExpression;
|
||||||
import org.elasticsearch.xpack.sql.expression.Order;
|
import org.elasticsearch.xpack.sql.expression.Order;
|
||||||
import org.elasticsearch.xpack.sql.expression.UnresolvedAttribute;
|
import org.elasticsearch.xpack.sql.expression.UnresolvedAttribute;
|
||||||
|
@ -21,6 +24,7 @@ import org.elasticsearch.xpack.sql.plan.logical.Filter;
|
||||||
import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan;
|
import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan;
|
||||||
import org.elasticsearch.xpack.sql.plan.logical.OrderBy;
|
import org.elasticsearch.xpack.sql.plan.logical.OrderBy;
|
||||||
import org.elasticsearch.xpack.sql.plan.logical.Project;
|
import org.elasticsearch.xpack.sql.plan.logical.Project;
|
||||||
|
import org.elasticsearch.xpack.sql.plan.logical.UnresolvedRelation;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -46,6 +50,24 @@ public class SqlParserTests extends ESTestCase {
|
||||||
return type.cast(p);
|
return type.cast(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testEscapeDoubleQuotes() {
|
||||||
|
Project project = project(parseStatement("SELECT bar FROM \"fo\"\"o\""));
|
||||||
|
assertTrue(project.child() instanceof UnresolvedRelation);
|
||||||
|
assertEquals("fo\"o", ((UnresolvedRelation) project.child()).table().index());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEscapeSingleQuotes() {
|
||||||
|
Alias a = singleProjection(project(parseStatement("SELECT '''ab''c' AS \"escaped_text\"")), Alias.class);
|
||||||
|
assertEquals("'ab'c", ((Literal) a.child()).value());
|
||||||
|
assertEquals("escaped_text", a.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEscapeSingleAndDoubleQuotes() {
|
||||||
|
Alias a = singleProjection(project(parseStatement("SELECT 'ab''c' AS \"escaped\"\"text\"")), Alias.class);
|
||||||
|
assertEquals("ab'c", ((Literal) a.child()).value());
|
||||||
|
assertEquals("escaped\"text", a.name());
|
||||||
|
}
|
||||||
|
|
||||||
public void testSelectField() {
|
public void testSelectField() {
|
||||||
UnresolvedAttribute a = singleProjection(project(parseStatement("SELECT bar FROM foo")), UnresolvedAttribute.class);
|
UnresolvedAttribute a = singleProjection(project(parseStatement("SELECT bar FROM foo")), UnresolvedAttribute.class);
|
||||||
assertEquals("bar", a.name());
|
assertEquals("bar", a.name());
|
||||||
|
|
Loading…
Reference in New Issue