Scalar function escape sequence support prototype implemented.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/98a21256 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/98a21256 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/98a21256 Branch: refs/heads/ignite-3716 Commit: 98a21256b9aff373766521c18e662811433bfa76 Parents: f376cbb Author: Andrey V. Mashenkov <[email protected]> Authored: Mon Aug 22 17:10:11 2016 +0300 Committer: Andrey V. Mashenkov <[email protected]> Committed: Mon Aug 22 17:10:11 2016 +0300 ---------------------------------------------------------------------- .../processors/odbc/escape/EscapeSQLParser.java | 108 +++++++++++++++++++ .../OdbcScalarFunctionEscapeSequenceTest.java | 49 +++++++++ 2 files changed, 157 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/98a21256/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/EscapeSQLParser.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/EscapeSQLParser.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/EscapeSQLParser.java new file mode 100644 index 0000000..4630e7d --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/escape/EscapeSQLParser.java @@ -0,0 +1,108 @@ +package org.apache.ignite.internal.processors.odbc.escape; + +import org.apache.ignite.internal.util.typedef.T2; +import org.jetbrains.annotations.NotNull; + +/** + * Process ODBC escape sequences in sql query. + * See ODBC escape sequence syntax. + */ +public class EscapeSQLParser { + + /** + * Process ODBC escape sequences in sql query. + * @param sql - sql query text + * @return - processed sql query text + */ + public String parse(String sql) { + T2<Integer, String> value = parseInternal(sql, 0, false); + + return value.get2(); + } + + /** + * Parse escape sequence using appropriate parser. + * Supports: + * - Scalar function escape sequence. Example: "{fn func(arg1, arg2)}" + * + * @param sql - sql query text + * @param startPosition - parser start position + * @return pair of end of processed sequence position and parse result + */ + T2<Integer, String> parseEscapeSqeuence(String sql, int startPosition) { + assert sql.charAt(startPosition) == '{'; + + int pos = sql.indexOf(' ', startPosition + 1); + + if (pos == -1) + throw new IllegalStateException("Escape sequence parsing error at (" + startPosition + "): " + sql); + + String esType = sql.substring(startPosition + 1, pos); + + switch (esType) { + case "fn": + return parseInternal(sql, pos + 1, true); + + default: + throw new IllegalStateException("Unsupported escape sequence found at (" + startPosition + "): " + sql); + } + } + + /** + * Process ODBC escape sequences in sql query. + * @param sql - sql query text + * @param startPosition - parser start position + * @param insideEscapeSequence - inside escape sequence flag + * @return pair of end of processed sequence position and parse result + */ + @NotNull private T2<Integer, String> parseInternal(String sql, int startPosition, boolean insideEscapeSequence) { + StringBuffer sb = null; + + int end = -1; + int offset = startPosition; + + for (int i = startPosition; i < sql.length(); i++) { + // Current escape sequence ends up. + if (sql.charAt(i) == '}') { + end = i; + + break; + } + // Inner escape sequence starts + else if (sql.charAt(i) == '{') { + T2<Integer, String> res = parseEscapeSqeuence(sql, i); + + if (sb == null) + sb = new StringBuffer(); + + sb.append(sql, offset, i); + sb.append(res.get2()); + + i = res.get1(); + + assert sql.charAt(i) == '}'; + + offset = res.get1() + 1; + } + } + + if (insideEscapeSequence && end == -1) + // Closing bracket character '}' was not found in inner escape sequence + throw new IllegalStateException("Escape sequence started at position (" + startPosition + ") is not closed: " + sql); + else if (!insideEscapeSequence && end != -1) + // Closing bracket character '}' was found out of escape sequence + throw new IllegalStateException("Unexpected end of escaped sequence found at position (" + startPosition + ") is not closed: " + sql); + + if (end == -1) + end = sql.length(); + + // Add tail to the result + if (sb == null) + return new T2<>(end, sql.substring(startPosition, end)); + else if (offset < end) + sb.append(sql, offset, end); + + return new T2<>(end, sb.toString()); + } + +} http://git-wip-us.apache.org/repos/asf/ignite/blob/98a21256/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 new file mode 100644 index 0000000..7d60b42 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/odbc/OdbcScalarFunctionEscapeSequenceTest.java @@ -0,0 +1,49 @@ +package org.apache.ignite.internal.processors.odbc; + +import junit.framework.TestCase; +import org.apache.ignite.internal.processors.odbc.escape.EscapeSQLParser; + +public class OdbcScalarFunctionEscapeSequenceTest extends TestCase { + + public void testTrivialFunction() throws Exception { + String sqlQry = "select {fn func(field1)} {fn func(field2)} from table;"; + String expRes = "select func(field1) func(field2) from table;"; + + EscapeSQLParser parser = new EscapeSQLParser(); + + String actualRes = parser.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)"; + + parser = new EscapeSQLParser(); + + actualRes = parser.parse(sqlQry); + + assertEquals("Scalar function escape sequence parsing fails", expRes, actualRes); + } + + public void testNestedFunction() throws Exception { + String sqlQry = "select {fn func1(field1, {fn func2(field2)}, field3)} from SomeTable;"; + String expRes = "select func1(field1, func2(field2), field3) from SomeTable;"; + + EscapeSQLParser parser = new EscapeSQLParser(); + + String actualResult = parser.parse(sqlQry); + + assertEquals("Scalar function escape sequence parsing fails", expRes, actualResult); + + + sqlQry = "select {fn func1(field1, {fn func2(field2)}, field3)}"; + expRes = "select func1(field1, func2(field2), field3)"; + + parser = new EscapeSQLParser(); + + actualResult = parser.parse(sqlQry); + + assertEquals("Scalar function escape sequence parsing fails", expRes, actualResult); + } +}
