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 464662d666b Add Oracle alternative quoted literal parser support
(#38895)
464662d666b is described below
commit 464662d666bb8b67b618dc336887850ae146199c
Author: Liang Zhang <[email protected]>
AuthorDate: Tue Jun 23 19:13:13 2026 +0800
Add Oracle alternative quoted literal parser support (#38895)
* Add Oracle alternative quoted literal parser support
* Add Oracle alternative quoted literal parser support
* Add Oracle alternative quoted literal parser support
---
.../src/main/antlr4/imports/oracle/Literals.g4 | 72 ++++++++++++++++++++++
.../visitor/statement/OracleStatementVisitor.java | 67 ++++++++++++++++++--
.../parser/src/main/resources/case/dml/select.xml | 52 ++++++++++++++++
.../main/resources/sql/supported/dml/select.xml | 3 +
4 files changed, 189 insertions(+), 5 deletions(-)
diff --git
a/parser/sql/engine/dialect/oracle/src/main/antlr4/imports/oracle/Literals.g4
b/parser/sql/engine/dialect/oracle/src/main/antlr4/imports/oracle/Literals.g4
index c4e8efacdd0..5af239ae56e 100644
---
a/parser/sql/engine/dialect/oracle/src/main/antlr4/imports/oracle/Literals.g4
+++
b/parser/sql/engine/dialect/oracle/src/main/antlr4/imports/oracle/Literals.g4
@@ -25,12 +25,84 @@ IDENTIFIER_
STRING_
: SINGLE_QUOTED_TEXT
+ | ALTERNATIVE_QUOTED_TEXT
;
SINGLE_QUOTED_TEXT
: (SQ_ ('\'\'' | ~'\'')* SQ_)
;
+ALTERNATIVE_QUOTED_TEXT
+ : Q SQ_ LBT_ .*? RBT_ SQ_
+ | Q SQ_ RBT_ .*? RBT_ SQ_
+ | Q SQ_ LBE_ .*? RBE_ SQ_
+ | Q SQ_ RBE_ .*? RBE_ SQ_
+ | Q SQ_ LP_ .*? RP_ SQ_
+ | Q SQ_ RP_ .*? RP_ SQ_
+ | Q SQ_ LT_ .*? GT_ SQ_
+ | Q SQ_ NOT_ .*? NOT_ SQ_
+ | Q SQ_ TILDE_ .*? TILDE_ SQ_
+ | Q SQ_ CARET_ .*? CARET_ SQ_
+ | Q SQ_ VERTICAL_BAR_ .*? VERTICAL_BAR_ SQ_
+ | Q SQ_ AMPERSAND_ .*? AMPERSAND_ SQ_
+ | Q SQ_ MOD_ .*? MOD_ SQ_
+ | Q SQ_ COLON_ .*? COLON_ SQ_
+ | Q SQ_ PLUS_ .*? PLUS_ SQ_
+ | Q SQ_ MINUS_ .*? MINUS_ SQ_
+ | Q SQ_ ASTERISK_ .*? ASTERISK_ SQ_
+ | Q SQ_ SLASH_ .*? SLASH_ SQ_
+ | Q SQ_ BACKSLASH_ .*? BACKSLASH_ SQ_
+ | Q SQ_ DOT_ .*? DOT_ SQ_
+ | Q SQ_ EQ_ .*? EQ_ SQ_
+ | Q SQ_ GT_ .*? GT_ SQ_
+ | Q SQ_ LT_ .*? LT_ SQ_
+ | Q SQ_ POUND_ .*? POUND_ SQ_
+ | Q SQ_ COMMA_ .*? COMMA_ SQ_
+ | Q SQ_ DQ_ .*? DQ_ SQ_
+ | Q SQ_ BQ_ .*? BQ_ SQ_
+ | Q SQ_ QUESTION_ .*? QUESTION_ SQ_
+ | Q SQ_ AT_ .*? AT_ SQ_
+ | Q SQ_ SEMI_ .*? SEMI_ SQ_
+ | Q SQ_ DOLLAR_ .*? DOLLAR_ SQ_
+ | Q SQ_ UL_ .*? UL_ SQ_
+ | Q SQ_ A .*? A SQ_
+ | Q SQ_ B .*? B SQ_
+ | Q SQ_ C .*? C SQ_
+ | Q SQ_ D .*? D SQ_
+ | Q SQ_ E .*? E SQ_
+ | Q SQ_ F .*? F SQ_
+ | Q SQ_ G .*? G SQ_
+ | Q SQ_ H .*? H SQ_
+ | Q SQ_ I .*? I SQ_
+ | Q SQ_ J .*? J SQ_
+ | Q SQ_ K .*? K SQ_
+ | Q SQ_ L .*? L SQ_
+ | Q SQ_ M .*? M SQ_
+ | Q SQ_ N .*? N SQ_
+ | Q SQ_ O .*? O SQ_
+ | Q SQ_ P .*? P SQ_
+ | Q SQ_ Q .*? Q SQ_
+ | Q SQ_ R .*? R SQ_
+ | Q SQ_ S .*? S SQ_
+ | Q SQ_ T .*? T SQ_
+ | Q SQ_ U .*? U SQ_
+ | Q SQ_ V .*? V SQ_
+ | Q SQ_ W .*? W SQ_
+ | Q SQ_ X .*? X SQ_
+ | Q SQ_ Y .*? Y SQ_
+ | Q SQ_ Z .*? Z SQ_
+ | Q SQ_ '0' .*? '0' SQ_
+ | Q SQ_ '1' .*? '1' SQ_
+ | Q SQ_ '2' .*? '2' SQ_
+ | Q SQ_ '3' .*? '3' SQ_
+ | Q SQ_ '4' .*? '4' SQ_
+ | Q SQ_ '5' .*? '5' SQ_
+ | Q SQ_ '6' .*? '6' SQ_
+ | Q SQ_ '7' .*? '7' SQ_
+ | Q SQ_ '8' .*? '8' SQ_
+ | Q SQ_ '9' .*? '9' SQ_
+ ;
+
DOUBLE_QUOTED_TEXT
: (DQ_ ( '\\'. | '""' | ~('"'| '\\') )* DQ_)
;
diff --git
a/parser/sql/engine/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/engine/oracle/visitor/statement/OracleStatementVisitor.java
b/parser/sql/engine/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/engine/oracle/visitor/statement/OracleStatementVisitor.java
index 347a89401c3..697f2eb1a2c 100644
---
a/parser/sql/engine/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/engine/oracle/visitor/statement/OracleStatementVisitor.java
+++
b/parser/sql/engine/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/engine/oracle/visitor/statement/OracleStatementVisitor.java
@@ -254,6 +254,9 @@ public abstract class OracleStatementVisitor extends
OracleStatementBaseVisitor<
if (advanceInBlockComment(sql, state)) {
continue;
}
+ if (advanceInAlternativeQuotedText(sql, state)) {
+ continue;
+ }
if (advanceInOrToggleStringLiteral(sql, state)) {
continue;
}
@@ -294,6 +297,55 @@ public abstract class OracleStatementVisitor extends
OracleStatementBaseVisitor<
return true;
}
+ private boolean advanceInAlternativeQuotedText(final String sql, final
ParameterMarkerScanState state) {
+ if (state.inStringLiteral) {
+ return false;
+ }
+ int quoteIndicatorIndex =
isNationalOrUnicodeLiteralIndicator(sql.charAt(state.index)) ? state.index + 1
: state.index;
+ if (quoteIndicatorIndex + 2 >= sql.length() ||
!isAlternativeQuoteIndicator(sql.charAt(quoteIndicatorIndex)) || '\'' !=
sql.charAt(quoteIndicatorIndex + 1)) {
+ return false;
+ }
+ char quoteDelimiter = sql.charAt(quoteIndicatorIndex + 2);
+ if (isInvalidAlternativeQuoteDelimiter(quoteDelimiter)) {
+ return false;
+ }
+ char closeDelimiter =
getAlternativeQuoteCloseDelimiter(quoteDelimiter);
+ for (int index = quoteIndicatorIndex + 3; index + 1 < sql.length();
index++) {
+ if (closeDelimiter == sql.charAt(index) && '\'' ==
sql.charAt(index + 1)) {
+ state.index = index + 2;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isAlternativeQuoteIndicator(final char ch) {
+ return 'q' == ch || 'Q' == ch;
+ }
+
+ private boolean isNationalOrUnicodeLiteralIndicator(final char ch) {
+ return 'n' == ch || 'N' == ch || 'u' == ch || 'U' == ch;
+ }
+
+ private boolean isInvalidAlternativeQuoteDelimiter(final char
quoteDelimiter) {
+ return ' ' == quoteDelimiter || '\t' == quoteDelimiter || '\r' ==
quoteDelimiter || '\n' == quoteDelimiter;
+ }
+
+ private char getAlternativeQuoteCloseDelimiter(final char quoteDelimiter) {
+ switch (quoteDelimiter) {
+ case '[':
+ return ']';
+ case '{':
+ return '}';
+ case '(':
+ return ')';
+ case '<':
+ return '>';
+ default:
+ return quoteDelimiter;
+ }
+ }
+
private boolean advanceInOrToggleStringLiteral(final String sql, final
ParameterMarkerScanState state) {
if (sql.charAt(state.index) != '\'') {
return false;
@@ -372,11 +424,16 @@ public abstract class OracleStatementVisitor extends
OracleStatementBaseVisitor<
@Override
public final ASTNode visitStringLiterals(final StringLiteralsContext ctx) {
- if (null != ctx.STRING_()) {
- return new StringLiteralValue(ctx.getText());
- } else {
- return new StringLiteralValue(ctx.getText().substring(1));
- }
+ String text = null == ctx.STRING_() ? ctx.getText().substring(1) :
ctx.getText();
+ return new StringLiteralValue(isAlternativeQuotedText(text) ?
convertAlternativeQuotedText(text) : text);
+ }
+
+ private boolean isAlternativeQuotedText(final String text) {
+ return text.length() > 4 &&
isAlternativeQuoteIndicator(text.charAt(0)) && '\'' == text.charAt(1);
+ }
+
+ private String convertAlternativeQuotedText(final String text) {
+ return "'" + text.substring(3, text.length() - 2) + "'";
}
@Override
diff --git a/test/it/parser/src/main/resources/case/dml/select.xml
b/test/it/parser/src/main/resources/case/dml/select.xml
index 6637b0c95a6..9460bedcfd1 100644
--- a/test/it/parser/src/main/resources/case/dml/select.xml
+++ b/test/it/parser/src/main/resources/case/dml/select.xml
@@ -8730,6 +8730,58 @@
</from>
</select>
+ <select sql-case-id="select_national_alternative_quoted_literal_oracle">
+ <projections start-index="7" stop-index="25" literal-start-index="7"
literal-stop-index="25">
+ <expression-projection text="national text" start-index="7"
stop-index="25" literal-start-index="7"
+ literal-stop-index="25">
+ <literalText>national text</literalText>
+ <expr>
+ <literal-expression value="national text" start-index="7"
stop-index="25" literal-start-index="7"
+ literal-stop-index="25"/>
+ </expr>
+ </expression-projection>
+ </projections>
+ <from>
+ <simple-table name="DUAL" start-index="32" stop-index="35"
literal-start-index="32"
+ literal-stop-index="35"/>
+ </from>
+ </select>
+
+ <select
sql-case-id="select_alternative_quoted_literal_with_right_delimiter_oracle">
+ <projections start-index="7" stop-index="26" literal-start-index="7"
literal-stop-index="26">
+ <expression-projection text="right delimiter" start-index="7"
stop-index="26" literal-start-index="7"
+ literal-stop-index="26">
+ <literalText>right delimiter</literalText>
+ <expr>
+ <literal-expression value="right delimiter"
start-index="7" stop-index="26" literal-start-index="7"
+ literal-stop-index="26"/>
+ </expr>
+ </expression-projection>
+ </projections>
+ <from>
+ <simple-table name="DUAL" start-index="33" stop-index="36"
literal-start-index="33"
+ literal-stop-index="36"/>
+ </from>
+ </select>
+
+ <select
sql-case-id="select_alternative_quoted_literal_with_parameter_marker_oracle"
parameters="'OK'">
+ <projections start-index="7" stop-index="43" literal-start-index="7"
literal-stop-index="46">
+ <expression-projection text="John's ?" start-index="7"
stop-index="30" literal-start-index="7"
+ literal-stop-index="30" alias="content">
+ <literalText>John's ?</literalText>
+ <expr>
+ <literal-expression value="John's ?" start-index="7"
stop-index="19" literal-start-index="7"
+ literal-stop-index="19"/>
+ </expr>
+ </expression-projection>
+ <parameter-marker-expression parameter-index="0" start-index="33"
stop-index="43" literal-stop-index="46" />
+ </projections>
+ <from>
+ <simple-table name="DUAL" start-index="50" stop-index="53"
literal-start-index="53"
+ literal-stop-index="56"/>
+ </from>
+ </select>
+
<select sql-case-id="select_wm_concat_function1">
<projections start-index="7" stop-index="72">
<expression-projection alias="ID" text="TO_CHAR(WM_CONCAT(DISTINCT
TEST_ID) OVER(PARTITION BY TEST_ID))" start-index="7" stop-index="72">
diff --git a/test/it/parser/src/main/resources/sql/supported/dml/select.xml
b/test/it/parser/src/main/resources/sql/supported/dml/select.xml
index ec822be77b1..8fea5cd47e4 100644
--- a/test/it/parser/src/main/resources/sql/supported/dml/select.xml
+++ b/test/it/parser/src/main/resources/sql/supported/dml/select.xml
@@ -301,6 +301,9 @@
<sql-case id="select_numeric_operator" value="SELECT 5! AS RESULT;"
db-types="openGauss" />
<sql-case id="select_with_custom_distinct_function" value="select
custom_concat(distinct a.P1) From TEST a;" db-types="Oracle" />
<sql-case id="select_with_unicode_string" value="SELECT u'test', U'test'
from DUAL" db-types="Oracle" />
+ <sql-case id="select_national_alternative_quoted_literal_oracle"
value="SELECT nq'[national text]' FROM DUAL" db-types="Oracle" />
+ <sql-case
id="select_alternative_quoted_literal_with_right_delimiter_oracle"
value="SELECT q']right delimiter]' FROM DUAL" db-types="Oracle" />
+ <sql-case
id="select_alternative_quoted_literal_with_parameter_marker_oracle"
value="SELECT q'[John's ?]' AS content, ? AS status FROM DUAL"
db-types="Oracle" case-types="PLACEHOLDER" />
<sql-case id="select_wm_concat_function1" value="SELECT
TO_CHAR(WM_CONCAT(DISTINCT TEST_ID) OVER(PARTITION BY TEST_ID)) ID FROM
TEST_TABLE" db-types="Oracle" />
<sql-case id="select_wm_concat_function2" value="SELECT WM_CONCAT(NAME)
NAME FROM TEST_TABLE" db-types="Oracle" />
<sql-case id="select_wm_concat_function3" value="SELECT
REPLACE(WM_CONCAT(NAME),',','|') FROM TEST_TABLE" db-types="Oracle" />