IGNITE-3739: ODBC: Added GUID escape sequence support. This closes #988.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/ae0b5ebf Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/ae0b5ebf Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/ae0b5ebf Branch: refs/heads/master Commit: ae0b5ebf02f3eb70d24dd3b0eb63dde9843c82b0 Parents: 8aabd6e Author: Andrey V. Mashenkov <[email protected]> Authored: Fri Aug 26 11:12:31 2016 +0300 Committer: vozerov-gridgain <[email protected]> Committed: Fri Aug 26 11:12:31 2016 +0300 ---------------------------------------------------------------------- .../processors/odbc/escape/OdbcEscapeType.java | 16 ++-- .../processors/odbc/escape/OdbcEscapeUtils.java | 71 +++++++++++++--- .../odbc/OdbcEscapeSequenceSelfTest.java | 86 ++++++++++++++++---- 3 files changed, 136 insertions(+), 37 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/ae0b5ebf/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 96a2127..3bf0324 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 @@ -63,8 +63,8 @@ public enum OdbcEscapeType { /** Escape sequence body. */ private final String body; - /** Whether token must be delimited from the rest of escape sequence. */ - private final boolean delimited; + /** Whether this is a standard token with no special handling. */ + private final boolean standard; /** Whether empty escape sequence is allowed. */ private final boolean allowEmpty; @@ -73,12 +73,12 @@ public enum OdbcEscapeType { * Constructor. * * @param body Escape sequence body. - * @param delimited Whether token must be delimited from the rest of escape sequence. + * @param standard Whether this is a standard token with no special handling. * @param allowEmpty Whether empty escape sequence is allowed. */ - OdbcEscapeType(String body, boolean delimited, boolean allowEmpty) { + OdbcEscapeType(String body, boolean standard, boolean allowEmpty) { this.body = body; - this.delimited = delimited; + this.standard = standard; this.allowEmpty = allowEmpty; } @@ -90,10 +90,10 @@ public enum OdbcEscapeType { } /** - * @return Whether token must be delimited from the rest of escape sequence. + * @return Whether this is a standard token with no special handling. */ - public boolean delimited() { - return delimited; + public boolean standard() { + return standard; } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/ae0b5ebf/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 6299c7e..83ec9d8 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 @@ -20,11 +20,19 @@ package org.apache.ignite.internal.processors.odbc.escape; import org.apache.ignite.IgniteException; import java.util.LinkedList; +import java.util.regex.Pattern; /** * ODBC escape sequence parse. */ public class OdbcEscapeUtils { + + /** + * GUID regexp pattern: '12345678-9abc-def0-1234-123456789abc' + */ + private static final Pattern GUID_PATTERN = + Pattern.compile("^'\\p{XDigit}{8}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{12}'$"); + /** * Parse escape sequence. * @@ -145,14 +153,11 @@ public class OdbcEscapeUtils { OdbcEscapeToken token = parseToken(text, startPos, len); - switch (token.type()) { - case SCALAR_FUNCTION: - return parseScalarExpression(text, startPos, len, token); - - default: - throw new IgniteException("Unsupported escape sequence token [text=" + - substring(text, startPos, len) + ", token=" + token.type().body() + ']'); - } + 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() + ']'); } else { // Nothing to escape, return original string. @@ -191,7 +196,7 @@ public class OdbcEscapeUtils { else { empty = (startPos + len == pos + 1); - if (!empty && typ.delimited()) { + if (!empty && typ.standard()) { char charAfter = text.charAt(pos); if (!Character.isWhitespace(charAfter)) @@ -216,21 +221,61 @@ public class OdbcEscapeUtils { } /** - * Parse concrete expression. + * Parse standard token. * * @param text Text. * @param startPos Start position. * @param len Length. * @param token Token. - * @return Parsed expression. + * @return Result. */ - private static String parseScalarExpression(String text, int startPos, int len, OdbcEscapeToken token) { + private static String parseStandardExpression(String text, int startPos, int len, OdbcEscapeToken token) { assert validSubstring(text, startPos, len); + // Get expression borders. 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(); + switch (token.type()) { + case SCALAR_FUNCTION: + return parseScalarExpression(text, startPos0, len0); + + case GUID: + return parseGuidExpression(text, startPos0, len0); + + default: + throw new IgniteException("Unsupported escape sequence token [text=" + + substring(text, startPos, len) + ", token=" + token.type().body() + ']'); + } + } + + /** + * Parse scalar function expression. + * + * @param text Text. + * @param startPos Start position. + * @param len Length. + * @return Parsed expression. + */ + private static String parseScalarExpression(String text, int startPos, int len) { + return substring(text, startPos, len).trim(); + } + + /** + * Parse concrete expression. + * + * @param text Text. + * @param startPos Start position. + * @param len Length. + * @return Parsed expression. + */ + private static String parseGuidExpression(String text, int startPos, int len) { + String val = substring(text, startPos, len).trim(); + + if (!GUID_PATTERN.matcher(val).matches()) + throw new IgniteException("Invalid GUID escape sequence: " + substring(text, startPos, len)); + + return val; } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/ae0b5ebf/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 d9be6cc..7225c1a 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 @@ -25,18 +25,23 @@ import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import java.util.concurrent.Callable; /** - * Scalar function escape sequence parser tests. + * Escape sequence parser tests. */ public class OdbcEscapeSequenceSelfTest extends GridCommonAbstractTest { /** * Test simple cases. */ - public void testSimple() { + public void testTrivial() { check( "select * from table;", "select * from table;" ); + } + /** + * Test escape sequence series. + */ + public void testSimpleFunction() throws Exception { check( "test()", "{fn test()}" @@ -51,12 +56,7 @@ public class OdbcEscapeSequenceSelfTest extends GridCommonAbstractTest { "select test() from table;", "select {fn test()} from table;" ); - } - /** - * Test escape sequence series. - */ - public void testSimpleFunction() throws Exception { check( "func(field1) func(field2)", "{fn func(field1)} {fn func(field2)}" @@ -139,20 +139,15 @@ public class OdbcEscapeSequenceSelfTest extends GridCommonAbstractTest { } /** - * Test non-closed escape sequence. + * Test invalid escape sequence. */ - public void testFailedOnNonClosedEscapeSequence() { + public void testFailedOnInvalidFunctionSequence() { checkFail("select {fn func1(field1, {fn func2(field2), field3)} from SomeTable;"); - } - /** - * Test closing undeclared escape sequence. - */ - public void testFailedOnClosingNotOpenedSequence() { - checkFail("select {fn func1(field1, func2(field2)}, field3)} from SomeTable;"); + checkFail("select {fn func1(field1, fn func2(field2)}, field3)} from SomeTable;"); } - /** + /** * Test escape sequences with additional whitespace characters */ public void testFunctionEscapeSequenceWithWhitespaces() throws Exception { @@ -166,6 +161,65 @@ public class OdbcEscapeSequenceSelfTest extends GridCommonAbstractTest { } /** + * Test guid escape sequences + */ + public void testGuidEscapeSequence() { + check( + "'12345678-9abc-def0-1234-123456789abc'", + "{guid '12345678-9abc-def0-1234-123456789abc'}" + ); + + check( + "select '12345678-9abc-def0-1234-123456789abc' from SomeTable;", + "select {guid '12345678-9abc-def0-1234-123456789abc'} from SomeTable;" + ); + + check( + "select '12345678-9abc-def0-1234-123456789abc'", + "select {guid '12345678-9abc-def0-1234-123456789abc'}" + ); + + checkFail("select {guid '1234567-1234-1234-1234-123456789abc'}"); + + checkFail("select {guid '1234567-8123-4123-4123-4123456789abc'}"); + + checkFail("select {guid '12345678-9abc-defg-1234-123456789abc'}"); + + checkFail("select {guid '12345678-12345678-1234-1234-1234-123456789abc'}"); + + checkFail("select {guid '12345678-1234-1234-1234-123456789abcdef'}"); + } + + /** + * Test invalid escape sequence. + */ + public void testFailedOnInvalidGuidSequence() { + checkFail("select {guid '12345678-9abc-def0-1234-123456789abc' from SomeTable;"); + + checkFail("select guid '12345678-9abc-def0-1234-123456789abc'} from SomeTable;"); + } + + /** + * Test escape sequences with additional whitespace characters + */ + public void testGuidEscapeSequenceWithWhitespaces() throws Exception { + check( + "'12345678-9abc-def0-1234-123456789abc'", + "{ guid '12345678-9abc-def0-1234-123456789abc'}" + ); + + check( + "'12345678-9abc-def0-1234-123456789abc'", + "{ guid '12345678-9abc-def0-1234-123456789abc'}" + ); + + check( + "'12345678-9abc-def0-1234-123456789abc'", + "{ \n guid\n'12345678-9abc-def0-1234-123456789abc'}" + ); + } + + /** * Check parsing logic. * * @param exp Expected result.
