Skip to content

Commit

Permalink
IGNITE-3739: ODBC: Added GUID escape sequence support. This closes #988.
Browse files Browse the repository at this point in the history
  • Loading branch information
AMashenkov authored and vozerov-gridgain committed Aug 26, 2016
1 parent 8aabd6e commit ae0b5eb
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 37 deletions.
Expand Up @@ -63,8 +63,8 @@ public static OdbcEscapeType[] sortedValues() {
/** Escape sequence body. */ /** Escape sequence body. */
private final String body; private final String body;


/** Whether token must be delimited from the rest of escape sequence. */ /** Whether this is a standard token with no special handling. */
private final boolean delimited; private final boolean standard;


/** Whether empty escape sequence is allowed. */ /** Whether empty escape sequence is allowed. */
private final boolean allowEmpty; private final boolean allowEmpty;
Expand All @@ -73,12 +73,12 @@ public static OdbcEscapeType[] sortedValues() {
* Constructor. * Constructor.
* *
* @param body Escape sequence body. * @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. * @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.body = body;
this.delimited = delimited; this.standard = standard;
this.allowEmpty = allowEmpty; this.allowEmpty = allowEmpty;
} }


Expand All @@ -90,10 +90,10 @@ public String body() {
} }


/** /**
* @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() { public boolean standard() {
return delimited; return standard;
} }


/** /**
Expand Down
Expand Up @@ -20,11 +20,19 @@
import org.apache.ignite.IgniteException; import org.apache.ignite.IgniteException;


import java.util.LinkedList; import java.util.LinkedList;
import java.util.regex.Pattern;


/** /**
* ODBC escape sequence parse. * ODBC escape sequence parse.
*/ */
public class OdbcEscapeUtils { 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. * Parse escape sequence.
* *
Expand Down Expand Up @@ -145,14 +153,11 @@ private static String parseExpression(String text, int startPos, int len) {


OdbcEscapeToken token = parseToken(text, startPos, len); OdbcEscapeToken token = parseToken(text, startPos, len);


switch (token.type()) { if (token.type().standard())
case SCALAR_FUNCTION: return parseStandardExpression(text, startPos, len, token);
return parseScalarExpression(text, startPos, len, token); else

throw new IgniteException("Unsupported escape sequence token [text=" +
default: substring(text, startPos, len) + ", token=" + token.type().body() + ']');
throw new IgniteException("Unsupported escape sequence token [text=" +
substring(text, startPos, len) + ", token=" + token.type().body() + ']');
}
} }
else { else {
// Nothing to escape, return original string. // Nothing to escape, return original string.
Expand Down Expand Up @@ -191,7 +196,7 @@ private static OdbcEscapeToken parseToken(String text, int startPos, int len) {
else { else {
empty = (startPos + len == pos + 1); empty = (startPos + len == pos + 1);


if (!empty && typ.delimited()) { if (!empty && typ.standard()) {
char charAfter = text.charAt(pos); char charAfter = text.charAt(pos);


if (!Character.isWhitespace(charAfter)) if (!Character.isWhitespace(charAfter))
Expand All @@ -216,21 +221,61 @@ private static OdbcEscapeToken parseToken(String text, int startPos, int len) {
} }


/** /**
* Parse concrete expression. * Parse standard token.
* *
* @param text Text. * @param text Text.
* @param startPos Start position. * @param startPos Start position.
* @param len Length. * @param len Length.
* @param token Token. * @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); assert validSubstring(text, startPos, len);


// Get expression borders.
int startPos0 = startPos + 1 /* open brace */ + token.length() /* token. */; int startPos0 = startPos + 1 /* open brace */ + token.length() /* token. */;
int len0 = len - 1 /* open brace */ - token.length() /* token */ - 1 /* close brace */; 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;
} }


/** /**
Expand Down
Expand Up @@ -25,18 +25,23 @@
import java.util.concurrent.Callable; import java.util.concurrent.Callable;


/** /**
* Scalar function escape sequence parser tests. * Escape sequence parser tests.
*/ */
public class OdbcEscapeSequenceSelfTest extends GridCommonAbstractTest { public class OdbcEscapeSequenceSelfTest extends GridCommonAbstractTest {
/** /**
* Test simple cases. * Test simple cases.
*/ */
public void testSimple() { public void testTrivial() {
check( check(
"select * from table;", "select * from table;",
"select * from table;" "select * from table;"
); );
}


/**
* Test escape sequence series.
*/
public void testSimpleFunction() throws Exception {
check( check(
"test()", "test()",
"{fn test()}" "{fn test()}"
Expand All @@ -51,12 +56,7 @@ public void testSimple() {
"select test() from table;", "select test() from table;",
"select {fn test()} from table;" "select {fn test()} from table;"
); );
}


/**
* Test escape sequence series.
*/
public void testSimpleFunction() throws Exception {
check( check(
"func(field1) func(field2)", "func(field1) func(field2)",
"{fn func(field1)} {fn func(field2)}" "{fn func(field1)} {fn func(field2)}"
Expand Down Expand Up @@ -139,20 +139,15 @@ public void testNestedFunctionMixed() {
} }


/** /**
* 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;"); checkFail("select {fn func1(field1, {fn func2(field2), field3)} from SomeTable;");
}


/** 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;");
} }


/** /**
* Test escape sequences with additional whitespace characters * Test escape sequences with additional whitespace characters
*/ */
public void testFunctionEscapeSequenceWithWhitespaces() throws Exception { public void testFunctionEscapeSequenceWithWhitespaces() throws Exception {
Expand All @@ -165,6 +160,65 @@ public void testFunctionEscapeSequenceWithWhitespaces() throws Exception {
checkFail("{ \n func1()}"); checkFail("{ \n func1()}");
} }


/**
* 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. * Check parsing logic.
* *
Expand Down

0 comments on commit ae0b5eb

Please sign in to comment.