Implement negative substring offset
Project: http://git-wip-us.apache.org/repos/asf/incubator-drill/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-drill/commit/068a1dfd Tree: http://git-wip-us.apache.org/repos/asf/incubator-drill/tree/068a1dfd Diff: http://git-wip-us.apache.org/repos/asf/incubator-drill/diff/068a1dfd Branch: refs/heads/master Commit: 068a1dfddde0ef16db9b003cf7b28a79911417b5 Parents: ce7cf0d Author: Ben Becker <[email protected]> Authored: Fri Aug 2 12:31:16 2013 -0700 Committer: Jacques Nadeau <[email protected]> Committed: Mon Aug 5 16:44:57 2013 -0700 ---------------------------------------------------------------------- .../drill/exec/expr/fn/impl/Substring.java | 37 +++++++++++++++++-- .../exec/physical/impl/TestSimpleFunctions.java | 38 ++++++++++++++++++++ .../test/resources/functions/testSubstring.json | 2 +- .../functions/testSubstringNegative.json | 37 +++++++++++++++++++ 4 files changed, 110 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/068a1dfd/sandbox/prototype/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/Substring.java ---------------------------------------------------------------------- diff --git a/sandbox/prototype/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/Substring.java b/sandbox/prototype/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/Substring.java index 785ed7d..5466159 100644 --- a/sandbox/prototype/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/Substring.java +++ b/sandbox/prototype/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/Substring.java @@ -30,8 +30,22 @@ import org.apache.drill.exec.record.RecordBatch; import org.apache.drill.exec.vector.*; // TODO: implement optional length parameter -// implement negative start position +// implement UTF-8 and UTF-16 support +/** + * Evaluate a substring expression for a given value; specifying the start + * position, and optionally the end position. + * + * - If the start position is negative, start from abs(start) characters from + * the end of the buffer. + * + * - If no length is specified, continue to the end of the string. + * + * - If the substring expression's length exceeds the value's upward bound, the + * value's length will be used. + * + * - If the substring is invalid, return an empty string. + */ @FunctionTemplate(name = "substring", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) @@ -48,8 +62,25 @@ public class Substring implements DrillFunc { @Override public void eval() { out.buffer = string.buffer; - out.start = (int)offset.value; - out.end = (int)length.value; + + // handle invalid values; e.g. SUBSTRING(value, 0, x) or SUBSTRING(value, x, 0) + if (offset.value == 0 || length.value <= 0) { + out.start = 0; + out.end = 0; + return; + } + + // handle negative and positive offset values + if (offset.value < 0) + out.start = string.end + (int)offset.value; + else + out.start = (int)offset.value - 1; + + // calculate end position from length and truncate to upper value bounds + if (out.start + length.value > string.end) + out.end = string.end; + else + out.end = out.start + (int)length.value; } public static class Provider implements CallProvider { http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/068a1dfd/sandbox/prototype/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestSimpleFunctions.java ---------------------------------------------------------------------- diff --git a/sandbox/prototype/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestSimpleFunctions.java b/sandbox/prototype/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestSimpleFunctions.java index bed976c..17932e7 100644 --- a/sandbox/prototype/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestSimpleFunctions.java +++ b/sandbox/prototype/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestSimpleFunctions.java @@ -143,6 +143,44 @@ public class TestSimpleFunctions { } + @Test + public void testSubstringNegative(@Injectable final DrillbitContext bitContext, + @Injectable UserServer.UserClientConnection connection) throws Throwable{ + + new NonStrictExpectations(){{ + bitContext.getMetrics(); result = new MetricRegistry("test"); + bitContext.getAllocator(); result = BufferAllocator.getAllocator(c); + }}; + + PhysicalPlanReader reader = new PhysicalPlanReader(c, c.getMapper(), CoordinationProtos.DrillbitEndpoint.getDefaultInstance()); + PhysicalPlan plan = reader.readPhysicalPlan(Files.toString(FileUtils.getResourceAsFile("/functions/testSubstringNegative.json"), Charsets.UTF_8)); + FunctionImplementationRegistry registry = new FunctionImplementationRegistry(c); + FragmentContext context = new FragmentContext(bitContext, ExecProtos.FragmentHandle.getDefaultInstance(), connection, null, registry); + SimpleRootExec exec = new SimpleRootExec(ImplCreator.getExec(context, (FragmentRoot) plan.getSortedOperators(false).iterator().next())); + + while(exec.next()){ + NullableVarCharVector c1 = exec.getValueVectorById(new SchemaPath("col3", ExpressionPosition.UNKNOWN), NullableVarCharVector.class); + NullableVarCharVector.Accessor a1; + a1 = c1.getAccessor(); + + int count = 0; + for(int i = 0; i < c1.getAccessor().getValueCount(); i++){ + if (!a1.isNull(i)) { + NullableVarCharHolder holder = new NullableVarCharHolder(); + a1.get(i, holder); + assertEquals("aa", holder.toString()); + ++count; + } + } + assertEquals(50, count); + } + + if(context.getFailureCause() != null){ + throw context.getFailureCause(); + } + assertTrue(!context.isFailed()); + + } @After public void tearDown() throws Exception{ http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/068a1dfd/sandbox/prototype/exec/java-exec/src/test/resources/functions/testSubstring.json ---------------------------------------------------------------------- diff --git a/sandbox/prototype/exec/java-exec/src/test/resources/functions/testSubstring.json b/sandbox/prototype/exec/java-exec/src/test/resources/functions/testSubstring.json index 33b0da0..08a8ece 100644 --- a/sandbox/prototype/exec/java-exec/src/test/resources/functions/testSubstring.json +++ b/sandbox/prototype/exec/java-exec/src/test/resources/functions/testSubstring.json @@ -25,7 +25,7 @@ child: 1, pop:"project", exprs: [ - { ref: "col3", expr:"substring(yellow, 0, 4)" } + { ref: "col3", expr:"substring(yellow, 1, 4)" } ] }, { http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/068a1dfd/sandbox/prototype/exec/java-exec/src/test/resources/functions/testSubstringNegative.json ---------------------------------------------------------------------- diff --git a/sandbox/prototype/exec/java-exec/src/test/resources/functions/testSubstringNegative.json b/sandbox/prototype/exec/java-exec/src/test/resources/functions/testSubstringNegative.json new file mode 100644 index 0000000..4951913 --- /dev/null +++ b/sandbox/prototype/exec/java-exec/src/test/resources/functions/testSubstringNegative.json @@ -0,0 +1,37 @@ +{ + head:{ + type:"APACHE_DRILL_PHYSICAL", + version:"1", + generator:{ + type:"manual" + } + }, + graph:[ + { + @id:1, + pop:"mock-scan", + url: "http://apache.org", + entries:[ + {records: 100, types: [ + {name: "blue", type: "INT", mode: "REQUIRED"}, + {name: "red", type: "BIGINT", mode: "REQUIRED"}, + {name: "yellow", type: "VARCHAR", mode: "OPTIONAL"}, + {name: "green", type: "INT", mode: "REQUIRED"} + ]} + ] + }, + { + @id:2, + child: 1, + pop:"project", + exprs: [ + { ref: "col3", expr:"substring(yellow, -3, 2)" } + ] + }, + { + @id: 3, + child: 2, + pop: "screen" + } + ] +} \ No newline at end of file
