blob: c126370694f67339e464e3d159c0b192ed201ca1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.beam.sdk.extensions.sql.zetasql;
import java.util.regex.Pattern;
import org.apache.beam.vendor.calcite.v1_20_0.org.apache.calcite.linq4j.function.Strict;
import org.apache.beam.vendor.calcite.v1_20_0.org.apache.calcite.runtime.SqlFunctions;
/** StringFunctions. */
public class StringFunctions {
public static final String SUBSTR_PARAMETER_EXCEED_INTEGER =
"SUBSTR function only allows: "
+ Integer.MIN_VALUE
+ " <= position or length <= "
+ Integer.MAX_VALUE;
@Strict
public static Boolean startsWith(String str1, String str2) {
return str1.startsWith(str2);
}
@Strict
public static Boolean endsWith(String str1, String str2) {
return str1.endsWith(str2);
}
@Strict
public static String concat(String arg) {
return arg;
}
@Strict
public static String concat(String arg1, String arg2) {
return concatIfNotIncludeNull(arg1, arg2);
}
@Strict
public static String concat(String arg1, String arg2, String arg3) {
return concatIfNotIncludeNull(arg1, arg2, arg3);
}
@Strict
public static String concat(String arg1, String arg2, String arg3, String arg4) {
return concatIfNotIncludeNull(arg1, arg2, arg3, arg4);
}
@Strict
public static String concat(String arg1, String arg2, String arg3, String arg4, String arg5) {
return concatIfNotIncludeNull(arg1, arg2, arg3, arg4, arg5);
}
@Strict
private static String concatIfNotIncludeNull(String... args) {
return String.join("", args);
}
// https://jira.apache.org/jira/browse/CALCITE-2889
// public static String concat(String... args) {
// StringBuilder stringBuilder = new StringBuilder();
// for (String arg : args) {
// stringBuilder.append(arg);
// }
// return stringBuilder.toString();
// }
@Strict
public static String replace(String origin, String target, String replacement) {
// Java's string.replace behaves differently when target = "". When target = "",
// Java's replace function replace every character in origin with replacement,
// while origin value should not be changed is expected in SQL.
if (target.length() == 0) {
return origin;
}
return origin.replace(target, replacement);
}
public static String trim(String str) {
return trim(str, " ");
}
@Strict
public static String trim(String str, String seek) {
return SqlFunctions.trim(true, true, seek, str, false);
}
public static String ltrim(String str) {
return ltrim(str, " ");
}
@Strict
public static String ltrim(String str, String seek) {
return SqlFunctions.trim(true, false, seek, str, false);
}
public static String rtrim(String str) {
return rtrim(str, " ");
}
@Strict
public static String rtrim(String str, String seek) {
return SqlFunctions.trim(false, true, seek, str, false);
}
public static String substr(String str, long from, long len) {
if (from > Integer.MAX_VALUE
|| len > Integer.MAX_VALUE
|| from < Integer.MIN_VALUE
|| len < Integer.MIN_VALUE) {
throw new RuntimeException(SUBSTR_PARAMETER_EXCEED_INTEGER);
}
return SqlFunctions.substring(str, (int) from, (int) len);
}
@Strict
public static String reverse(String str) {
return new StringBuilder(str).reverse().toString();
}
@Strict
public static Long charLength(String str) {
return (long) str.length();
}
// ZetaSQL's LIKE statement does not support the ESCAPE clause. Instead it
// always uses \ as an escape character.
@Strict
public static Boolean like(String s, String pattern) {
String regex = sqlToRegexLike(pattern, '\\');
return Pattern.matches(regex, s);
}
private static final String JAVA_REGEX_SPECIALS = "[]()|^-+*?{}$\\.";
/**
* Translates a SQL LIKE pattern to Java regex pattern. Modified from Apache Calcite's
* Like.sqlToRegexLike
*/
private static String sqlToRegexLike(String sqlPattern, char escapeChar) {
int i;
final int len = sqlPattern.length();
final StringBuilder javaPattern = new StringBuilder(len + len);
for (i = 0; i < len; i++) {
char c = sqlPattern.charAt(i);
if (c == escapeChar) {
if (i == (sqlPattern.length() - 1)) {
throw new RuntimeException("LIKE pattern ends with a backslash");
}
char nextChar = sqlPattern.charAt(++i);
if (JAVA_REGEX_SPECIALS.indexOf(nextChar) >= 0) {
javaPattern.append('\\');
}
javaPattern.append(nextChar);
} else if (c == '_') {
javaPattern.append('.');
} else if (c == '%') {
javaPattern.append("(?s:.*)");
} else {
if (JAVA_REGEX_SPECIALS.indexOf(c) >= 0) {
javaPattern.append('\\');
}
javaPattern.append(c);
}
}
return javaPattern.toString();
}
}