You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2016/08/29 18:04:52 UTC

[08/24] ignite git commit: IGNITE-3738: ODBC: Fixed escape sequence whitespaces handling. This closes #982.

IGNITE-3738: ODBC: Fixed escape sequence whitespaces handling. This closes #982.


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

Branch: refs/heads/master
Commit: e21111f287039011bc9437c94fb574e61e2ac226
Parents: 0e3a6e2
Author: Andrey V. Mashenkov <an...@gmail.com>
Authored: Thu Aug 25 16:26:02 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Thu Aug 25 16:26:02 2016 +0300

----------------------------------------------------------------------
 .../processors/odbc/escape/OdbcEscapeToken.java |  61 +++++++++++
 .../processors/odbc/escape/OdbcEscapeType.java  |  81 ++++++++++++++-
 .../processors/odbc/escape/OdbcEscapeUtils.java | 103 +++++++++++++------
 .../odbc/OdbcEscapeSequenceSelfTest.java        |  15 ++-
 4 files changed, 225 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/e21111f2/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeToken.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeToken.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeToken.java
new file mode 100644
index 0000000..6bb4f81
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcEscapeToken.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.odbc.escape;
+
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * ODBC escape sequence token.
+ */
+public class OdbcEscapeToken {
+    /** Escape sequence type. */
+    private final OdbcEscapeType type;
+
+    /** Token length. */
+    private final int len;
+
+    /**
+     * Constructor.
+     *
+     * @param type Escape sequence type.
+     * @param len Token length.
+     */
+    public OdbcEscapeToken(OdbcEscapeType type, int len) {
+        this.type = type;
+        this.len = len;
+    }
+
+    /**
+     * @return Escape sequence type.
+     */
+    public OdbcEscapeType type() {
+        return type;
+    }
+
+    /**
+     * @return Token length.
+     */
+    public int length() {
+        return len;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(OdbcEscapeToken.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/e21111f2/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 2df413f..96a2127 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
@@ -22,5 +22,84 @@ package org.apache.ignite.internal.processors.odbc.escape;
  */
 public enum OdbcEscapeType {
     /** Scalar function. */
-    FN
+    SCALAR_FUNCTION("fn", true, false),
+
+    /** Outer join. */
+    OUTER_JOIN("oj", true, false),
+
+    /** Date. */
+    DATE("d", true, false),
+
+    /** Timestamp. */
+    TIMESTAMP("ts", true, false),
+
+    /** Time. */
+    TIME("t", true, false),
+
+    /** GUID. */
+    GUID("guid", true, false),
+
+    /** LIKE clause. */
+    LIKE("\'", false, true);
+
+    /** Values in convenient order. */
+    private static final OdbcEscapeType[] VALS = new OdbcEscapeType[] {
+        SCALAR_FUNCTION, // Assume that scalar functions are very frequent.
+        DATE, TIMESTAMP, // Date and timestamp are relatively frequent as well; also TS must go before T.
+        OUTER_JOIN,      // Joins are less frequent,
+        LIKE, TIME, GUID // LIKE, TIME and GUID are even less frequent.
+    };
+
+    /**
+     * Get values in convenient order, where the most frequent values goes first, and "startsWith" invocation is
+     * enough to get type (i.e. "ts" goes before "t").
+     *
+     * @return Values.
+     */
+    public static OdbcEscapeType[] sortedValues() {
+        return VALS;
+    }
+
+    /** Escape sequence body. */
+    private final String body;
+
+    /** Whether token must be delimited from the rest of escape sequence. */
+    private final boolean delimited;
+
+    /** Whether empty escape sequence is allowed. */
+    private final boolean allowEmpty;
+
+    /**
+     * Constructor.
+     *
+     * @param body Escape sequence body.
+     * @param delimited Whether token must be delimited from the rest of escape sequence.
+     * @param allowEmpty Whether empty escape sequence is allowed.
+     */
+    OdbcEscapeType(String body, boolean delimited, boolean allowEmpty) {
+        this.body = body;
+        this.delimited = delimited;
+        this.allowEmpty = allowEmpty;
+    }
+
+    /**
+     * @return Escape sequence body.
+     */
+    public String body() {
+        return body;
+    }
+
+    /**
+     * @return Whether token must be delimited from the rest of escape sequence.
+     */
+    public boolean delimited() {
+        return delimited;
+    }
+
+    /**
+     * @return Whether empty escape sequence is allowed.
+     */
+    public boolean allowEmpty() {
+        return allowEmpty;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/e21111f2/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 4d8ca69..6299c7e 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
@@ -90,18 +90,18 @@ public class OdbcEscapeUtils {
 
                     if (nested == null)
                         // Found sequence without nesting, process it.
-                        parseRes = parseExpression(text, openPos, curPos - openPos);
+                        parseRes = parseExpression(text, openPos, curPos + 1 - openPos);
                     else {
                         // Special case to process nesting.
                         String res0 = appendNested(text, openPos, curPos + 1, nested);
 
                         nested = null;
 
-                        parseRes = parseExpression(res0, 0, res0.length()-1);
+                        parseRes = parseExpression(res0, 0, res0.length());
                     }
 
                     if (earlyExit)
-                        return new OdbcEscapeParseResult(startPos, curPos - startPos + 1, parseRes);
+                        return new OdbcEscapeParseResult(startPos, curPos + 1 - startPos, parseRes);
                     else
                         res.append(parseRes);
 
@@ -137,23 +137,21 @@ public class OdbcEscapeUtils {
         char firstChar = text.charAt(startPos);
 
         if (firstChar == '{') {
-            char lastChar = text.charAt(startPos + len);
+            char lastChar = text.charAt(startPos + len - 1);
 
             if (lastChar != '}')
                 throw new IgniteException("Failed to parse escape sequence because it is not enclosed: " +
                     substring(text, startPos, len));
 
-            OdbcEscapeType typ = sequenceType(text, startPos, len);
+            OdbcEscapeToken token = parseToken(text, startPos, len);
 
-            switch (typ) {
-                case FN:
-                    return parseScalarExpression(text, startPos, len);
+            switch (token.type()) {
+                case SCALAR_FUNCTION:
+                    return parseScalarExpression(text, startPos, len, token);
 
-                default: {
-                    assert false : "Unknown expression type: " + typ;
-
-                    return null;
-                }
+                default:
+                    throw new IgniteException("Unsupported escape sequence token [text=" +
+                        substring(text, startPos, len) + ", token=" + token.type().body() + ']');
             }
         }
         else {
@@ -161,8 +159,60 @@ public class OdbcEscapeUtils {
             if (startPos == 0 || text.length() == len)
                 return text;
             else
-                return text.substring(startPos, startPos + len);
+                return substring(text, startPos, len);
+        }
+    }
+
+    /**
+     * Get escape sequence info.
+     *
+     * @param text Text.
+     * @param startPos Start position.
+     * @return Escape sequence info.
+     */
+    private static OdbcEscapeToken parseToken(String text, int startPos, int len) {
+        assert validSubstring(text, startPos, len);
+        assert text.charAt(startPos) == '{';
+
+        int pos = startPos + 1;
+
+        while (Character.isWhitespace(text.charAt(pos)))
+            pos++;
+
+        OdbcEscapeType curTyp = null;
+        boolean empty = false;
+
+        for (OdbcEscapeType typ : OdbcEscapeType.sortedValues()) {
+            if (text.startsWith(typ.body(), pos)) {
+                pos += typ.body().length();
+
+                if (typ == OdbcEscapeType.LIKE)
+                    throw new IgniteException("LIKE escape sequence is not supported yet.");
+                else {
+                    empty = (startPos + len == pos + 1);
+
+                    if (!empty && typ.delimited()) {
+                        char charAfter = text.charAt(pos);
+
+                        if (!Character.isWhitespace(charAfter))
+                            throw new IgniteException("Unexpected escape sequence token: " +
+                                substring(text, startPos, len));
+                    }
+                }
+
+                curTyp = typ;
+
+                break;
+            }
         }
+
+        if (curTyp == null)
+            throw new IgniteException("Unsupported escape sequence: " + substring(text, startPos, len));
+
+        if (empty && !curTyp.allowEmpty())
+            throw new IgniteException("Escape sequence cannot be empty: " + substring(text, startPos, len));
+
+        return new OdbcEscapeToken(curTyp, pos - (startPos + 1));
     }
 
     /**
@@ -171,12 +221,16 @@ public class OdbcEscapeUtils {
      * @param text Text.
      * @param startPos Start position.
      * @param len Length.
+     * @param token Token.
      * @return Parsed expression.
      */
-    private static String parseScalarExpression(String text, int startPos, int len) {
+    private static String parseScalarExpression(String text, int startPos, int len, OdbcEscapeToken token) {
         assert validSubstring(text, startPos, len);
 
-        return substring(text, startPos + 3, len - 3).trim();
+        int startPos0 = startPos + 1 /* open brace */ + token.length() /* token. */;
+        int len0 = len - 1 /* open brace */ - token.length() /* token */ - 1 /* close brace */;
+
+        return substring(text, startPos0, len0).trim();
     }
 
     /**
@@ -212,23 +266,6 @@ public class OdbcEscapeUtils {
     }
 
     /**
-     * Get escape sequence type.
-     *
-     * @param text Text.
-     * @param startPos Start position.
-     * @return Escape sequence type.
-     */
-    private static OdbcEscapeType sequenceType(String text, int startPos, int len) {
-        assert validSubstring(text, startPos, len);
-        assert text.charAt(startPos) == '{';
-
-        if (text.startsWith("fn", startPos + 1))
-            return OdbcEscapeType.FN;
-
-        throw new IgniteException("Unsupported escape sequence: " + text.substring(startPos, startPos + len));
-    }
-
-    /**
      * Perform "substring" using start position and length.
      *
      * @param text Text.

http://git-wip-us.apache.org/repos/asf/ignite/blob/e21111f2/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 73fa0f4..d9be6cc 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
@@ -141,7 +141,7 @@ public class OdbcEscapeSequenceSelfTest extends GridCommonAbstractTest {
     /**
      * Test non-closed escape sequence.
      */
-    public void testFailedOnInvalidSequence1() {
+    public void testFailedOnNonClosedEscapeSequence() {
         checkFail("select {fn func1(field1, {fn func2(field2), field3)} from SomeTable;");
     }
 
@@ -153,6 +153,19 @@ public class OdbcEscapeSequenceSelfTest extends GridCommonAbstractTest {
     }
 
     /**
+     * Test escape sequences with additional whitespace characters
+     */
+    public void testFunctionEscapeSequenceWithWhitespaces() throws Exception {
+        check("func1()", "{ fn func1()}");
+
+        check("func1()", "{    fn  func1()}");
+
+        check("func1()", "{ \n fn  func1()}");
+
+        checkFail("{ \n func1()}");
+    }
+
+    /**
      * Check parsing logic.
      *
      * @param exp Expected result.