This is an automated email from the ASF dual-hosted git repository.
terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new 00c22f18328 Support PostgreSQL escaped and Unicode identifiers (#40220)
00c22f18328 is described below
commit 00c22f1832839b0b03a5f21fbc6f2279ab90ecd5
Author: Liang Zhang <[email protected]>
AuthorDate: Sun Oct 18 20:28:28 2026 +0800
Support PostgreSQL escaped and Unicode identifiers (#40220)
* Support PostgreSQL escaped and Unicode identifiers
* Preserve decoded PostgreSQL identifiers in projection SQL
---
.../pojo/generic/SubstitutableColumnNameToken.java | 9 +-
.../generic/SubstitutableColumnNameTokenTest.java | 41 ++++
.../src/main/antlr4/imports/postgresql/Literals.g4 | 2 +-
.../statement/PostgreSQLStatementVisitor.java | 214 +++++++++++----------
.../type/PostgreSQLDALStatementVisitor.java | 3 +-
.../type/PostgreSQLDCLStatementVisitor.java | 18 +-
.../type/PostgreSQLDDLStatementVisitor.java | 6 +-
.../core/value/identifier/IdentifierValue.java | 23 ++-
.../core/value/identifier/IdentifierValueTest.java | 43 +++++
.../main/resources/case/dml/select-expression.xml | 20 ++
.../sql/supported/dml/select-expression.xml | 2 +
.../postgresql/InternalPostgreSQLParserIT.java | 117 +++++++++++
12 files changed, 380 insertions(+), 118 deletions(-)
diff --git
a/infra/rewrite/core/src/main/java/org/apache/shardingsphere/infra/rewrite/sql/token/common/pojo/generic/SubstitutableColumnNameToken.java
b/infra/rewrite/core/src/main/java/org/apache/shardingsphere/infra/rewrite/sql/token/common/pojo/generic/SubstitutableColumnNameToken.java
index 417ea3099e3..55bfdc02da4 100644
---
a/infra/rewrite/core/src/main/java/org/apache/shardingsphere/infra/rewrite/sql/token/common/pojo/generic/SubstitutableColumnNameToken.java
+++
b/infra/rewrite/core/src/main/java/org/apache/shardingsphere/infra/rewrite/sql/token/common/pojo/generic/SubstitutableColumnNameToken.java
@@ -35,6 +35,7 @@ import
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.Iden
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
/**
* Substitutable column name token.
@@ -112,13 +113,17 @@ public final class SubstitutableColumnNameToken extends
SQLToken implements Subs
if (columnProjection.getOwner().isPresent()) {
IdentifierValue owner = columnProjection.getOwner().get();
String actualTableOwner =
logicActualTableNames.getOrDefault(owner.getValue(), owner.getValue());
- builder.append(getValueWithQuoteCharacters(new
IdentifierValue(actualTableOwner, owner.getQuoteCharacter()))).append('.');
+ IdentifierValue actualOwner = Objects.equals(actualTableOwner,
owner.getValue()) ? owner : new IdentifierValue(actualTableOwner,
owner.getQuoteCharacter());
+
builder.append(getValueWithQuoteCharacters(actualOwner)).append('.');
}
builder.append(getValueWithQuoteCharacters(columnProjection.getName()));
columnProjection.getRightParentheses().ifPresent(optional ->
builder.append(")"));
}
private String getValueWithQuoteCharacters(final IdentifierValue
identifierValue) {
- return QuoteCharacter.NONE == identifierValue.getQuoteCharacter() ?
identifierValue.getValue() : quoteCharacter.wrap(identifierValue.getValue());
+ if (QuoteCharacter.NONE == identifierValue.getQuoteCharacter()) {
+ return identifierValue.getValue();
+ }
+ return quoteCharacter == identifierValue.getQuoteCharacter() ?
identifierValue.getValueWithQuoteCharacters() :
quoteCharacter.wrap(identifierValue.getValue());
}
}
diff --git
a/infra/rewrite/core/src/test/java/org/apache/shardingsphere/infra/rewrite/sql/token/common/pojo/generic/SubstitutableColumnNameTokenTest.java
b/infra/rewrite/core/src/test/java/org/apache/shardingsphere/infra/rewrite/sql/token/common/pojo/generic/SubstitutableColumnNameTokenTest.java
index f5ffbe4bc54..6a5b8c9ffa4 100644
---
a/infra/rewrite/core/src/test/java/org/apache/shardingsphere/infra/rewrite/sql/token/common/pojo/generic/SubstitutableColumnNameTokenTest.java
+++
b/infra/rewrite/core/src/test/java/org/apache/shardingsphere/infra/rewrite/sql/token/common/pojo/generic/SubstitutableColumnNameTokenTest.java
@@ -18,23 +18,33 @@
package org.apache.shardingsphere.infra.rewrite.sql.token.common.pojo.generic;
import
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
+import
org.apache.shardingsphere.database.connector.core.metadata.database.metadata.DialectDatabaseMetaData;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
import
org.apache.shardingsphere.infra.binder.context.segment.select.projection.Projection;
import
org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.ColumnProjection;
import
org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.SubqueryProjection;
+import org.apache.shardingsphere.infra.route.context.RouteMapper;
import org.apache.shardingsphere.infra.route.context.RouteUnit;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.SubqueryProjectionSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.MockedConstruction;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.when;
class SubstitutableColumnNameTokenTest {
@@ -71,4 +81,35 @@ class SubstitutableColumnNameTokenTest {
new ColumnProjection(null, "name", null,
databaseType), new IdentifierValue("name"), databaseType));
assertThat(new SubstitutableColumnNameToken(0, 1, projections,
databaseType).toString(mock(RouteUnit.class)), is("temp.id AS id, name AS
name"));
}
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("getQuotedIdentifiers")
+ void assertToStringWithQuotedIdentifiers(final String name, final
IdentifierValue owner, final IdentifierValue columnName, final IdentifierValue
alias, final Collection<RouteMapper> tableMappers,
+ final String expected) {
+ DialectDatabaseMetaData databaseMetaData =
mock(DialectDatabaseMetaData.class);
+
when(databaseMetaData.getQuoteCharacter()).thenReturn(QuoteCharacter.QUOTE);
+ Collection<Projection> projections = Collections.singletonList(new
ColumnProjection(owner, columnName, alias, databaseType));
+ RouteUnit routeUnit = new RouteUnit(new RouteMapper("foo_ds",
"foo_ds"), tableMappers);
+ try (
+ MockedConstruction<DatabaseTypeRegistry> ignored =
mockConstruction(DatabaseTypeRegistry.class,
+ (registry, context) ->
when(registry.getDialectDatabaseMetaData()).thenReturn(databaseMetaData))) {
+ assertThat(new SubstitutableColumnNameToken(0, 1, projections,
databaseType).toString(routeUnit), is(expected));
+ }
+ }
+
+ private static Stream<Arguments> getQuotedIdentifiers() {
+ return Stream.of(
+ Arguments.of("decoded alias", null, new
IdentifierValue("foo_column"),
IdentifierValue.fromDecodedQuotedName("foo\"bar"), Collections.emptyList(),
"foo_column AS \"foo\"\"bar\""),
+ Arguments.of("adjacent decoded alias quotes", null, new
IdentifierValue("foo_column"),
IdentifierValue.fromDecodedQuotedName("foo\"\"bar"), Collections.emptyList(),
+ "foo_column AS \"foo\"\"\"\"bar\""),
+ Arguments.of("decoded column", null,
IdentifierValue.fromDecodedQuotedName("foo\"bar"), null,
Collections.emptyList(), "\"foo\"\"bar\""),
+ Arguments.of("decoded owner without mapping",
IdentifierValue.fromDecodedQuotedName("foo\"table"), new
IdentifierValue("foo_column"), null, Collections.emptyList(),
+ "\"foo\"\"table\".foo_column"),
+ Arguments.of("decoded owner with same-name mapping",
IdentifierValue.fromDecodedQuotedName("foo\"table"), new
IdentifierValue("foo_column"), null,
+ Collections.singletonList(new
RouteMapper("foo\"table", "foo\"table")), "\"foo\"\"table\".foo_column"),
+ Arguments.of("renamed owner",
IdentifierValue.fromDecodedQuotedName("foo\"table"), new
IdentifierValue("foo_column"), null,
+ Collections.singletonList(new
RouteMapper("foo\"table", "bar_table")), "\"bar_table\".foo_column"),
+ Arguments.of("legacy raw doubled quotes", null, new
IdentifierValue("foo\"\"bar", QuoteCharacter.QUOTE), null,
Collections.emptyList(), "\"foo\"\"bar\""),
+ Arguments.of("back-quote conversion", null, new
IdentifierValue("foo_column", QuoteCharacter.BACK_QUOTE), null,
Collections.emptyList(), "\"foo_column\""));
+ }
}
diff --git
a/parser/sql/engine/dialect/postgresql/src/main/antlr4/imports/postgresql/Literals.g4
b/parser/sql/engine/dialect/postgresql/src/main/antlr4/imports/postgresql/Literals.g4
index 92e30e36f4a..5179be7ea1e 100644
---
a/parser/sql/engine/dialect/postgresql/src/main/antlr4/imports/postgresql/Literals.g4
+++
b/parser/sql/engine/dialect/postgresql/src/main/antlr4/imports/postgresql/Literals.g4
@@ -19,7 +19,7 @@ lexer grammar Literals;
import Alphabet, Symbol;
-IDENTIFIER_: IDENTIFIER_START_CHAR_ IDENTIFIER_CHAR_* | DQ_ ~'"'+ DQ_;
+IDENTIFIER_: IDENTIFIER_START_CHAR_ IDENTIFIER_CHAR_* | DQ_ (DQ_ DQ_ | ~'"')+
DQ_;
STRING_: E ESCAPE_STRING_LITERAL_ (STRING_CONTINUATION_
ESCAPE_STRING_LITERAL_)* | STRING_LITERAL_ (STRING_CONTINUATION_
STRING_LITERAL_)*;
NUMBER_: (INT_ (DOT_ INT_?)? | DOT_ INT_) (E (PLUS_ | MINUS_)? INT_)?;
HEX_DIGIT_: X SQ_ HEX_* SQ_ (STRING_CONTINUATION_ SQ_ HEX_* SQ_)*;
diff --git
a/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/PostgreSQLStatementVisitor.java
b/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/PostgreSQLStatementVisitor.java
index 09ce8672ff2..d956fa21379 100644
---
a/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/PostgreSQLStatementVisitor.java
+++
b/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/PostgreSQLStatementVisitor.java
@@ -295,7 +295,84 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
@Override
public final ASTNode visitIdentifier(final IdentifierContext ctx) {
UnreservedWordContext unreservedWord = ctx.unreservedWord();
- return null == unreservedWord ? new IdentifierValue(ctx.getText()) :
visit(unreservedWord);
+ return null == unreservedWord ? createIdentifierValue(ctx.getText()) :
visit(unreservedWord);
+ }
+
+ protected final IdentifierValue createIdentifierValue(final String text) {
+ boolean unicode = text.regionMatches(true, 0, "U&", 0, 2);
+ if (!unicode && !text.startsWith("\"")) {
+ return new IdentifierValue(text);
+ }
+ int stopIndex = text.lastIndexOf('"');
+ String value = text.substring(unicode ? 3 : 1,
stopIndex).replace("\"\"", "\"");
+ if (unicode) {
+ char escape = stopIndex + 1 == text.length() ? '\\' :
getUnicodeEscapeCharacter(text.substring(stopIndex + 8));
+ value = decodeUnicode(value, escape);
+ }
+ return IdentifierValue.fromDecodedQuotedName(value);
+ }
+
+ private char getUnicodeEscapeCharacter(final String text) {
+ String literal = concatenateStringLiterals(text);
+ String value = literal.substring(1, literal.length() - 1);
+ if (!text.regionMatches(true, 0, "E", 0, 1) || '\\' !=
value.charAt(0)) {
+ return value.charAt(0);
+ }
+ char character = value.charAt(1);
+ if ('x' == character || 'u' == character || 'U' == character) {
+ return (char) Integer.parseInt(value.substring(2), 16);
+ }
+ if ('0' <= character && '7' >= character) {
+ return (char) Integer.parseInt(value.substring(1), 8);
+ }
+ return 'b' == character ? '\b' : character;
+ }
+
+ private String concatenateStringLiterals(final String text) {
+ StringBuilder result = new StringBuilder("'");
+ boolean escaped = 'E' == text.charAt(0) || 'e' == text.charAt(0);
+ int index = text.indexOf('\'') + 1;
+ while (index < text.length()) {
+ char character = text.charAt(index);
+ if (escaped && '\\' == character) {
+ result.append(character).append(text.charAt(++index));
+ } else if ('\'' != character) {
+ result.append(character);
+ } else if (index + 1 < text.length() && '\'' == text.charAt(index
+ 1)) {
+ result.append("''");
+ index++;
+ } else {
+ int nextIndex = text.indexOf('\'', index + 1);
+ if (-1 == nextIndex) {
+ break;
+ }
+ index = nextIndex;
+ }
+ index++;
+ }
+ return result.append('\'').toString();
+ }
+
+ private String decodeUnicode(final String value, final char escape) {
+ StringBuilder result = new StringBuilder();
+ int index = 0;
+ while (index < value.length()) {
+ char character = value.charAt(index);
+ if (escape != character) {
+ result.append(character);
+ index++;
+ } else if (escape == value.charAt(index + 1)) {
+ result.append(escape);
+ index += 2;
+ } else {
+ int startIndex = index + 1;
+ int length = '+' == value.charAt(startIndex) ? 6 : 4;
+ startIndex += 6 == length ? 1 : 0;
+
result.appendCodePoint(Integer.parseInt(value.substring(startIndex, startIndex
+ length), 16));
+ index = startIndex + length;
+ }
+ }
+ return result.toString();
}
@Override
@@ -657,7 +734,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
return createWindowItem(ctx.windowSpecification());
}
WindowItemSegment result = new
WindowItemSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex());
- result.setWindowName(new IdentifierValue(ctx.colId().getText()));
+ result.setWindowName(createIdentifierValue(ctx.colId().getText()));
return result;
}
@@ -830,69 +907,6 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
return new StringLiteralValue(text);
}
- private String concatenateStringLiterals(final String text) {
- StringBuilder result = new StringBuilder("'");
- boolean escaped = 'E' == text.charAt(0) || 'e' == text.charAt(0);
- int index = text.indexOf('\'') + 1;
- while (index < text.length()) {
- char character = text.charAt(index);
- if (escaped && '\\' == character) {
- result.append(character).append(text.charAt(++index));
- } else if ('\'' != character) {
- result.append(character);
- } else if (index + 1 < text.length() && '\'' == text.charAt(index
+ 1)) {
- result.append("''");
- index++;
- } else {
- int nextIndex = text.indexOf('\'', index + 1);
- if (-1 == nextIndex) {
- break;
- }
- index = nextIndex;
- }
- index++;
- }
- return result.append('\'').toString();
- }
-
- private char getUnicodeEscapeCharacter(final String text) {
- String literal = concatenateStringLiterals(text);
- String value = literal.substring(1, literal.length() - 1);
- if (!text.regionMatches(true, 0, "E", 0, 1) || '\\' !=
value.charAt(0)) {
- return value.charAt(0);
- }
- char character = value.charAt(1);
- if ('x' == character || 'u' == character || 'U' == character) {
- return (char) Integer.parseInt(value.substring(2), 16);
- }
- if ('0' <= character && '7' >= character) {
- return (char) Integer.parseInt(value.substring(1), 8);
- }
- return 'b' == character ? '\b' : character;
- }
-
- private String decodeUnicode(final String value, final char escape) {
- StringBuilder result = new StringBuilder();
- int index = 0;
- while (index < value.length()) {
- char character = value.charAt(index);
- if (escape != character) {
- result.append(character);
- index++;
- } else if (escape == value.charAt(index + 1)) {
- result.append(escape);
- index += 2;
- } else {
- int startIndex = index + 1;
- int length = '+' == value.charAt(startIndex) ? 6 : 4;
- startIndex += 6 == length ? 1 : 0;
-
result.appendCodePoint(Integer.parseInt(value.substring(startIndex, startIndex
+ length), 16));
- index = startIndex + length;
- }
- }
- return result.toString();
- }
-
@Override
public ASTNode visitColumnref(final ColumnrefContext ctx) {
if (null != ctx.indirection()) {
@@ -906,17 +920,17 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
}
if (null != ctx.indirection() && null !=
ctx.indirection().indirectionEl().DOT_ASTERISK_()) {
ColumnSegment result = new
ColumnSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), new
IdentifierValue("*"));
- result.setOwner(new
OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), new IdentifierValue(ctx.colId().getText())));
+ result.setOwner(new
OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), createIdentifierValue(ctx.colId().getText())));
return result;
}
if (null != ctx.indirection() && null !=
ctx.indirection().indirectionEl().attrName()) {
AttrNameContext attrName =
ctx.indirection().indirectionEl().attrName();
- ColumnSegment result = new
ColumnSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), new
IdentifierValue(attrName.getText()));
- OwnerSegment owner = new
OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), new IdentifierValue(ctx.colId().getText()));
+ ColumnSegment result = new
ColumnSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex(),
createIdentifierValue(attrName.getText()));
+ OwnerSegment owner = new
OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), createIdentifierValue(ctx.colId().getText()));
result.setOwner(owner);
return result;
}
- return new ColumnSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), new IdentifierValue(ctx.colId().getText()));
+ return new ColumnSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), createIdentifierValue(ctx.colId().getText()));
}
private ColumnSegment createSubscriptColumn(final ColumnrefContext ctx,
final Collection<IndirectionElContext> elements) {
@@ -930,10 +944,10 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
}
}
if (null == columnName) {
- return new ColumnSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), new IdentifierValue(ctx.colId().getText()));
+ return new ColumnSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), createIdentifierValue(ctx.colId().getText()));
}
- ColumnSegment result = new ColumnSegment(ctx.start.getStartIndex(),
columnName.stop.getStopIndex(), new IdentifierValue(columnName.getText()));
- result.setOwner(new OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), new IdentifierValue(ctx.colId().getText())));
+ ColumnSegment result = new ColumnSegment(ctx.start.getStartIndex(),
columnName.stop.getStopIndex(), createIdentifierValue(columnName.getText()));
+ result.setOwner(new OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), createIdentifierValue(ctx.colId().getText())));
return result;
}
@@ -1150,7 +1164,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
SimpleTableSegment result = (SimpleTableSegment)
visit(ctx.qualifiedName());
if (null != ctx.AS()) {
ColIdContext colId = ctx.colId();
- result.setAlias(new AliasSegment(colId.start.getStartIndex(),
colId.stop.getStopIndex(), new IdentifierValue(colId.getText())));
+ result.setAlias(new AliasSegment(colId.start.getStartIndex(),
colId.stop.getStopIndex(), createIdentifierValue(colId.getText())));
}
return result;
}
@@ -1172,8 +1186,8 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
public ASTNode visitQualifiedName(final QualifiedNameContext ctx) {
if (null != ctx.indirection()) {
AttrNameContext attrName =
ctx.indirection().indirectionEl().attrName();
- TableNameSegment tableName = new
TableNameSegment(attrName.start.getStartIndex(), attrName.stop.getStopIndex(),
new IdentifierValue(attrName.getText()));
- OwnerSegment owner = new
OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), new IdentifierValue(ctx.colId().getText()));
+ TableNameSegment tableName = new
TableNameSegment(attrName.start.getStartIndex(), attrName.stop.getStopIndex(),
createIdentifierValue(attrName.getText()));
+ OwnerSegment owner = new
OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), createIdentifierValue(ctx.colId().getText()));
SimpleTableSegment result = new SimpleTableSegment(tableName);
if (null != ctx.indirection().indirection()) {
OwnerSegment tableOwner =
createTableOwner(ctx.indirection().indirection());
@@ -1184,7 +1198,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
}
return result;
}
- return new SimpleTableSegment(new
TableNameSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), new IdentifierValue(ctx.colId().getText())));
+ return new SimpleTableSegment(new
TableNameSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), createIdentifierValue(ctx.colId().getText())));
}
@SuppressWarnings("unchecked")
@@ -1228,11 +1242,11 @@ public abstract class PostgreSQLStatementVisitor
extends PostgreSQLStatementPars
visitIndirectionExpressions(ctx.optIndirection());
if (null != ctx.optIndirection().indirectionEl() && null !=
ctx.optIndirection().indirectionEl().attrName()) {
AttrNameContext attrName =
ctx.optIndirection().indirectionEl().attrName();
- ColumnSegment result = new
ColumnSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), new
IdentifierValue(attrName.getText()));
- result.setOwner(new
OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), new IdentifierValue(ctx.colId().getText())));
+ ColumnSegment result = new
ColumnSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex(),
createIdentifierValue(attrName.getText()));
+ result.setOwner(new
OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), createIdentifierValue(ctx.colId().getText())));
return result;
}
- return new ColumnSegment(ctx.colId().start.getStartIndex(),
ctx.stop.getStopIndex(), new IdentifierValue(ctx.colId().getText()));
+ return new ColumnSegment(ctx.colId().start.getStartIndex(),
ctx.stop.getStopIndex(), createIdentifierValue(ctx.colId().getText()));
}
private void visitIndirectionExpressions(final OptIndirectionContext ctx) {
@@ -1301,7 +1315,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
public ASTNode visitSetTarget(final SetTargetContext ctx) {
visitIndirectionExpressions(ctx.optIndirection());
List<IdentifierValue> fieldAttributes =
getSetTargetFieldAttributes(ctx.optIndirection());
- ColumnSegment result = new
ColumnSegment(ctx.colId().start.getStartIndex(), ctx.stop.getStopIndex(), new
IdentifierValue(ctx.colId().getText()));
+ ColumnSegment result = new
ColumnSegment(ctx.colId().start.getStartIndex(), ctx.stop.getStopIndex(),
createIdentifierValue(ctx.colId().getText()));
if (!fieldAttributes.isEmpty()) {
result.setNestedObjectAttributes(fieldAttributes);
}
@@ -1319,7 +1333,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
() -> new SQLParsingException("Only field indirection is
supported in assignment targets"));
AttrNameContext attrName = current.indirectionEl().attrName();
if (null != attrName) {
- result.add(0, new IdentifierValue(attrName.getText()));
+ result.add(0, createIdentifierValue(attrName.getText()));
}
current = current.optIndirection();
}
@@ -1330,7 +1344,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
public ASTNode visitRelationExprOptAlias(final RelationExprOptAliasContext
ctx) {
SimpleTableSegment result = (SimpleTableSegment)
visit(ctx.relationExpr().qualifiedName());
if (null != ctx.colId()) {
- result.setAlias(new
AliasSegment(ctx.colId().start.getStartIndex(), ctx.stop.getStopIndex(), new
IdentifierValue(ctx.colId().getText())));
+ result.setAlias(new
AliasSegment(ctx.colId().start.getStartIndex(), ctx.stop.getStopIndex(),
createIdentifierValue(ctx.colId().getText())));
}
return result;
}
@@ -1415,7 +1429,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
public ASTNode visitReturningClause(final ReturningClauseContext ctx) {
ReturningSegment result = new
ReturningSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex(),
(ProjectionsSegment) visit(ctx.targetList()));
for (ReturningOptionContext each : ctx.returningOption()) {
- AliasSegment alias = new
AliasSegment(each.colId().start.getStartIndex(),
each.colId().stop.getStopIndex(), new IdentifierValue(each.colId().getText()));
+ AliasSegment alias = new
AliasSegment(each.colId().start.getStartIndex(),
each.colId().stop.getStopIndex(),
createIdentifierValue(each.colId().getText()));
if (null != each.OLD()) {
result.setOldAlias(alias);
} else {
@@ -1538,12 +1552,12 @@ public abstract class PostgreSQLStatementVisitor
extends PostgreSQLStatementPars
}
private ColumnSegment createColumnSegment(final ColIdContext ctx) {
- return new ColumnSegment(ctx.start.getStartIndex(),
ctx.stop.getStopIndex(), new IdentifierValue(ctx.getText()));
+ return new ColumnSegment(ctx.start.getStartIndex(),
ctx.stop.getStopIndex(), createIdentifierValue(ctx.getText()));
}
@Override
public ASTNode visitAlias(final AliasContext ctx) {
- return new AliasSegment(ctx.start.getStartIndex(),
ctx.stop.getStopIndex(), new IdentifierValue(ctx.identifier().getText()));
+ return new AliasSegment(ctx.start.getStartIndex(),
ctx.stop.getStopIndex(), createIdentifierValue(ctx.identifier().getText()));
}
@Override
@@ -1665,7 +1679,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
@Override
public ASTNode visitWindowDefinition(final WindowDefinitionContext ctx) {
WindowItemSegment result = new
WindowItemSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex());
- result.setWindowName(new IdentifierValue(ctx.colId().getText()));
+ result.setWindowName(createIdentifierValue(ctx.colId().getText()));
setWindowSpecification(ctx.windowSpecification(), result);
return result;
}
@@ -1673,7 +1687,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
private WindowItemSegment createWindowItem(final
WindowSpecificationContext ctx) {
WindowItemSegment result = new
WindowItemSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex());
if (null != ctx.existingWindowName()) {
- result.setWindowName(new
IdentifierValue(ctx.existingWindowName().colId().getText()));
+
result.setWindowName(createIdentifierValue(ctx.existingWindowName().colId().getText()));
}
setWindowSpecification(ctx, result);
return result;
@@ -1748,7 +1762,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
ProjectionSegment result = createProjectionSegment(ctx, ctx.aExpr());
ParserRuleContext alias = null == ctx.colLabel() ? ctx.identifier() :
ctx.colLabel();
if (null != alias) {
- ((AliasAvailable) result).setAlias(new
AliasSegment(alias.start.getStartIndex(), alias.stop.getStopIndex(), new
IdentifierValue(alias.getText())));
+ ((AliasAvailable) result).setAlias(new
AliasSegment(alias.start.getStartIndex(), alias.stop.getStopIndex(),
createIdentifierValue(alias.getText())));
}
return result;
}
@@ -1759,7 +1773,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
}
if (null != ctx.DOT_ASTERISK_()) {
ShorthandProjectionSegment result = new
ShorthandProjectionSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex());
- result.setOwner(new
OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), new IdentifierValue(ctx.colId().getText())));
+ result.setOwner(new
OwnerSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), createIdentifierValue(ctx.colId().getText())));
return result;
}
if (null != ctx.aExpr()) {
@@ -1893,7 +1907,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
} else if (null != ctx.funcAliasClause()) {
if (null != ctx.funcAliasClause().colId()) {
ColIdContext alias = ctx.funcAliasClause().colId();
- result.setAlias(new AliasSegment(alias.start.getStartIndex(),
alias.stop.getStopIndex(), new IdentifierValue(alias.getText())));
+ result.setAlias(new AliasSegment(alias.start.getStartIndex(),
alias.stop.getStopIndex(), createIdentifierValue(alias.getText())));
}
result.getColumns().addAll(createTableFunctionColumns(ctx.funcAliasClause().tableFuncElementList().tableFuncElement()));
}
@@ -1968,7 +1982,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
defaultString = getOriginalText(each.bExpr());
} else {
stringsAsIdentifiers.add(new
XmlNamespaceStringAsIdentifierSegment(each.start.getStartIndex(),
each.stop.getStopIndex(), getOriginalText(each.bExpr()),
- new
IdentifierValue(each.identifier().getText()).getValue(),
getOriginalText(each)));
+
createIdentifierValue(each.identifier().getText()).getValue(),
getOriginalText(each)));
}
}
XmlNamespacesClauseSegment result = new
XmlNamespacesClauseSegment(ctx.xmlNamespaceList().start.getStartIndex(),
ctx.xmlNamespaceList().stop.getStopIndex(), defaultString,
@@ -1994,7 +2008,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
params.add(expression);
}
}
- return new XmlTableColumnSegment(ctx.start.getStartIndex(),
ctx.stop.getStopIndex(), new IdentifierValue(ctx.colId().getText()).getValue(),
+ return new XmlTableColumnSegment(ctx.start.getStartIndex(),
ctx.stop.getStopIndex(),
createIdentifierValue(ctx.colId().getText()).getValue(),
null == ctx.typeName() ? null :
getOriginalText(ctx.typeName()), path, defaultExpr, getOriginalText(ctx));
}
@@ -2008,7 +2022,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
}
FunctionSegment result = new
FunctionSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex(),
functionName, getOriginalText(ctx));
if (null != funcNameContext.colId()) {
- result.setOwner(new
OwnerSegment(funcNameContext.colId().start.getStartIndex(),
funcNameContext.colId().stop.getStopIndex(), new
IdentifierValue(funcNameContext.colId().getText())));
+ result.setOwner(new
OwnerSegment(funcNameContext.colId().start.getStartIndex(),
funcNameContext.colId().stop.getStopIndex(),
createIdentifierValue(funcNameContext.colId().getText())));
} else if (null != funcNameContext.owner() && null !=
funcNameContext.typeFunctionName()) {
result.setOwner((OwnerSegment) visit(funcNameContext.owner()));
}
@@ -2073,7 +2087,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
if (null != ctx.USING()) {
joinTableSource.setUsing(generateUsingColumn(ctx.nameList()));
if (null != ctx.colId()) {
- joinTableSource.setUsingAlias(new
AliasSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), new IdentifierValue(ctx.colId().getText())));
+ joinTableSource.setUsingAlias(new
AliasSegment(ctx.colId().start.getStartIndex(),
ctx.colId().stop.getStopIndex(), createIdentifierValue(ctx.colId().getText())));
}
}
}
@@ -2084,14 +2098,14 @@ public abstract class PostgreSQLStatementVisitor
extends PostgreSQLStatementPars
result.addAll(generateUsingColumn(ctx.nameList()));
}
if (null != ctx.name()) {
- result.add(new ColumnSegment(ctx.name().start.getStartIndex(),
ctx.name().stop.getStopIndex(), new IdentifierValue(ctx.name().getText())));
+ result.add(new ColumnSegment(ctx.name().start.getStartIndex(),
ctx.name().stop.getStopIndex(), createIdentifierValue(ctx.name().getText())));
}
return result;
}
@Override
public ASTNode visitAliasClause(final AliasClauseContext ctx) {
- AliasSegment result = new
AliasSegment(ctx.colId().start.getStartIndex(), ctx.stop.getStopIndex(), new
IdentifierValue(ctx.colId().getText()));
+ AliasSegment result = new
AliasSegment(ctx.colId().start.getStartIndex(), ctx.stop.getStopIndex(),
createIdentifierValue(ctx.colId().getText()));
if (null != ctx.nameList()) {
result.getColumnAliases().addAll(generateAliasColumns(ctx.nameList()));
}
@@ -2104,14 +2118,14 @@ public abstract class PostgreSQLStatementVisitor
extends PostgreSQLStatementPars
result.addAll(generateAliasColumns(ctx.nameList()));
}
if (null != ctx.name()) {
- result.add(new IdentifierValue(ctx.name().getText()));
+ result.add(createIdentifierValue(ctx.name().getText()));
}
return result;
}
private OwnerSegment createTableOwner(final IndirectionContext ctx) {
AttrNameContext attrName = ctx.indirectionEl().attrName();
- return new OwnerSegment(attrName.start.getStartIndex(),
attrName.stop.getStopIndex(), new IdentifierValue(attrName.getText()));
+ return new OwnerSegment(attrName.start.getStartIndex(),
attrName.stop.getStopIndex(), createIdentifierValue(attrName.getText()));
}
@Override
@@ -2237,7 +2251,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
if (null != ctx.attrs()) {
result.combine((CollectionValue<NameSegment>) visit(ctx.attrs()));
}
- result.getValue().add(new
NameSegment(ctx.colId().getStart().getStartIndex(),
ctx.colId().getStop().getStopIndex(), new
IdentifierValue(ctx.colId().getText())));
+ result.getValue().add(new
NameSegment(ctx.colId().getStart().getStartIndex(),
ctx.colId().getStop().getStopIndex(),
createIdentifierValue(ctx.colId().getText())));
return result;
}
@@ -2245,7 +2259,7 @@ public abstract class PostgreSQLStatementVisitor extends
PostgreSQLStatementPars
@SuppressWarnings("unchecked")
public ASTNode visitAttrs(final AttrsContext ctx) {
CollectionValue<NameSegment> result = new CollectionValue<>();
- result.getValue().add(new
NameSegment(ctx.attrName().getStart().getStartIndex(),
ctx.attrName().getStop().getStopIndex(), new
IdentifierValue(ctx.attrName().getText())));
+ result.getValue().add(new
NameSegment(ctx.attrName().getStart().getStartIndex(),
ctx.attrName().getStop().getStopIndex(),
createIdentifierValue(ctx.attrName().getText())));
if (null != ctx.attrs()) {
result.combine((CollectionValue<NameSegment>) visit(ctx.attrs()));
}
diff --git
a/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDALStatementVisitor.java
b/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDALStatementVisitor.java
index 065ea5e8263..64c807ec88c 100644
---
a/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDALStatementVisitor.java
+++
b/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDALStatementVisitor.java
@@ -49,7 +49,6 @@ import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.Se
import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.ShowStatement;
import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.MergeStatement;
import
org.apache.shardingsphere.sql.parser.statement.core.value.collection.CollectionValue;
-import
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
import
org.apache.shardingsphere.sql.parser.statement.postgresql.dal.PostgreSQLCheckpointStatement;
import
org.apache.shardingsphere.sql.parser.statement.postgresql.dal.PostgreSQLLoadStatement;
import
org.apache.shardingsphere.sql.parser.statement.postgresql.dal.PostgreSQLResetParameterStatement;
@@ -142,7 +141,7 @@ public final class PostgreSQLDALStatementVisitor extends
PostgreSQLStatementVisi
CollectionValue<SimpleTableSegment> result = new CollectionValue<>();
for (VacuumRelationContext each : ctx.vacuumRelation()) {
ColIdContext colId = each.qualifiedName().colId();
- TableNameSegment tableName = new
TableNameSegment(colId.start.getStartIndex(), colId.stop.getStopIndex(), new
IdentifierValue(colId.getText()));
+ TableNameSegment tableName = new
TableNameSegment(colId.start.getStartIndex(), colId.stop.getStopIndex(),
createIdentifierValue(colId.getText()));
result.getValue().add(new SimpleTableSegment(tableName));
}
return result;
diff --git
a/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDCLStatementVisitor.java
b/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDCLStatementVisitor.java
index 3bf7d7c1d2c..41f43f693c4 100644
---
a/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDCLStatementVisitor.java
+++
b/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDCLStatementVisitor.java
@@ -171,12 +171,12 @@ public final class PostgreSQLDCLStatementVisitor extends
PostgreSQLStatementVisi
fillNamePrivilegeObjects(result, objectType, ctx.nameList());
}
NameContext name = ctx.name();
- addPrivilegeObject(result, objectType, name,
Collections.singletonList(new IdentifierValue(name.getText())));
+ addPrivilegeObject(result, objectType, name,
Collections.singletonList(createIdentifierValue(name.getText())));
}
private List<IdentifierValue> getAnyNameIdentifiers(final AnyNameContext
ctx) {
List<IdentifierValue> result = new LinkedList<>();
- result.add(new IdentifierValue(ctx.colId().getText()));
+ result.add(createIdentifierValue(ctx.colId().getText()));
if (null != ctx.attrs()) {
fillAttrsIdentifiers(result, ctx.attrs());
}
@@ -187,7 +187,7 @@ public final class PostgreSQLDCLStatementVisitor extends
PostgreSQLStatementVisi
if (null != ctx.attrs()) {
fillAttrsIdentifiers(result, ctx.attrs());
}
- result.add(new IdentifierValue(ctx.attrName().getText()));
+ result.add(createIdentifierValue(ctx.attrName().getText()));
}
private List<IdentifierValue> getFunctionIdentifiers(final
FunctionWithArgtypesContext ctx) {
@@ -198,7 +198,7 @@ public final class PostgreSQLDCLStatementVisitor extends
PostgreSQLStatementVisi
if (null != ctx.typeFuncNameKeyword()) {
result.add(new
IdentifierValue(ctx.typeFuncNameKeyword().getText()));
} else {
- result.add(new IdentifierValue(ctx.colId().getText()));
+ result.add(createIdentifierValue(ctx.colId().getText()));
fillIndirectionIdentifiers(result, ctx.indirection());
}
return result;
@@ -207,14 +207,14 @@ public final class PostgreSQLDCLStatementVisitor extends
PostgreSQLStatementVisi
private List<IdentifierValue> getFunctionIdentifiers(final FuncNameContext
ctx) {
List<IdentifierValue> result = new LinkedList<>();
if (null == ctx.typeFunctionName()) {
- result.add(new IdentifierValue(ctx.colId().getText()));
+ result.add(createIdentifierValue(ctx.colId().getText()));
fillIndirectionIdentifiers(result, ctx.indirection());
return result;
}
if (null != ctx.owner()) {
- result.add(new IdentifierValue(ctx.owner().getText()));
+ result.add(createIdentifierValue(ctx.owner().getText()));
}
- result.add(new IdentifierValue(ctx.typeFunctionName().getText()));
+ result.add(createIdentifierValue(ctx.typeFunctionName().getText()));
return result;
}
@@ -226,7 +226,7 @@ public final class PostgreSQLDCLStatementVisitor extends
PostgreSQLStatementVisi
fillIndirectionIdentifiers(result, ctx.indirection());
}
if (null != ctx.indirectionEl().attrName()) {
- result.add(new
IdentifierValue(ctx.indirectionEl().attrName().getText()));
+
result.add(createIdentifierValue(ctx.indirectionEl().attrName().getText()));
}
}
@@ -252,7 +252,7 @@ public final class PostgreSQLDCLStatementVisitor extends
PostgreSQLStatementVisi
identifiers.add(new IdentifierValue("*"));
} else if (null != ctx.schemaName()) {
identifiers.add((IdentifierValue) visit(ctx.schemaName()));
- identifiers.add(new IdentifierValue(ctx.routineName().getText()));
+
identifiers.add(createIdentifierValue(ctx.routineName().getText()));
} else {
identifiers.add(new IdentifierValue("*"));
if (null != ctx.DOT_ASTERISK_()) {
diff --git
a/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDDLStatementVisitor.java
b/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDDLStatementVisitor.java
index 78a2eaeb9a9..4a99422cb4b 100644
---
a/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDDLStatementVisitor.java
+++
b/parser/sql/engine/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/engine/postgresql/visitor/statement/type/PostgreSQLDDLStatementVisitor.java
@@ -765,7 +765,7 @@ public final class PostgreSQLDDLStatementVisitor extends
PostgreSQLStatementVisi
CollectionValue<ColumnSegment> result = new CollectionValue<>();
for (IndexElemContext each : ctx.indexElem()) {
if (null != each.colId()) {
- result.getValue().add(new
ColumnSegment(each.colId().start.getStartIndex(),
each.colId().stop.getStopIndex(), new IdentifierValue(each.colId().getText())));
+ result.getValue().add(new
ColumnSegment(each.colId().start.getStartIndex(),
each.colId().stop.getStopIndex(),
createIdentifierValue(each.colId().getText())));
}
if (null != each.functionExprWindowless()) {
ExpressionSegment expression = (ExpressionSegment)
visit(each.functionExprWindowless());
@@ -958,7 +958,7 @@ public final class PostgreSQLDDLStatementVisitor extends
PostgreSQLStatementVisi
@Override
public ASTNode visitCreateTrigger(final CreateTriggerContext ctx) {
CreateTriggerStatement result = new
CreateTriggerStatement(getDatabaseType());
- result.setTriggerName(new
FunctionNameSegment(ctx.name().start.getStartIndex(),
ctx.name().stop.getStopIndex(), new IdentifierValue(ctx.name().getText())));
+ result.setTriggerName(new
FunctionNameSegment(ctx.name().start.getStartIndex(),
ctx.name().stop.getStopIndex(), createIdentifierValue(ctx.name().getText())));
result.setTable((SimpleTableSegment) visit(ctx.qualifiedName(0)));
return result;
}
@@ -1120,7 +1120,7 @@ public final class PostgreSQLDDLStatementVisitor extends
PostgreSQLStatementVisi
public ASTNode visitCreateSchema(final CreateSchemaContext ctx) {
CreateSchemaStatement result = new
CreateSchemaStatement(getDatabaseType());
if (null != ctx.createSchemaClauses().colId()) {
- result.setSchemaName(new
IdentifierValue(ctx.createSchemaClauses().colId().getText()));
+
result.setSchemaName(createIdentifierValue(ctx.createSchemaClauses().colId().getText()));
}
if (null != ctx.createSchemaClauses().roleSpec() && null !=
ctx.createSchemaClauses().roleSpec().identifier()) {
result.setUsername((IdentifierValue)
visit(ctx.createSchemaClauses().roleSpec().identifier()));
diff --git
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/value/identifier/IdentifierValue.java
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/value/identifier/IdentifierValue.java
index e15bca764d1..a4241930ae2 100644
---
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/value/identifier/IdentifierValue.java
+++
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/value/identifier/IdentifierValue.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.sql.parser.statement.core.value.identifier;
+import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@@ -38,6 +39,11 @@ public final class IdentifierValue implements
ValueASTNode<String> {
private final QuoteCharacter quoteCharacter;
+ @Getter(AccessLevel.NONE)
+ @EqualsAndHashCode.Exclude
+ @ToString.Exclude
+ private boolean escapeQuoteCharacters;
+
public IdentifierValue(final String text) {
quoteCharacter = QuoteCharacter.getQuoteCharacter(text);
if (QuoteCharacter.BACK_QUOTE == quoteCharacter) {
@@ -47,12 +53,27 @@ public final class IdentifierValue implements
ValueASTNode<String> {
}
}
+ /**
+ * Create identifier value from a decoded double-quoted name.
+ *
+ * @param value decoded identifier name
+ * @return identifier value whose internal double quotes are escaped when
rendered
+ */
+ public static IdentifierValue fromDecodedQuotedName(final String value) {
+ IdentifierValue result = new IdentifierValue(value,
QuoteCharacter.QUOTE);
+ result.escapeQuoteCharacters = true;
+ return result;
+ }
+
/**
* Get value with quote characters, i.e. `table1` or `field1`
*
* @return value with quote characters
*/
public String getValueWithQuoteCharacters() {
- return null == value ? "" : quoteCharacter.wrap(value);
+ if (null == value) {
+ return "";
+ }
+ return quoteCharacter.wrap(escapeQuoteCharacters ? value.replace("\"",
"\"\"") : value);
}
}
diff --git
a/parser/sql/statement/core/src/test/java/org/apache/shardingsphere/sql/parser/statement/core/value/identifier/IdentifierValueTest.java
b/parser/sql/statement/core/src/test/java/org/apache/shardingsphere/sql/parser/statement/core/value/identifier/IdentifierValueTest.java
index 57e271bc4cd..246e5281178 100644
---
a/parser/sql/statement/core/src/test/java/org/apache/shardingsphere/sql/parser/statement/core/value/identifier/IdentifierValueTest.java
+++
b/parser/sql/statement/core/src/test/java/org/apache/shardingsphere/sql/parser/statement/core/value/identifier/IdentifierValueTest.java
@@ -17,7 +17,13 @@
package org.apache.shardingsphere.sql.parser.statement.core.value.identifier;
+import
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@@ -48,6 +54,29 @@ class IdentifierValueTest {
assertThat(new IdentifierValue(text).getValue(),
is("ds_${[1,2]}.t_order"));
}
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("getDecodedQuotedNames")
+ void assertFromDecodedQuotedName(final String name, final String value,
final String expectedQuotedValue) {
+ IdentifierValue actual = IdentifierValue.fromDecodedQuotedName(value);
+ assertThat(actual.getValue(), is(value));
+ assertThat(actual.getValueWithQuoteCharacters(),
is(expectedQuotedValue));
+ }
+
+ private static Stream<Arguments> getDecodedQuotedNames() {
+ return Stream.of(
+ Arguments.of("normal name", "foo_table", "\"foo_table\""),
+ Arguments.of("single interior quote", "foo_\"table",
"\"foo_\"\"table\""),
+ Arguments.of("adjacent interior quotes", "foo_\"\"table",
"\"foo_\"\"\"\"table\""));
+ }
+
+ @Test
+ void assertFromDecodedQuotedNameWithExistingEquality() {
+ IdentifierValue actual =
IdentifierValue.fromDecodedQuotedName("foo_\"table");
+ IdentifierValue expected = new IdentifierValue("foo_\"table",
QuoteCharacter.QUOTE);
+ assertThat(actual, is(expected));
+ assertThat(actual.hashCode(), is(expected.hashCode()));
+ }
+
@Test
void assertGetValueWithQuoteCharactersWithNullValue() {
assertThat(new IdentifierValue(null).getValueWithQuoteCharacters(),
is(""));
@@ -58,4 +87,18 @@ class IdentifierValueTest {
String text = "[foo]";
assertThat(new IdentifierValue(text).getValueWithQuoteCharacters(),
is("[foo]"));
}
+
+ @Test
+ void assertGetValueWithQuoteCharactersWithEscapedRawName() {
+ IdentifierValue actual = new IdentifierValue("\"foo_\"\"table\"");
+ assertThat(actual.getValue(), is("foo_\"\"table"));
+ assertThat(actual.getValueWithQuoteCharacters(),
is("\"foo_\"\"table\""));
+ }
+
+ @Test
+ void assertGetValueWithQuoteCharactersWithEscapedExplicitName() {
+ IdentifierValue actual = new IdentifierValue("foo_\"\"table",
QuoteCharacter.QUOTE);
+ assertThat(actual.getValue(), is("foo_\"\"table"));
+ assertThat(actual.getValueWithQuoteCharacters(),
is("\"foo_\"\"table\""));
+ }
}
diff --git a/test/it/parser/src/main/resources/case/dml/select-expression.xml
b/test/it/parser/src/main/resources/case/dml/select-expression.xml
index 311308a7cbd..465f75de876 100644
--- a/test/it/parser/src/main/resources/case/dml/select-expression.xml
+++ b/test/it/parser/src/main/resources/case/dml/select-expression.xml
@@ -4422,6 +4422,26 @@
</projections>
</select>
+ <select sql-case-id="select_postgresql_escaped_identifiers">
+ <projections start-index="7" stop-index="81">
+ <column-projection name="foo"bar" start-delimiter="""
end-delimiter=""" start-index="7" stop-index="16" />
+ <column-projection name="foo""bar"
start-delimiter=""" end-delimiter=""" start-index="19"
stop-index="30" />
+ <column-projection name="data" start-delimiter="""
end-delimiter=""" start-index="33" stop-index="51" />
+ <column-projection name="data" start-delimiter="""
end-delimiter=""" start-index="54" stop-index="81" />
+ </projections>
+ </select>
+
+ <select sql-case-id="select_postgresql_unicode_identifier_controls">
+ <projections start-index="7" stop-index="211">
+ <column-projection name="data" start-delimiter="""
end-delimiter=""" start-index="7" stop-index="35" />
+ <column-projection name="data" start-delimiter="""
end-delimiter=""" start-index="38" stop-index="69" />
+ <column-projection name="data" start-delimiter="""
end-delimiter=""" start-index="72" stop-index="103" />
+ <column-projection name="data" start-delimiter="""
end-delimiter=""" start-index="106" stop-index="139" />
+ <column-projection name="data" start-delimiter="""
end-delimiter=""" start-index="142" stop-index="179" />
+ <column-projection name="data" start-delimiter="""
end-delimiter=""" start-index="182" stop-index="211" />
+ </projections>
+ </select>
+
<select sql-case-id="select_postgresql_unicode_strings">
<projections start-index="7" stop-index="69">
<expression-projection start-index="7" stop-index="25"
text="U&'d\0061t\+000061'">
diff --git
a/test/it/parser/src/main/resources/sql/supported/dml/select-expression.xml
b/test/it/parser/src/main/resources/sql/supported/dml/select-expression.xml
index 1a636901b35..de991a052a0 100644
--- a/test/it/parser/src/main/resources/sql/supported/dml/select-expression.xml
+++ b/test/it/parser/src/main/resources/sql/supported/dml/select-expression.xml
@@ -188,6 +188,8 @@
<sql-case id="select_postgresql_numeric_literals" value="SELECT 1_000, .5,
4., 4664.E+5, 0x2a, 0X_FF, 0o_77, 0O17, 0b_1010, 0B11, -0x2A, +0o10,
0xffffffffffffffffffff" db-types="PostgreSQL" case-types="literal" />
<sql-case id="select_postgresql_bit_literals" value="SELECT B'1010', b'',
X'2a', x'', B'10' '01', X'2' 'a'" db-types="PostgreSQL"
case-types="literal" />
<sql-case id="select_postgresql_string_continuation" value="SELECT
'foo' 'bar', E'foo' 'bar', 'a\b', 'a''b'" db-types="PostgreSQL"
case-types="literal" />
+ <sql-case id="select_postgresql_escaped_identifiers" value="SELECT
"foo""bar", "foo""""bar",
U&"d\0061t\+000061", U&"d!0061t!0061" UESCAPE '!'"
db-types="PostgreSQL" case-types="literal" />
+ <sql-case id="select_postgresql_unicode_identifier_controls" value="SELECT
U&"d!0061t!0061" UESCAPE E'!', U&"d!0061t!0061"
UESCAPE E'\041', U&"d!0061t!0061" UESCAPE E'\x21',
U&"d!0061t!0061" UESCAPE E'\u0021',
U&"d!0061t!0061" UESCAPE E'\U00000021',
U&"d\0061t\0061" UESCAPE E'\\'" db-types="PostgreSQL"
case-types="literal" />
<sql-case id="select_postgresql_unicode_strings" value="SELECT
U&'d\0061t\+000061', U&'d!0061t!0061' UESCAPE '!', U&'foo\\bar'"
db-types="PostgreSQL" case-types="literal" />
<sql-case id="select_postgresql_custom_binary_operators" value="SELECT 1
*= 2, 1 *<= 2, 1 ^@ 2, 1 ~<~ 2, 1 <<< 2, 1 === 2, 1 @- 2, 1
OPERATOR(pg_catalog.+) 2" db-types="PostgreSQL" case-types="literal" />
<sql-case id="select_postgresql_custom_prefix_operators" value="SELECT @
?, |/ ?, !! ?, ~ ?" db-types="PostgreSQL" case-types="placeholder" />
diff --git
a/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/postgresql/InternalPostgreSQLParserIT.java
b/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/postgresql/InternalPostgreSQLParserIT.java
index 1bdef0860f7..881cea64ad9 100644
---
a/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/postgresql/InternalPostgreSQLParserIT.java
+++
b/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/postgresql/InternalPostgreSQLParserIT.java
@@ -25,6 +25,7 @@ import
org.apache.shardingsphere.sql.parser.statement.core.enums.JoinType;
import
org.apache.shardingsphere.sql.parser.statement.core.extractor.ColumnExtractor;
import
org.apache.shardingsphere.sql.parser.statement.core.extractor.ExpressionExtractor;
import
org.apache.shardingsphere.sql.parser.statement.core.extractor.TableExtractor;
+import
org.apache.shardingsphere.sql.parser.statement.core.segment.dcl.PrivilegeObjectSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.assignment.ColumnAssignmentSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.ColumnSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BinaryOperationExpression;
@@ -47,8 +48,13 @@ import
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table
import
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
import
org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.type.TableSQLStatementAttribute;
+import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.AnalyzeTableStatement;
+import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dcl.GrantStatement;
import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.FetchStatement;
import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.MoveStatement;
+import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.index.CreateIndexStatement;
+import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.schema.CreateSchemaStatement;
+import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.trigger.CreateTriggerStatement;
import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.DeleteStatement;
import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.InsertStatement;
import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
@@ -79,6 +85,117 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
@InternalSQLParserITSettings("PostgreSQL")
class InternalPostgreSQLParserIT extends InternalSQLParserIT {
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("getQuotedIdentifierCases")
+ void assertQuotedIdentifierRoundTrip(final String name, final String
identifier, final String expectedValue, final String expectedQuotedValue) {
+ String sql = "SELECT " + identifier + " AS " + identifier + " FROM " +
identifier + "." + identifier + " AS " + identifier;
+ SelectStatement actual = (SelectStatement) new
SQLStatementVisitorEngine("PostgreSQL").visit(new SQLParserEngine("PostgreSQL",
new CacheOption(128, 1024L)).parse(sql, false));
+ ColumnProjectionSegment actualProjection = (ColumnProjectionSegment)
actual.getProjections().getProjections().iterator().next();
+ SimpleTableSegment actualTable = (SimpleTableSegment)
actual.getFrom().get();
+ ColumnSegment actualColumn = actualProjection.getColumn();
+ assertThat(sql.substring(actualColumn.getStartIndex(),
actualColumn.getStopIndex() + 1), is(identifier));
+ assertThat(sql.substring(actualTable.getTableName().getStartIndex(),
actualTable.getTableName().getStopIndex() + 1), is(identifier));
+ Collection<IdentifierValue> actualIdentifiers =
Arrays.asList(actualColumn.getIdentifier(), actualProjection.getAlias().get(),
actualTable.getOwner().get().getIdentifier(),
+ actualTable.getTableName().getIdentifier(),
actualTable.getAlias().get());
+ for (IdentifierValue each : actualIdentifiers) {
+ assertThat(each.getValue(), is(expectedValue));
+ assertThat(each.getValueWithQuoteCharacters(),
is(expectedQuotedValue));
+ String roundTripSQL = "SELECT " +
each.getValueWithQuoteCharacters();
+ SelectStatement actualRoundTrip = (SelectStatement) new
SQLStatementVisitorEngine("PostgreSQL")
+ .visit(new SQLParserEngine("PostgreSQL", new
CacheOption(128, 1024L)).parse(roundTripSQL, false));
+ assertThat(((ColumnProjectionSegment)
actualRoundTrip.getProjections().getProjections().iterator().next()).getColumn().getIdentifier().getValue(),
is(expectedValue));
+ }
+ }
+
+ private static Stream<Arguments> getQuotedIdentifierCases() {
+ return Stream.of(
+ Arguments.of("one embedded quote", "\"foo\"\"bar\"",
"foo\"bar", "\"foo\"\"bar\""),
+ Arguments.of("two adjacent embedded quotes",
"\"foo\"\"\"\"bar\"", "foo\"\"bar", "\"foo\"\"\"\"bar\""),
+ Arguments.of("default Unicode escape", "U&\"foo\\0061\"",
"fooa", "\"fooa\""),
+ Arguments.of("six digit Unicode escape", "U&\"foo\\+01F600\"",
"foo😀", "\"foo😀\""),
+ Arguments.of("surrogate pair Unicode escape",
"U&\"foo\\D83D\\DE00\"", "foo😀", "\"foo😀\""),
+ Arguments.of("literal Unicode escape character",
"U&\"foo\\\\bar\"", "foo\\bar", "\"foo\\bar\""),
+ Arguments.of("custom Unicode escape", "U&\"foo!0061\" UESCAPE
'!'", "fooa", "\"fooa\""),
+ Arguments.of("escaped custom Unicode escape", "U&\"foo!0061\"
UESCAPE E'\\041'", "fooa", "\"fooa\""),
+ Arguments.of("escaped backslash Unicode escape",
"U&\"foo\\0061\" UESCAPE E'\\\\'", "fooa", "\"fooa\""),
+ Arguments.of("Unicode embedded quote", "U&\"foo\\0022bar\"",
"foo\"bar", "\"foo\"\"bar\""),
+ Arguments.of("Unicode adjacent decoded quotes",
"U&\"foo\\0022\\0022bar\"", "foo\"\"bar", "\"foo\"\"\"\"bar\""),
+ Arguments.of("Unicode decoded and literal quotes",
"U&\"foo!0022\"\"bar\" UESCAPE '!'", "foo\"\"bar", "\"foo\"\"\"\"bar\""));
+ }
+
+ @Test
+ void assertCreateSchemaQuotedIdentifierRoundTrip() {
+ String sql = "CREATE SCHEMA U&\"foo!0022\"\"schema\" UESCAPE '!'";
+ CreateSchemaStatement actual = (CreateSchemaStatement) new
SQLStatementVisitorEngine("PostgreSQL").visit(new SQLParserEngine("PostgreSQL",
new CacheOption(128, 1024L)).parse(sql, false));
+ IdentifierValue actualIdentifier = actual.getSchemaName().get();
+ assertThat(actualIdentifier.getValue(), is("foo\"\"schema"));
+ String roundTripSQL = "CREATE SCHEMA " +
actualIdentifier.getValueWithQuoteCharacters();
+ CreateSchemaStatement actualRoundTrip = (CreateSchemaStatement) new
SQLStatementVisitorEngine("PostgreSQL")
+ .visit(new SQLParserEngine("PostgreSQL", new CacheOption(128,
1024L)).parse(roundTripSQL, false));
+ assertThat(actualRoundTrip.getSchemaName().get().getValue(),
is("foo\"\"schema"));
+ }
+
+ @Test
+ void assertCreateIndexQuotedColumnRoundTrip() {
+ String identifier = "U&\"foo\\0022\\0022column\"";
+ String sql = "CREATE INDEX foo_index ON foo_table (" + identifier +
")";
+ CreateIndexStatement actual = (CreateIndexStatement) new
SQLStatementVisitorEngine("PostgreSQL").visit(new SQLParserEngine("PostgreSQL",
new CacheOption(128, 1024L)).parse(sql, false));
+ ColumnSegment actualColumn = actual.getColumns().iterator().next();
+ assertThat(actualColumn.getIdentifier().getValue(),
is("foo\"\"column"));
+ assertThat(sql.substring(actualColumn.getStartIndex(),
actualColumn.getStopIndex() + 1), is(identifier));
+ String roundTripSQL = "CREATE INDEX foo_index ON foo_table (" +
actualColumn.getIdentifier().getValueWithQuoteCharacters() + ")";
+ CreateIndexStatement actualRoundTrip = (CreateIndexStatement) new
SQLStatementVisitorEngine("PostgreSQL")
+ .visit(new SQLParserEngine("PostgreSQL", new CacheOption(128,
1024L)).parse(roundTripSQL, false));
+
assertThat(actualRoundTrip.getColumns().iterator().next().getIdentifier().getValue(),
is("foo\"\"column"));
+ }
+
+ @Test
+ void assertCreateTriggerQuotedIdentifierRoundTrip() {
+ String triggerClause = " BEFORE INSERT ON foo_table FOR EACH ROW
EXECUTE FUNCTION foo_function()";
+ String sql = "CREATE TRIGGER U&\"foo!0022!0022trigger\" UESCAPE '!'" +
triggerClause;
+ CreateTriggerStatement actual = (CreateTriggerStatement) new
SQLStatementVisitorEngine("PostgreSQL").visit(new SQLParserEngine("PostgreSQL",
new CacheOption(128, 1024L)).parse(sql, false));
+ IdentifierValue actualIdentifier =
actual.getTriggerName().get().getIdentifier();
+ assertThat(actualIdentifier.getValue(), is("foo\"\"trigger"));
+ String roundTripSQL = "CREATE TRIGGER " +
actualIdentifier.getValueWithQuoteCharacters() + triggerClause;
+ CreateTriggerStatement actualRoundTrip = (CreateTriggerStatement) new
SQLStatementVisitorEngine("PostgreSQL")
+ .visit(new SQLParserEngine("PostgreSQL", new CacheOption(128,
1024L)).parse(roundTripSQL, false));
+
assertThat(actualRoundTrip.getTriggerName().get().getIdentifier().getValue(),
is("foo\"\"trigger"));
+ }
+
+ @Test
+ void assertAnalyzeQuotedTableRoundTrip() {
+ String identifier = "U&\"foo!0022\"\"table\" UESCAPE '!'";
+ String sql = "ANALYZE " + identifier;
+ AnalyzeTableStatement actual = (AnalyzeTableStatement) new
SQLStatementVisitorEngine("PostgreSQL").visit(new SQLParserEngine("PostgreSQL",
new CacheOption(128, 1024L)).parse(sql, false));
+ SimpleTableSegment actualTable = actual.getTables().iterator().next();
+ assertThat(actualTable.getTableName().getIdentifier().getValue(),
is("foo\"\"table"));
+ assertThat(sql.substring(actualTable.getStartIndex(),
actualTable.getStopIndex() + 1), is(identifier));
+ String roundTripSQL = "ANALYZE " +
actualTable.getTableName().getIdentifier().getValueWithQuoteCharacters();
+ AnalyzeTableStatement actualRoundTrip = (AnalyzeTableStatement) new
SQLStatementVisitorEngine("PostgreSQL")
+ .visit(new SQLParserEngine("PostgreSQL", new CacheOption(128,
1024L)).parse(roundTripSQL, false));
+
assertThat(actualRoundTrip.getTables().iterator().next().getTableName().getIdentifier().getValue(),
is("foo\"\"table"));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("getQuotedPrivilegeObjectCases")
+ void assertQuotedPrivilegeObjectRoundTrip(final String name, final String
sqlPrefix, final String objectName, final String sqlSuffix, final
Collection<String> expectedIdentifiers) {
+ String sql = sqlPrefix + objectName + sqlSuffix;
+ GrantStatement actual = (GrantStatement) new
SQLStatementVisitorEngine("PostgreSQL").visit(new SQLParserEngine("PostgreSQL",
new CacheOption(128, 1024L)).parse(sql, false));
+ PrivilegeObjectSegment actualObject =
actual.getPrivilegeObjects().iterator().next();
+
assertThat(actualObject.getIdentifiers().stream().map(IdentifierValue::getValue).collect(Collectors.toList()),
is(expectedIdentifiers));
+ String roundTripSQL = sqlPrefix +
actualObject.getIdentifiers().stream().map(IdentifierValue::getValueWithQuoteCharacters).collect(Collectors.joining("."))
+ sqlSuffix;
+ GrantStatement actualRoundTrip = (GrantStatement) new
SQLStatementVisitorEngine("PostgreSQL").visit(new SQLParserEngine("PostgreSQL",
new CacheOption(128, 1024L)).parse(roundTripSQL, false));
+
assertThat(actualRoundTrip.getPrivilegeObjects().iterator().next().getIdentifiers().stream().map(IdentifierValue::getValue).collect(Collectors.toList()),
is(expectedIdentifiers));
+ }
+
+ private static Stream<Arguments> getQuotedPrivilegeObjectCases() {
+ return Stream.of(
+ Arguments.of("schema name", "GRANT ALL ON SCHEMA ",
"U&\"foo\\0061\"", " TO foo_role", Collections.singletonList("fooa")),
+ Arguments.of("type name and attribute", "GRANT ALL ON TYPE ",
"U&\"foo\\0061\".U&\"bar!0022!0022\" UESCAPE '!'", " TO foo_role",
Arrays.asList("fooa", "bar\"\"")),
+ Arguments.of("function owner and name", "GRANT ALL ON FUNCTION
", "U&\"foo\\0061\".U&\"bar!0062\" UESCAPE '!'", "(integer) TO foo_role",
Arrays.asList("fooa", "barb")),
+ Arguments.of("function indirection", "GRANT ALL ON FUNCTION ",
"U&\"foo\\0061\".U&\"bar!0062\" UESCAPE '!'", " TO foo_role",
Arrays.asList("fooa", "barb")));
+ }
+
@ParameterizedTest(name = "{0}")
@MethodSource("getUnsupportedFilterExpressions")
void assertUnsupportedFilter(final String expression) {