[CALCITE-2572] SQL standard semantics for SUBSTRING function (Andrew Pilloud)

Close apache/calcite#842


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/4f1b5eff
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/4f1b5eff
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/4f1b5eff

Branch: refs/heads/master
Commit: 4f1b5eff9dbed20ecd8b327ada7c6db1bfd8d78d
Parents: 19826da
Author: Andrew Pilloud <apill...@google.com>
Authored: Wed Sep 19 10:57:55 2018 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Sep 19 20:11:30 2018 -0700

----------------------------------------------------------------------
 .../apache/calcite/runtime/SqlFunctions.java    | 42 +++++++++++++---
 .../calcite/sql/test/SqlOperatorBaseTest.java   | 52 ++++++++++++++++++++
 2 files changed, 86 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/4f1b5eff/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java 
b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index d795102..c7c3875 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -108,23 +108,49 @@ public class SqlFunctions {
   }
 
   /** SQL SUBSTRING(string FROM ... FOR ...) function. */
-  public static String substring(String s, int from, int for_) {
-    return s.substring(from - 1, Math.min(from - 1 + for_, s.length()));
+  public static String substring(String c, int s, int l) {
+    int lc = c.length();
+    if (s < 0) {
+      s += lc + 1;
+    }
+    int e = s + l;
+    if (e < s) {
+      throw new IllegalArgumentException("substring error: negative substring 
length not allowed");
+    }
+    if (s > lc || e < 1) {
+      return "";
+    }
+    int s1 = Math.max(s, 1);
+    int e1 = Math.min(e, lc + 1);
+    return c.substring(s1 - 1, e1 - 1);
   }
 
   /** SQL SUBSTRING(string FROM ...) function. */
-  public static String substring(String s, int from) {
-    return s.substring(from - 1);
+  public static String substring(String c, int s) {
+    return substring(c, s, c.length() + 1);
   }
 
   /** SQL SUBSTRING(binary FROM ... FOR ...) function. */
-  public static ByteString substring(ByteString b, int from, int for_) {
-    return b.substring(from - 1, Math.min(from - 1 + for_, b.length()));
+  public static ByteString substring(ByteString c, int s, int l) {
+    int lc = c.length();
+    if (s < 0) {
+      s += lc + 1;
+    }
+    int e = s + l;
+    if (e < s) {
+      throw new IllegalArgumentException("substring error: negative substring 
length not allowed");
+    }
+    if (s > lc || e < 1) {
+      return ByteString.EMPTY;
+    }
+    int s1 = Math.max(s, 1);
+    int e1 = Math.min(e, lc + 1);
+    return c.substring(s1 - 1, e1 - 1);
   }
 
   /** SQL SUBSTRING(binary FROM ...) function. */
-  public static ByteString substring(ByteString b, int from) {
-    return b.substring(from - 1);
+  public static ByteString substring(ByteString c, int s) {
+    return substring(c, s, c.length() + 1);
   }
 
   /** SQL UPPER(string) function. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/4f1b5eff/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java 
b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
index aac111c..c892f94 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
@@ -5209,14 +5209,66 @@ public abstract class SqlOperatorBaseTest {
         "ab",
         "VARCHAR(3) NOT NULL");
     tester.checkString(
+        "substring('abc' from 2 for 8)",
+        "bc",
+        "VARCHAR(3) NOT NULL");
+    tester.checkString(
+        "substring('abc' from 0 for 2)",
+        "a",
+        "VARCHAR(3) NOT NULL");
+    tester.checkString(
+        "substring('abc' from 0 for 0)",
+        "",
+        "VARCHAR(3) NOT NULL");
+    tester.checkString(
+        "substring('abc' from 8 for 2)",
+        "",
+        "VARCHAR(3) NOT NULL");
+    tester.checkFails(
+        "substring('abc' from 1 for -1)",
+        "substring error: negative substring length not allowed",
+        true);
+    tester.checkString(
         "substring('abc' from 2)", "bc", "VARCHAR(3) NOT NULL");
+    tester.checkString(
+        "substring('abc' from 0)", "abc", "VARCHAR(3) NOT NULL");
+    tester.checkString(
+        "substring('abc' from 8)", "", "VARCHAR(3) NOT NULL");
+    tester.checkString(
+        "substring('abc' from -2)", "bc", "VARCHAR(3) NOT NULL");
 
     tester.checkString(
         "substring(x'aabbcc' from 1 for 2)",
         "aabb",
         "VARBINARY(3) NOT NULL");
     tester.checkString(
+        "substring(x'aabbcc' from 2 for 8)",
+        "bbcc",
+        "VARBINARY(3) NOT NULL");
+    tester.checkString(
+        "substring(x'aabbcc' from 0 for 2)",
+        "aa",
+        "VARBINARY(3) NOT NULL");
+    tester.checkString(
+        "substring(x'aabbcc' from 0 for 0)",
+        "",
+        "VARBINARY(3) NOT NULL");
+    tester.checkString(
+        "substring(x'aabbcc' from 8 for 2)",
+        "",
+        "VARBINARY(3) NOT NULL");
+    tester.checkFails(
+        "substring(x'aabbcc' from 1 for -1)",
+        "substring error: negative substring length not allowed",
+        true);
+    tester.checkString(
         "substring(x'aabbcc' from 2)", "bbcc", "VARBINARY(3) NOT NULL");
+    tester.checkString(
+        "substring(x'aabbcc' from 0)", "aabbcc", "VARBINARY(3) NOT NULL");
+    tester.checkString(
+        "substring(x'aabbcc' from 8)", "", "VARBINARY(3) NOT NULL");
+    tester.checkString(
+        "substring(x'aabbcc' from -2)", "bbcc", "VARBINARY(3) NOT NULL");
 
     if (Bug.FRG296_FIXED) {
       // substring regexp not supported yet

Reply via email to