Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.hazelcast.shaded.org.apache.calcite.runtime.SqlFunctions Maven / Gradle / Ivy
/*
* 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 com.hazelcast.shaded.org.apache.calcite.runtime;
import com.hazelcast.shaded.org.apache.calcite.DataContext;
import com.hazelcast.shaded.org.apache.calcite.avatica.util.ByteString;
import com.hazelcast.shaded.org.apache.calcite.avatica.util.DateTimeUtils;
import com.hazelcast.shaded.org.apache.calcite.avatica.util.Spaces;
import com.hazelcast.shaded.org.apache.calcite.avatica.util.TimeUnitRange;
import com.hazelcast.shaded.org.apache.calcite.interpreter.Row;
import com.hazelcast.shaded.org.apache.calcite.linq4j.AbstractEnumerable;
import com.hazelcast.shaded.org.apache.calcite.linq4j.CartesianProductEnumerator;
import com.hazelcast.shaded.org.apache.calcite.linq4j.Enumerable;
import com.hazelcast.shaded.org.apache.calcite.linq4j.Enumerator;
import com.hazelcast.shaded.org.apache.calcite.linq4j.Linq4j;
import com.hazelcast.shaded.org.apache.calcite.linq4j.function.Deterministic;
import com.hazelcast.shaded.org.apache.calcite.linq4j.function.Experimental;
import com.hazelcast.shaded.org.apache.calcite.linq4j.function.Function1;
import com.hazelcast.shaded.org.apache.calcite.linq4j.function.NonDeterministic;
import com.hazelcast.shaded.org.apache.calcite.linq4j.tree.Primitive;
import com.hazelcast.shaded.org.apache.calcite.rel.type.TimeFrame;
import com.hazelcast.shaded.org.apache.calcite.rel.type.TimeFrameSet;
import com.hazelcast.shaded.org.apache.calcite.runtime.FlatLists.ComparableList;
import com.hazelcast.shaded.org.apache.calcite.sql.SqlUtil;
import com.hazelcast.shaded.org.apache.calcite.sql.fun.SqlLibraryOperators;
import com.hazelcast.shaded.org.apache.calcite.util.NumberUtil;
import com.hazelcast.shaded.org.apache.calcite.util.TimeWithTimeZoneString;
import com.hazelcast.shaded.org.apache.calcite.util.TimestampWithTimeZoneString;
import com.hazelcast.shaded.org.apache.calcite.util.Unsafe;
import com.hazelcast.shaded.org.apache.calcite.util.Util;
import com.hazelcast.shaded.org.apache.calcite.util.format.FormatElement;
import com.hazelcast.shaded.org.apache.calcite.util.format.FormatModels;
import com.hazelcast.shaded.org.apache.commons.codec.DecoderException;
import com.hazelcast.shaded.org.apache.commons.codec.binary.Base32;
import com.hazelcast.shaded.org.apache.commons.codec.binary.Hex;
import com.hazelcast.shaded.org.apache.commons.codec.digest.DigestUtils;
import com.hazelcast.shaded.org.apache.commons.codec.language.Soundex;
import com.hazelcast.shaded.com.google.common.base.Splitter;
import com.hazelcast.shaded.com.google.common.base.Strings;
import com.hazelcast.shaded.com.google.common.collect.ImmutableList;
import com.hazelcast.shaded.org.checkerframework.checker.nullness.qual.Nullable;
import com.hazelcast.shaded.org.checkerframework.checker.nullness.qual.PolyNull;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.SignStyle;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BinaryOperator;
import java.util.regex.Pattern;
import static com.hazelcast.shaded.org.apache.calcite.linq4j.Nullness.castNonNull;
import static com.hazelcast.shaded.org.apache.calcite.util.Static.RESOURCE;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
/**
* Helper methods to implement SQL functions in generated code.
*
* Not present: and, or, not (builtin operators are better, because they
* use lazy evaluation. Implementations do not check for null values; the
* calling code must do that.
*
*
Many of the functions do not check for null values. This is intentional.
* If null arguments are possible, the code-generation framework checks for
* nulls before calling the functions.
*/
@SuppressWarnings("UnnecessaryUnboxing")
@Deterministic
public class SqlFunctions {
@SuppressWarnings("unused")
private static final DecimalFormat DOUBLE_FORMAT =
NumberUtil.decimalFormat("0.0E0");
private static final TimeZone LOCAL_TZ = TimeZone.getDefault();
private static final DateTimeFormatter ROOT_DAY_FORMAT =
DateTimeFormatter.ofPattern("EEEE", Locale.ROOT);
private static final DateTimeFormatter ROOT_MONTH_FORMAT =
DateTimeFormatter.ofPattern("MMMM", Locale.ROOT);
private static final Soundex SOUNDEX = new Soundex();
private static final int SOUNDEX_LENGTH = 4;
private static final Pattern FROM_BASE64_REGEXP = Pattern.compile("[\\t\\n\\r\\s]");
private static final Base32 BASE_32 = new Base32();
private static final Function1, Enumerable> LIST_AS_ENUMERABLE =
a0 -> a0 == null ? Linq4j.emptyEnumerable() : Linq4j.asEnumerable(a0);
@SuppressWarnings("unused")
private static final Function1> ARRAY_CARTESIAN_PRODUCT =
lists -> {
final List> enumerators = new ArrayList<>();
for (Object list : lists) {
enumerators.add(Linq4j.enumerator((List) list));
}
final Enumerator> product = Linq4j.product(enumerators);
return new AbstractEnumerable<@Nullable Object[]>() {
@Override public Enumerator<@Nullable Object[]> enumerator() {
return Linq4j.transform(product, List::toArray);
}
};
};
/** Holds, for each thread, a map from sequence name to sequence current
* value.
*
* This is a straw man of an implementation whose main goal is to prove
* that sequences can be parsed, validated and planned. A real application
* will want persistent values for sequences, shared among threads. */
private static final ThreadLocal<@Nullable Map> THREAD_SEQUENCES =
ThreadLocal.withInitial(HashMap::new);
private static final Pattern PATTERN_0_STAR_E = Pattern.compile("0*E");
/** A byte string consisting of a single byte that is the ASCII space
* character (0x20). */
private static final ByteString SINGLE_SPACE_BYTE_STRING =
ByteString.of("20", 16);
// Date formatter for BigQuery's timestamp literals:
// https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#timestamp_literals
private static final DateTimeFormatter BIG_QUERY_TIMESTAMP_LITERAL_FORMATTER =
new DateTimeFormatterBuilder()
// Unlike ISO 8601, BQ only supports years between 1 - 9999,
// but can support single-digit month and day parts.
.appendValue(ChronoField.YEAR, 4)
.appendLiteral('-')
.appendValue(ChronoField.MONTH_OF_YEAR, 1, 2, SignStyle.NOT_NEGATIVE)
.appendLiteral('-')
.appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE)
// Everything after the date is optional. Optional sections can be nested.
.optionalStart()
// BQ accepts either a literal 'T' or a space to separate the date from the time,
// so make the 'T' optional but pad with 1 space if it's omitted.
.padNext(1, ' ')
.optionalStart()
.appendLiteral('T')
.optionalEnd()
// Unlike ISO 8601, BQ can support single-digit hour, minute, and second parts.
.appendValue(ChronoField.HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE)
.appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE)
.appendLiteral(':')
.appendValue(ChronoField.SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE)
// ISO 8601 supports up to nanosecond precision, but BQ only up to microsecond.
.optionalStart()
.appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true)
.optionalEnd()
.optionalStart()
.parseLenient()
.appendOffsetId()
.toFormatter(Locale.ROOT);
/** Whether the current Java version is 8 (1.8). */
private static final boolean IS_JDK_8 =
System.getProperty("java.version").startsWith("1.8");
private SqlFunctions() {
}
/** Internal THROW_UNLESS(condition, message) function.
*
* The method is marked {@link NonDeterministic} to prevent the generator
* from storing its value as a constant. */
@NonDeterministic
public static boolean throwUnless(boolean condition, String message) {
if (!condition) {
throw new IllegalStateException(message);
}
return condition;
}
/** SQL TO_BASE64(string) function. */
public static String toBase64(String string) {
return toBase64_(string.getBytes(UTF_8));
}
/** SQL TO_BASE64(string) function for binary string. */
public static String toBase64(ByteString string) {
return toBase64_(string.getBytes());
}
private static String toBase64_(byte[] bytes) {
String base64 = Base64.getEncoder().encodeToString(bytes);
StringBuilder str = new StringBuilder(base64.length() + base64.length() / 76);
Splitter.fixedLength(76).split(base64).iterator().forEachRemaining(s -> {
str.append(s);
str.append("\n");
});
return str.substring(0, str.length() - 1);
}
/** SQL FROM_BASE64(string) function. */
public static @Nullable ByteString fromBase64(String base64) {
try {
base64 = FROM_BASE64_REGEXP.matcher(base64).replaceAll("");
return new ByteString(Base64.getDecoder().decode(base64));
} catch (IllegalArgumentException e) {
return null;
}
}
/** SQL TO_BASE32(string) function. */
public static String toBase32(String string) {
return toBase32_(string.getBytes(UTF_8));
}
/** SQL TO_BASE32(string) function for binary string. */
public static String toBase32(ByteString string) {
return toBase32_(string.getBytes());
}
private static String toBase32_(byte[] bytes) {
return BASE_32.encodeToString(bytes);
}
/** SQL FROM_BASE32(string) function. */
public static ByteString fromBase32(String base32) {
return new ByteString(BASE_32.decode(base32));
}
/** SQL FROM_HEX(varchar) function. */
public static ByteString fromHex(String hex) {
try {
return new ByteString(Hex.decodeHex(hex));
} catch (DecoderException e) {
throw new IllegalArgumentException(
String.format(Locale.ROOT, "Failed to decode hex string: %s", hex), e);
}
}
/** SQL TO_HEX(binary) function. */
public static String toHex(ByteString byteString) {
return Hex.encodeHexString(byteString.getBytes());
}
/** SQL MD5(string) function. */
public static String md5(String string) {
return DigestUtils.md5Hex(string.getBytes(UTF_8));
}
/** SQL MD5(string) function for binary string. */
public static String md5(ByteString string) {
return DigestUtils.md5Hex(string.getBytes());
}
/** SQL SHA1(string) function. */
public static String sha1(String string) {
return DigestUtils.sha1Hex(string.getBytes(UTF_8));
}
/** SQL SHA1(string) function for binary string. */
public static String sha1(ByteString string) {
return DigestUtils.sha1Hex(string.getBytes());
}
/** SQL SHA256(string) function. */
public static String sha256(String string) {
return DigestUtils.sha256Hex(string.getBytes(UTF_8));
}
/** SQL SHA256(string) function for binary string. */
public static String sha256(ByteString string) {
return DigestUtils.sha256Hex(string.getBytes());
}
/** SQL SHA512(string) function. */
public static String sha512(String string) {
return DigestUtils.sha512Hex(string.getBytes(UTF_8));
}
/** SQL SHA512(string) function for binary string. */
public static String sha512(ByteString string) {
return DigestUtils.sha512Hex(string.getBytes());
}
/** SQL {@code REGEXP_REPLACE} function with 3 arguments. */
public static String regexpReplace(String s, String regex,
String replacement) {
return regexpReplace(s, regex, replacement, 1, 0, null);
}
/** SQL {@code REGEXP_REPLACE} function with 4 arguments. */
public static String regexpReplace(String s, String regex, String replacement,
int pos) {
return regexpReplace(s, regex, replacement, pos, 0, null);
}
/** SQL {@code REGEXP_REPLACE} function with 5 arguments. */
public static String regexpReplace(String s, String regex, String replacement,
int pos, int occurrence) {
return regexpReplace(s, regex, replacement, pos, occurrence, null);
}
/** SQL {@code REGEXP_REPLACE} function with 6 arguments. */
public static String regexpReplace(String s, String regex, String replacement,
int pos, int occurrence, @Nullable String matchType) {
if (pos < 1 || pos > s.length()) {
throw RESOURCE.invalidInputForRegexpReplace(Integer.toString(pos)).ex();
}
final int flags = makeRegexpFlags(matchType);
final Pattern pattern = Pattern.compile(regex, flags);
return Unsafe.regexpReplace(s, pattern, replacement, pos, occurrence);
}
private static int makeRegexpFlags(@Nullable String stringFlags) {
int flags = 0;
if (stringFlags != null) {
for (int i = 0; i < stringFlags.length(); ++i) {
switch (stringFlags.charAt(i)) {
case 'i':
flags |= Pattern.CASE_INSENSITIVE;
break;
case 'c':
flags &= ~Pattern.CASE_INSENSITIVE;
break;
case 'n':
flags |= Pattern.DOTALL;
break;
case 'm':
flags |= Pattern.MULTILINE;
break;
default:
throw RESOURCE.invalidInputForRegexpReplace(stringFlags).ex();
}
}
}
return flags;
}
/** SQL {@code LPAD(string, integer, string)} function. */
public static String lpad(String originalValue, int returnLength,
String pattern) {
if (returnLength < 0) {
throw RESOURCE.illegalNegativePadLength().ex();
}
if (pattern.isEmpty()) {
throw RESOURCE.illegalEmptyPadPattern().ex();
}
if (returnLength <= originalValue.length()) {
return originalValue.substring(0, returnLength);
}
int paddingLengthRequired = returnLength - originalValue.length();
int patternLength = pattern.length();
final StringBuilder paddedS = new StringBuilder();
for (int i = 0; i < paddingLengthRequired; i++) {
char curChar = pattern.charAt(i % patternLength);
paddedS.append(curChar);
}
paddedS.append(originalValue);
return paddedS.toString();
}
/** SQL {@code LPAD(string, integer)} function. */
public static String lpad(String originalValue, int returnLength) {
return lpad(originalValue, returnLength, " ");
}
/** SQL {@code LPAD(binary, integer, binary)} function. */
public static ByteString lpad(ByteString originalValue, int returnLength,
ByteString pattern) {
if (returnLength < 0) {
throw RESOURCE.illegalNegativePadLength().ex();
}
if (pattern.length() == 0) {
throw RESOURCE.illegalEmptyPadPattern().ex();
}
if (returnLength <= originalValue.length()) {
return originalValue.substring(0, returnLength);
}
int paddingLengthRequired = returnLength - originalValue.length();
int patternLength = pattern.length();
byte[] bytes = new byte[returnLength];
for (int i = 0; i < paddingLengthRequired; i++) {
byte curByte = pattern.byteAt(i % patternLength);
bytes[i] = curByte;
}
for (int i = paddingLengthRequired; i < returnLength; i++) {
bytes[i] = originalValue.byteAt(i - paddingLengthRequired);
}
return new ByteString(bytes);
}
/** SQL {@code LPAD(binary, integer, binary)} function. */
public static ByteString lpad(ByteString originalValue, int returnLength) {
// 0x20 is the hexadecimal character for space ' '
return lpad(originalValue, returnLength, SINGLE_SPACE_BYTE_STRING);
}
/** SQL {@code RPAD(string, integer, string)} function. */
public static String rpad(String originalValue, int returnLength,
String pattern) {
if (returnLength < 0) {
throw RESOURCE.illegalNegativePadLength().ex();
}
if (pattern.isEmpty()) {
throw RESOURCE.illegalEmptyPadPattern().ex();
}
if (returnLength <= originalValue.length()) {
return originalValue.substring(0, returnLength);
}
int paddingLengthRequired = returnLength - originalValue.length();
int patternLength = pattern.length();
final StringBuilder paddedS = new StringBuilder();
paddedS.append(originalValue);
for (int i = 0; i < paddingLengthRequired; i++) {
char curChar = pattern.charAt(i % patternLength);
paddedS.append(curChar);
}
return paddedS.toString();
}
/** SQL {@code RPAD(string, integer)} function. */
public static String rpad(String originalValue, int returnLength) {
return rpad(originalValue, returnLength, " ");
}
/** SQL {@code RPAD(binary, integer, binary)} function. */
public static ByteString rpad(ByteString originalValue, int returnLength,
ByteString pattern) {
if (returnLength < 0) {
throw RESOURCE.illegalNegativePadLength().ex();
}
if (pattern.length() == 0) {
throw RESOURCE.illegalEmptyPadPattern().ex();
}
int originalLength = originalValue.length();
if (returnLength <= originalLength) {
return originalValue.substring(0, returnLength);
}
int paddingLengthRequired = returnLength - originalLength;
int patternLength = pattern.length();
byte[] bytes = new byte[returnLength];
for (int i = 0; i < originalLength; i++) {
bytes[i] = originalValue.byteAt(i);
}
for (int i = returnLength - paddingLengthRequired; i < returnLength; i++) {
byte curByte = pattern.byteAt(i % patternLength);
bytes[i] = curByte;
}
return new ByteString(bytes);
}
/** SQL {@code RPAD(binary, integer)} function. */
public static ByteString rpad(ByteString originalValue, int returnLength) {
return rpad(originalValue, returnLength, SINGLE_SPACE_BYTE_STRING);
}
/** SQL {@code ENDS_WITH(string, string)} function. */
public static boolean endsWith(String s0, String s1) {
return s0.endsWith(s1);
}
/** SQL {@code ENDS_WITH(binary, binary)} function. */
public static boolean endsWith(ByteString s0, ByteString s1) {
return s0.endsWith(s1);
}
/** SQL {@code STARTS_WITH(string, string)} function. */
public static boolean startsWith(String s0, String s1) {
return s0.startsWith(s1);
}
/** SQL {@code STARTS_WITH(binary, binary)} function. */
public static boolean startsWith(ByteString s0, ByteString s1) {
return s0.startsWith(s1);
}
/** SQL {@code SPLIT(string, string)} function. */
public static List split(String s, String delimiter) {
if (s.isEmpty()) {
return ImmutableList.of();
}
if (delimiter.isEmpty()) {
return ImmutableList.of(s); // prevent mischief
}
final ImmutableList.Builder list = ImmutableList.builder();
for (int i = 0;;) {
int j = s.indexOf(delimiter, i);
if (j < 0) {
list.add(s.substring(i));
return list.build();
}
list.add(s.substring(i, j));
i = j + delimiter.length();
}
}
/** SQL {@code SPLIT(string)} function. */
public static List split(String s) {
return split(s, ",");
}
/** SQL {@code SPLIT(binary, binary)} function. */
public static List split(ByteString s, ByteString delimiter) {
if (s.length() == 0) {
return ImmutableList.of();
}
if (delimiter.length() == 0) {
return ImmutableList.of(s); // prevent mischief
}
final ImmutableList.Builder list = ImmutableList.builder();
for (int i = 0;;) {
int j = s.indexOf(delimiter, i);
if (j < 0) {
list.add(s.substring(i));
return list.build();
}
list.add(s.substring(i, j));
i = j + delimiter.length();
}
}
/** SQL SUBSTRING(string FROM ...) function. */
public static String substring(String c, int s) {
final int s0 = s - 1;
if (s0 <= 0) {
return c;
}
if (s > c.length()) {
return "";
}
return c.substring(s0);
}
/** SQL SUBSTRING(string FROM ... FOR ...) function. */
public static String substring(String c, int s, int l) {
int lc = c.length();
long e = (long) s + (long) l;
if (l < 0) {
throw RESOURCE.illegalNegativeSubstringLength().ex();
}
// Prevent overflow in addition
if (s > lc || e < 1L) {
return "";
}
final int s0 = Math.max(s - 1, 0);
final long e0 = Math.min(e - 1, (long) lc);
// We know that e0 cannot exceed Integer.MAX_VALUE, since it's smaller than lc
return c.substring(s0, (int) e0);
}
/** SQL SUBSTRING(binary FROM ...) function for binary. */
public static ByteString substring(ByteString c, int s) {
final int s0 = s - 1;
if (s0 <= 0) {
return c;
}
if (s > c.length()) {
return ByteString.EMPTY;
}
return c.substring(s0);
}
/** SQL SUBSTRING(binary FROM ... FOR ...) function for binary. */
public static ByteString substring(ByteString c, int s, int l) {
int lc = c.length();
int e = s + l;
if (l < 0) {
throw RESOURCE.illegalNegativeSubstringLength().ex();
}
if (s > lc || e < 1) {
return ByteString.EMPTY;
}
final int s0 = Math.max(s - 1, 0);
final int e0 = Math.min(e - 1, lc);
return c.substring(s0, e0);
}
/** SQL UPPER(string) function. */
public static String upper(String s) {
return s.toUpperCase(Locale.ROOT);
}
/** SQL LOWER(string) function. */
public static String lower(String s) {
return s.toLowerCase(Locale.ROOT);
}
/** SQL INITCAP(string) function. */
public static String initcap(String s) {
// Assumes Alpha as [A-Za-z0-9]
// white space is treated as everything else.
final int len = s.length();
boolean start = true;
final StringBuilder newS = new StringBuilder();
for (int i = 0; i < len; i++) {
char curCh = s.charAt(i);
final int c = (int) curCh;
if (start) { // curCh is whitespace or first character of word.
if (c > 47 && c < 58) { // 0-9
start = false;
} else if (c > 64 && c < 91) { // A-Z
start = false;
} else if (c > 96 && c < 123) { // a-z
start = false;
curCh = (char) (c - 32); // Uppercase this character
}
// else {} whitespace
} else { // Inside of a word or white space after end of word.
if (c > 47 && c < 58) { // 0-9
// noop
} else if (c > 64 && c < 91) { // A-Z
curCh = (char) (c + 32); // Lowercase this character
} else if (c > 96 && c < 123) { // a-z
// noop
} else { // whitespace
start = true;
}
}
newS.append(curCh);
} // for each character in s
return newS.toString();
}
/** SQL REVERSE(string) function. */
public static String reverse(String s) {
final StringBuilder buf = new StringBuilder(s);
return buf.reverse().toString();
}
/** SQL ASCII(string) function. */
public static int ascii(String s) {
return s.isEmpty()
? 0 : s.codePointAt(0);
}
/** SQL REPEAT(string, int) function. */
public static String repeat(String s, int n) {
if (n < 1) {
return "";
}
return Strings.repeat(s, n);
}
/** SQL SPACE(int) function. */
public static String space(int n) {
return repeat(" ", n);
}
/** SQL STRCMP(String,String) function. */
public static int strcmp(String s0, String s1) {
return (int) Math.signum(s1.compareTo(s0));
}
/** SQL SOUNDEX(string) function. */
public static String soundex(String s) {
return SOUNDEX.soundex(s);
}
/** SQL DIFFERENCE(string, string) function. */
public static int difference(String s0, String s1) {
String result0 = soundex(s0);
String result1 = soundex(s1);
for (int i = 0; i < SOUNDEX_LENGTH; i++) {
if (result0.charAt(i) != result1.charAt(i)) {
return i;
}
}
return SOUNDEX_LENGTH;
}
/** SQL LEFT(string, integer) function. */
public static String left(String s, int n) {
if (n <= 0) {
return "";
}
int len = s.length();
if (n >= len) {
return s;
}
return s.substring(0, n);
}
/** SQL LEFT(ByteString, integer) function. */
public static ByteString left(ByteString s, int n) {
if (n <= 0) {
return ByteString.EMPTY;
}
int len = s.length();
if (n >= len) {
return s;
}
return s.substring(0, n);
}
/** SQL RIGHT(string, integer) function. */
public static String right(String s, int n) {
if (n <= 0) {
return "";
}
int len = s.length();
if (n >= len) {
return s;
}
return s.substring(len - n);
}
/** SQL RIGHT(ByteString, integer) function. */
public static ByteString right(ByteString s, int n) {
if (n <= 0) {
return ByteString.EMPTY;
}
final int len = s.length();
if (n >= len) {
return s;
}
return s.substring(len - n);
}
/** SQL CHAR(integer) function, as in MySQL and Spark.
*
* Returns the ASCII character of {@code n} modulo 256,
* or null if {@code n} < 0. */
public static @Nullable String charFromAscii(int n) {
if (n < 0) {
return null;
}
return String.valueOf(Character.toChars(n % 256));
}
/** SQL CHR(integer) function, as in Oracle and Postgres.
*
*
Returns the UTF-8 character whose code is {@code n}. */
public static String charFromUtf8(int n) {
return String.valueOf(Character.toChars(n));
}
/** SQL OCTET_LENGTH(binary) function. */
public static int octetLength(ByteString s) {
return s.length();
}
/** SQL CHARACTER_LENGTH(string) function. */
public static int charLength(String s) {
return s.length();
}
/** SQL BIT_LENGTH(string) function. */
public static int bitLength(String s) {
return s.getBytes(UTF_8).length * 8;
}
/** SQL BIT_LENGTH(binary) function. */
public static int bitLength(ByteString s) {
return s.length() * 8;
}
/** SQL {@code string || string} operator. */
public static String concat(String s0, String s1) {
return s0 + s1;
}
/** Concatenates two strings.
* Returns null only when both s0 and s1 are null,
* otherwise null is treated as empty string. */
public static @Nullable String concatWithNull(@Nullable String s0,
@Nullable String s1) {
if (s0 == null) {
return s1;
} else if (s1 == null) {
return s0;
} else {
return s0 + s1;
}
}
/** SQL {@code binary || binary} operator. */
public static ByteString concat(ByteString s0, ByteString s1) {
return s0.concat(s1);
}
/** SQL {@code CONCAT(arg0, arg1, arg2, ...)} function. */
public static String concatMulti(String... args) {
return String.join("", args);
}
/** SQL {@code CONCAT(arg0, ...)} function which can accept null
* but never return null. Always treats null as empty string. */
public static String concatMultiWithNull(String... args) {
StringBuilder sb = new StringBuilder();
for (String arg : args) {
sb.append(arg == null ? "" : arg);
}
return sb.toString();
}
/** SQL {@code CONCAT_WS(sep, arg1, arg2, ...)} function;
* treats null arguments as empty strings. */
public static String concatMultiWithSeparator(String... args) {
// the separator arg could be null
final String sep = args[0] == null ? "" : args[0];
StringBuilder sb = new StringBuilder();
for (int i = 1; i < args.length; i++) {
if (args[i] != null) {
if (i < args.length - 1) {
sb.append(args[i]).append(sep);
} else {
// no separator after the last arg
sb.append(args[i]);
}
}
}
return sb.toString();
}
/** SQL {@code CONVERT(s, src_charset, dest_charset)} function. */
public static String convertWithCharset(String s, String srcCharset,
String destCharset) {
final Charset src = SqlUtil.getCharset(srcCharset);
final Charset dest = SqlUtil.getCharset(destCharset);
byte[] bytes = s.getBytes(src);
final CharsetDecoder decoder = dest.newDecoder();
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
try {
return decoder.decode(buffer).toString();
} catch (CharacterCodingException ex) {
throw RESOURCE.charsetEncoding(s, dest.name()).ex();
}
}
/** SQL {@code TRANSLATE(s USING transcodingName)} function,
* also known as {@code CONVERT(s USING transcodingName)}. */
public static String translateWithCharset(String s, String transcodingName) {
final Charset charset = SqlUtil.getCharset(transcodingName);
byte[] bytes = s.getBytes(Charset.defaultCharset());
final CharsetDecoder decoder = charset.newDecoder();
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
try {
return decoder.decode(buffer).toString();
} catch (CharacterCodingException ex) {
throw RESOURCE.charsetEncoding(s, charset.name()).ex();
}
}
/** SQL {@code RTRIM} function applied to string. */
public static String rtrim(String s) {
return trim(false, true, " ", s);
}
/** SQL {@code LTRIM} function. */
public static String ltrim(String s) {
return trim(true, false, " ", s);
}
/** SQL {@code TRIM(... seek FROM s)} function. */
public static String trim(boolean left, boolean right, String seek,
String s) {
return trim(left, right, seek, s, true);
}
public static String trim(boolean left, boolean right, String seek,
String s, boolean strict) {
if (strict && seek.length() != 1) {
throw RESOURCE.trimError().ex();
}
int j = s.length();
if (right) {
for (;;) {
if (j == 0) {
return "";
}
if (seek.indexOf(s.charAt(j - 1)) < 0) {
break;
}
--j;
}
}
int i = 0;
if (left) {
for (;;) {
if (i == j) {
return "";
}
if (seek.indexOf(s.charAt(i)) < 0) {
break;
}
++i;
}
}
return s.substring(i, j);
}
/** SQL {@code TRIM} function applied to binary string. */
public static ByteString trim(ByteString s) {
return trim_(s, true, true);
}
/** Helper for CAST. */
public static ByteString rtrim(ByteString s) {
return trim_(s, false, true);
}
/** SQL {@code TRIM} function applied to binary string. */
private static ByteString trim_(ByteString s, boolean left, boolean right) {
int j = s.length();
if (right) {
for (;;) {
if (j == 0) {
return ByteString.EMPTY;
}
if (s.byteAt(j - 1) != 0) {
break;
}
--j;
}
}
int i = 0;
if (left) {
for (;;) {
if (i == j) {
return ByteString.EMPTY;
}
if (s.byteAt(i) != 0) {
break;
}
++i;
}
}
return s.substring(i, j);
}
/** SQL {@code OVERLAY} function. */
public static String overlay(String s, String r, int start) {
return s.substring(0, start - 1)
+ r
+ s.substring(start - 1 + r.length());
}
/** SQL {@code OVERLAY} function. */
public static String overlay(String s, String r, int start, int length) {
return s.substring(0, start - 1)
+ r
+ s.substring(start - 1 + length);
}
/** SQL {@code OVERLAY} function applied to binary strings. */
public static ByteString overlay(ByteString s, ByteString r, int start) {
return s.substring(0, start - 1)
.concat(r)
.concat(s.substring(start - 1 + r.length()));
}
/** SQL {@code OVERLAY} function applied to binary strings. */
public static ByteString overlay(ByteString s, ByteString r, int start,
int length) {
return s.substring(0, start - 1)
.concat(r)
.concat(s.substring(start - 1 + length));
}
/** SQL {@code LIKE} function. */
public static boolean like(String s, String pattern) {
final String regex = Like.sqlToRegexLike(pattern, null);
return Pattern.matches(regex, s);
}
/** SQL {@code LIKE} function with escape. */
public static boolean like(String s, String pattern, String escape) {
final String regex = Like.sqlToRegexLike(pattern, escape);
return Pattern.matches(regex, s);
}
/** SQL {@code ILIKE} function. */
public static boolean ilike(String s, String pattern) {
final String regex = Like.sqlToRegexLike(pattern, null);
return Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(s).matches();
}
/** SQL {@code ILIKE} function with escape. */
public static boolean ilike(String s, String pattern, String escape) {
final String regex = Like.sqlToRegexLike(pattern, escape);
return Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(s).matches();
}
/** SQL {@code RLIKE} function. */
public static boolean rlike(String s, String pattern) {
return Pattern.compile(pattern).matcher(s).find();
}
/** SQL {@code SIMILAR} function. */
public static boolean similar(String s, String pattern) {
final String regex = Like.sqlToRegexSimilar(pattern, null);
return Pattern.matches(regex, s);
}
/** SQL {@code SIMILAR} function with escape. */
public static boolean similar(String s, String pattern, String escape) {
final String regex = Like.sqlToRegexSimilar(pattern, escape);
return Pattern.matches(regex, s);
}
public static boolean posixRegex(String s, String regex, boolean caseSensitive) {
final Pattern pattern = Like.posixRegexToPattern(regex, caseSensitive);
return pattern.matcher(s).find();
}
// =
/** SQL =
operator applied to BigDecimal values (neither may be
* null). */
public static boolean eq(BigDecimal b0, BigDecimal b1) {
return b0.stripTrailingZeros().equals(b1.stripTrailingZeros());
}
/** SQL =
operator applied to Object[] values (neither may be
* null). */
public static boolean eq(@Nullable Object @Nullable [] b0, @Nullable Object @Nullable [] b1) {
return Arrays.deepEquals(b0, b1);
}
/** SQL =
operator applied to Object values (including String;
* neither side may be null). */
public static boolean eq(Object b0, Object b1) {
return b0.equals(b1);
}
/** SQL =
operator applied to String values with a certain Comparator. */
public static boolean eq(String s0, String s1, Comparator comparator) {
return comparator.compare(s0, s1) == 0;
}
/** SQL =
operator applied to Object values (at least one operand
* has ANY type; neither may be null). */
public static boolean eqAny(Object b0, Object b1) {
if (b0.getClass().equals(b1.getClass())) {
// The result of SqlFunctions.eq(BigDecimal, BigDecimal) makes more sense
// than BigDecimal.equals(BigDecimal). So if both of types are BigDecimal,
// we just use SqlFunctions.eq(BigDecimal, BigDecimal).
if (BigDecimal.class.isInstance(b0)) {
return eq((BigDecimal) b0, (BigDecimal) b1);
} else {
return b0.equals(b1);
}
} else if (allAssignable(Number.class, b0, b1)) {
return eq(toBigDecimal((Number) b0), toBigDecimal((Number) b1));
}
// We shouldn't rely on implementation even though overridden equals can
// handle other types which may create worse result: for example,
// a.equals(b) != b.equals(a)
return false;
}
/** Returns whether two objects can both be assigned to a given class. */
private static boolean allAssignable(Class clazz, Object o0, Object o1) {
return clazz.isInstance(o0) && clazz.isInstance(o1);
}
// <>
/** SQL <gt;
operator applied to BigDecimal values. */
public static boolean ne(BigDecimal b0, BigDecimal b1) {
return b0.compareTo(b1) != 0;
}
/** SQL <gt;
operator applied to Object values (including
* String; neither side may be null). */
public static boolean ne(Object b0, Object b1) {
return !eq(b0, b1);
}
/** SQL <gt;
operator applied to OString values with a certain Comparator. */
public static boolean ne(String s0, String s1, Comparator comparator) {
return !eq(s0, s1, comparator);
}
/** SQL <gt;
operator applied to Object values (at least one
* operand has ANY type, including String; neither may be null). */
public static boolean neAny(Object b0, Object b1) {
return !eqAny(b0, b1);
}
// <
/** SQL <
operator applied to boolean values. */
public static boolean lt(boolean b0, boolean b1) {
return Boolean.compare(b0, b1) < 0;
}
/** SQL <
operator applied to String values. */
public static boolean lt(String b0, String b1) {
return b0.compareTo(b1) < 0;
}
/** SQL <
operator applied to String values. */
public static boolean lt(String b0, String b1, Comparator comparator) {
return comparator.compare(b0, b1) < 0;
}
/** SQL <
operator applied to ByteString values. */
public static boolean lt(ByteString b0, ByteString b1) {
return b0.compareTo(b1) < 0;
}
/** SQL <
operator applied to BigDecimal values. */
public static boolean lt(BigDecimal b0, BigDecimal b1) {
return b0.compareTo(b1) < 0;
}
/** Returns whether {@code b0} is less than {@code b1}
* (or {@code b1} is null). Helper for {@code ARG_MIN}. */
public static > boolean ltNullable(T b0, T b1) {
return b1 == null || b0 != null && b0.compareTo(b1) < 0;
}
public static boolean lt(byte b0, byte b1) {
return b0 < b1;
}
public static boolean lt(char b0, char b1) {
return b0 < b1;
}
public static boolean lt(short b0, short b1) {
return b0 < b1;
}
public static boolean lt(int b0, int b1) {
return b0 < b1;
}
public static boolean lt(long b0, long b1) {
return b0 < b1;
}
public static boolean lt(float b0, float b1) {
return b0 < b1;
}
public static boolean lt(double b0, double b1) {
return b0 < b1;
}
/** SQL <
operator applied to Object values. */
public static boolean ltAny(Object b0, Object b1) {
if (b0.getClass().equals(b1.getClass())
&& b0 instanceof Comparable) {
//noinspection unchecked
return ((Comparable) b0).compareTo(b1) < 0;
} else if (allAssignable(Number.class, b0, b1)) {
return lt(toBigDecimal((Number) b0), toBigDecimal((Number) b1));
}
throw notComparable("<", b0, b1);
}
// <=
/** SQL ≤
operator applied to boolean values. */
public static boolean le(boolean b0, boolean b1) {
return Boolean.compare(b0, b1) <= 0;
}
/** SQL ≤
operator applied to String values. */
public static boolean le(String b0, String b1) {
return b0.compareTo(b1) <= 0;
}
/** SQL ≤
operator applied to String values. */
public static boolean le(String b0, String b1, Comparator comparator) {
return comparator.compare(b0, b1) <= 0;
}
/** SQL ≤
operator applied to ByteString values. */
public static boolean le(ByteString b0, ByteString b1) {
return b0.compareTo(b1) <= 0;
}
/** SQL ≤
operator applied to BigDecimal values. */
public static boolean le(BigDecimal b0, BigDecimal b1) {
return b0.compareTo(b1) <= 0;
}
/** SQL ≤
operator applied to Object values (at least one
* operand has ANY type; neither may be null). */
public static boolean leAny(Object b0, Object b1) {
if (b0.getClass().equals(b1.getClass())
&& b0 instanceof Comparable) {
//noinspection unchecked
return ((Comparable) b0).compareTo(b1) <= 0;
} else if (allAssignable(Number.class, b0, b1)) {
return le(toBigDecimal((Number) b0), toBigDecimal((Number) b1));
}
throw notComparable("<=", b0, b1);
}
// >
/** SQL >
operator applied to boolean values. */
public static boolean gt(boolean b0, boolean b1) {
return Boolean.compare(b0, b1) > 0;
}
/** SQL >
operator applied to String values. */
public static boolean gt(String b0, String b1) {
return b0.compareTo(b1) > 0;
}
/** SQL >
operator applied to String values. */
public static boolean gt(String b0, String b1, Comparator comparator) {
return comparator.compare(b0, b1) > 0;
}
/** SQL >
operator applied to ByteString values. */
public static boolean gt(ByteString b0, ByteString b1) {
return b0.compareTo(b1) > 0;
}
/** SQL >
operator applied to BigDecimal values. */
public static boolean gt(BigDecimal b0, BigDecimal b1) {
return b0.compareTo(b1) > 0;
}
/** Returns whether {@code b0} is greater than {@code b1}
* (or {@code b1} is null). Helper for {@code ARG_MAX}. */
public static > boolean gtNullable(T b0, T b1) {
return b1 == null || b0 != null && b0.compareTo(b1) > 0;
}
public static boolean gt(byte b0, byte b1) {
return b0 > b1;
}
public static boolean gt(char b0, char b1) {
return b0 > b1;
}
public static boolean gt(short b0, short b1) {
return b0 > b1;
}
public static boolean gt(int b0, int b1) {
return b0 > b1;
}
public static boolean gt(long b0, long b1) {
return b0 > b1;
}
public static boolean gt(float b0, float b1) {
return b0 > b1;
}
public static boolean gt(double b0, double b1) {
return b0 > b1;
}
/** SQL >
operator applied to Object values (at least one
* operand has ANY type; neither may be null). */
public static boolean gtAny(Object b0, Object b1) {
if (b0.getClass().equals(b1.getClass())
&& b0 instanceof Comparable) {
//noinspection unchecked
return ((Comparable) b0).compareTo(b1) > 0;
} else if (allAssignable(Number.class, b0, b1)) {
return gt(toBigDecimal((Number) b0), toBigDecimal((Number) b1));
}
throw notComparable(">", b0, b1);
}
// >=
/** SQL ≥
operator applied to boolean values. */
public static boolean ge(boolean b0, boolean b1) {
return Boolean.compare(b0, b1) >= 0;
}
/** SQL ≥
operator applied to String values. */
public static boolean ge(String b0, String b1) {
return b0.compareTo(b1) >= 0;
}
/** SQL ≥
operator applied to String values. */
public static boolean ge(String b0, String b1, Comparator comparator) {
return comparator.compare(b0, b1) >= 0;
}
/** SQL ≥
operator applied to ByteString values. */
public static boolean ge(ByteString b0, ByteString b1) {
return b0.compareTo(b1) >= 0;
}
/** SQL ≥
operator applied to BigDecimal values. */
public static boolean ge(BigDecimal b0, BigDecimal b1) {
return b0.compareTo(b1) >= 0;
}
/** SQL ≥
operator applied to Object values (at least one
* operand has ANY type; neither may be null). */
public static boolean geAny(Object b0, Object b1) {
if (b0.getClass().equals(b1.getClass())
&& b0 instanceof Comparable) {
//noinspection unchecked
return ((Comparable) b0).compareTo(b1) >= 0;
} else if (allAssignable(Number.class, b0, b1)) {
return ge(toBigDecimal((Number) b0), toBigDecimal((Number) b1));
}
throw notComparable(">=", b0, b1);
}
// +
/** SQL +
operator applied to int values. */
public static int plus(int b0, int b1) {
return b0 + b1;
}
/** SQL +
operator applied to int values; left side may be
* null. */
public static @PolyNull Integer plus(@PolyNull Integer b0, int b1) {
return b0 == null ? castNonNull(null) : (b0 + b1);
}
/** SQL +
operator applied to int values; right side may be
* null. */
public static @PolyNull Integer plus(int b0, @PolyNull Integer b1) {
return b1 == null ? castNonNull(null) : (b0 + b1);
}
/** SQL +
operator applied to nullable int values. */
public static @PolyNull Integer plus(@PolyNull Integer b0, @PolyNull Integer b1) {
return (b0 == null || b1 == null) ? castNonNull(null) : (b0 + b1);
}
/** SQL +
operator applied to nullable long and int values. */
public static @PolyNull Long plus(@PolyNull Long b0, @PolyNull Integer b1) {
return (b0 == null || b1 == null)
? castNonNull(null)
: (b0.longValue() + b1.longValue());
}
/** SQL +
operator applied to nullable int and long values. */
public static @PolyNull Long plus(@PolyNull Integer b0, @PolyNull Long b1) {
return (b0 == null || b1 == null)
? castNonNull(null)
: (b0.longValue() + b1.longValue());
}
/** SQL +
operator applied to BigDecimal values. */
public static @PolyNull BigDecimal plus(@PolyNull BigDecimal b0,
@PolyNull BigDecimal b1) {
return (b0 == null || b1 == null) ? castNonNull(null) : b0.add(b1);
}
/** SQL +
operator applied to Object values (at least one operand
* has ANY type; either may be null). */
public static @PolyNull Object plusAny(@PolyNull Object b0,
@PolyNull Object b1) {
if (b0 == null || b1 == null) {
return castNonNull(null);
}
if (allAssignable(Number.class, b0, b1)) {
return plus(toBigDecimal((Number) b0), toBigDecimal((Number) b1));
}
throw notArithmetic("+", b0, b1);
}
// -
/** SQL -
operator applied to int values. */
public static int minus(int b0, int b1) {
return b0 - b1;
}
/** SQL -
operator applied to int values; left side may be
* null. */
public static @PolyNull Integer minus(@PolyNull Integer b0, int b1) {
return b0 == null ? castNonNull(null) : (b0 - b1);
}
/** SQL -
operator applied to int values; right side may be
* null. */
public static @PolyNull Integer minus(int b0, @PolyNull Integer b1) {
return b1 == null ? castNonNull(null) : (b0 - b1);
}
/** SQL -
operator applied to nullable int values. */
public static @PolyNull Integer minus(@PolyNull Integer b0, @PolyNull Integer b1) {
return (b0 == null || b1 == null) ? castNonNull(null) : (b0 - b1);
}
/** SQL -
operator applied to nullable long and int values. */
public static @PolyNull Long minus(@PolyNull Long b0, @PolyNull Integer b1) {
return (b0 == null || b1 == null)
? castNonNull(null)
: (b0.longValue() - b1.longValue());
}
/** SQL -
operator applied to nullable int and long values. */
public static @PolyNull Long minus(@PolyNull Integer b0, @PolyNull Long b1) {
return (b0 == null || b1 == null)
? castNonNull(null)
: (b0.longValue() - b1.longValue());
}
/** SQL -
operator applied to nullable BigDecimal values. */
public static @PolyNull BigDecimal minus(@PolyNull BigDecimal b0,
@PolyNull BigDecimal b1) {
return (b0 == null || b1 == null) ? castNonNull(null) : b0.subtract(b1);
}
/** SQL -
operator applied to Object values (at least one operand
* has ANY type; either may be null). */
public static @PolyNull Object minusAny(@PolyNull Object b0, @PolyNull Object b1) {
if (b0 == null || b1 == null) {
return castNonNull(null);
}
if (allAssignable(Number.class, b0, b1)) {
return minus(toBigDecimal((Number) b0), toBigDecimal((Number) b1));
}
throw notArithmetic("-", b0, b1);
}
// /
/** SQL /
operator applied to int values. */
public static int divide(int b0, int b1) {
return b0 / b1;
}
/** SQL /
operator applied to int values; left side may be
* null. */
public static @PolyNull Integer divide(@PolyNull Integer b0, int b1) {
return b0 == null ? castNonNull(null) : (b0 / b1);
}
/** SQL /
operator applied to int values; right side may be
* null. */
public static @PolyNull Integer divide(int b0, @PolyNull Integer b1) {
return b1 == null ? castNonNull(null) : (b0 / b1);
}
/** SQL /
operator applied to nullable int values. */
public static @PolyNull Integer divide(@PolyNull Integer b0,
@PolyNull Integer b1) {
return (b0 == null || b1 == null) ? castNonNull(null) : (b0 / b1);
}
/** SQL /
operator applied to nullable long and int values. */
public static @PolyNull Long divide(Long b0, @PolyNull Integer b1) {
return (b0 == null || b1 == null)
? castNonNull(null)
: (b0.longValue() / b1.longValue());
}
/** SQL /
operator applied to nullable int and long values. */
public static @PolyNull Long divide(@PolyNull Integer b0, @PolyNull Long b1) {
return (b0 == null || b1 == null)
? castNonNull(null)
: (b0.longValue() / b1.longValue());
}
/** SQL /
operator applied to BigDecimal values. */
public static @PolyNull BigDecimal divide(@PolyNull BigDecimal b0,
@PolyNull BigDecimal b1) {
return (b0 == null || b1 == null)
? castNonNull(null)
: b0.divide(b1, MathContext.DECIMAL64);
}
/** SQL /
operator applied to Object values (at least one operand
* has ANY type; either may be null). */
public static @PolyNull Object divideAny(@PolyNull Object b0,
@PolyNull Object b1) {
if (b0 == null || b1 == null) {
return castNonNull(null);
}
if (allAssignable(Number.class, b0, b1)) {
return divide(toBigDecimal((Number) b0), toBigDecimal((Number) b1));
}
throw notArithmetic("/", b0, b1);
}
public static int divide(int b0, BigDecimal b1) {
return BigDecimal.valueOf(b0)
.divide(b1, RoundingMode.HALF_DOWN).intValue();
}
public static long divide(long b0, BigDecimal b1) {
return BigDecimal.valueOf(b0)
.divide(b1, RoundingMode.HALF_DOWN).longValue();
}
// *
/** SQL *
operator applied to int values. */
public static int multiply(int b0, int b1) {
return b0 * b1;
}
/** SQL *
operator applied to int values; left side may be
* null. */
public static @PolyNull Integer multiply(@PolyNull Integer b0, int b1) {
return b0 == null ? castNonNull(null) : (b0 * b1);
}
/** SQL *
operator applied to int values; right side may be
* null. */
public static @PolyNull Integer multiply(int b0, @PolyNull Integer b1) {
return b1 == null ? castNonNull(null) : (b0 * b1);
}
/** SQL *
operator applied to nullable int values. */
public static @PolyNull Integer multiply(@PolyNull Integer b0,
@PolyNull Integer b1) {
return (b0 == null || b1 == null) ? castNonNull(null) : (b0 * b1);
}
/** SQL *
operator applied to nullable long and int values. */
public static @PolyNull Long multiply(@PolyNull Long b0, @PolyNull Integer b1) {
return (b0 == null || b1 == null)
? castNonNull(null)
: (b0.longValue() * b1.longValue());
}
/** SQL *
operator applied to nullable int and long values. */
public static @PolyNull Long multiply(@PolyNull Integer b0, @PolyNull Long b1) {
return (b0 == null || b1 == null)
? castNonNull(null)
: (b0.longValue() * b1.longValue());
}
/** SQL *
operator applied to nullable BigDecimal values. */
public static @PolyNull BigDecimal multiply(@PolyNull BigDecimal b0,
@PolyNull BigDecimal b1) {
return (b0 == null || b1 == null) ? castNonNull(null) : b0.multiply(b1);
}
/** SQL *
operator applied to Object values (at least one operand
* has ANY type; either may be null). */
public static @PolyNull Object multiplyAny(@PolyNull Object b0,
@PolyNull Object b1) {
if (b0 == null || b1 == null) {
return castNonNull(null);
}
if (allAssignable(Number.class, b0, b1)) {
return multiply(toBigDecimal((Number) b0), toBigDecimal((Number) b1));
}
throw notArithmetic("*", b0, b1);
}
private static RuntimeException notArithmetic(String op, Object b0,
Object b1) {
return RESOURCE.invalidTypesForArithmetic(b0.getClass().toString(),
op, b1.getClass().toString()).ex();
}
private static RuntimeException notComparable(String op, Object b0,
Object b1) {
return RESOURCE.invalidTypesForComparison(b0.getClass().toString(),
op, b1.getClass().toString()).ex();
}
/** Bitwise function BIT_AND
applied to integer values. */
public static long bitAnd(long b0, long b1) {
return b0 & b1;
}
/** Bitwise function BIT_AND
applied to binary values. */
public static ByteString bitAnd(ByteString b0, ByteString b1) {
return binaryOperator(b0, b1, (x, y) -> (byte) (x & y));
}
/** Bitwise function BIT_OR
applied to integer values. */
public static long bitOr(long b0, long b1) {
return b0 | b1;
}
/** Bitwise function BIT_OR
applied to binary values. */
public static ByteString bitOr(ByteString b0, ByteString b1) {
return binaryOperator(b0, b1, (x, y) -> (byte) (x | y));
}
/** Bitwise function BIT_XOR
applied to integer values. */
public static long bitXor(long b0, long b1) {
return b0 ^ b1;
}
/** Bitwise function BIT_XOR
applied to binary values. */
public static ByteString bitXor(ByteString b0, ByteString b1) {
return binaryOperator(b0, b1, (x, y) -> (byte) (x ^ y));
}
/**
* Utility for bitwise function applied to two byteString values.
*
* @param b0 The first byteString value operand of bitwise function.
* @param b1 The second byteString value operand of bitwise function.
* @param bitOp BitWise binary operator.
* @return ByteString after bitwise operation.
*/
private static ByteString binaryOperator(
ByteString b0, ByteString b1, BinaryOperator bitOp) {
if (b0.length() == 0) {
return b1;
}
if (b1.length() == 0) {
return b0;
}
if (b0.length() != b1.length()) {
throw RESOURCE.differentLengthForBitwiseOperands(
b0.length(), b1.length()).ex();
}
final byte[] result = new byte[b0.length()];
for (int i = 0; i < b0.length(); i++) {
result[i] = bitOp.apply(b0.byteAt(i), b1.byteAt(i));
}
return new ByteString(result);
}
// EXP
/** SQL EXP
operator applied to double values. */
public static double exp(double b0) {
return Math.exp(b0);
}
public static double exp(BigDecimal b0) {
return Math.exp(b0.doubleValue());
}
// POWER
/** SQL POWER
operator applied to double values. */
public static double power(double b0, double b1) {
return Math.pow(b0, b1);
}
public static double power(double b0, BigDecimal b1) {
return Math.pow(b0, b1.doubleValue());
}
public static double power(BigDecimal b0, double b1) {
return Math.pow(b0.doubleValue(), b1);
}
public static double power(BigDecimal b0, BigDecimal b1) {
return Math.pow(b0.doubleValue(), b1.doubleValue());
}
// LN, LOG, LOG10
/** SQL {@code LOG(number, number2)} function applied to double values. */
public static double log(double d0, double d1) {
return Math.log(d0) / Math.log(d1);
}
/** SQL {@code LOG(number, number2)} function applied to
* double and BigDecimal values. */
public static double log(double d0, BigDecimal d1) {
return Math.log(d0) / Math.log(d1.doubleValue());
}
/** SQL {@code LOG(number, number2)} function applied to
* BigDecimal and double values. */
public static double log(BigDecimal d0, double d1) {
return Math.log(d0.doubleValue()) / Math.log(d1);
}
/** SQL {@code LOG(number, number2)} function applied to double values. */
public static double log(BigDecimal d0, BigDecimal d1) {
return Math.log(d0.doubleValue()) / Math.log(d1.doubleValue());
}
// MOD
/** SQL MOD
operator applied to byte values. */
public static byte mod(byte b0, byte b1) {
return (byte) (b0 % b1);
}
/** SQL MOD
operator applied to short values. */
public static short mod(short b0, short b1) {
return (short) (b0 % b1);
}
/** SQL MOD
operator applied to int values. */
public static int mod(int b0, int b1) {
return b0 % b1;
}
/** SQL MOD
operator applied to long values. */
public static long mod(long b0, long b1) {
return b0 % b1;
}
// temporary
public static BigDecimal mod(BigDecimal b0, int b1) {
return mod(b0, BigDecimal.valueOf(b1));
}
// temporary
public static BigDecimal mod(int b0, BigDecimal b1) {
return mod(BigDecimal.valueOf(b0), b1);
}
public static BigDecimal mod(BigDecimal b0, BigDecimal b1) {
final BigDecimal[] bigDecimals = b0.divideAndRemainder(b1);
return bigDecimals[1];
}
// FLOOR
public static double floor(double b0) {
return Math.floor(b0);
}
public static float floor(float b0) {
return (float) Math.floor(b0);
}
public static BigDecimal floor(BigDecimal b0) {
return b0.setScale(0, RoundingMode.FLOOR);
}
/** SQL FLOOR
operator applied to byte values. */
public static byte floor(byte b0, byte b1) {
return (byte) floor((int) b0, (int) b1);
}
/** SQL FLOOR
operator applied to short values. */
public static short floor(short b0, short b1) {
return (short) floor((int) b0, (int) b1);
}
/** SQL FLOOR
operator applied to int values. */
public static int floor(int b0, int b1) {
int r = b0 % b1;
if (r < 0) {
r += b1;
}
return b0 - r;
}
/** SQL FLOOR
operator applied to long values. */
public static long floor(long b0, long b1) {
long r = b0 % b1;
if (r < 0) {
r += b1;
}
return b0 - r;
}
// temporary
public static BigDecimal floor(BigDecimal b0, int b1) {
return floor(b0, BigDecimal.valueOf(b1));
}
// temporary
public static int floor(int b0, BigDecimal b1) {
return floor(b0, b1.intValue());
}
public static BigDecimal floor(BigDecimal b0, BigDecimal b1) {
final BigDecimal[] bigDecimals = b0.divideAndRemainder(b1);
BigDecimal r = bigDecimals[1];
if (r.signum() < 0) {
r = r.add(b1);
}
return b0.subtract(r);
}
// CEIL
public static double ceil(double b0) {
return Math.ceil(b0);
}
public static float ceil(float b0) {
return (float) Math.ceil(b0);
}
public static BigDecimal ceil(BigDecimal b0) {
return b0.setScale(0, RoundingMode.CEILING);
}
/** SQL CEIL
operator applied to byte values. */
public static byte ceil(byte b0, byte b1) {
return floor((byte) (b0 + b1 - 1), b1);
}
/** SQL CEIL
operator applied to short values. */
public static short ceil(short b0, short b1) {
return floor((short) (b0 + b1 - 1), b1);
}
/** SQL CEIL
operator applied to int values. */
public static int ceil(int b0, int b1) {
int r = b0 % b1;
if (r > 0) {
r -= b1;
}
return b0 - r;
}
/** SQL CEIL
operator applied to long values. */
public static long ceil(long b0, long b1) {
return floor(b0 + b1 - 1, b1);
}
// temporary
public static BigDecimal ceil(BigDecimal b0, int b1) {
return ceil(b0, BigDecimal.valueOf(b1));
}
// temporary
public static int ceil(int b0, BigDecimal b1) {
return ceil(b0, b1.intValue());
}
public static BigDecimal ceil(BigDecimal b0, BigDecimal b1) {
final BigDecimal[] bigDecimals = b0.divideAndRemainder(b1);
BigDecimal r = bigDecimals[1];
if (r.signum() > 0) {
r = r.subtract(b1);
}
return b0.subtract(r);
}
// ABS
/** SQL ABS
operator applied to byte values. */
public static byte abs(byte b0) {
return (byte) Math.abs(b0);
}
/** SQL ABS
operator applied to short values. */
public static short abs(short b0) {
return (short) Math.abs(b0);
}
/** SQL ABS
operator applied to int values. */
public static int abs(int b0) {
return Math.abs(b0);
}
/** SQL ABS
operator applied to long values. */
public static long abs(long b0) {
return Math.abs(b0);
}
/** SQL ABS
operator applied to float values. */
public static float abs(float b0) {
return Math.abs(b0);
}
/** SQL ABS
operator applied to double values. */
public static double abs(double b0) {
return Math.abs(b0);
}
/** SQL ABS
operator applied to BigDecimal values. */
public static BigDecimal abs(BigDecimal b0) {
return b0.abs();
}
// ACOS
/** SQL ACOS
operator applied to BigDecimal values. */
public static double acos(BigDecimal b0) {
return Math.acos(b0.doubleValue());
}
/** SQL ACOS
operator applied to double values. */
public static double acos(double b0) {
return Math.acos(b0);
}
// ACOSH
/** SQL ACOSH
operator applied to BigDecimal values. */
public static double acosh(BigDecimal b0) {
return acosh(b0.doubleValue());
}
/** SQL ACOSH
operator applied to double values. */
public static double acosh(double b0) {
if (b0 < 1) {
throw new IllegalArgumentException("Input parameter of acosh cannot be less than 1!");
}
return Math.log(Math.sqrt(b0 * b0 - 1.0d) + b0);
}
// ASIN
/** SQL ASIN
operator applied to BigDecimal values. */
public static double asin(BigDecimal b0) {
return Math.asin(b0.doubleValue());
}
/** SQL ASIN
operator applied to double values. */
public static double asin(double b0) {
return Math.asin(b0);
}
// ASINH
/** SQL ASINH
operator applied to BigDecimal values. */
public static double asinh(BigDecimal b0) {
return asinh(b0.doubleValue());
}
/** SQL ASINH
operator applied to double values. */
public static double asinh(double b0) {
final double sign;
// check the sign bit of the raw representation to handle -0.
if (Double.doubleToRawLongBits(b0) < 0) {
b0 = Math.abs(b0);
sign = -1.0d;
} else {
sign = 1.0d;
}
return sign * Math.log(Math.sqrt(b0 * b0 + 1.0d) + b0);
}
// ATAN
/** SQL ATAN
operator applied to BigDecimal values. */
public static double atan(BigDecimal b0) {
return Math.atan(b0.doubleValue());
}
/** SQL ATAN
operator applied to double values. */
public static double atan(double b0) {
return Math.atan(b0);
}
// ATAN2
/** SQL ATAN2
operator applied to double/BigDecimal values. */
public static double atan2(double b0, BigDecimal b1) {
return Math.atan2(b0, b1.doubleValue());
}
/** SQL ATAN2
operator applied to BigDecimal/double values. */
public static double atan2(BigDecimal b0, double b1) {
return Math.atan2(b0.doubleValue(), b1);
}
/** SQL ATAN2
operator applied to BigDecimal values. */
public static double atan2(BigDecimal b0, BigDecimal b1) {
return Math.atan2(b0.doubleValue(), b1.doubleValue());
}
/** SQL ATAN2
operator applied to double values. */
public static double atan2(double b0, double b1) {
return Math.atan2(b0, b1);
}
// ATANH
/** SQL ATANH
operator applied to BigDecimal values. */
public static double atanh(BigDecimal b) {
return atanh(b.doubleValue());
}
/** SQL ATANH
operator applied to double values. */
public static double atanh(double b) {
if (Math.abs(b) >= 1) {
throw new IllegalArgumentException("Input parameter of atanh cannot be out of the "
+ "range (-1, 1)!");
}
final double mult;
// check the sign bit of the raw representation to handle -0.
if (Double.doubleToRawLongBits(b) < 0) {
b = Math.abs(b);
mult = -0.5d;
} else {
mult = 0.5d;
}
return mult * Math.log((1.0d + b) / (1.0d - b));
}
// CBRT
/** SQL CBRT
operator applied to BigDecimal values. */
public static double cbrt(BigDecimal b) {
return cbrt(b.doubleValue());
}
/** SQL CBRT
operator applied to double values. */
public static double cbrt(double b) {
return Math.cbrt(b);
}
// COS
/** SQL COS
operator applied to BigDecimal values. */
public static double cos(BigDecimal b0) {
return Math.cos(b0.doubleValue());
}
/** SQL COS
operator applied to double values. */
public static double cos(double b0) {
return Math.cos(b0);
}
// COSH
/** SQL COSH
operator applied to BigDecimal values. */
public static double cosh(BigDecimal b) {
return cosh(b.doubleValue());
}
/** SQL COSH
operator applied to double values. */
public static double cosh(double b) {
return Math.cosh(b);
}
// COT
/** SQL COT
operator applied to BigDecimal values. */
public static double cot(BigDecimal b0) {
return 1.0d / Math.tan(b0.doubleValue());
}
/** SQL COT
operator applied to double values. */
public static double cot(double b0) {
return 1.0d / Math.tan(b0);
}
/** SQL COTH
operator applied to BigDecimal values. */
public static double coth(BigDecimal b0) {
return 1.0d / Math.tanh(b0.doubleValue());
}
/** SQL COTH
operator applied to double values. */
public static double coth(double b0) {
return 1.0d / Math.tanh(b0);
}
/** SQL CSCH
operator applied to BigDecimal values. */
public static double csch(BigDecimal b0) {
return 1.0d / Math.sinh(b0.doubleValue());
}
/** SQL CSCH
operator applied to double values. */
public static double csch(double b0) {
return 1.0d / Math.sinh(b0);
}
// DEGREES
/** SQL DEGREES
operator applied to BigDecimal values. */
public static double degrees(BigDecimal b0) {
return Math.toDegrees(b0.doubleValue());
}
/** SQL DEGREES
operator applied to double values. */
public static double degrees(double b0) {
return Math.toDegrees(b0);
}
// RADIANS
/** SQL RADIANS
operator applied to BigDecimal values. */
public static double radians(BigDecimal b0) {
return Math.toRadians(b0.doubleValue());
}
/** SQL RADIANS
operator applied to double values. */
public static double radians(double b0) {
return Math.toRadians(b0);
}
/** SQL SECH
operator applied to BigDecimal values. */
public static double sech(BigDecimal b0) {
return 1.0d / Math.cosh(b0.doubleValue());
}
/** SQL SECH
operator applied to double values. */
public static double sech(double b0) {
return 1.0d / Math.cosh(b0);
}
// SQL ROUND
/** SQL ROUND
operator applied to int values. */
public static int sround(int b0) {
return sround(b0, 0);
}
/** SQL ROUND
operator applied to int values. */
public static int sround(int b0, int b1) {
return sround(BigDecimal.valueOf(b0), b1).intValue();
}
/** SQL ROUND
operator applied to long values. */
public static long sround(long b0) {
return sround(b0, 0);
}
/** SQL ROUND
operator applied to long values. */
public static long sround(long b0, int b1) {
return sround(BigDecimal.valueOf(b0), b1).longValue();
}
/** SQL ROUND
operator applied to BigDecimal values. */
public static BigDecimal sround(BigDecimal b0) {
return sround(b0, 0);
}
/** SQL ROUND
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 ROUND
operator applied to double values. */
public static double sround(double b0) {
return sround(b0, 0);
}
/** SQL ROUND
operator applied to double values. */
public static double sround(double b0, int b1) {
return sround(BigDecimal.valueOf(b0), b1).doubleValue();
}
// SQL TRUNCATE
/** SQL TRUNCATE
operator applied to int values. */
public static int struncate(int b0) {
return struncate(b0, 0);
}
public static int struncate(int b0, int b1) {
return struncate(BigDecimal.valueOf(b0), b1).intValue();
}
/** SQL TRUNCATE
operator applied to long values. */
public static long struncate(long b0) {
return struncate(b0, 0);
}
public static long struncate(long b0, int b1) {
return struncate(BigDecimal.valueOf(b0), b1).longValue();
}
/** SQL TRUNCATE
operator applied to BigDecimal values. */
public static BigDecimal struncate(BigDecimal b0) {
return struncate(b0, 0);
}
public static BigDecimal struncate(BigDecimal b0, int b1) {
return b0.movePointRight(b1)
.setScale(0, RoundingMode.DOWN).movePointLeft(b1);
}
/** SQL TRUNCATE
operator applied to double values. */
public static double struncate(double b0) {
return struncate(b0, 0);
}
public static double struncate(double b0, int b1) {
return struncate(BigDecimal.valueOf(b0), b1).doubleValue();
}
// SIGN
/** SQL SIGN
operator applied to int values. */
public static int sign(int b0) {
return Integer.signum(b0);
}
/** SQL SIGN
operator applied to long values. */
public static long sign(long b0) {
return Long.signum(b0);
}
/** SQL SIGN
operator applied to BigDecimal values. */
public static BigDecimal sign(BigDecimal b0) {
return BigDecimal.valueOf(b0.signum());
}
/** SQL SIGN
operator applied to double values. */
public static double sign(double b0) {
return Math.signum(b0);
}
// SIN
/** SQL SIN
operator applied to BigDecimal values. */
public static double sin(BigDecimal b0) {
return Math.sin(b0.doubleValue());
}
/** SQL SIN
operator applied to double values. */
public static double sin(double b0) {
return Math.sin(b0);
}
// SINH
/** SQL SINH
operator applied to BigDecimal values. */
public static double sinh(BigDecimal b) {
return sinh(b.doubleValue());
}
/** SQL SINH
operator applied to double values. */
public static double sinh(double b) {
return Math.sinh(b);
}
// TAN
/** SQL TAN
operator applied to BigDecimal values. */
public static double tan(BigDecimal b0) {
return Math.tan(b0.doubleValue());
}
/** SQL TAN
operator applied to double values. */
public static double tan(double b0) {
return Math.tan(b0);
}
// TANH
/** SQL TANH
operator applied to BigDecimal values. */
public static double tanh(BigDecimal b) {
return tanh(b.doubleValue());
}
/** SQL TANH
operator applied to double values. */
public static double tanh(double b) {
return Math.tanh(b);
}
// CSC
/** SQL CSC
operator applied to BigDecimal values. */
public static double csc(BigDecimal b0) {
return 1.0d / Math.sin(b0.doubleValue());
}
/** SQL CSC
operator applied to double values. */
public static double csc(double b0) {
return 1.0d / Math.sin(b0);
}
// SEC
/** SQL SEC
operator applied to BigDecimal values. */
public static double sec(BigDecimal b0) {
return 1.0d / Math.cos(b0.doubleValue());
}
/** SQL SEC
operator applied to double values. */
public static double sec(double b0) {
return 1.0d / Math.cos(b0);
}
// Helpers
/** Helper for implementing MIN. Somewhat similar to LEAST operator. */
public static > T lesser(T b0, T b1) {
return b0 == null || b0.compareTo(b1) > 0 ? b1 : b0;
}
/** LEAST operator. */
public static > T least(T b0, T b1) {
return b0 == null || b1 != null && b0.compareTo(b1) > 0 ? b1 : b0;
}
public static boolean greater(boolean b0, boolean b1) {
return b0 || b1;
}
public static boolean lesser(boolean b0, boolean b1) {
return b0 && b1;
}
public static byte greater(byte b0, byte b1) {
return b0 > b1 ? b0 : b1;
}
public static byte lesser(byte b0, byte b1) {
return b0 > b1 ? b1 : b0;
}
public static char greater(char b0, char b1) {
return b0 > b1 ? b0 : b1;
}
public static char lesser(char b0, char b1) {
return b0 > b1 ? b1 : b0;
}
public static short greater(short b0, short b1) {
return b0 > b1 ? b0 : b1;
}
public static short lesser(short b0, short b1) {
return b0 > b1 ? b1 : b0;
}
public static int greater(int b0, int b1) {
return b0 > b1 ? b0 : b1;
}
public static int lesser(int b0, int b1) {
return b0 > b1 ? b1 : b0;
}
public static long greater(long b0, long b1) {
return b0 > b1 ? b0 : b1;
}
public static long lesser(long b0, long b1) {
return b0 > b1 ? b1 : b0;
}
public static float greater(float b0, float b1) {
return b0 > b1 ? b0 : b1;
}
public static float lesser(float b0, float b1) {
return b0 > b1 ? b1 : b0;
}
public static double greater(double b0, double b1) {
return b0 > b1 ? b0 : b1;
}
public static double lesser(double b0, double b1) {
return b0 > b1 ? b1 : b0;
}
/** Helper for implementing MAX. Somewhat similar to GREATEST operator. */
public static > T greater(T b0, T b1) {
return b0 == null || b0.compareTo(b1) < 0 ? b1 : b0;
}
/** GREATEST operator. */
public static > T greatest(T b0, T b1) {
return b0 == null || b1 != null && b0.compareTo(b1) < 0 ? b1 : b0;
}
/** CAST(FLOAT AS VARCHAR). */
public static String toString(float x) {
if (x == 0) {
return "0E0";
}
BigDecimal bigDecimal =
new BigDecimal(x, MathContext.DECIMAL32).stripTrailingZeros();
final String s = bigDecimal.toString();
return PATTERN_0_STAR_E.matcher(s).replaceAll("E").replace("E+", "E");
}
/** CAST(DOUBLE AS VARCHAR). */
public static String toString(double x) {
if (x == 0) {
return "0E0";
}
BigDecimal bigDecimal =
new BigDecimal(x, MathContext.DECIMAL64).stripTrailingZeros();
final String s = bigDecimal.toString();
return PATTERN_0_STAR_E.matcher(s).replaceAll("E").replace("E+", "E");
}
/** CAST(DECIMAL AS VARCHAR). */
public static String toString(BigDecimal x) {
final String s = x.toString();
if (s.equals("0")) {
return s;
} else if (s.startsWith("0.")) {
// we want ".1" not "0.1"
return s.substring(1);
} else if (s.startsWith("-0.")) {
// we want "-.1" not "-0.1"
return "-" + s.substring(2);
} else {
return s;
}
}
/** CAST(BOOLEAN AS VARCHAR). */
public static String toString(boolean x) {
// Boolean.toString returns lower case -- no good.
return x ? "TRUE" : "FALSE";
}
@NonDeterministic
private static Object cannotConvert(Object o, Class toType) {
throw RESOURCE.cannotConvert(String.valueOf(o), toType.toString()).ex();
}
/** CAST(VARCHAR AS BOOLEAN). */
public static boolean toBoolean(String s) {
s = trim(true, true, " ", s);
if (s.equalsIgnoreCase("TRUE")) {
return true;
} else if (s.equalsIgnoreCase("FALSE")) {
return false;
} else {
throw RESOURCE.invalidCharacterForCast(s).ex();
}
}
public static boolean toBoolean(Number number) {
return !number.equals(0);
}
public static boolean toBoolean(Object o) {
return o instanceof Boolean ? (Boolean) o
: o instanceof Number ? toBoolean((Number) o)
: o instanceof String ? toBoolean((String) o)
: (Boolean) cannotConvert(o, boolean.class);
}
// Don't need parseByte etc. - Byte.parseByte is sufficient.
public static byte toByte(Object o) {
return o instanceof Byte ? (Byte) o
: o instanceof Number ? toByte((Number) o)
: Byte.parseByte(o.toString());
}
public static byte toByte(Number number) {
return number.byteValue();
}
public static char toChar(String s) {
return s.charAt(0);
}
public static Character toCharBoxed(String s) {
return s.charAt(0);
}
public static short toShort(String s) {
return Short.parseShort(s.trim());
}
public static short toShort(Number number) {
return number.shortValue();
}
public static short toShort(Object o) {
return o instanceof Short ? (Short) o
: o instanceof Number ? toShort((Number) o)
: o instanceof String ? toShort((String) o)
: (Short) cannotConvert(o, short.class);
}
/**
* Converts a SQL DATE value from the Java type
* ({@link java.sql.Date}) to the internal representation type
* (number of days since January 1st, 1970 as {@code int})
* in the local time zone.
*
* Since a time zone is not available, the date is converted to represent
* the same date as a Unix date in UTC as the {@link java.sql.Date} value in
* the local time zone.
*
* @see #toInt(java.sql.Date, TimeZone)
* @see #internalToDate(int) converse method
*/
public static int toInt(java.sql.Date v) {
return toInt(v, LOCAL_TZ);
}
/**
* Converts a SQL DATE value from the Java type
* ({@link java.sql.Date}) to the internal representation type
* (number of days since January 1st, 1970 as {@code int}).
*
*
The {@link java.sql.Date} class uses the standard Gregorian calendar
* which switches from the Julian calendar to the Gregorian calendar in
* October 1582. For compatibility with ISO-8601, the internal representation
* is converted to use the proleptic Gregorian calendar.
*
*
If the date contains a partial day, it will be rounded to a full day
* depending on the milliseconds value. If the milliseconds value is positive,
* it will be rounded down to the closest full day. If the milliseconds value
* is negative, it will be rounded up to the closest full day.
*/
public static int toInt(java.sql.Date v, TimeZone timeZone) {
return DateTimeUtils.sqlDateToUnixDate(v, timeZone);
}
/**
* Converts a nullable SQL DATE value from the Java type
* ({@link java.sql.Date}) to the internal representation type
* (number of days since January 1st, 1970 as {@link Integer})
* in the local time zone.
*
*
Since a time zone is not available, the date is converted to represent
* the same date as a Unix date in UTC as the {@link java.sql.Date} value in
* the local time zone.
*
* @see #toInt(java.sql.Date, TimeZone)
* @see #internalToDate(Integer) converse method
*/
public static @PolyNull Integer toIntOptional(java.sql.@PolyNull Date v) {
return v == null
? castNonNull(null)
: toInt(v);
}
/**
* Converts a nullable SQL DATE value from the Java type
* ({@link java.sql.Date}) to the internal representation type
* (number of days since January 1st, 1970 as {@link Integer}).
*
* @see #toInt(java.sql.Date, TimeZone)
*/
public static @PolyNull Integer toIntOptional(java.sql.@PolyNull Date v,
TimeZone timeZone) {
return v == null
? castNonNull(null)
: toInt(v, timeZone);
}
/**
* Converts a SQL TIME value from the Java type
* ({@link java.sql.Time}) to the internal representation type
* (number of milliseconds since January 1st, 1970 as {@code int})
* in the local time zone.
*
* @see #toIntOptional(java.sql.Time)
* @see #internalToTime(int) converse method
*/
public static int toInt(java.sql.Time v) {
return DateTimeUtils.sqlTimeToUnixTime(v, LOCAL_TZ);
}
/**
* Converts a nullable SQL TIME value from the Java type
* ({@link java.sql.Time}) to the internal representation type
* (number of milliseconds since January 1st, 1970 as {@link Integer}).
*
* @see #toInt(java.sql.Time)
* @see #internalToTime(Integer) converse method
*/
public static @PolyNull Integer toIntOptional(java.sql.@PolyNull Time v) {
return v == null ? castNonNull(null) : toInt(v);
}
public static int toInt(String s) {
return Integer.parseInt(s.trim());
}
public static int toInt(Number number) {
return number.intValue();
}
public static int toInt(Object o) {
return o instanceof Integer ? (Integer) o
: o instanceof Number ? toInt((Number) o)
: o instanceof String ? toInt((String) o)
: o instanceof java.sql.Date ? toInt((java.sql.Date) o)
: o instanceof java.sql.Time ? toInt((java.sql.Time) o)
: (Integer) cannotConvert(o, int.class);
}
public static @PolyNull Integer toIntOptional(@PolyNull Object o) {
return o == null ? castNonNull(null) : toInt(o);
}
/**
* Converts a SQL TIMESTAMP value from the Java type
* ({@link java.util.Date}) to the internal representation type
* (number of milliseconds since January 1st, 1970 as {@code long}).
*
*
Since a time zone is not available, converts the timestamp to represent
* the same date and time as a Unix timestamp in UTC as the
* {@link java.util.Date} value in the local time zone.
*
*
The {@link java.util.Date} class uses the standard Gregorian calendar
* which switches from the Julian calendar to the Gregorian calendar in
* October 1582. For compatibility with ISO-8601, converts the internal
* representation to use the proleptic Gregorian calendar.
*/
public static long toLong(java.util.Date v) {
return DateTimeUtils.utilDateToUnixTimestamp(v, LOCAL_TZ);
}
/**
* Converts a SQL TIMESTAMP value from the Java type
* ({@link Timestamp}) to the internal representation type
* (number of milliseconds since January 1st, 1970 as {@code long}).
*
*
Since a time zone is not available, converts the timestamp to represent
* the same date and time as a Unix timestamp in UTC as the
* {@link Timestamp} value in the local time zone.
*
* @see #toLong(Timestamp, TimeZone)
* @see #internalToTimestamp(Long) converse method
*/
public static long toLong(Timestamp v) {
return toLong(v, LOCAL_TZ);
}
/**
* Converts a SQL TIMESTAMP value from the Java type
* ({@link Timestamp}) to the internal representation type
* (number of milliseconds since January 1st, 1970 as {@code long}).
*
*
For backwards compatibility, time zone offsets are calculated in
* relation to the local time zone instead of UTC. Providing the default time
* zone or {@code null} will return the timestamp unmodified.
*
*
The {@link Timestamp} class uses the standard Gregorian calendar which
* switches from the Julian calendar to the Gregorian calendar in
* October 1582. For compatibility with ISO-8601, the internal representation
* is converted to use the proleptic Gregorian calendar.
*/
public static long toLong(Timestamp v, TimeZone timeZone) {
return DateTimeUtils.sqlTimestampToUnixTimestamp(v, timeZone);
}
/**
* Converts a nullable SQL TIMESTAMP value from the Java type
* ({@link Timestamp}) to the internal representation type
* (number of milliseconds since January 1st, 1970 as {@link Long})
* in the local time zone.
*
* @see #toLong(Timestamp, TimeZone)
* @see #internalToTimestamp(Long) converse method
*/
public static @PolyNull Long toLongOptional(@PolyNull Timestamp v) {
return v == null ? castNonNull(null) : toLong(v, LOCAL_TZ);
}
/**
* Converts a nullable SQL TIMESTAMP value from the Java type
* ({@link Timestamp}) to the internal representation type
* (number of milliseconds since January 1st, 1970 as {@link Long}).
*
* @see #toLong(Timestamp, TimeZone)
*/
public static @PolyNull Long toLongOptional(@PolyNull Timestamp v,
TimeZone timeZone) {
if (v == null) {
return castNonNull(null);
}
return toLong(v, timeZone);
}
public static long toLong(String s) {
if (s.startsWith("199") && s.contains(":")) {
return Timestamp.valueOf(s).getTime();
}
return Long.parseLong(s.trim());
}
public static long toLong(Number number) {
return number.longValue();
}
public static long toLong(Object o) {
return o instanceof Long ? (Long) o
: o instanceof Number ? toLong((Number) o)
: o instanceof String ? toLong((String) o)
: o instanceof java.sql.Date ? toInt((java.sql.Date) o)
: o instanceof java.sql.Time ? toInt((java.sql.Time) o)
: o instanceof java.sql.Timestamp ? toLong((java.sql.Timestamp) o)
: o instanceof java.util.Date ? toLong((java.util.Date) o)
: (Long) cannotConvert(o, long.class);
}
public static @PolyNull Long toLongOptional(@PolyNull Object o) {
return o == null ? castNonNull(null) : toLong(o);
}
public static float toFloat(String s) {
return Float.parseFloat(s.trim());
}
public static float toFloat(Number number) {
return number.floatValue();
}
public static float toFloat(Object o) {
return o instanceof Float ? (Float) o
: o instanceof Number ? toFloat((Number) o)
: o instanceof String ? toFloat((String) o)
: (Float) cannotConvert(o, float.class);
}
public static double toDouble(String s) {
return Double.parseDouble(s.trim());
}
public static double toDouble(Number number) {
return number.doubleValue();
}
public static double toDouble(Object o) {
return o instanceof Double ? (Double) o
: o instanceof Number ? toDouble((Number) o)
: o instanceof String ? toDouble((String) o)
: (Double) cannotConvert(o, double.class);
}
public static BigDecimal toBigDecimal(String s) {
return new BigDecimal(s.trim());
}
public static BigDecimal toBigDecimal(Number number) {
// There are some values of "long" that cannot be represented as "double".
// Not so "int". If it isn't a long, go straight to double.
return number instanceof BigDecimal ? (BigDecimal) number
: number instanceof BigInteger ? new BigDecimal((BigInteger) number)
: number instanceof Long ? new BigDecimal(number.longValue())
: new BigDecimal(number.doubleValue());
}
public static BigDecimal toBigDecimal(Object o) {
return o instanceof Number ? toBigDecimal((Number) o)
: toBigDecimal(o.toString());
}
/**
* Converts a SQL DATE value from the internal representation type
* (number of days since January 1st, 1970) to the Java type
* ({@link java.sql.Date}).
*
*
Since a time zone is not available, converts the date to represent the
* same date as a {@link java.sql.Date} in the local time zone as the Unix
* date in UTC.
*
*
The Unix date should be the number of days since January 1st, 1970
* using the proleptic Gregorian calendar as defined by ISO-8601. The
* returned {@link java.sql.Date} object will use the standard Gregorian
* calendar which switches from the Julian calendar to the Gregorian calendar
* in October 1582.
*
* @see #internalToDate(Integer)
* @see #toInt(java.sql.Date) converse method
*/
public static java.sql.Date internalToDate(int v) {
final LocalDate date = LocalDate.ofEpochDay(v);
return java.sql.Date.valueOf(date);
}
/**
* Converts a nullable SQL DATE value from the internal representation type
* (number of days since January 1st, 1970) to the Java type
* ({@link java.sql.Date}).
*
* @see #internalToDate(int)
* @see #toIntOptional(java.sql.Date) converse method
*/
public static java.sql.@PolyNull Date internalToDate(@PolyNull Integer v) {
return v == null ? castNonNull(null) : internalToDate(v.intValue());
}
/**
* Converts a SQL TIME value from the internal representation type
* (number of milliseconds since January 1st, 1970) to the Java type
* ({@link java.sql.Time}).
*
* @see #internalToTime(Integer)
* @see #toInt(java.sql.Time) converse method
*/
public static java.sql.Time internalToTime(int v) {
return new java.sql.Time(v - LOCAL_TZ.getOffset(v));
}
/**
* Converts a nullable SQL TIME value from the internal representation type
* (number of milliseconds since January 1st, 1970) to the Java type
* ({@link java.sql.Time}).
*
* @see #internalToTime(Integer)
* @see #toIntOptional(java.sql.Time) converse method
*/
public static java.sql.@PolyNull Time internalToTime(@PolyNull Integer v) {
return v == null ? castNonNull(null) : internalToTime(v.intValue());
}
public static @PolyNull Integer toTimeWithLocalTimeZone(@PolyNull String v) {
if (v == null) {
return castNonNull(null);
}
return new TimeWithTimeZoneString(v)
.withTimeZone(DateTimeUtils.UTC_ZONE)
.getLocalTimeString()
.getMillisOfDay();
}
public static @PolyNull Integer toTimeWithLocalTimeZone(@PolyNull String v,
TimeZone timeZone) {
if (v == null) {
return castNonNull(null);
}
return new TimeWithTimeZoneString(v + " " + timeZone.getID())
.withTimeZone(DateTimeUtils.UTC_ZONE)
.getLocalTimeString()
.getMillisOfDay();
}
public static int timeWithLocalTimeZoneToTime(int v, TimeZone timeZone) {
return TimeWithTimeZoneString.fromMillisOfDay(v)
.withTimeZone(timeZone)
.getLocalTimeString()
.getMillisOfDay();
}
public static long timeWithLocalTimeZoneToTimestamp(String date, int v, TimeZone timeZone) {
final TimeWithTimeZoneString tTZ = TimeWithTimeZoneString.fromMillisOfDay(v)
.withTimeZone(DateTimeUtils.UTC_ZONE);
return new TimestampWithTimeZoneString(date + " " + tTZ.toString())
.withTimeZone(timeZone)
.getLocalTimestampString()
.getMillisSinceEpoch();
}
public static long timeWithLocalTimeZoneToTimestampWithLocalTimeZone(String date, int v) {
final TimeWithTimeZoneString tTZ = TimeWithTimeZoneString.fromMillisOfDay(v)
.withTimeZone(DateTimeUtils.UTC_ZONE);
return new TimestampWithTimeZoneString(date + " " + tTZ.toString())
.getLocalTimestampString()
.getMillisSinceEpoch();
}
public static String timeWithLocalTimeZoneToString(int v, TimeZone timeZone) {
return TimeWithTimeZoneString.fromMillisOfDay(v)
.withTimeZone(timeZone)
.toString();
}
private static String internalFormatDatetime(String fmtString, java.util.Date date) {
StringBuilder sb = new StringBuilder();
List elements = FormatModels.BIG_QUERY.parse(fmtString);
elements.forEach(ele -> ele.format(sb, date));
return sb.toString();
}
public static String formatTimestamp(DataContext ctx, String fmtString, long timestamp) {
return internalFormatDatetime(fmtString, internalToTimestamp(timestamp));
}
public static String toChar(long timestamp, String pattern) {
List elements = FormatModels.POSTGRESQL.parse(pattern);
StringBuilder sb = new StringBuilder();
elements.forEach(ele -> ele.format(sb, internalToTimestamp(timestamp)));
return sb.toString().trim();
}
public static String formatDate(DataContext ctx, String fmtString, int date) {
return internalFormatDatetime(fmtString, internalToDate(date));
}
public static String formatTime(DataContext ctx, String fmtString, int time) {
return internalFormatDatetime(fmtString, internalToTime(time));
}
private static String parseDatetimePattern(String fmtString) {
StringBuilder sb = new StringBuilder();
List elements = FormatModels.BIG_QUERY.parse(fmtString);
elements.forEach(ele -> ele.toPattern(sb));
return sb.toString();
}
private static long internalParseDatetime(String fmtString, String datetime) {
return internalParseDatetime(fmtString, datetime,
DateTimeUtils.DEFAULT_ZONE);
}
private static long internalParseDatetime(String fmt, String datetime,
TimeZone tz) {
final String javaFmt = parseDatetimePattern(fmt);
// TODO: make Locale configurable. ENGLISH set for weekday parsing (e.g.
// Thursday, Friday).
final DateFormat parser = new SimpleDateFormat(javaFmt, Locale.ENGLISH);
final ParsePosition pos = new ParsePosition(0);
parser.setLenient(false);
parser.setCalendar(Calendar.getInstance(tz, Locale.ROOT));
Date parsed = parser.parse(datetime, pos);
// Throw if either the parse was unsuccessful, or the format string did not
// contain enough elements to parse the datetime string completely.
if (pos.getErrorIndex() >= 0 || pos.getIndex() != datetime.length()) {
SQLException e =
new SQLException(
String.format(Locale.ROOT,
"Invalid format: '%s' for datetime string: '%s'.", fmt,
datetime));
throw Util.toUnchecked(e);
}
// Suppress the Errorprone warning "[JavaUtilDate] Date has a bad API that
// leads to bugs; prefer java.time.Instant or LocalDate" because we know
// what we're doing.
@SuppressWarnings("JavaUtilDate")
final long millisSinceEpoch = parsed.getTime();
return millisSinceEpoch;
}
public static int parseDate(String fmtString, String date) {
final long millisSinceEpoch = internalParseDatetime(fmtString, date);
return toInt(new java.sql.Date(millisSinceEpoch));
}
public static long parseDatetime(String fmtString, String datetime) {
final long millisSinceEpoch = internalParseDatetime(fmtString, datetime);
return toLong(new java.sql.Timestamp(millisSinceEpoch));
}
public static int parseTime(String fmtString, String time) {
final long millisSinceEpoch = internalParseDatetime(fmtString, time);
return toInt(new java.sql.Time(millisSinceEpoch));
}
public static long parseTimestamp(String fmtString, String timestamp) {
return parseTimestamp(fmtString, timestamp, "UTC");
}
public static long parseTimestamp(String fmtString, String timestamp,
String timeZone) {
TimeZone tz = TimeZone.getTimeZone(timeZone);
final long millisSinceEpoch =
internalParseDatetime(fmtString, timestamp, tz);
return toLong(new java.sql.Timestamp(millisSinceEpoch), tz);
}
/**
* Converts a SQL TIMESTAMP value from the internal representation type
* (number of milliseconds since January 1st, 1970) to the Java Type
* ({@link Timestamp})
* in the local time zone.
*
* Since a time zone is not available, the timestamp is converted to
* represent the same timestamp as a {@link Timestamp} in the local time zone
* as the Unix timestamp in UTC.
*
*
The Unix timestamp should be the number of milliseconds since
* January 1st, 1970 using the proleptic Gregorian calendar as defined by
* ISO-8601. The returned {@link Timestamp} object will use the standard
* Gregorian calendar which switches from the Julian calendar to the
* Gregorian calendar in October 1582.
*
* @see #internalToTimestamp(Long)
* @see #toLong(Timestamp, TimeZone)
* @see #toLongOptional(Timestamp)
* @see #toLongOptional(Timestamp, TimeZone)
* @see #toLong(Timestamp) converse method
*/
public static java.sql.Timestamp internalToTimestamp(long v) {
final LocalDateTime dateTime =
LocalDateTime.ofEpochSecond(
Math.floorDiv(v, DateTimeUtils.MILLIS_PER_SECOND),
(int) (Math.floorMod(v, DateTimeUtils.MILLIS_PER_SECOND)
* DateTimeUtils.NANOS_PER_MILLI),
ZoneOffset.UTC);
return java.sql.Timestamp.valueOf(dateTime);
}
/**
* Converts a nullable SQL TIMESTAMP value from the internal representation
* type (number of milliseconds since January 1st, 1970) to the Java Type
* ({@link Timestamp})
* in the local time zone.
*
* @see #internalToTimestamp(long)
* @see #toLong(Timestamp)
* @see #toLong(Timestamp, TimeZone)
* @see #toLongOptional(Timestamp, TimeZone)
* @see #toLongOptional(Timestamp) converse method
*/
public static java.sql.@PolyNull Timestamp internalToTimestamp(@PolyNull Long v) {
return v == null ? castNonNull(null) : internalToTimestamp(v.longValue());
}
public static int timestampWithLocalTimeZoneToDate(long v, TimeZone timeZone) {
return TimestampWithTimeZoneString.fromMillisSinceEpoch(v)
.withTimeZone(timeZone)
.getLocalDateString()
.getDaysSinceEpoch();
}
public static int timestampWithLocalTimeZoneToTime(long v, TimeZone timeZone) {
return TimestampWithTimeZoneString.fromMillisSinceEpoch(v)
.withTimeZone(timeZone)
.getLocalTimeString()
.getMillisOfDay();
}
public static long timestampWithLocalTimeZoneToTimestamp(long v, TimeZone timeZone) {
return TimestampWithTimeZoneString.fromMillisSinceEpoch(v)
.withTimeZone(timeZone)
.getLocalTimestampString()
.getMillisSinceEpoch();
}
public static String timestampWithLocalTimeZoneToString(long v, TimeZone timeZone) {
return TimestampWithTimeZoneString.fromMillisSinceEpoch(v)
.withTimeZone(timeZone)
.toString();
}
public static int timestampWithLocalTimeZoneToTimeWithLocalTimeZone(long v) {
return TimestampWithTimeZoneString.fromMillisSinceEpoch(v)
.getLocalTimeString()
.getMillisOfDay();
}
/** For {@link SqlLibraryOperators#TIMESTAMP_SECONDS}. */
public static long timestampSeconds(long v) {
return v * 1000;
}
/** For {@link SqlLibraryOperators#TIMESTAMP_MILLIS}. */
public static long timestampMillis(long v) {
// translation is trivial, because Calcite represents TIMESTAMP values as
// millis since epoch
return v;
}
/** For {@link SqlLibraryOperators#TIMESTAMP_MICROS}. */
public static long timestampMicros(long v) {
return v / 1000;
}
/** For {@link SqlLibraryOperators#UNIX_SECONDS}. */
public static long unixSeconds(long v) {
return v / 1000;
}
/** For {@link SqlLibraryOperators#UNIX_MILLIS}. */
public static long unixMillis(long v) {
// translation is trivial, because Calcite represents TIMESTAMP values as
// millis since epoch
return v;
}
/** For {@link SqlLibraryOperators#UNIX_MICROS}. */
public static long unixMicros(long v) {
return v * 1000;
}
/** For {@link SqlLibraryOperators#DATE_FROM_UNIX_DATE}. */
public static int dateFromUnixDate(int v) {
// translation is trivial, because Calcite represents dates as Unix integers
return v;
}
/** For {@link SqlLibraryOperators#UNIX_DATE}. */
public static int unixDate(int v) {
// translation is trivial, because Calcite represents dates as Unix integers
return v;
}
/** SQL {@code DATE(year, month, day)} function. */
public static int date(int year, int month, int day) {
// Calcite represents dates as Unix integers (days since epoch).
return (int) LocalDate.of(year, month, day).toEpochDay();
}
/** SQL {@code DATE(TIMESTAMP)} and
* {@code DATE(TIMESTAMP WITH LOCAL TIME ZONE)} functions. */
public static int date(long timestampMillis) {
// Calcite represents dates as Unix integers (days since epoch).
// Unix time ignores leap seconds; every day has the exact same number of
// milliseconds. BigQuery TIMESTAMP and DATETIME values (Calcite TIMESTAMP
// WITH LOCAL TIME ZONE and TIMESTAMP, respectively) are represented
// internally as milliseconds since epoch (or epoch UTC).
return (int) (timestampMillis / DateTimeUtils.MILLIS_PER_DAY);
}
/** SQL {@code DATE(TIMESTAMP WITH LOCAL TIME, timeZone)} function. */
public static int date(long timestampMillis, String timeZone) {
// Calcite represents dates as Unix integers (days since epoch).
return (int) OffsetDateTime.ofInstant(Instant.ofEpochMilli(timestampMillis),
ZoneId.of(timeZone))
.toLocalDate()
.toEpochDay();
}
/** SQL {@code DATETIME(, , , , , )}
* function. */
public static long datetime(int year, int month, int day, int hour,
int minute, int second) {
// BigQuery's DATETIME function returns a Calcite TIMESTAMP,
// represented internally as milliseconds since epoch UTC.
return LocalDateTime.of(year, month, day, hour, minute, second)
.toEpochSecond(ZoneOffset.UTC)
* DateTimeUtils.MILLIS_PER_SECOND;
}
/** SQL {@code DATETIME(DATE)} function; returns a Calcite TIMESTAMP. */
public static long datetime(int daysSinceEpoch) {
// BigQuery's DATETIME function returns a Calcite TIMESTAMP,
// represented internally as milliseconds since epoch.
return daysSinceEpoch * DateTimeUtils.MILLIS_PER_DAY;
}
/** SQL {@code DATETIME(DATE, TIME)} function; returns a Calcite TIMESTAMP. */
public static long datetime(int daysSinceEpoch, int millisSinceMidnight) {
// BigQuery's DATETIME function returns a Calcite TIMESTAMP,
// represented internally as milliseconds since epoch UTC.
return daysSinceEpoch * DateTimeUtils.MILLIS_PER_DAY + millisSinceMidnight;
}
/** SQL {@code DATETIME(TIMESTAMP WITH LOCAL TIME ZONE)} function;
* returns a Calcite TIMESTAMP. */
public static long datetime(long millisSinceEpoch) {
// BigQuery TIMESTAMP and DATETIME values (Calcite TIMESTAMP WITH LOCAL TIME
// ZONE and TIMESTAMP, respectively) are represented internally as
// milliseconds since epoch (or epoch UTC).
return millisSinceEpoch;
}
/** SQL {@code DATETIME(TIMESTAMP, timeZone)} function;
* returns a Calcite TIMESTAMP. */
public static long datetime(long millisSinceEpoch, String timeZone) {
// BigQuery TIMESTAMP and DATETIME values (Calcite TIMESTAMP WITH LOCAL TIME
// ZONE and TIMESTAMP, respectively) are represented internally as
// milliseconds since epoch (or epoch UTC).
return OffsetDateTime.ofInstant(Instant.ofEpochMilli(millisSinceEpoch),
ZoneId.of(timeZone))
.atZoneSimilarLocal(ZoneId.of("UTC"))
.toInstant()
.toEpochMilli();
}
/** SQL {@code TIMESTAMP()} function. */
public static long timestamp(String expression) {
// Calcite represents TIMESTAMP WITH LOCAL TIME ZONE as Unix integers
// (milliseconds since epoch).
return parseBigQueryTimestampLiteral(expression).toInstant().toEpochMilli();
}
/** SQL {@code TIMESTAMP(, )} function. */
public static long timestamp(String expression, String timeZone) {
// Calcite represents TIMESTAMP WITH LOCAL TIME ZONE as Unix integers
// (milliseconds since epoch).
return parseBigQueryTimestampLiteral(expression)
.atZoneSimilarLocal(ZoneId.of(timeZone))
.toInstant()
.toEpochMilli();
}
private static OffsetDateTime parseBigQueryTimestampLiteral(String expression) {
// First try to parse with an offset, otherwise parse as a local and assume
// UTC ("no offset").
try {
return OffsetDateTime.parse(expression,
BIG_QUERY_TIMESTAMP_LITERAL_FORMATTER);
} catch (DateTimeParseException e) {
// ignore
}
if (IS_JDK_8
&& expression.matches(".*[+-][0-9][0-9]$")) {
// JDK 8 has a bug that prevents matching offsets like "+00" but can
// match "+00:00".
try {
expression += ":00";
return OffsetDateTime.parse(expression,
BIG_QUERY_TIMESTAMP_LITERAL_FORMATTER);
} catch (DateTimeParseException e) {
// ignore
}
}
try {
return LocalDateTime
.parse(expression, BIG_QUERY_TIMESTAMP_LITERAL_FORMATTER)
.atOffset(ZoneOffset.UTC);
} catch (DateTimeParseException e2) {
throw new IllegalArgumentException(
String.format(Locale.ROOT,
"Could not parse BigQuery timestamp literal: %s", expression),
e2);
}
}
/** SQL {@code TIMESTAMP()} function. */
public static long timestamp(int days) {
// Calcite represents TIMESTAMP WITH LOCAL TIME ZONE as Unix integers
// (milliseconds since epoch). Unix time ignores leap seconds; every day
// has the same number of milliseconds.
return ((long) days) * DateTimeUtils.MILLIS_PER_DAY;
}
/** SQL {@code TIMESTAMP(, )} function. */
public static long timestamp(int days, String timeZone) {
// Calcite represents TIMESTAMP WITH LOCAL TIME ZONE as Unix integers
// (milliseconds since epoch).
final LocalDateTime localDateTime =
LocalDateTime.of(LocalDate.ofEpochDay(days), LocalTime.MIDNIGHT);
final ZoneOffset zoneOffset =
ZoneId.of(timeZone).getRules().getOffset(localDateTime);
return OffsetDateTime.of(localDateTime, zoneOffset)
.toInstant()
.toEpochMilli();
}
/** SQL {@code TIMESTAMP()} function; returns a TIMESTAMP WITH
* LOCAL TIME ZONE. */
public static long timestamp(long millisSinceEpoch) {
// BigQuery TIMESTAMP and DATETIME values (Calcite TIMESTAMP WITH LOCAL
// TIME ZONE and TIMESTAMP, respectively) are represented internally as
// milliseconds since epoch UTC and epoch.
return millisSinceEpoch;
}
/** SQL {@code TIMESTAMP(, )} function; returns a
* TIMESTAMP WITH LOCAL TIME ZONE. */
public static long timestamp(long millisSinceEpoch, String timeZone) {
// BigQuery TIMESTAMP and DATETIME values (Calcite TIMESTAMP WITH LOCAL
// TIME ZONE and TIMESTAMP, respectively) are represented internally as
// milliseconds since epoch UTC and epoch.
final Instant instant = Instant.ofEpochMilli(millisSinceEpoch);
final ZoneId utcZone = ZoneId.of("UTC");
return OffsetDateTime.ofInstant(instant, utcZone)
.atZoneSimilarLocal(ZoneId.of(timeZone))
.toInstant()
.toEpochMilli();
}
/** SQL {@code TIME(, , )} function. */
public static int time(int hour, int minute, int second) {
// Calcite represents time as Unix integers (milliseconds since midnight).
return (int) (LocalTime.of(hour, minute, second).toSecondOfDay()
* DateTimeUtils.MILLIS_PER_SECOND);
}
/** SQL {@code TIME()} and {@code TIME()}
* functions. */
public static int time(long timestampMillis) {
// Calcite represents time as Unix integers (milliseconds since midnight).
// Unix time ignores leap seconds; every day has the same number of
// milliseconds.
//
// BigQuery TIMESTAMP and DATETIME values (Calcite TIMESTAMP WITH LOCAL
// TIME ZONE and TIMESTAMP, respectively) are represented internally as
// milliseconds since epoch UTC and epoch.
return (int) (timestampMillis % DateTimeUtils.MILLIS_PER_DAY);
}
/** SQL {@code TIME(, )} function. */
public static int time(long timestampMillis, String timeZone) {
// Calcite represents time as Unix integers (milliseconds since midnight).
// Unix time ignores leap seconds; every day has the same number of
// milliseconds.
final Instant instant = Instant.ofEpochMilli(timestampMillis);
final ZoneId zoneId = ZoneId.of(timeZone);
return (int) (OffsetDateTime.ofInstant(instant, zoneId)
.toLocalTime()
.toNanoOfDay()
/ (1000L * 1000L)); // milli > micro > nano
}
public static @PolyNull Long toTimestampWithLocalTimeZone(@PolyNull String v) {
if (v == null) {
return castNonNull(null);
}
return new TimestampWithTimeZoneString(v)
.withTimeZone(DateTimeUtils.UTC_ZONE)
.getLocalTimestampString()
.getMillisSinceEpoch();
}
public static @PolyNull Long toTimestampWithLocalTimeZone(@PolyNull String v,
TimeZone timeZone) {
if (v == null) {
return castNonNull(null);
}
return new TimestampWithTimeZoneString(v + " " + timeZone.getID())
.withTimeZone(DateTimeUtils.UTC_ZONE)
.getLocalTimestampString()
.getMillisSinceEpoch();
}
// Don't need shortValueOf etc. - Short.valueOf is sufficient.
/** Helper for CAST(... AS VARCHAR(maxLength)). */
public static @PolyNull String truncate(@PolyNull String s, int maxLength) {
if (s == null) {
return s;
} else if (s.length() > maxLength) {
return s.substring(0, maxLength);
} else {
return s;
}
}
/** Helper for CAST(... AS CHAR(maxLength)). */
public static @PolyNull String truncateOrPad(@PolyNull String s, int maxLength) {
if (s == null) {
return s;
} else {
final int length = s.length();
if (length > maxLength) {
return s.substring(0, maxLength);
} else {
return length < maxLength ? Spaces.padRight(s, maxLength) : s;
}
}
}
/** Helper for CAST(... AS VARBINARY(maxLength)). */
public static @PolyNull ByteString truncate(@PolyNull ByteString s, int maxLength) {
if (s == null) {
return s;
} else if (s.length() > maxLength) {
return s.substring(0, maxLength);
} else {
return s;
}
}
/** Helper for CAST(... AS BINARY(maxLength)). */
public static @PolyNull ByteString truncateOrPad(@PolyNull ByteString s, int maxLength) {
if (s == null) {
return s;
} else {
final int length = s.length();
if (length > maxLength) {
return s.substring(0, maxLength);
} else if (length < maxLength) {
return s.concat(new ByteString(new byte[maxLength - length]));
} else {
return s;
}
}
}
/** SQL {@code POSITION(seek IN string)} function. */
public static int position(String seek, String s) {
return s.indexOf(seek) + 1;
}
/** SQL {@code POSITION(seek IN string)} function for byte strings. */
public static int position(ByteString seek, ByteString s) {
return s.indexOf(seek) + 1;
}
/** SQL {@code POSITION(seek IN string FROM integer)} function. */
public static int position(String seek, String s, int from) {
if (from == 0) {
throw RESOURCE.fromNotZero().ex();
}
if (from > 0) {
return positionForwards(seek, s, from);
} else {
from += s.length(); // convert negative position to positive index
return positionBackwards(seek, s, from);
}
}
/** SQL {@code POSITION(seek IN string FROM integer)} function for byte
* strings. */
public static int position(ByteString seek, ByteString s, int from) {
if (from == 0) {
throw RESOURCE.fromNotZero().ex();
}
if (from > 0) {
return positionForwards(seek, s, from);
} else {
from += s.length(); // convert negative position to 0-based index
return positionBackwards(seek, s, from);
}
}
/** Returns the position (1-based) of {@code seek} in string {@code s}
* seeking forwards from {@code from} (1-based). */
private static int positionForwards(String seek, String s, int from) {
final int from0 = from - 1; // 0-based
if (from0 >= s.length()) {
return 0;
} else {
return s.indexOf(seek, from0) + 1;
}
}
/** Returns the position (1-based) of {@code seek} in byte string {@code s}
* seeking forwards from {@code from} (1-based). */
private static int positionForwards(ByteString seek, ByteString s,
int from) {
final int from0 = from - 1; // 0-based
if (from0 >= s.length()) {
return 0;
} else {
return s.indexOf(seek, from0) + 1;
}
}
/** Returns the position (1-based) of {@code seek} in string {@code s}
* seeking backwards from {@code rightIndex} (0-based). */
private static int positionBackwards(String seek, String s, int rightIndex) {
if (rightIndex <= 0) {
return 0;
}
int lastIndex = s.lastIndexOf(seek) + 1;
while (lastIndex > rightIndex + 1) {
lastIndex = s.substring(0, lastIndex - 1).lastIndexOf(seek) + 1;
if (lastIndex == 0) {
return 0;
}
}
return lastIndex;
}
/** Returns the position (1-based) of {@code seek} in byte string {@code s}
* seeking backwards from {@code rightIndex} (0-based). */
private static int positionBackwards(ByteString seek, ByteString s,
int rightIndex) {
if (rightIndex <= 0) {
return 0;
}
int lastIndex = 0;
while (lastIndex < rightIndex) {
// NOTE: When [CALCITE-5682] is fixed, use ByteString.lastIndexOf
int indexOf = s.substring(lastIndex).indexOf(seek) + 1;
if (indexOf == 0 || lastIndex + indexOf > rightIndex + 1) {
break;
}
lastIndex += indexOf;
}
return lastIndex;
}
/** SQL {@code POSITION(seek, string, from, occurrence)} function. */
public static int position(String seek, String s, int from, int occurrence) {
if (from == 0) {
throw RESOURCE.fromNotZero().ex();
}
if (occurrence == 0) {
throw RESOURCE.occurrenceNotZero().ex();
}
if (from > 0) {
// Forwards
--from; // compensate for the '++from' 2 lines down
for (int i = 0; i < occurrence; i++) {
++from; // move on to next occurrence
from = positionForwards(seek, s, from);
if (from == 0) {
return 0;
}
}
} else {
// Backwards
from += s.length() + 1; // convert negative position to positive index
++from; // compensate for the '--from' 2 lines down
for (int i = 0; i < occurrence; i++) {
--from; // move on to next occurrence
from = positionBackwards(seek, s, from - 1);
if (from == 0) {
return 0;
}
}
}
return from;
}
/** SQL {@code POSITION(seek, string, from, occurrence)} function for byte
* strings. */
public static int position(ByteString seek, ByteString s, int from,
int occurrence) {
if (from == 0) {
throw RESOURCE.fromNotZero().ex();
}
if (occurrence == 0) {
throw RESOURCE.occurrenceNotZero().ex();
}
if (from > 0) {
// Forwards
--from; // compensate for the '++from' 2 lines down
for (int i = 0; i < occurrence; i++) {
++from; // move on to next occurrence
from = positionForwards(seek, s, from);
if (from == 0) {
return 0;
}
}
} else {
// Backwards
from += s.length() + 1; // convert negative position to positive index
++from; // compensate for the '--from' 2 lines down
for (int i = 0; i < occurrence; i++) {
--from; // move on to next occurrence
from = positionBackwards(seek, s, from - 1);
if (from == 0) {
return 0;
}
}
}
return from;
}
/** Helper for rounding. Truncate(12345, 1000) returns 12000. */
public static long round(long v, long x) {
return truncate(v + x / 2, x);
}
/** Helper for rounding. Truncate(12345, 1000) returns 12000. */
public static long truncate(long v, long x) {
long remainder = v % x;
if (remainder < 0) {
remainder += x;
}
return v - remainder;
}
/** Helper for rounding. Truncate(12345, 1000) returns 12000. */
public static int round(int v, int x) {
return truncate(v + x / 2, x);
}
/** Helper for rounding. Truncate(12345, 1000) returns 12000. */
public static int truncate(int v, int x) {
int remainder = v % x;
if (remainder < 0) {
remainder += x;
}
return v - remainder;
}
/**
* SQL {@code DAYNAME} function, applied to a TIMESTAMP argument.
*
* @param timestamp Milliseconds from epoch
* @param locale Locale
* @return Name of the weekday in the given locale
*/
public static String dayNameWithTimestamp(long timestamp, Locale locale) {
return timeStampToLocalDate(timestamp)
.format(ROOT_DAY_FORMAT.withLocale(locale));
}
/**
* SQL {@code DAYNAME} function, applied to a DATE argument.
*
* @param date Days since epoch
* @param locale Locale
* @return Name of the weekday in the given locale
*/
public static String dayNameWithDate(int date, Locale locale) {
return dateToLocalDate(date)
.format(ROOT_DAY_FORMAT.withLocale(locale));
}
/**
* SQL {@code MONTHNAME} function, applied to a TIMESTAMP argument.
*
* @param timestamp Milliseconds from epoch
* @param locale Locale
* @return Name of the month in the given locale
*/
public static String monthNameWithTimestamp(long timestamp, Locale locale) {
return timeStampToLocalDate(timestamp)
.format(ROOT_MONTH_FORMAT.withLocale(locale));
}
/**
* SQL {@code MONTHNAME} function, applied to a DATE argument.
*
* @param date Days from epoch
* @param locale Locale
* @return Name of the month in the given locale
*/
public static String monthNameWithDate(int date, Locale locale) {
return dateToLocalDate(date)
.format(ROOT_MONTH_FORMAT.withLocale(locale));
}
/**
* Converts a date (days since epoch) to a {@link LocalDate}.
*
* @param date days since epoch
* @return localDate
*/
private static LocalDate dateToLocalDate(int date) {
int y0 = (int) DateTimeUtils.unixDateExtract(TimeUnitRange.YEAR, date);
int m0 = (int) DateTimeUtils.unixDateExtract(TimeUnitRange.MONTH, date);
int d0 = (int) DateTimeUtils.unixDateExtract(TimeUnitRange.DAY, date);
return LocalDate.of(y0, m0, d0);
}
/**
* Converts a timestamp (milliseconds since epoch) to a {@link LocalDate}.
*
* @param timestamp milliseconds from epoch
* @return localDate
*/
private static LocalDate timeStampToLocalDate(long timestamp) {
int date = timestampToDate(timestamp);
return dateToLocalDate(date);
}
/** Converts a timestamp (milliseconds since epoch)
* to a date (days since epoch). */
public static int timestampToDate(long timestamp) {
return (int) (timestamp / DateTimeUtils.MILLIS_PER_DAY);
}
/** Converts a timestamp (milliseconds since epoch)
* to a time (milliseconds since midnight). */
public static int timestampToTime(long timestamp) {
return (int) (timestamp % DateTimeUtils.MILLIS_PER_DAY);
}
/** SQL {@code CURRENT_TIMESTAMP} function. */
@NonDeterministic
public static long currentTimestamp(DataContext root) {
return DataContext.Variable.CURRENT_TIMESTAMP.get(root);
}
/** SQL {@code CURRENT_TIME} function. */
@NonDeterministic
public static int currentTime(DataContext root) {
int time = timestampToTime(currentTimestamp(root));
if (time < 0) {
time = (int) (time + DateTimeUtils.MILLIS_PER_DAY);
}
return time;
}
/** SQL {@code CURRENT_DATE} function. */
@NonDeterministic
public static int currentDate(DataContext root) {
final long timestamp = currentTimestamp(root);
int date = timestampToDate(timestamp);
final int time = timestampToTime(timestamp);
if (time < 0) {
--date;
}
return date;
}
/** SQL {@code LOCAL_TIMESTAMP} function. */
@NonDeterministic
public static long localTimestamp(DataContext root) {
return DataContext.Variable.LOCAL_TIMESTAMP.get(root);
}
/** SQL {@code LOCAL_TIME} function. */
@NonDeterministic
public static int localTime(DataContext root) {
return timestampToTime(localTimestamp(root));
}
@NonDeterministic
public static TimeZone timeZone(DataContext root) {
return DataContext.Variable.TIME_ZONE.get(root);
}
/** SQL {@code USER} function. */
@Deterministic
public static String user(DataContext root) {
return requireNonNull(DataContext.Variable.USER.get(root));
}
/** SQL {@code SYSTEM_USER} function. */
@Deterministic
public static String systemUser(DataContext root) {
return requireNonNull(DataContext.Variable.SYSTEM_USER.get(root));
}
@NonDeterministic
public static Locale locale(DataContext root) {
return DataContext.Variable.LOCALE.get(root);
}
/** SQL {@code DATEADD} function applied to a custom time frame.
*
* Custom time frames are created as part of a {@link TimeFrameSet}.
* This method retrieves the session's time frame set from the
* {@link DataContext.Variable#TIME_FRAME_SET} variable, then looks up the
* time frame by name. */
public static int customDateAdd(DataContext root,
String timeFrameName, int interval, int date) {
final TimeFrameSet timeFrameSet =
requireNonNull(DataContext.Variable.TIME_FRAME_SET.get(root));
final TimeFrame timeFrame = timeFrameSet.get(timeFrameName);
return timeFrameSet.addDate(date, interval, timeFrame);
}
/** SQL {@code TIMESTAMPADD} function applied to a custom time frame.
*
*
Custom time frames are created and accessed as described in
* {@link #customDateAdd}. */
public static long customTimestampAdd(DataContext root,
String timeFrameName, long interval, long timestamp) {
final TimeFrameSet timeFrameSet =
requireNonNull(DataContext.Variable.TIME_FRAME_SET.get(root));
final TimeFrame timeFrame = timeFrameSet.get(timeFrameName);
return timeFrameSet.addTimestamp(timestamp, interval, timeFrame);
}
/** SQL {@code DATEDIFF} function applied to a custom time frame.
*
*
Custom time frames are created and accessed as described in
* {@link #customDateAdd}. */
public static int customDateDiff(DataContext root,
String timeFrameName, int date, int date2) {
final TimeFrameSet timeFrameSet =
requireNonNull(DataContext.Variable.TIME_FRAME_SET.get(root));
final TimeFrame timeFrame = timeFrameSet.get(timeFrameName);
return timeFrameSet.diffDate(date, date2, timeFrame);
}
/** SQL {@code TIMESTAMPDIFF} function applied to a custom time frame.
*
*
Custom time frames are created and accessed as described in
* {@link #customDateAdd}. */
public static long customTimestampDiff(DataContext root,
String timeFrameName, long timestamp, long timestamp2) {
final TimeFrameSet timeFrameSet =
requireNonNull(DataContext.Variable.TIME_FRAME_SET.get(root));
final TimeFrame timeFrame = timeFrameSet.get(timeFrameName);
return timeFrameSet.diffTimestamp(timestamp, timestamp2, timeFrame);
}
/** SQL {@code FLOOR} function applied to a {@code DATE} value
* and a custom time frame.
*
*
Custom time frames are created and accessed as described in
* {@link #customDateAdd}. */
public static int customDateFloor(DataContext root,
String timeFrameName, int date) {
final TimeFrameSet timeFrameSet =
requireNonNull(DataContext.Variable.TIME_FRAME_SET.get(root));
final TimeFrame timeFrame = timeFrameSet.get(timeFrameName);
return timeFrameSet.floorDate(date, timeFrame);
}
/** SQL {@code CEIL} function applied to a {@code DATE} value
* and a custom time frame.
*
*
Custom time frames are created and accessed as described in
* {@link #customDateAdd}. */
public static int customDateCeil(DataContext root,
String timeFrameName, int date) {
final TimeFrameSet timeFrameSet =
requireNonNull(DataContext.Variable.TIME_FRAME_SET.get(root));
final TimeFrame timeFrame = timeFrameSet.get(timeFrameName);
return timeFrameSet.ceilDate(date, timeFrame);
}
/** SQL {@code FLOOR} function applied to a {@code TIMESTAMP} value
* and a custom time frame.
*
*
Custom time frames are created and accessed as described in
* {@link #customDateAdd}. */
public static long customTimestampFloor(DataContext root,
String timeFrameName, long timestamp) {
final TimeFrameSet timeFrameSet =
requireNonNull(DataContext.Variable.TIME_FRAME_SET.get(root));
final TimeFrame timeFrame = timeFrameSet.get(timeFrameName);
return timeFrameSet.floorTimestamp(timestamp, timeFrame);
}
/** SQL {@code CEIL} function applied to a {@code TIMESTAMP} value
* and a custom time frame.
*
*
Custom time frames are created and accessed as described in
* {@link #customDateAdd}. */
public static long customTimestampCeil(DataContext root,
String timeFrameName, long timestamp) {
final TimeFrameSet timeFrameSet =
requireNonNull(DataContext.Variable.TIME_FRAME_SET.get(root));
final TimeFrame timeFrame = timeFrameSet.get(timeFrameName);
return timeFrameSet.ceilTimestamp(timestamp, timeFrame);
}
/** SQL {@code TRANSLATE(string, search_chars, replacement_chars)}
* function. */
public static String translate3(String s, String search, String replacement) {
return org.apache.commons.lang3.StringUtils.replaceChars(s, search, replacement);
}
/** SQL {@code REPLACE(string, search, replacement)} function. */
public static String replace(String s, String search, String replacement) {
return s.replace(search, replacement);
}
/** Helper for "array element reference". Caller has already ensured that
* array and index are not null.
*
*
Index may be 0- or 1-based depending on which array subscript operator
* is being used. {@code ITEM}, {@code ORDINAL}, and {@code SAFE_ORDINAL}
* are 1-based, while {@code OFFSET} and {@code SAFE_OFFSET} are 0-based.
*
*
The {@code ITEM}, {@code SAFE_OFFSET}, and {@code SAFE_ORDINAL}
* operators return null if the index is out of bounds, while the others
* throw an error. */
public static @Nullable Object arrayItem(List list, int item, int offset,
boolean safe) {
if (item < offset || item > list.size() + 1 - offset) {
if (safe) {
return null;
} else {
throw RESOURCE.arrayIndexOutOfBounds(item).ex();
}
}
return list.get(item - offset);
}
/** Helper for "map element reference". Caller has already ensured that
* array and index are not null. Index is 1-based, per SQL. */
public static @Nullable Object mapItem(Map map, Object item) {
return map.get(item);
}
/** Implements the {@code [ ... ]} operator on an object whose type is not
* known until runtime.
*/
public static @Nullable Object item(Object object, Object index) {
if (object instanceof Map) {
return mapItem((Map) object, index);
}
if (object instanceof List && index instanceof Number) {
return arrayItem((List) object, ((Number) index).intValue(), 1, true);
}
if (index instanceof Number) {
return structAccess(object, ((Number) index).intValue() - 1, null); // 1 indexed
}
if (index instanceof String) {
return structAccess(object, -1, index.toString());
}
return null;
}
/** As {@link #arrayItem} method, but allows array to be nullable. */
public static @Nullable Object arrayItemOptional(@Nullable List list,
int item, int offset, boolean safe) {
if (list == null) {
return null;
}
return arrayItem(list, item, offset, safe);
}
/** As {@link #mapItem} method, but allows map to be nullable. */
public static @Nullable Object mapItemOptional(@Nullable Map map,
Object item) {
if (map == null) {
return null;
}
return mapItem(map, item);
}
/** As {@link #item} method, but allows object to be nullable. */
public static @Nullable Object itemOptional(@Nullable Object object,
Object index) {
if (object == null) {
return null;
}
return item(object, index);
}
/** NULL → FALSE, FALSE → FALSE, TRUE → TRUE. */
public static boolean isTrue(@Nullable Boolean b) {
return b != null && b;
}
/** NULL → FALSE, FALSE → TRUE, TRUE → FALSE. */
public static boolean isFalse(@Nullable Boolean b) {
return b != null && !b;
}
/** NULL → TRUE, FALSE → TRUE, TRUE → FALSE. */
public static boolean isNotTrue(@Nullable Boolean b) {
return b == null || !b;
}
/** NULL → TRUE, FALSE → FALSE, TRUE → TRUE. */
public static boolean isNotFalse(@Nullable Boolean b) {
return b == null || b;
}
/** NULL → NULL, FALSE → TRUE, TRUE → FALSE. */
public static @PolyNull Boolean not(@PolyNull Boolean b) {
return b == null ? castNonNull(null) : !b;
}
/** Converts a JDBC array to a list. */
public static @PolyNull List arrayToList(final java.sql.@PolyNull Array a) {
if (a == null) {
return castNonNull(null);
}
try {
return Primitive.asList(a.getArray());
} catch (SQLException e) {
throw Util.toUnchecked(e);
}
}
/** Support the {@code CURRENT VALUE OF sequence} operator. */
@NonDeterministic
public static long sequenceCurrentValue(String key) {
return getAtomicLong(key).get();
}
/** Support the {@code NEXT VALUE OF sequence} operator. */
@NonDeterministic
public static long sequenceNextValue(String key) {
return getAtomicLong(key).incrementAndGet();
}
private static AtomicLong getAtomicLong(String key) {
final Map map =
requireNonNull(THREAD_SEQUENCES.get(), "THREAD_SEQUENCES.get()");
AtomicLong atomic = map.get(key);
if (atomic == null) {
atomic = new AtomicLong();
map.put(key, atomic);
}
return atomic;
}
/** Support the ARRAYS_OVERLAP function. */
public static @Nullable Boolean arraysOverlap(List list1, List list2) {
if (list1.size() > list2.size()) {
return arraysOverlap(list2, list1);
}
final List smaller = list1;
final List bigger = list2;
boolean hasNull = false;
if (smaller.size() > 0 && bigger.size() > 0) {
final Set smallestSet = new HashSet(smaller);
hasNull = smallestSet.remove(null);
for (Object element : bigger) {
if (element == null) {
hasNull = true;
} else if (smallestSet.contains(element)) {
return true;
}
}
}
if (hasNull) {
return null;
} else {
return false;
}
}
/** Support the ARRAYS_ZIP function. */
@SuppressWarnings("argument.type.incompatible")
public static List arraysZip(List... lists) {
final int biggestCardinality = lists.length == 0
? 0
: Arrays.stream(lists).mapToInt(List::size).max().getAsInt();
final List result = new ArrayList(biggestCardinality);
for (int i = 0; i < biggestCardinality; i++) {
List row = new ArrayList<>();
Object value;
for (List list : lists) {
if (i < list.size() && list.get(i) != null) {
value = list.get(i);
} else {
value = null;
}
row.add(value);
}
result.add(row);
}
return result;
}
/** Support the ARRAY_COMPACT function. */
public static List compact(List list) {
final List result = new ArrayList();
for (Object element : list) {
if (element != null) {
result.add(element);
}
}
return result;
}
/** Support the ARRAY_APPEND function. */
public static List arrayAppend(List list, Object element) {
final List result = new ArrayList(list.size() + 1);
result.addAll(list);
result.add(element);
return result;
}
/** Support the ARRAY_DISTINCT function.
*
* Note: If the list does not contain null,
* {@link Util#distinctList(List)} is probably faster. */
public static List distinct(List list) {
Set result = new LinkedHashSet<>(list);
return new ArrayList<>(result);
}
/** Support the ARRAY_MAX function. */
public static @Nullable > T arrayMax(
List extends T> list) {
T max = null;
for (int i = 0; i < list.size(); i++) {
T item = list.get(i);
if (item != null && (max == null || item.compareTo(max) > 0)) {
max = item;
}
}
return max;
}
/** Support the ARRAY_MIN function. */
public static @Nullable > T arrayMin(
List extends T> list) {
T min = null;
for (int i = 0; i < list.size(); i++) {
T item = list.get(i);
if (item != null && (min == null || item.compareTo(min) < 0)) {
min = item;
}
}
return min;
}
/** Support the ARRAY_PREPEND function. */
public static List arrayPrepend(List list, Object element) {
final List result = new ArrayList(list.size() + 1);
result.add(element);
result.addAll(list);
return result;
}
/** Support the ARRAY_POSITION function. */
public static Long arrayPosition(List list, Object element) {
final int index = list.indexOf(element);
if (index != -1) {
return Long.valueOf(index + 1L);
}
return 0L;
}
/** Support the ARRAY_REMOVE function. */
public static List arrayRemove(List list, Object element) {
final List result = new ArrayList();
for (Object obj : list) {
if (obj == null || !obj.equals(element)) {
result.add(obj);
}
}
return result;
}
/** Support the ARRAY_REPEAT function. */
public static @Nullable List repeat(Object element, Object count) {
if (count == null) {
return null;
}
int numberOfElement = (int) count;
if (numberOfElement < 0) {
numberOfElement = 0;
}
return Collections.nCopies(numberOfElement, element);
}
/** Support the ARRAY_EXCEPT function. */
public static List arrayExcept(List list1, List list2) {
final Set result = new LinkedHashSet<>(list1);
result.removeAll(list2);
return new ArrayList<>(result);
}
/** Support the ARRAY_INTERSECT function. */
public static List arrayIntersect(List list1, List list2) {
final Set result = new LinkedHashSet<>(list1);
result.retainAll(list2);
return new ArrayList<>(result);
}
/** Support the ARRAY_UNION function. */
public static List arrayUnion(List list1, List list2) {
final Set result = new LinkedHashSet<>();
result.addAll(list1);
result.addAll(list2);
return new ArrayList<>(result);
}
/** Support the SORT_ARRAY function. */
public static List sortArray(List list, boolean ascending) {
Comparator comparator = ascending
? Comparator.nullsFirst(Comparator.naturalOrder())
: Comparator.nullsLast(Comparator.reverseOrder());
list.sort(comparator);
return list;
}
/** Support the MAP_CONCAT function. */
public static Map mapConcat(Map... maps) {
final Map result = new LinkedHashMap();
Arrays.stream(maps).forEach(result::putAll);
return result;
}
/** Support the MAP_ENTRIES function. */
public static List mapEntries(Map map) {
final List result = new ArrayList(map.size());
for (Map.Entry entry : map.entrySet()) {
result.add(Arrays.asList(entry.getKey(), entry.getValue()));
}
return result;
}
/** Support the MAP_KEYS function. */
public static List mapKeys(Map map) {
return new ArrayList<>(map.keySet());
}
/** Support the MAP_VALUES function. */
public static List mapValues(Map map) {
return new ArrayList<>(map.values());
}
/** Support the MAP_FROM_ARRAYS function. */
public static Map mapFromArrays(List keysArray, List valuesArray) {
if (keysArray.size() != valuesArray.size()) {
throw RESOURCE.illegalArgumentsInMapFromArraysFunc(keysArray.size(), valuesArray.size()).ex();
}
final Map map = new LinkedHashMap<>();
for (int i = 0; i < keysArray.size(); i++) {
map.put(keysArray.get(i), valuesArray.get(i));
}
return map;
}
/** Support the MAP_FROM_ENTRIES function. */
public static @Nullable Map mapFromEntries(List entries) {
final Map map = new LinkedHashMap<>();
for (Object entry: entries) {
if (entry == null) {
return null;
}
map.put(structAccess(entry, 0, null), structAccess(entry, 1, null));
}
return map;
}
/** Support the STR_TO_MAP function. */
public static Map strToMap(String string, String stringDelimiter, String keyValueDelimiter) {
final Map map = new LinkedHashMap();
final String[] keyValues = string.split(stringDelimiter, -1);
for (String s : keyValues) {
String[] keyValueArray = s.split(keyValueDelimiter, 2);
String key = keyValueArray[0];
String value = keyValueArray.length < 2
? null
: keyValueArray[1];
map.put(key, value);
}
return map;
}
/** Support the SLICE function. */
public static List slice(List list) {
List result = new ArrayList(list.size());
for (Object e : list) {
result.add(structAccess(e, 0, null));
}
return result;
}
/** Support the ELEMENT function. */
public static @Nullable Object element(List list) {
switch (list.size()) {
case 0:
return null;
case 1:
return list.get(0);
default:
throw RESOURCE.moreThanOneValueInList(list.toString()).ex();
}
}
/** Support the MEMBER OF function. */
public static boolean memberOf(@Nullable Object object, Collection collection) {
return collection.contains(object);
}
/** Support the MULTISET INTERSECT DISTINCT function. */
public static Collection multisetIntersectDistinct(Collection c1,
Collection c2) {
final Set result = new HashSet<>(c1);
result.retainAll(c2);
return new ArrayList<>(result);
}
/** Support the MULTISET INTERSECT ALL function. */
public static Collection multisetIntersectAll(Collection c1,
Collection c2) {
final List result = new ArrayList<>(c1.size());
final List c2Copy = new ArrayList<>(c2);
for (E e : c1) {
if (c2Copy.remove(e)) {
result.add(e);
}
}
return result;
}
/** Support the MULTISET EXCEPT ALL function. */
@SuppressWarnings("JdkObsolete")
public static Collection multisetExceptAll(Collection c1,
Collection c2) {
// TOOD: use Multisets?
final List result = new LinkedList<>(c1);
for (E e : c2) {
result.remove(e);
}
return result;
}
/** Support the MULTISET EXCEPT DISTINCT function. */
public static Collection multisetExceptDistinct(Collection c1,
Collection c2) {
final Set result = new HashSet<>(c1);
result.removeAll(c2);
return new ArrayList<>(result);
}
/** Support the IS A SET function. */
public static boolean isASet(Collection collection) {
if (collection instanceof Set) {
return true;
}
// capacity calculation is in the same way like for new HashSet(Collection)
// however return immediately in case of duplicates
Set set = new HashSet(Math.max((int) (collection.size() / .75f) + 1, 16));
for (Object e : collection) {
if (!set.add(e)) {
return false;
}
}
return true;
}
/** Support the SUBMULTISET OF function. */
@SuppressWarnings("JdkObsolete")
public static boolean submultisetOf(Collection possibleSubMultiset,
Collection multiset) {
if (possibleSubMultiset.size() > multiset.size()) {
return false;
}
// TODO: use Multisets?
Collection multisetLocal = new LinkedList(multiset);
for (Object e : possibleSubMultiset) {
if (!multisetLocal.remove(e)) {
return false;
}
}
return true;
}
/** Support the MULTISET UNION function. */
public static Collection multisetUnionDistinct(Collection collection1,
Collection collection2) {
// capacity calculation is in the same way like for new HashSet(Collection)
Set resultCollection =
new HashSet(Math.max((int) ((collection1.size() + collection2.size()) / .75f) + 1, 16));
resultCollection.addAll(collection1);
resultCollection.addAll(collection2);
return new ArrayList(resultCollection);
}
/** Support the MULTISET UNION ALL function. */
public static Collection multisetUnionAll(Collection collection1,
Collection collection2) {
List resultCollection = new ArrayList(collection1.size() + collection2.size());
resultCollection.addAll(collection1);
resultCollection.addAll(collection2);
return resultCollection;
}
/** Support the ARRAY_REVERSE function. */
public static List reverse(List list) {
Collections.reverse(list);
return list;
}
/** SQL {@code ARRAY_TO_STRING(array, delimiter)} function. */
public static String arrayToString(List list, String delimiter) {
return arrayToString(list, delimiter, null);
}
/** SQL {@code ARRAY_TO_STRING(array, delimiter, nullText)} function. */
public static String arrayToString(List list, String delimiter, @Nullable String nullText) {
StringBuilder sb = new StringBuilder();
boolean isFirst = true;
for (Object item : list) {
String str;
if (item == null) {
if (nullText == null) {
continue;
} else {
str = nullText;
}
} else if (item instanceof String) {
str = (String) item;
} else if (item instanceof ByteString) {
str = item.toString();
} else {
throw new IllegalStateException(
"arrayToString supports only String or ByteString, but got "
+ item.getClass().getName());
}
if (!isFirst) {
sb.append(delimiter);
}
sb.append(str);
isFirst = false;
}
return sb.toString();
}
/**
* Function that, given a certain List containing single-item structs (i.e. arrays / lists with
* a single item), builds an Enumerable that returns those single items inside the structs.
*/
public static Function1, Enumerable> flatList() {
return inputList -> Linq4j.asEnumerable(inputList).select(v -> structAccess(v, 0, null));
}
public static Function1>> flatProduct(
final int[] fieldCounts, final boolean withOrdinality,
final FlatProductInputType[] inputTypes) {
if (fieldCounts.length == 1) {
if (!withOrdinality && inputTypes[0] == FlatProductInputType.SCALAR) {
//noinspection unchecked
return (Function1) LIST_AS_ENUMERABLE;
} else {
return row -> p2(new Object[] { row }, fieldCounts, withOrdinality,
inputTypes);
}
}
return lists -> p2((Object[]) lists, fieldCounts, withOrdinality,
inputTypes);
}
private static Enumerable> p2(
Object[] lists, int[] fieldCounts, boolean withOrdinality,
FlatProductInputType[] inputTypes) {
final List>> enumerators = new ArrayList<>();
int totalFieldCount = 0;
for (int i = 0; i < lists.length; i++) {
int fieldCount = fieldCounts[i];
FlatProductInputType inputType = inputTypes[i];
Object inputObject = lists[i];
switch (inputType) {
case SCALAR:
@SuppressWarnings("unchecked") List list =
(List) inputObject;
enumerators.add(
Linq4j.transform(
Linq4j.enumerator(list), FlatLists::of));
break;
case LIST:
@SuppressWarnings("unchecked") List> listList =
(List>) inputObject;
enumerators.add(Linq4j.enumerator(listList));
break;
case MAP:
@SuppressWarnings("unchecked") Map map =
(Map) inputObject;
Enumerator> enumerator =
Linq4j.enumerator(map.entrySet());
Enumerator> transformed =
Linq4j.transform(enumerator,
e -> FlatLists.of(e.getKey(), e.getValue()));
enumerators.add(transformed);
break;
default:
break;
}
if (fieldCount < 0) {
++totalFieldCount;
} else {
totalFieldCount += fieldCount;
}
}
if (withOrdinality) {
++totalFieldCount;
}
return product(enumerators, totalFieldCount, withOrdinality);
}
public static Object[] array(Object... args) {
return args;
}
/**
* Returns whether there is an element in {@code list} for which {@code predicate} is true.
* Also, if {@code predicate} returns null for any element of {@code list}
* and does not return {@code true} for any element of {@code list},
* the result will be {@code null}, not {@code false}.
*/
public static @Nullable Boolean nullableExists(List extends E> list,
Function1 predicate) {
boolean nullExists = false;
for (E e : list) {
Boolean res = predicate.apply(e);
if (res == null) {
nullExists = true;
} else if (res) {
return true;
}
}
return nullExists ? null : false;
}
/**
* Returns whether {@code predicate} is true for all elements of {@code list}.
* Also, if {@code predicate} returns null for any element of {@code list}
* and does not return {@code false} for any element,
* the result will be {@code null}, not {@code true}.
*/
public static @Nullable Boolean nullableAll(List extends E> list,
Function1 predicate) {
boolean nullExists = false;
for (E e : list) {
Boolean res = predicate.apply(e);
if (res == null) {
nullExists = true;
} else if (!res) {
return false;
}
}
return nullExists ? null : true;
}
/** Similar to {@link Linq4j#product(Iterable)} but each resulting list
* implements {@link FlatLists.ComparableList}. */
public static Enumerable> product(
final List>> enumerators, final int fieldCount,
final boolean withOrdinality) {
return new AbstractEnumerable>() {
@Override public Enumerator> enumerator() {
return new ProductComparableListEnumerator<>(enumerators, fieldCount,
withOrdinality);
}
};
}
/**
* Implements the {@code .} (field access) operator on an object
* whose type is not known until runtime.
*
* A struct object can be represented in various ways by the
* runtime and depends on the
* {@link com.hazelcast.shaded.org.apache.calcite.adapter.enumerable.JavaRowFormat}.
*/
@Experimental
public static @Nullable Object structAccess(@Nullable Object structObject, int index,
@Nullable String fieldName) {
if (structObject == null) {
return null;
}
if (structObject instanceof Object[]) {
return ((Object[]) structObject)[index];
} else if (structObject instanceof List) {
return ((List) structObject).get(index);
} else if (structObject instanceof Row) {
return ((Row) structObject).getObject(index);
} else {
Class> beanClass = structObject.getClass();
try {
if (fieldName == null) {
throw new IllegalStateException("Field name cannot be null for struct field access");
}
Field structField = beanClass.getDeclaredField(fieldName);
return structField.get(structObject);
} catch (NoSuchFieldException | IllegalAccessException ex) {
throw RESOURCE.failedToAccessField(fieldName, index, beanClass.getName()).ex(ex);
}
}
}
/** Enumerates over the cartesian product of the given lists, returning
* a comparable list for each row.
*
* @param element type */
private static class ProductComparableListEnumerator
extends CartesianProductEnumerator, FlatLists.ComparableList> {
final Object[] flatElements;
final List list;
private final boolean withOrdinality;
private int ordinality;
ProductComparableListEnumerator(List>> enumerators,
int fieldCount, boolean withOrdinality) {
super(enumerators);
this.withOrdinality = withOrdinality;
flatElements = new Object[fieldCount];
list = Arrays.asList(flatElements);
}
@Override public boolean moveNext() {
boolean hasNext = super.moveNext();
if (hasNext && withOrdinality) {
ordinality++;
}
return hasNext;
}
@Override public FlatLists.ComparableList current() {
int i = 0;
for (Object element : (Object[]) elements) {
Object[] a;
if (element.getClass().isArray()) {
a = (Object[]) element;
} else {
final List list2 = (List) element;
a = list2.toArray();
}
System.arraycopy(a, 0, flatElements, i, a.length);
i += a.length;
}
if (withOrdinality) {
flatElements[i] = ordinality;
}
//noinspection unchecked
return (FlatLists.ComparableList) FlatLists.of(list);
}
@Override public void reset() {
super.reset();
if (withOrdinality) {
ordinality = 0;
}
}
}
/** Type of argument passed into {@link #flatProduct}. */
public enum FlatProductInputType {
SCALAR, LIST, MAP
}
}