You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2016/09/14 11:10:34 UTC

[18/50] ignite git commit: IGNITE-3741: ODBC: Added character escape support to expression parser. This closes #1004.

IGNITE-3741: ODBC: Added character escape support to expression parser. This closes #1004.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/008cf644
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/008cf644
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/008cf644

Branch: refs/heads/ignite-3661
Commit: 008cf64429f40635e396a71f2c0aaf184077ff2b
Parents: e353301
Author: Andrey V. Mashenkov <an...@gmail.com>
Authored: Mon Sep 5 15:17:53 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Mon Sep 5 15:17:53 2016 +0300

----------------------------------------------------------------------
 .../processors/odbc/escape/OdbcEscapeType.java  |  9 ++-
 .../processors/odbc/escape/OdbcEscapeUtils.java | 53 ++++++------
 .../odbc/OdbcEscapeSequenceSelfTest.java        | 85 +++++++++++++++++++-
 3 files changed, 119 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/008cf644/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeType.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeType.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeType.java
index c7e3234..44d8361 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeType.java
@@ -42,8 +42,11 @@ public enum OdbcEscapeType {
     /** GUID. */
     GUID("guid", true, false),
 
-    /** LIKE clause. */
-    LIKE("\'", false, true);
+    /** LIKE escape character clause. */
+    ESCAPE_WO_TOKEN("\'", false, false),
+
+    /** LIKE escape character clause. */
+    ESCAPE("escape", true, false);
 
     /** Values in convenient order. */
     private static final OdbcEscapeType[] VALS = new OdbcEscapeType[] {
@@ -51,7 +54,7 @@ public enum OdbcEscapeType {
         DATE, TIMESTAMP, // Date and timestamp are relatively frequent as well; also TS must go before T.
         OUTER_JOIN,      // Joins are less frequent,
         CALL,            // Procedure calls are less frequent than joins.
-        LIKE, TIME, GUID // LIKE, TIME and GUID are even less frequent.
+        ESCAPE_WO_TOKEN, ESCAPE, TIME, GUID // LIKE, TIME and GUID are even less frequent.
     };
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/008cf644/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeUtils.java
index 88afc52..bbf19c7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeUtils.java
@@ -76,12 +76,9 @@ public class OdbcEscapeUtils {
         while (curPos < text.length()) {
             char curChar = text.charAt(curPos);
 
-            if (curChar == '\'') {
-                if (!insideLiteral)
-                    insideLiteral = true;
-                else if (text.charAt(curPos - 1) != '\\')
-                    insideLiteral = false;
-            }
+            if (curChar == '\'')
+                /* Escaped quote in odbc is two successive singe quotes. They'll flip flag twice without side-effect. */
+                insideLiteral = !insideLiteral;
             else if (!insideLiteral) {
                 if (curChar == '{') {
                     if (openPos == -1) {
@@ -173,11 +170,7 @@ public class OdbcEscapeUtils {
 
             OdbcEscapeToken token = parseToken(text, startPos, len);
 
-            if (token.type().standard())
-                return parseStandardExpression(text, startPos, len, token);
-            else
-                throw new IgniteException("Unsupported escape sequence token [text=" +
-                    substring(text, startPos, len) + ", token=" + token.type().body() + ']');
+            return parseEscapeSequence(text, startPos, len, token);
         }
         else {
             // Nothing to escape, return original string.
@@ -209,20 +202,17 @@ public class OdbcEscapeUtils {
 
         for (OdbcEscapeType typ : OdbcEscapeType.sortedValues()) {
             if (text.startsWith(typ.body(), pos)) {
-                pos += typ.body().length();
+                if (typ.standard())
+                    pos += typ.body().length();
 
-                if (typ == OdbcEscapeType.LIKE)
-                    throw new IgniteException("LIKE escape sequence is not supported yet.");
-                else {
-                    empty = (startPos + len == pos + 1);
+                empty = (startPos + len == pos + 1);
 
-                    if (!empty && typ.standard()) {
-                        char charAfter = text.charAt(pos);
+                if (!empty && typ.standard()) {
+                    char charAfter = text.charAt(pos);
 
-                        if (!Character.isWhitespace(charAfter))
-                            throw new IgniteException("Unexpected escape sequence token: " +
-                                substring(text, startPos, len));
-                    }
+                    if (!Character.isWhitespace(charAfter))
+                        throw new IgniteException("Unexpected escape sequence token: " +
+                            substring(text, startPos, len));
                 }
 
                 curTyp = typ;
@@ -249,7 +239,7 @@ public class OdbcEscapeUtils {
      * @param token Token.
      * @return Result.
      */
-    private static String parseStandardExpression(String text, int startPos, int len, OdbcEscapeToken token) {
+    private static String parseEscapeSequence(String text, int startPos, int len, OdbcEscapeToken token) {
         assert validSubstring(text, startPos, len);
 
         // Get expression borders.
@@ -283,6 +273,11 @@ public class OdbcEscapeUtils {
 
                 return "CALL " + val;
             }
+
+            case ESCAPE:
+            case ESCAPE_WO_TOKEN:
+                return parseLikeEscCharacterExpression(text, startPos0, len0);
+
             default:
                 throw new IgniteException("Unsupported escape sequence token [text=" +
                     substring(text, startPos, len) + ", token=" + token.type().body() + ']');
@@ -302,6 +297,18 @@ public class OdbcEscapeUtils {
     }
 
     /**
+     * Parse LIKE escape character expression.
+     *
+     * @param text Text.
+     * @param startPos Start position.
+     * @param len Length.
+     * @return Parsed expression.
+     */
+    private static String parseLikeEscCharacterExpression(String text, int startPos, int len) {
+        return "ESCAPE " + substring(text, startPos, len).trim();
+    }
+
+    /**
      * Parse expression and validate against ODBC specification with regex pattern.
      *
      * @param text Text.

http://git-wip-us.apache.org/repos/asf/ignite/blob/008cf644/modules/core/src/test/java/org/apache/ignite/internal/processors/odbc/OdbcEscapeSequenceSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/odbc/OdbcEscapeSequenceSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/odbc/OdbcEscapeSequenceSelfTest.java
index 26221ea..5303c6e 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/odbc/OdbcEscapeSequenceSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/odbc/OdbcEscapeSequenceSelfTest.java
@@ -528,9 +528,10 @@ public class OdbcEscapeSequenceSelfTest extends GridCommonAbstractTest {
             "select '{' + {fn func()} + '}' from table;"
         );
 
+        // quoted two single quotes should be interpret as apostrophe
         check(
-            "select '{\\'{fn test()}\\'}' from table;",
-            "select '{\\'{fn test()}\\'}' from table;"
+            "select '{''{fn test()}''}' from table;",
+            "select '{''{fn test()}''}' from table;"
         );
 
         checkFail("'{fn test()}");
@@ -666,6 +667,86 @@ public class OdbcEscapeSequenceSelfTest extends GridCommonAbstractTest {
     }
 
     /**
+     * Test escape sequence series.
+     */
+    public void testLikeEscapeSequence() throws Exception {
+        check(
+            "ESCAPE '\\'",
+            "{'\\'}"
+        );
+
+        check(
+            "ESCAPE '\\'",
+            "{escape '\\'}"
+        );
+
+        check(
+            "ESCAPE ''",
+            "{''}"
+        );
+
+        check(
+            "ESCAPE ''",
+            "{escape ''}"
+        );
+
+        check(
+            "select * from t where value LIKE '\\%AAA%' ESCAPE '\\'",
+            "select * from t where value LIKE '\\%AAA%' {'\\'}"
+        );
+
+        check(
+            "select * from t where value LIKE '\\%AAA%' ESCAPE '\\'",
+            "select * from t where value LIKE '\\%AAA%' {escape '\\'}"
+        );
+
+        check(
+            "select * from t where value LIKE '\\%AAA%' ESCAPE '\\' ORDER BY id;",
+            "select * from t where value LIKE '\\%AAA%' {'\\'} ORDER BY id;"
+        );
+
+        check(
+            "select * from t where value LIKE '\\%AAA%' ESCAPE '\\' ORDER BY id;",
+            "select * from t where value LIKE '\\%AAA%' {escape '\\'} ORDER BY id;"
+        );
+
+        check(
+            "select * from t where value LIKE '\\%AAA''s%' ESCAPE '\\'",
+            "select * from t where value LIKE '\\%AAA''s%' {escape '\\'}"
+        );
+    }
+
+    /**
+     * Test escape sequences with additional whitespace characters
+     */
+    public void testLikeEscapeSequenceWithWhitespaces() throws Exception {
+        check("ESCAPE '\\'", "{ '\\' }");
+        check("ESCAPE '\\'", "{ escape '\\'}");
+
+        check("ESCAPE '\\'", "{   '\\' }");
+        check("ESCAPE '\\'", "{   escape   '\\' }");
+
+        check("ESCAPE '\\'", "{ \n '\\' }");
+        check("ESCAPE '\\'", "{ \n escape\n'\\' }");
+    }
+
+    /**
+     * Test invalid escape sequence.
+     */
+    public void testLikeOnInvalidLikeEscapeSequence() {
+        checkFail("LIKE 'AAA's'");
+        checkFail("LIKE 'AAA\'s'");
+
+        checkFail("LIKE '\\%AAA%' {escape'\\' }");
+
+        checkFail("LIKE '\\%AAA%' {'\\' ORDER BY id");
+        checkFail("LIKE '\\%AAA%' {escape '\\' ORDER BY id;");
+
+        checkFail("LIKE '\\%AAA%' '\\'} ORDER BY id");
+        checkFail("LIKE '\\%AAA%' escape '\\'} ORDER BY id;");
+    }
+
+    /**
      * Check parsing logic.
      *
      * @param exp Expected result.