blob: a6ce1b1db3614f6635e4108bd1664704cc55d5ca [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.flink.table.codegen.calls
import org.apache.calcite.runtime.SqlFunctions
import org.apache.calcite.sql.SqlOperator
import org.apache.calcite.sql.fun.SqlStdOperatorTable
import org.apache.calcite.sql.fun.SqlStdOperatorTable._
import org.apache.calcite.sql.fun.SqlTrimFunction.Flag.{BOTH, LEADING, TRAILING}
import org.apache.flink.table.api.types.{DataTypes, InternalType, MapType}
import org.apache.flink.table.codegen.CodeGenUtils._
import org.apache.flink.table.codegen.CodeGeneratorContext.BINARY_STRING
import org.apache.flink.table.codegen.calls.CallGenerator.{
generateCallIfArgsNotNull, generateCallIfArgsNullable, generateReturnStringCallIfArgsNotNull}
import org.apache.flink.table.codegen.calls.ScalarOperators.generateNot
import org.apache.flink.table.codegen.{CodeGeneratorContext, GeneratedExpression}
import org.apache.flink.table.dataformat.BinaryMap
import org.apache.flink.table.functions.sql.ScalarSqlFunctions
import org.apache.flink.table.runtime.conversion.DataStructureConverters.genToInternal
import org.apache.flink.table.runtime.functions.{BuildInScalarFunctions, ScalarFunctions}
/**
* Code generator for call with string parameters or return value.
* 1.Some specific optimization of BinaryString.
* 2.Deal with conversions between Java String and internal String.
*
* <p>TODO Need to rewrite most of the methods here, calculated directly on the BinaryString
* instead of convert BinaryString to String.
*/
object BinaryStringCallGen {
def generateCallExpression(
ctx: CodeGeneratorContext,
operator: SqlOperator,
operands: Seq[GeneratedExpression],
returnType: InternalType): Option[GeneratedExpression] = {
val generator = operator match {
case SqlStdOperatorTable.LIKE =>
new LikeCallGen().generate(ctx, operands, DataTypes.BOOLEAN, nullCheck = true)
case SqlStdOperatorTable.NOT_LIKE => generateNot(ctx, nullCheck = true,
new LikeCallGen().generate(ctx, operands, DataTypes.BOOLEAN, nullCheck = true))
case ScalarSqlFunctions.SUBSTRING | ScalarSqlFunctions.SUBSTR =>
generateSubString(ctx, operands)
case ScalarSqlFunctions.LEFT => generateLeft(ctx, operands.head, operands(1))
case ScalarSqlFunctions.RIGHT => generateRight(ctx, operands.head, operands(1))
case CHAR_LENGTH | CHARACTER_LENGTH | ScalarSqlFunctions.LENGTH =>
generateCharLength(ctx, operands)
case SIMILAR_TO => generateSimilarTo(ctx, operands)
case NOT_SIMILAR_TO => generateNot(ctx, nullCheck = true, generateSimilarTo(ctx, operands))
case ScalarSqlFunctions.REGEXP_EXTRACT => generateRegexpExtract(ctx, operands)
case ScalarSqlFunctions.REGEXP_REPLACE => generateRegexpReplace(ctx, operands)
case ScalarSqlFunctions.IS_DECIMAL => generateIsDecimal(ctx, operands)
case ScalarSqlFunctions.IS_DIGIT => generateIsDigit(ctx, operands)
case ScalarSqlFunctions.IS_ALPHA => generateIsAlpha(ctx, operands)
case UPPER => generateUpper(ctx, operands)
case LOWER => generateLower(ctx, operands)
case INITCAP => generateInitcap(ctx, operands)
case POSITION => generatePosition(ctx, operands)
case ScalarSqlFunctions.LOCATE => generateLocate(ctx, operands)
case OVERLAY => generateOverlay(ctx, operands)
case ScalarSqlFunctions.LPAD => generateLpad(ctx, operands)
case ScalarSqlFunctions.RPAD => generateRpad(ctx, operands)
case ScalarSqlFunctions.REPEAT => generateRepeat(ctx, operands)
case ScalarSqlFunctions.REVERSE => generateReverse(ctx, operands)
case ScalarSqlFunctions.REPLACE => generateReplace(ctx, operands)
case ScalarSqlFunctions.SPLIT_INDEX => generateSplitIndex(ctx, operands)
case ScalarSqlFunctions.KEYVALUE => generateKeyValue(ctx, operands)
case ScalarSqlFunctions.HASH_CODE if operands.head.resultType == DataTypes.STRING =>
generateHashCode(ctx, operands)
case ScalarSqlFunctions.MD5 => generateMd5(ctx, operands)
case ScalarSqlFunctions.SHA1 => generateSha1(ctx, operands)
case ScalarSqlFunctions.SHA224 => generateSha224(ctx, operands)
case ScalarSqlFunctions.SHA256 => generateSha256(ctx, operands)
case ScalarSqlFunctions.SHA384 => generateSha384(ctx, operands)
case ScalarSqlFunctions.SHA512 => generateSha512(ctx, operands)
case ScalarSqlFunctions.SHA2 => generateSha2(ctx, operands)
case ScalarSqlFunctions.PARSE_URL => generateParserUrl(ctx, operands)
case ScalarSqlFunctions.FROM_BASE64 => generateFromBase64(ctx, operands)
case ScalarSqlFunctions.TO_BASE64 => generateToBase64(ctx, operands)
case ScalarSqlFunctions.CHR => generateChr(ctx, operands)
case ScalarSqlFunctions.REGEXP => generateRegExp(ctx, operands)
case ScalarSqlFunctions.JSON_VALUE => generateJsonValue(ctx, operands)
case ScalarSqlFunctions.BIN => generateBin(ctx, operands)
case ScalarSqlFunctions.CONCAT =>
operands.foreach(requireString(_, operator.getName))
generateConcat(ctx, nullCheck = true, operands)
case ScalarSqlFunctions.CONCAT_WS =>
operands.foreach(requireString(_, operator.getName))
generateConcatWs(ctx, operands)
case ScalarSqlFunctions.STR_TO_MAP => generateStrToMap(ctx, operands)
case TRIM => generateTrim(ctx, operands)
case ScalarSqlFunctions.LTRIM => generateTrimLeft(ctx, operands)
case ScalarSqlFunctions.RTRIM => generateTrimRight(ctx, operands)
case CONCAT =>
val left = operands.head
val right = operands(1)
requireString(left, operator.getName)
generateArithmeticConcat(ctx, left, right)
case ScalarSqlFunctions.UUID => generateUuid(ctx, operands)
case ScalarSqlFunctions.ASCII => generateAscii(ctx, operands.head)
case ScalarSqlFunctions.ENCODE => generateEncode(ctx, operands.head, operands(1))
case ScalarSqlFunctions.DECODE => generateDecode(ctx, operands.head, operands(1))
case ScalarSqlFunctions.INSTR => generateInstr(ctx, operands)
case _ => null
}
Option(generator)
}
private def toStringTerms(terms: Seq[String], operands: Seq[GeneratedExpression]) = {
terms.zipWithIndex.map { case (term, index) =>
if (operands(index).resultType == DataTypes.STRING) {
s"$term.toString()"
} else {
term
}
}.mkString(",")
}
private def safeToStringTerms(terms: Seq[String], operands: Seq[GeneratedExpression]) = {
terms.zipWithIndex.map { case (term, index) =>
if (operands(index).resultType == DataTypes.STRING) {
s"$BINARY_STRING.safeToString($term)"
} else {
term
}
}.mkString(",")
}
def generateConcat(
ctx: CodeGeneratorContext,
nullCheck: Boolean,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
generateCallIfArgsNullable(ctx, nullCheck, DataTypes.STRING, operands) {
terms => s"$BINARY_STRING.concat(${terms.mkString(", ")})"
}
}
def generateConcatWs(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
generateCallIfArgsNullable(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms => s"$BINARY_STRING.concatWs(${terms.mkString(", ")})"
}
}
/**
* Optimization: use BinaryString equals instead of compare.
*/
def generateStringEquals(
ctx: CodeGeneratorContext,
left: GeneratedExpression,
right: GeneratedExpression): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.BOOLEAN, Seq(left, right)) {
terms => s"(${terms.head}.equals(${terms(1)}))"
}
}
/**
* Optimization: use BinaryString equals instead of compare.
*/
def generateStringNotEquals(
ctx: CodeGeneratorContext,
left: GeneratedExpression,
right: GeneratedExpression): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.BOOLEAN, Seq(left, right)) {
terms => s"!(${terms.head}.equals(${terms(1)}))"
}
}
def generateSubString(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms => s"${terms.head}.substringSQL(${terms.drop(1).mkString(", ")})"
}
}
def generateLeft(
ctx: CodeGeneratorContext,
str: GeneratedExpression,
len: GeneratedExpression): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, Seq(str, len)) {
val emptyString = s"$BINARY_STRING.EMPTY_UTF8"
terms => s"${terms(1)} <= 0 ? $emptyString : ${terms.head}.substringSQL(1, ${terms(1)})"
}
}
def generateRight(
ctx: CodeGeneratorContext,
str: GeneratedExpression,
len: GeneratedExpression): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, Seq(str, len)) {
terms =>
s"""
|${terms(1)} <= 0 ?
| $BINARY_STRING.EMPTY_UTF8 :
| ${terms(1)} >= ${terms.head}.numChars() ?
| ${terms.head} :
| ${terms.head}.substringSQL(-${terms(1)})
""".stripMargin
}
}
def generateCharLength(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.INT, operands) {
terms => s"${terms.head}.numChars()"
}
}
def generateSimilarTo(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[SqlFunctions].getCanonicalName
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.BOOLEAN, operands) {
terms => s"$className.similar(${toStringTerms(terms, operands)})"
}
}
def generateRegexpExtract(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateCallIfArgsNullable(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms =>
s"$BINARY_STRING.fromString($className.regExpExtract(${
safeToStringTerms(terms, operands)}))"
}
}
def generateRegexpReplace(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => s"$className.regExpReplace(${toStringTerms(terms, operands)})"
}
}
def generateIsDecimal(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[BuildInScalarFunctions].getCanonicalName
generateCallIfArgsNullable(ctx, nullCheck = true, DataTypes.BOOLEAN, operands) {
terms => s"$className.isDecimal(${safeToStringTerms(terms, operands)})"
}
}
def generateIsDigit(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[BuildInScalarFunctions].getCanonicalName
generateCallIfArgsNullable(ctx, nullCheck = true, DataTypes.BOOLEAN, operands) {
terms => s"$className.isDigit(${safeToStringTerms(terms, operands)})"
}
}
def generateAscii(
ctx: CodeGeneratorContext,
str: GeneratedExpression): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.INT, Seq(str)) {
terms => s"${terms.head}.numBytes() <= 0 ? 0 : (int) ${terms.head}.getByte(0)"
}
}
def generateIsAlpha(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[BuildInScalarFunctions].getCanonicalName
generateCallIfArgsNullable(ctx, nullCheck = true, DataTypes.BOOLEAN, operands) {
terms => s"$className.isAlpha(${safeToStringTerms(terms, operands)})"
}
}
def generateUpper(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms => s"${terms.head}.toUpperCase()"
}
}
def generateLower(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms => s"${terms.head}.toLowerCase()"
}
}
def generateInitcap(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[SqlFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => s"$className.initcap(${terms.head}.toString())"
}
}
def generatePosition(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.INT, operands) {
terms => s"$className.position(${terms.mkString(",")})"
}
}
def generateLocate(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.INT, operands) {
terms => s"$className.position(${terms.mkString(",")})"
}
}
def generateInstr(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.INT, operands) {
terms =>
val startPosition = if (operands.length < 3) 1 else terms(2)
val nthAppearance = if (operands.length < 4) 1 else terms(3)
s"$className.instr(${terms.head}, ${terms(1)}, " +
s"$startPosition, $nthAppearance)"
}
}
def generateOverlay(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => s"$className.overlay(${toStringTerms(terms, operands)})"
}
}
def generateArithmeticConcat(
ctx: CodeGeneratorContext,
left: GeneratedExpression,
right: GeneratedExpression): GeneratedExpression = {
generateReturnStringCallIfArgsNotNull(ctx, Seq(left, right)) {
terms => s"${terms.head}.toString() + String.valueOf(${terms(1)})"
}
}
def generateLpad(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms =>
s"$className.lpad(${toStringTerms(terms, operands)})"
}
}
def generateRpad(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms =>
s"$className.rpad(${toStringTerms(terms, operands)})"
}
}
def generateRepeat(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => s"$className.repeat(${toStringTerms(terms, operands)})"
}
}
def generateReverse(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms => s"${terms.head}.reverse()"
}
}
def generateReplace(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => s"$className.replace(${toStringTerms(terms, operands)})"
}
}
def generateSplitIndex(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => s"$className.splitIndex(${toStringTerms(terms, operands)})"
}
}
def generateKeyValue(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateCallIfArgsNullable(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms =>
s"$className.keyValue(${terms.mkString(",")})"
}
}
def generateHashCode(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[BuildInScalarFunctions].getCanonicalName
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.INT, operands) {
terms => s"$className.hashCode(${terms.head}.toString())"
}
}
def generateMd5(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression =
generateHashInternal(ctx, "MD5", operands)
def generateHashInternal(
ctx: CodeGeneratorContext,
algorithm: String,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val digestTerm = ctx.addReusableMessageDigest(algorithm)
if (operands.length == 1) {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms =>s"${terms.head}.hash($digestTerm)"
}
} else {
val className = classOf[ScalarFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => s"$className.hash($digestTerm, ${toStringTerms(terms, operands)})"
}
}
}
def generateSha1(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression =
generateHashInternal(ctx, "SHA", operands)
def generateSha224(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression =
generateHashInternal(ctx, "SHA-224", operands)
def generateSha256(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression =
generateHashInternal(ctx, "SHA-256", operands)
def generateSha384(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression =
generateHashInternal(ctx, "SHA-384", operands)
def generateSha512(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression =
generateHashInternal(ctx, "SHA-512", operands)
def generateSha2(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
if (operands.last.literal) {
val digestTerm = ctx.addReusableSha2MessageDigest(operands.last, nullCheck = true)
if (operands.length == 2) {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms =>s"${terms.head}.hash($digestTerm)"
}
} else {
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms =>
s"$className.hash($digestTerm," +
s"${toStringTerms(terms.dropRight(1), operands.dropRight(1))})"
}
}
} else {
if (operands.length == 2) {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms =>
s"""${terms.head}.hash("SHA-" + ${terms.last})"""
}
} else {
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => {
val strTerms = toStringTerms(terms.dropRight(1), operands.dropRight(1))
s"""$className.hash("SHA-" + ${terms.last}, $strTerms)"""
}
}
}
}
}
def generateParserUrl(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateCallIfArgsNullable(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms =>
s"$BINARY_STRING.fromString($className.parseUrl(${safeToStringTerms(terms, operands)}))"
}
}
def generateFromBase64(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[BuildInScalarFunctions].getCanonicalName
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.BYTE_ARRAY, operands) {
terms => s"$className.fromBase64(${terms.head}.toString())"
}
}
def generateToBase64(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[BuildInScalarFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => s"$className.toBase64(${terms.head})"
}
}
def generateChr(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[ScalarFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => s"$className.chr(${terms.head})"
}
}
def generateRegExp(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[BuildInScalarFunctions].getCanonicalName
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.BOOLEAN, operands) {
terms => s"$className.regExp(${toStringTerms(terms, operands)})"
}
}
def generateJsonValue(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[BuildInScalarFunctions].getCanonicalName
generateCallIfArgsNullable(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms =>
s"$BINARY_STRING.fromString($className.jsonValue(${safeToStringTerms(terms, operands)}))"
}
}
def generateBin(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => s"Long.toBinaryString(${terms.head})"
}
}
def generateTrim(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms =>
val leading = compareEnum(terms.head, BOTH) || compareEnum(terms.head, LEADING)
val trailing = compareEnum(terms.head, BOTH) || compareEnum(terms.head, TRAILING)
val args = s"$leading, $trailing, ${terms(1)}"
s"${terms(2)}.trim($args)"
}
}
def generateTrimLeft(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms => s"${terms.head}.trimLeft(${terms.drop(1).mkString(", ")})"
}
}
def generateTrimRight(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, operands) {
terms => s"${terms.head}.trimRight(${terms.drop(1).mkString(", ")})"
}
}
def generateUuid(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[BuildInScalarFunctions].getCanonicalName
generateReturnStringCallIfArgsNotNull(ctx, operands) {
terms => s"$className.uuid(${terms.mkString(",")})"
}
}
def generateStrToMap(
ctx: CodeGeneratorContext,
operands: Seq[GeneratedExpression]): GeneratedExpression = {
val className = classOf[BuildInScalarFunctions].getCanonicalName
val t = new MapType(DataTypes.STRING, DataTypes.STRING)
val convertFunc = genToInternal(ctx, t)
generateCallIfArgsNotNull(ctx, nullCheck = true, t, operands) {
terms =>
val map = s"$className.strToMap(${toStringTerms(terms, operands)})"
s"(${classOf[BinaryMap].getCanonicalName}) ${convertFunc(map)}"
}
}
def generateEncode(
ctx: CodeGeneratorContext,
str: GeneratedExpression,
charset: GeneratedExpression): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.BYTE_ARRAY, Seq(str, charset)) {
terms => s"${terms.head}.toString().getBytes(${terms(1)}.toString())"
}
}
def generateDecode(
ctx: CodeGeneratorContext,
binary: GeneratedExpression,
charset: GeneratedExpression): GeneratedExpression = {
generateCallIfArgsNotNull(ctx, nullCheck = true, DataTypes.STRING, Seq(binary, charset)) {
terms =>
s"$BINARY_STRING.fromString(new String(${terms.head}, ${terms(1)}.toString()))"
}
}
}