You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2016/08/31 06:32:58 UTC
[08/38] 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/ignite-3443
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.