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" />

Reply via email to