blob: c9016eb8bbaf4f12b7e71644b022e5efd1fc0288 [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.flink.table.runtime.functions;
import org.apache.flink.table.data.DecimalData;
import org.apache.flink.table.data.DecimalDataUtils;
import org.apache.flink.table.data.binary.BinaryStringData;
import org.apache.flink.table.data.binary.BinaryStringDataUtil;
import org.apache.flink.table.utils.EncodingUtils;
import org.apache.flink.table.utils.ThreadLocalCache;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.apache.flink.table.data.DecimalDataUtils.castFrom;
import static org.apache.flink.table.data.DecimalDataUtils.doubleValue;
/**
* Built-in scalar runtime functions.
*
* <p>NOTE: Before you add functions here, check if Calcite provides it in {@code
* org.apache.calcite.runtime.SqlFunctions}. Furthermore, make sure to implement the function
* efficiently. Sometimes it makes sense to create a {@code
* org.apache.flink.table.codegen.calls.CallGenerator} instead to avoid massive object creation and
* reuse instances.
*/
public class SqlFunctionUtils {
private static final Logger LOG = LoggerFactory.getLogger(SqlFunctionUtils.class);
private static final ThreadLocalCache<String, Pattern> REGEXP_PATTERN_CACHE =
new ThreadLocalCache<String, Pattern>() {
@Override
public Pattern getNewInstance(String regex) {
return Pattern.compile(regex);
}
};
private static final ThreadLocalCache<String, URL> URL_CACHE =
new ThreadLocalCache<String, URL>() {
public URL getNewInstance(String url) {
try {
return new URL(url);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
};
private static final Map<String, String> EMPTY_MAP = new HashMap<>(0);
public static double exp(DecimalData d) {
return Math.exp(doubleValue(d));
}
public static double power(double base, DecimalData exponent) {
return Math.pow(base, doubleValue(exponent));
}
public static double power(DecimalData base, DecimalData exponent) {
return Math.pow(doubleValue(base), doubleValue(exponent));
}
public static double power(DecimalData base, double exponent) {
return Math.pow(doubleValue(base), exponent);
}
public static double cosh(DecimalData x) {
return Math.cosh(doubleValue(x));
}
public static double acos(DecimalData a) {
return Math.acos(doubleValue(a));
}
public static double asin(DecimalData a) {
return Math.asin(doubleValue(a));
}
public static double atan(DecimalData a) {
return Math.atan(doubleValue(a));
}
public static double atan2(DecimalData y, DecimalData x) {
return Math.atan2(doubleValue(y), doubleValue(x));
}
public static double sin(DecimalData a) {
return Math.sin(doubleValue(a));
}
public static double sinh(DecimalData a) {
return Math.sinh(doubleValue(a));
}
public static double cos(DecimalData a) {
return Math.cos(doubleValue(a));
}
public static double tan(DecimalData a) {
return Math.tan(doubleValue(a));
}
/** Calculates the hyperbolic tangent of a big decimal number. */
public static double tanh(DecimalData a) {
return Math.tanh(doubleValue(a));
}
public static double cot(DecimalData a) {
return 1.0d / Math.tan(doubleValue(a));
}
public static double degrees(DecimalData angrad) {
return Math.toDegrees(doubleValue(angrad));
}
public static double radians(DecimalData angdeg) {
return Math.toRadians(doubleValue(angdeg));
}
public static DecimalData abs(DecimalData a) {
return DecimalDataUtils.abs(a);
}
public static DecimalData floor(DecimalData a) {
return DecimalDataUtils.floor(a);
}
public static DecimalData ceil(DecimalData a) {
return DecimalDataUtils.ceil(a);
}
// -------------------------- natural logarithm ------------------------
/** Returns the natural logarithm of "x". */
public static double log(double x) {
return Math.log(x);
}
public static double log(DecimalData x) {
return Math.log(doubleValue(x));
}
/** Returns the logarithm of "x" with base "base". */
public static double log(double base, double x) {
return Math.log(x) / Math.log(base);
}
public static double log(double base, DecimalData x) {
return log(base, doubleValue(x));
}
public static double log(DecimalData base, double x) {
return log(doubleValue(base), x);
}
public static double log(DecimalData base, DecimalData x) {
return log(doubleValue(base), doubleValue(x));
}
/** Returns the logarithm of "a" with base 2. */
public static double log2(double x) {
return Math.log(x) / Math.log(2);
}
public static double log2(DecimalData x) {
return log2(doubleValue(x));
}
public static double log10(double x) {
return Math.log10(x);
}
public static double log10(DecimalData x) {
return log10(doubleValue(x));
}
// -------------------------- string functions ------------------------
/**
* Returns the string str left-padded with the string pad to a length of len characters. If str
* is longer than len, the return value is shortened to len characters.
*/
public static String lpad(String base, int len, String pad) {
if (len < 0 || "".equals(pad)) {
return null;
} else if (len == 0) {
return "";
}
char[] data = new char[len];
char[] baseChars = base.toCharArray();
char[] padChars = pad.toCharArray();
// the length of the padding needed
int pos = Math.max(len - base.length(), 0);
// copy the padding
for (int i = 0; i < pos; i += pad.length()) {
for (int j = 0; j < pad.length() && j < pos - i; j++) {
data[i + j] = padChars[j];
}
}
// copy the base
int i = 0;
while (pos + i < len && i < base.length()) {
data[pos + i] = baseChars[i];
i += 1;
}
return new String(data);
}
/**
* Returns the string str right-padded with the string pad to a length of len characters. If str
* is longer than len, the return value is shortened to len characters.
*/
public static String rpad(String base, int len, String pad) {
if (len < 0 || "".equals(pad)) {
return null;
} else if (len == 0) {
return "";
}
char[] data = new char[len];
char[] baseChars = base.toCharArray();
char[] padChars = pad.toCharArray();
int pos = 0;
// copy the base
while (pos < base.length() && pos < len) {
data[pos] = baseChars[pos];
pos += 1;
}
// copy the padding
while (pos < len) {
int i = 0;
while (i < pad.length() && i < len - pos) {
data[pos + i] = padChars[i];
i += 1;
}
pos += pad.length();
}
return new String(data);
}
/** Returns a string that repeats the base string n times. */
public static String repeat(String str, int repeat) {
return EncodingUtils.repeat(str, repeat);
}
/** Replaces all the old strings with the replacement string. */
public static String replace(String str, String oldStr, String replacement) {
return str.replace(oldStr, replacement);
}
/**
* Split target string with custom separator and pick the index-th(start with 0) result.
*
* @param str target string.
* @param separator custom separator.
* @param index index of the result which you want.
* @return the string at the index of split results.
*/
public static String splitIndex(String str, String separator, int index) {
if (index < 0) {
return null;
}
String[] values = StringUtils.splitByWholeSeparatorPreserveAllTokens(str, separator);
if (index >= values.length) {
return null;
} else {
return values[index];
}
}
/**
* Split target string with custom separator and pick the index-th(start with 0) result.
*
* @param str target string.
* @param character int value of the separator character
* @param index index of the result which you want.
* @return the string at the index of split results.
*/
public static String splitIndex(String str, int character, int index) {
if (character > 255 || character < 1 || index < 0) {
return null;
}
String[] values = StringUtils.splitPreserveAllTokens(str, (char) character);
if (index >= values.length) {
return null;
} else {
return values[index];
}
}
/**
* Returns a string resulting from replacing all substrings that match the regular expression
* with replacement.
*/
public static String regexpReplace(String str, String regex, String replacement) {
if (str == null || regex == null || replacement == null) {
return null;
}
try {
return str.replaceAll(regex, Matcher.quoteReplacement(replacement));
} catch (Exception e) {
LOG.error(
String.format(
"Exception in regexpReplace('%s', '%s', '%s')",
str, regex, replacement),
e);
// return null if exception in regex replace
return null;
}
}
/**
* Returns a string extracted with a specified regular expression and a regex match group index.
*/
public static String regexpExtract(String str, String regex, int extractIndex) {
if (str == null || regex == null) {
return null;
}
try {
Matcher m = Pattern.compile(regex).matcher(str);
if (m.find()) {
MatchResult mr = m.toMatchResult();
return mr.group(extractIndex);
}
} catch (Exception e) {
LOG.error(
String.format(
"Exception in regexpExtract('%s', '%s', '%d')",
str, regex, extractIndex),
e);
}
return null;
}
public static String regexpExtract(String str, String regex, long extractIndex) {
return regexpExtract(str, regex, (int) extractIndex);
}
/** Returns the first string extracted with a specified regular expression. */
public static String regexpExtract(String str, String regex) {
return regexpExtract(str, regex, 0);
}
/**
* Parse string as key-value string and return the value matches key name. example:
* keyvalue('k1=v1;k2=v2', ';', '=', 'k2') = 'v2' keyvalue('k1:v1,k2:v2', ',', ':', 'k3') = NULL
*
* @param str target string.
* @param pairSeparator separator between key-value tuple.
* @param kvSeparator separator between key and value.
* @param keyName name of the key whose value you want return.
* @return target value.
*/
public static BinaryStringData keyValue(
BinaryStringData str,
BinaryStringData pairSeparator,
BinaryStringData kvSeparator,
BinaryStringData keyName) {
if (str == null || str.getSizeInBytes() == 0) {
return null;
}
if (pairSeparator != null
&& pairSeparator.getSizeInBytes() == 1
&& kvSeparator != null
&& kvSeparator.getSizeInBytes() == 1) {
return BinaryStringDataUtil.keyValue(
str, pairSeparator.byteAt(0), kvSeparator.byteAt(0), keyName);
} else {
return BinaryStringData.fromString(
keyValue(
BinaryStringDataUtil.safeToString(str),
BinaryStringDataUtil.safeToString(pairSeparator),
BinaryStringDataUtil.safeToString(kvSeparator),
BinaryStringDataUtil.safeToString(keyName)));
}
}
private static String keyValue(
String str, String pairSeparator, String kvSeparator, String keyName) {
try {
if (StringUtils.isEmpty(str)) {
return null;
}
String[] values = StringUtils.split(str, pairSeparator);
for (String value : values) {
if (!StringUtils.isEmpty(value)) {
String[] kv = StringUtils.split(kvSeparator);
if (kv != null && kv.length == 2 && kv[0].equals(keyName)) {
return kv[1];
}
}
}
return null;
} catch (Exception e) {
LOG.error("Exception when parse key-value", e);
return null;
}
}
/**
* Calculate the hash value of a given string.
*
* @param algorithm message digest algorithm.
* @param str string to hash.
* @return hash value of string.
*/
public static String hash(String algorithm, String str) {
return hash(algorithm, str, "");
}
/**
* Calculate the hash value of a given string.
*
* @param algorithm message digest algorithm.
* @param str string to hash.
* @param charsetName charset of string.
* @return hash value of string.
*/
public static String hash(String algorithm, String str, String charsetName) {
try {
byte[] digest =
MessageDigest.getInstance(algorithm)
.digest(strToBytesWithCharset(str, charsetName));
return EncodingUtils.hex(digest);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Unsupported algorithm: " + algorithm, e);
}
}
private static byte[] strToBytesWithCharset(String str, String charsetName) {
byte[] byteArray = null;
if (!StringUtils.isEmpty(charsetName)) {
try {
byteArray = str.getBytes(charsetName);
} catch (UnsupportedEncodingException e) {
LOG.warn(
"Unsupported encoding: " + charsetName + ", fallback to system charset", e);
byteArray = null;
}
}
if (byteArray == null) {
byteArray = str.getBytes(StandardCharsets.UTF_8);
}
return byteArray;
}
/**
* Parse url and return various components of the URL. If accept any null arguments, return
* null.
*
* @param urlStr URL string.
* @param partToExtract determines which components would return. accept values:
* HOST,PATH,QUERY,REF, PROTOCOL,FILE,AUTHORITY,USERINFO
* @return target value.
*/
public static String parseUrl(String urlStr, String partToExtract) {
URL url;
try {
url = URL_CACHE.get(urlStr);
} catch (Exception e) {
LOG.error("Parse URL error: " + urlStr, e);
return null;
}
if ("HOST".equals(partToExtract)) {
return url.getHost();
}
if ("PATH".equals(partToExtract)) {
return url.getPath();
}
if ("QUERY".equals(partToExtract)) {
return url.getQuery();
}
if ("REF".equals(partToExtract)) {
return url.getRef();
}
if ("PROTOCOL".equals(partToExtract)) {
return url.getProtocol();
}
if ("FILE".equals(partToExtract)) {
return url.getFile();
}
if ("AUTHORITY".equals(partToExtract)) {
return url.getAuthority();
}
if ("USERINFO".equals(partToExtract)) {
return url.getUserInfo();
}
return null;
}
/**
* Parse url and return various parameter of the URL. If accept any null arguments, return null.
*
* @param urlStr URL string.
* @param partToExtract must be QUERY, or return null.
* @param key parameter name.
* @return target value.
*/
public static String parseUrl(String urlStr, String partToExtract, String key) {
if (!"QUERY".equals(partToExtract)) {
return null;
}
String query = parseUrl(urlStr, partToExtract);
if (query == null) {
return null;
}
Pattern p = Pattern.compile("(&|^)" + Pattern.quote(key) + "=([^&]*)");
Matcher m = p.matcher(query);
if (m.find()) {
return m.group(2);
}
return null;
}
public static int divideInt(int a, int b) {
return a / b;
}
public static String subString(String str, long start, long len) {
if (len < 0) {
LOG.error(
"len of 'substring(str, start, len)' must be >= 0 and Int type, but len = {}",
len);
return null;
}
if (len > Integer.MAX_VALUE || start > Integer.MAX_VALUE) {
LOG.error(
"len or start of 'substring(str, start, len)' must be Int type, but len = {}, start = {}",
len,
start);
return null;
}
int length = (int) len;
int pos = (int) start;
if (str.isEmpty()) {
return "";
}
int startPos;
int endPos;
if (pos > 0) {
startPos = pos - 1;
if (startPos >= str.length()) {
return "";
}
} else if (pos < 0) {
startPos = str.length() + pos;
if (startPos < 0) {
return "";
}
} else {
startPos = 0;
}
if ((str.length() - startPos) < length) {
endPos = str.length();
} else {
endPos = startPos + length;
}
return str.substring(startPos, endPos);
}
public static String subString(String str, long start) {
return subString(str, start, Integer.MAX_VALUE);
}
public static String chr(long chr) {
if (chr < 0) {
return "";
} else if ((chr & 0xFF) == 0) {
return String.valueOf(Character.MIN_VALUE);
} else {
return String.valueOf((char) (chr & 0xFF));
}
}
public static String overlay(String s, String r, long start, long length) {
if (start <= 0 || start > s.length()) {
return s;
} else {
StringBuilder sb = new StringBuilder();
int startPos = (int) start;
int len = (int) length;
sb.append(s, 0, startPos - 1);
sb.append(r);
if ((startPos + len) <= s.length() && len > 0) {
sb.append(s.substring(startPos - 1 + len));
}
return sb.toString();
}
}
public static String overlay(String s, String r, long start) {
return overlay(s, r, start, r.length());
}
public static int position(BinaryStringData seek, BinaryStringData s) {
return position(seek, s, 1);
}
public static int position(BinaryStringData seek, BinaryStringData s, int from) {
return s.indexOf(seek, from - 1) + 1;
}
public static int instr(
BinaryStringData str,
BinaryStringData subString,
int startPosition,
int nthAppearance) {
if (nthAppearance <= 0) {
throw new IllegalArgumentException("nthAppearance must be positive!");
}
if (startPosition == 0) {
return 0;
} else if (startPosition > 0) {
int startIndex = startPosition;
int index = 0;
for (int i = 0; i < nthAppearance; i++) {
index = str.indexOf(subString, startIndex - 1) + 1;
if (index == 0) {
return 0;
}
startIndex = index + 1;
}
return index;
} else {
int pos =
instr(
BinaryStringDataUtil.reverse(str),
BinaryStringDataUtil.reverse(subString),
-startPosition,
nthAppearance);
if (pos == 0) {
return 0;
} else {
return str.numChars() + 2 - pos - subString.numChars();
}
}
}
/** Returns the hex string of a long argument. */
public static String hex(long x) {
return Long.toHexString(x).toUpperCase();
}
/** Returns the hex string of a string argument. */
public static String hex(String x) {
return EncodingUtils.hex(x.getBytes(StandardCharsets.UTF_8)).toUpperCase();
}
/**
* Creates a map by parsing text. Split text into key-value pairs using two delimiters. The
* first delimiter separates pairs, and the second delimiter separates key and value. If only
* one parameter is given, default delimiters are used: ',' as delimiter1 and '=' as delimiter2.
*
* @param text the input text
* @return the map
*/
public static Map<String, String> strToMap(String text) {
return strToMap(text, ",", "=");
}
/**
* Creates a map by parsing text. Split text into key-value pairs using two delimiters. The
* first delimiter separates pairs, and the second delimiter separates key and value.
*
* @param text the input text
* @param listDelimiter the delimiter to separates pairs
* @param keyValueDelimiter the delimiter to separates key and value
* @return the map
*/
public static Map<String, String> strToMap(
String text, String listDelimiter, String keyValueDelimiter) {
if (StringUtils.isEmpty(text)) {
return EMPTY_MAP;
}
String[] keyValuePairs = text.split(listDelimiter);
Map<String, String> ret = new HashMap<>(keyValuePairs.length);
for (String keyValuePair : keyValuePairs) {
String[] keyValue = keyValuePair.split(keyValueDelimiter, 2);
if (keyValue.length < 2) {
ret.put(keyValuePair, null);
} else {
ret.put(keyValue[0], keyValue[1]);
}
}
return ret;
}
// SQL ROUND
/** SQL <code>ROUND</code> operator applied to byte values. */
public static byte sround(byte b0) {
return sround(b0, 0);
}
/** SQL <code>ROUND</code> operator applied to byte values. */
public static byte sround(byte b0, int b1) {
return sround(BigDecimal.valueOf(b0), b1).byteValue();
}
/** SQL <code>ROUND</code> operator applied to short values. */
public static short sround(short b0) {
return sround(b0, 0);
}
/** SQL <code>ROUND</code> operator applied to short values. */
public static short sround(short b0, int b1) {
return sround(BigDecimal.valueOf(b0), b1).shortValue();
}
/** SQL <code>ROUND</code> operator applied to int values. */
public static int sround(int b0) {
return sround(b0, 0);
}
/** SQL <code>ROUND</code> operator applied to int values. */
public static int sround(int b0, int b1) {
return sround(BigDecimal.valueOf(b0), b1).intValue();
}
/** SQL <code>ROUND</code> operator applied to long values. */
public static long sround(long b0) {
return sround(b0, 0);
}
/** SQL <code>ROUND</code> operator applied to long values. */
public static long sround(long b0, int b1) {
return sround(BigDecimal.valueOf(b0), b1).longValue();
}
/** SQL <code>ROUND</code> operator applied to BigDecimal values. */
public static BigDecimal sround(BigDecimal b0) {
return sround(b0, 0);
}
/** SQL <code>ROUND</code> operator applied to BigDecimal values. */
public static BigDecimal sround(BigDecimal b0, int b1) {
return b0.movePointRight(b1).setScale(0, RoundingMode.HALF_UP).movePointLeft(b1);
}
/** SQL <code>ROUND</code> operator applied to float values. */
public static float sround(float b0) {
return sround(b0, 0);
}
/** SQL <code>ROUND</code> operator applied to float values. */
public static float sround(float b0, int b1) {
return sround(BigDecimal.valueOf(b0), b1).floatValue();
}
/** SQL <code>ROUND</code> operator applied to double values. */
public static double sround(double b0) {
return sround(b0, 0);
}
/** SQL <code>ROUND</code> operator applied to double values. */
public static double sround(double b0, int b1) {
return sround(BigDecimal.valueOf(b0), b1).doubleValue();
}
/** SQL <code>ROUND</code> operator applied to DecimalData values. */
public static DecimalData sround(DecimalData b0) {
return sround(b0, 0);
}
/** SQL <code>ROUND</code> operator applied to DecimalData values. */
public static DecimalData sround(DecimalData b0, int b1) {
return DecimalDataUtils.sround(b0, b1);
}
public static DecimalData sign(DecimalData b0) {
return DecimalDataUtils.sign(b0);
}
public static boolean isDecimal(Object obj) {
if ((obj instanceof Long)
|| (obj instanceof Integer)
|| (obj instanceof Short)
|| (obj instanceof Byte)
|| (obj instanceof Float)
|| (obj instanceof Double)
|| (obj instanceof BigDecimal)
|| (obj instanceof BigInteger)) {
return true;
}
if (obj instanceof String || obj instanceof Character) {
String s = obj.toString();
if (s.isEmpty()) {
return false;
}
return isInteger(s) || isLong(s) || isDouble(s);
} else {
return false;
}
}
private static boolean isInteger(String s) {
boolean flag = true;
try {
Integer.parseInt(s);
} catch (NumberFormatException e) {
flag = false;
}
return flag;
}
private static boolean isLong(String s) {
boolean flag = true;
try {
Long.parseLong(s);
} catch (NumberFormatException e) {
flag = false;
}
return flag;
}
private static boolean isDouble(String s) {
boolean flag = true;
try {
Double.parseDouble(s);
} catch (NumberFormatException e) {
flag = false;
}
return flag;
}
public static boolean isDigit(Object obj) {
if ((obj instanceof Long)
|| (obj instanceof Integer)
|| (obj instanceof Short)
|| (obj instanceof Byte)) {
return true;
}
if (obj instanceof String) {
String s = obj.toString();
if (s.isEmpty()) {
return false;
}
return StringUtils.isNumeric(s);
} else {
return false;
}
}
public static boolean isAlpha(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof String)) {
return false;
}
String s = obj.toString();
if ("".equals(s)) {
return false;
}
return StringUtils.isAlpha(s);
}
public static Integer hashCode(String str) {
if (str == null) {
return Integer.MIN_VALUE;
}
return Math.abs(str.hashCode());
}
public static Boolean regExp(String s, String regex) {
if (regex.length() == 0) {
return false;
}
try {
return (REGEXP_PATTERN_CACHE.get(regex)).matcher(s).find(0);
} catch (Exception e) {
LOG.error("Exception when compile and match regex:" + regex + " on: " + s, e);
return false;
}
}
public static Byte bitAnd(Byte a, Byte b) {
if (a == null || b == null) {
return 0;
}
return (byte) (a & b);
}
public static Short bitAnd(Short a, Short b) {
if (a == null || b == null) {
return 0;
}
return (short) (a & b);
}
public static Integer bitAnd(Integer a, Integer b) {
if (a == null || b == null) {
return 0;
}
return a & b;
}
public static Long bitAnd(Long a, Long b) {
if (a == null || b == null) {
return 0L;
}
return a & b;
}
public static Byte bitNot(Byte a) {
if (a == null) {
a = 0;
}
return (byte) (~a);
}
public static Short bitNot(Short a) {
if (a == null) {
a = 0;
}
return (short) (~a);
}
public static Integer bitNot(Integer a) {
if (a == null) {
a = 0;
}
return ~a;
}
public static Long bitNot(Long a) {
if (a == null) {
a = 0L;
}
return ~a;
}
public static Byte bitOr(Byte a, Byte b) {
if (a == null || b == null) {
if (a == null) {
a = 0;
}
if (b == null) {
b = 0;
}
}
return (byte) (a | b);
}
public static Short bitOr(Short a, Short b) {
if (a == null || b == null) {
if (a == null) {
a = 0;
}
if (b == null) {
b = 0;
}
}
return (short) (a | b);
}
public static Integer bitOr(Integer a, Integer b) {
if (a == null || b == null) {
if (a == null) {
a = 0;
}
if (b == null) {
b = 0;
}
}
return a | b;
}
public static Long bitOr(Long a, Long b) {
if (a == null || b == null) {
if (a == null) {
a = 0L;
}
if (b == null) {
b = 0L;
}
}
return a | b;
}
public static Byte bitXor(Byte a, Byte b) {
if (a == null || b == null) {
if (a == null) {
a = 0;
}
if (b == null) {
b = 0;
}
}
return (byte) (a ^ b);
}
public static Short bitXor(Short a, Short b) {
if (a == null || b == null) {
if (a == null) {
a = 0;
}
if (b == null) {
b = 0;
}
}
return (short) (a ^ b);
}
public static Integer bitXor(Integer a, Integer b) {
if (a == null || b == null) {
if (a == null) {
a = 0;
}
if (b == null) {
b = 0;
}
}
return a ^ b;
}
public static Long bitXor(Long a, Long b) {
if (a == null || b == null) {
if (a == null) {
a = 0L;
}
if (b == null) {
b = 0L;
}
}
return a ^ b;
}
public static String toBase64(BinaryStringData bs) {
return toBase64(bs.toBytes());
}
public static String toBase64(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
public static BinaryStringData fromBase64(BinaryStringData bs) {
return BinaryStringData.fromBytes(Base64.getDecoder().decode(bs.toBytes()));
}
public static BinaryStringData fromBase64(byte[] bytes) {
return BinaryStringData.fromBytes(Base64.getDecoder().decode(bytes));
}
public static String uuid() {
return UUID.randomUUID().toString();
}
public static String uuid(byte[] b) {
return UUID.nameUUIDFromBytes(b).toString();
}
/** SQL <code>TRUNCATE</code> operator applied to BigDecimal values. */
public static DecimalData struncate(DecimalData b0) {
return struncate(b0, 0);
}
public static DecimalData struncate(DecimalData b0, int b1) {
if (b1 >= b0.scale()) {
return b0;
}
BigDecimal b2 =
b0.toBigDecimal()
.movePointRight(b1)
.setScale(0, RoundingMode.DOWN)
.movePointLeft(b1);
int p = b0.precision();
int s = b0.scale();
if (b1 < 0) {
return DecimalData.fromBigDecimal(b2, Math.min(38, 1 + p - s), 0);
} else {
return DecimalData.fromBigDecimal(b2, 1 + p - s + b1, b1);
}
}
/** SQL <code>TRUNCATE</code> operator applied to double values. */
public static float struncate(float b0) {
return struncate(b0, 0);
}
public static float struncate(float b0, int b1) {
return (float) doubleValue(struncate(castFrom((double) b0, 38, 18), b1));
}
/**
* Compares two byte arrays in lexicographical order.
*
* <p>The result is positive if {@code array1} is great than {@code array2}, negative if {@code
* array1} is less than {@code array2} and 0 if {@code array1} is equal to {@code array2}.
*
* <p>Note: Currently, this is used in {@code ScalarOperatorGens} for comparing two fields of
* binary or varbinary type.
*
* @param array1 byte array to compare.
* @param array2 byte array to compare.
* @return an Integer indicating which one is bigger
*/
public static int byteArrayCompare(byte[] array1, byte[] array2) {
for (int i = 0, j = 0; i < array1.length && j < array2.length; i++, j++) {
int a = (array1[i] & 0xff);
int b = (array2[j] & 0xff);
if (a != b) {
return a - b;
}
}
return array1.length - array2.length;
}
}