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/08/24 08:49:01 UTC
[09/19] ignite git commit: Alternate implementation.
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 <vo...@gridgain.com>
Authored: Tue Aug 23 11:58:44 2016 +0300
Committer: vozerov-gridgain <vo...@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);
+ }
}