ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From voze...@apache.org
Subject [09/19] ignite git commit: Alternate implementation.
Date Wed, 24 Aug 2016 08:49:01 GMT
Alternate implementation.


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

Branch: refs/heads/ignite-3716
Commit: cc8f1e92fffafc532ac547f3fdf57676ad1bebf5
Parents: 902b45b
Author: vozerov-gridgain <vozerov@gridgain.com>
Authored: Tue Aug 23 11:58:44 2016 +0300
Committer: vozerov-gridgain <vozerov@gridgain.com>
Committed: Tue Aug 23 11:58:44 2016 +0300

----------------------------------------------------------------------
 .../processors/odbc/escape/OdbcParser.java      | 289 +++++++++++++++++++
 .../OdbcScalarFunctionEscapeSequenceTest.java   |  82 ++++++
 2 files changed, 371 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/cc8f1e92/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcParser.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcParser.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcParser.java
new file mode 100644
index 0000000..3d5eff61
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/OdbcParser.java
@@ -0,0 +1,289 @@
+/*
+ * 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.IgniteException;
+
+import java.util.LinkedList;
+
+/**
+ * ODBC escape sequence parse.
+ */
+public class OdbcParser {
+    /**
+     * Parse escape sequence.
+     *
+     * @param text Original text.
+     * @return Result.
+     */
+    public static String parse(String text) {
+        if (text == null)
+            throw new IgniteException("Text cannot be null.");
+
+        return parse0(text.trim(), 0, false).res;
+    }
+
+    /**
+     * Internal parse routine.
+     *
+     * @param text Text.
+     * @param startPos Start position.
+     * @param earlyExit When set to {@code true} we must return as soon as single expression
is parsed.
+     * @return Parse result.
+     */
+    private static ParseResult parse0(String text, int startPos, boolean earlyExit) {
+        StringBuilder res = new StringBuilder();
+
+        int curPos = startPos;
+
+        int plainPos = startPos;
+        int openPos = -1;
+
+        LinkedList<ParseResult> nested = null;
+
+        while (curPos < text.length()) {
+            char curChar = text.charAt(curPos);
+
+            if (curChar == '{') {
+                if (openPos == -1) {
+                    // Top-level opening brace. Append previous portion and remember current
position.
+                    res.append(text, plainPos, curPos);
+
+                    openPos = curPos;
+                }
+                else {
+                    // Nested opening brace -> perform recursion.
+                    ParseResult nestedRes = parse0(text, curPos, true);
+
+                    if (nested == null)
+                        nested = new LinkedList<>();
+
+                    nested.add(nestedRes);
+
+                    curPos += nestedRes.originalLen;
+
+                    plainPos = curPos;
+                }
+            }
+            else if (curChar == '}') {
+                if (openPos == -1)
+                    // Close without open -> exception.
+                    throw new IgniteException("Malformed escape sequence " +
+                        "(closing curly brace without opening curly brace): " + text);
+                else {
+                    if (nested == null) {
+                        // Found sequence without nesting, process it.
+                        String parseRes = parseExpression(text, openPos, curPos - openPos);
+
+                        if (earlyExit)
+                            return new ParseResult(startPos, curPos - startPos + 1, parseRes);
+                        else
+                            res.append(parseRes);
+                    }
+                    else {
+                        // Process nesting.
+                        String res0 = appendNested(text, openPos, curPos + 1, nested);
+
+                        String parseRes = parse0(res0, 0, true).res;
+
+                        if (earlyExit)
+                            return new ParseResult(startPos, curPos - startPos + 1, parseRes);
+                        else
+                            res.append(parseRes);
+                    }
+
+                    openPos = -1;
+
+                    plainPos = curPos + 1;
+                }
+            }
+
+            curPos++;
+        }
+
+        if (curPos > plainPos)
+            res.append(text, plainPos, curPos);
+
+        return new ParseResult(startPos, curPos - startPos + 1, res.toString());
+    }
+
+    /**
+     * Append nested results.
+     *
+     * @param text Original text.
+     * @param startPos Start position.
+     * @param endPos End position.
+     * @param nestedRess Nested results.
+     * @return Result.
+     */
+    private static String appendNested(String text, int startPos, int endPos, LinkedList<ParseResult>
nestedRess) {
+        StringBuilder res = new StringBuilder();
+
+        int curPos = startPos;
+
+        for (ParseResult nestedRes : nestedRess) {
+            // Append text between current position and replace.
+            res.append(text, curPos, nestedRes.originalStart);
+
+            // Append replaced text.
+            res.append(nestedRes.res);
+
+            // Advance position.
+            curPos = nestedRes.originalStart + nestedRes.originalLen;
+        }
+
+        // Append remainder.
+        res.append(text, curPos, endPos);
+
+        return res.toString();
+    }
+
+    /**
+     * Parse concrete expression.
+     *
+     * @param text Text.
+     * @param startPos Start position within text.
+     * @param len Length.
+     * @return Result.
+     */
+    private static String parseExpression(String text, int startPos, int len) {
+        assert validSubstring(text, startPos, len);
+
+        char firstChar = text.charAt(startPos);
+
+        if (firstChar == '{') {
+            char lastChar = text.charAt(startPos + len);
+
+            if (lastChar != '}')
+                throw new IgniteException("Failed to parse escape sequence because it is
not enclosed: " +
+                    substring(text, startPos, len));
+
+            SequenceType typ = sequenceType(text, startPos, len);
+
+            switch (typ) {
+                case FN:
+                    return parseScalarExpression(text, startPos, len);
+
+                default: {
+                    assert false : "Unknown expression type: " + typ;
+
+                    return null;
+                }
+            }
+        }
+        else {
+            // Nothing to escape, return original string.
+            if (startPos == 0 || text.length() == len)
+                return text;
+            else
+                return text.substring(startPos, startPos + len);
+        }
+    }
+
+    /**
+     * Perform "substring" using start position and length.
+     *
+     * @param text Text.
+     * @param startPos Start position.
+     * @param len Length.
+     * @return Substring.
+     */
+    private static String substring(String text, int startPos, int len) {
+        assert validSubstring(text, startPos, len);
+
+        return text.substring(startPos, startPos + len);
+    }
+
+    /**
+     * Check whether substring is valid.
+     *
+     * @param text Substring.
+     * @param startPos Start position.
+     * @param len Length.
+     * @return {@code True} if valid.
+     */
+    private static boolean validSubstring(String text, int startPos, int len) {
+        return text != null && startPos + len <= text.length();
+    }
+
+    /**
+     * Parse concrete expression.
+     *
+     * @param text Text.
+     * @param startPos Start position.
+     * @param len Length.
+     * @return Parsed expression.
+     */
+    private static String parseScalarExpression(String text, int startPos, int len) {
+        assert validSubstring(text, startPos, len);
+
+        return substring(text, startPos + 3, len - 3).trim();
+    }
+
+    /**
+     * Get escape sequence type.
+     *
+     * @param text Text.
+     * @param startPos Start position.
+     * @return Escape sequence type.
+     */
+    private static SequenceType sequenceType(String text, int startPos, int len) {
+        assert validSubstring(text, startPos, len);
+        assert text.charAt(startPos) == '{';
+
+        if (text.startsWith("fn", startPos + 1))
+            return SequenceType.FN;
+
+        throw new IgniteException("Unsupported escape sequence: " + text.substring(startPos,
startPos + len));
+    }
+
+    /**
+     * Escape sequence type.
+     */
+    private enum SequenceType {
+        /** Scalar function. */
+        FN
+    }
+
+    /**
+     * Parse result.
+     */
+    private static class ParseResult {
+        /** Original start position. */
+        private final int originalStart;
+
+        /** Original length. */
+        private final int originalLen;
+
+        /** Resulting text. */
+        private final String res;
+
+        /**
+         * Constructor.
+         *
+         * @param originalStart Original start position.
+         * @param originalLen Original length.
+         * @param res Resulting text.
+         */
+        public ParseResult(int originalStart, int originalLen, String res) {
+            this.originalStart = originalStart;
+            this.originalLen = originalLen;
+            this.res = res;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/cc8f1e92/modules/core/src/test/java/org/apache/ignite/internal/processors/odbc/OdbcScalarFunctionEscapeSequenceTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/odbc/OdbcScalarFunctionEscapeSequenceTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/odbc/OdbcScalarFunctionEscapeSequenceTest.java
index bab9feb..4aa62c4 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/odbc/OdbcScalarFunctionEscapeSequenceTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/odbc/OdbcScalarFunctionEscapeSequenceTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.odbc;
 import java.util.concurrent.Callable;
 import junit.framework.TestCase;
 import org.apache.ignite.internal.processors.odbc.escape.OdbcEscapeSequenceUtils;
+import org.apache.ignite.internal.processors.odbc.escape.OdbcParser;
 import org.apache.ignite.testframework.GridTestUtils;
 
 /** */
@@ -106,4 +107,85 @@ public class OdbcScalarFunctionEscapeSequenceTest extends TestCase {
             }
         }, IllegalStateException.class, null);
     }
+
+    /** */
+    public void testTrivial2() throws Exception {
+        String sqlQry = "{fn test()}";
+        String expRes = "test()";
+
+        String actualRes = OdbcParser.parse(sqlQry);
+
+        assertEquals("Scalar function escape sequence parsing fails", expRes, actualRes);
+
+        sqlQry = "select * from table;";
+        expRes = "select * from table;";
+
+        actualRes = OdbcParser.parse(sqlQry);
+
+        assertEquals("Scalar function escape sequence parsing fails", expRes, actualRes);
+    }
+
+    /** */
+    public void testSimpleFunction2() throws Exception {
+        String sqlQry = "select {fn func(field1)} {fn func(field2)} from table;";
+        String expRes = "select func(field1) func(field2) from table;";
+
+        String actualRes = OdbcParser.parse(sqlQry);
+
+        assertEquals("Scalar function escape sequence parsing fails", expRes, actualRes);
+
+        sqlQry = "select {fn func(field1)} {fn func(field2)}";
+        expRes = "select func(field1) func(field2)";
+
+        actualRes = OdbcParser.parse(sqlQry);
+
+        assertEquals("Scalar function escape sequence parsing fails", expRes, actualRes);
+    }
+
+    /** */
+    public void testNestedFunction2() throws Exception {
+        String sqlQry = "select {fn func1(field1, {fn func2(field2)}, field3)} from SomeTable;";
+        String expRes = "select func1(field1, func2(field2), field3) from SomeTable;";
+
+        String actualRes = OdbcParser.parse(sqlQry);
+
+        assertEquals("Scalar function escape sequence parsing fails", expRes, actualRes);
+
+        sqlQry = "select {fn func1(field1, {fn func2(field2)}, field3)}";
+        expRes = "select func1(field1, func2(field2), field3)";
+
+        actualRes = OdbcParser.parse(sqlQry);
+
+        assertEquals("Scalar function escape sequence parsing fails", expRes, actualRes);
+    }
+
+    /** */
+    public void testFailedOnNotClosedSequence2() throws Exception {
+        GridTestUtils.assertThrows(null, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                String sqlQry = "select {fn func1(field1, {fn func2(field2), field3)} from
SomeTable;";
+
+                OdbcParser.parse(sqlQry);
+
+                fail("Scalar function escape sequence parsing should failed");
+
+                return null;
+            }
+        }, IllegalStateException.class, null);
+    }
+
+    /** */
+    public void testFailedOnClosingNotOpenedSequence2() throws Exception {
+        GridTestUtils.assertThrows(null, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                String sqlQry = "select {fn func1(field1, func2(field2)}, field3)} from SomeTable;";
+
+                OdbcParser.parse(sqlQry);
+
+                fail("Scalar function escape sequence parsing should failed");
+
+                return null;
+            }
+        }, IllegalStateException.class, null);
+    }
 }


Mime
View raw message