Coverage Summary for Class: MathFunctionBuilder (com.kotlinorm.functions.bundled.builders)

Class Class, % Method, % Branch, % Line, % Instruction, %
MathFunctionBuilder 100% (1/1) 100% (11/11) 30.2% (16/53) 46.7% (28/60) 51.3% (248/483)


 /**
  * Copyright 2022-2025 kronos-orm
  *
  * Licensed 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.kotlinorm.functions.bundled.builders
 
 import com.kotlinorm.beans.dsl.Field
 import com.kotlinorm.beans.dsl.FunctionField
 import com.kotlinorm.database.SqlManager.quoted
 import com.kotlinorm.enums.DBType
 import com.kotlinorm.exceptions.UnSupportedFunctionException
 import com.kotlinorm.interfaces.FunctionBuilder
 import com.kotlinorm.interfaces.KronosDataSourceWrapper
 
 object MathFunctionBuilder : FunctionBuilder {
     private val all = arrayOf(
         DBType.Mysql, DBType.Postgres, DBType.SQLite, DBType.Oracle, DBType.Mssql
     )
 
     private val common = arrayOf(DBType.Mysql, DBType.Postgres, DBType.Oracle)
 
     override val supportFunctionNames: (String) -> Array<DBType> = {
         when (it) {/*
             *  返回一个数的绝对值
             *  return the absolute value of a number
             *  exp: abs(-32) => 32
             *  Mysql: ABS(x) SQLite: ABS(x) Oracle: ABS(x) Postgres: ABS(x) Mssql: ABS(x)
             */
             "abs" -> all
             /**
              *  返回大于或等于指定表达式的最小整数(向上取整)
              *  return the smallest integer greater than or equal to a number
              *  exp: ceil(12.3) => 13
              *  Mysql: CEIL(x) SQLite: CEIL(x) Oracle: CEIL(x) Postgres: CEIL(x) Mssql: CEILING(x)
              */
             "ceil" -> all
             /**
              *  返回小于或等于指定表达式的最大整数(向下取整)
              *  return the largest integer less than or equal to a number
              *  exp: floor(12.3) => 12
              *  Mysql: FLOOR(x) SQLite: FLOOR(x) Oracle: FLOOR(x) Postgres: FLOOR(x) Mssql: FLOOR(x)
              */
             "floor" -> all
             /**
              *  返回e的x次幂
              *  return e raised to the power of a number
              *  exp: exp(1) => 2.718281828459045
              *  Mysql: EXP(x) SQLite: EXP(x) Oracle: EXP(x) Postgres: EXP(x) Mssql: EXP(x)
              */
             "exp" -> all
             /**
              *  返回一组值中的最大值
              *  return the largest value
              *  exp: greatest(1, 2, 3) => 3
              *  Mysql: GREATEST(x1, x2, ...) Oracle: GREATEST(x1, x2, ...) Postgres: GREATEST(x1, x2, ...)
              */
             "greatest" -> common
             /**
              *  返回一组值中的最小值
              *  return the smallest value
              *  exp: least(1, 2, 3) => 1
              *  Mysql: LEAST(x1, x2, ...) Oracle: LEAST(x1, x2, ...) Postgres: LEAST(x1, x2, ...)
              */
             "least" -> common
             /**
              *  返回一个数的自然对数
              *  return the natural logarithm of a number
              *  exp: ln(2) => 0.6931471805599453
              *  Mysql: LN(x) SQLite: LN(x) Oracle: LN(x) Postgres: LN(x) Mssql: LOG(x, EXP(1))
              */
             "ln" -> all
             /**
              *  返回一个数的对数
              *  return the logarithm of a number
              *  exp: log(2, 10) => 0.3010299956639812
              *  Mysql: LOG(x, base) SQLite: LOG(x, base) Oracle: LOG(base, x) Postgres: LOG(base, x) Mssql: LOG(base, x)
              */
             "log" -> all
             /**
              *  返回两个数相除的余数
              *  return the remainder of a division operation
              *  exp: mod(5, 2) => 1
              *  Mysql: x % y SQLite: x % y Oracle: MOD(x, y) Postgres: x % y Mssql: x % y
              */
             "mod" -> all
             /**
              *  返回圆周率
              *  return the value of pi
              *  exp: pi() => 3.141592653589793
              *  Mysql: PI() SQLite: PI() Oracle: PI() Postgres: PI() Mssql: PI()
              */
             "pi" -> all
             /**
              *  返回一个随机数
              *  return a random number
              *  exp: rand() => 0.123456789
              *  Mysql: RAND() SQLite: RANDOM() Oracle: DBMS_RANDOM.VALUE Postgres: RANDOM() Mssql: RAND()
              */
             "rand" -> all
             /**
              *  返回一个数四舍五入的值
              *  return the value of a number rounded to the nearest integer
              *  exp: round(12.3) => 12
              *  Mysql: ROUND(x) SQLite: ROUND(x) Oracle: ROUND(x) Postgres: ROUND(x) Mssql: ROUND(x)
              */
             "round" -> all
             /**
              *  返回一个数的符号
              *  return the sign of a number
              *  exp: sign(-12) => -1
              *  Mysql: SIGN(x) SQLite: SIGN(x) Oracle: SIGN(x) Postgres: SIGN(x) Mssql: SIGN(x)
              */
             "sign" -> all
             /**
              *  返回一个数的平方根
              *  return the square root of a number
              *  exp: sqrt(9) => 3
              *  Mysql: SQRT(x) SQLite: SQRT(x) Oracle: SQRT(x) Postgres: SQRT(x) Mssql: SQRT(x)
              */
             "sqrt" -> all
             /**
              *  返回一个数截断到指定的小数位数
              *  return a number truncated to a certain number of decimal places
              *  exp: truncate(12.345, 1) => 12.3
              *  Mysql: TRUNCATE(x, d) SQLite: TRUNC(x, d) Oracle: TRUNC(x, d) Postgres: TRUNC(x, d) Mssql: ROUND(x, d)
              */
             "trunc" -> all
             /**
              * 返回一组值的和
              * return the sum of a set of values
              * exp: add(1, 2, 3) => 6
              * Mysql: (x1 + x2 + ...) SQLite: (x1 + x2 + ...) Oracle: (x1 + x2 + ...) Postgres: (x1 + x2 + ...) Mssql: (x1 + x2 + ...)
              */
             "add" -> all
             /**
              * 返回一组值的差
              * return the difference of a set of values
              * exp: sub(1, 2, 3) => -4
              * Mysql: (x1 - x2 - ...) SQLite: (x1 - x2 - ...) Oracle: (x1 - x2 - ...) Postgres: (x1 - x2 - ...) Mssql: (x1 - x2 - ...)
              */
             "sub" -> all
             /**
              * 返回一组值的积
              * return the product of a set of values
              * exp: mul(1, 2, 3) => 6
              * Mysql: (x1 * x2 * ...) SQLite: (x1 * x2 * ...) Oracle: (x1 * x2 * ...) Postgres: (x1 * x2 * ...) Mssql: (x1 * x2 * ...)
              */
             "mul" -> all
             /**
              * 返回一组值的商
              * return the quotient of a set of values
              * exp: div(6, 2, 3) => 1
              * Mysql: (x1 / x2 / ...) SQLite: (x1 / x2 / ...) Oracle: (x1 / x2 / ...) Postgres: (x1 / x2 / ...) Mssql: (x1 / x2 / ...)
              */
             "div" -> all
             else -> emptyArray()
         }
     }
 
     override fun transform(
         field: FunctionField, dataSource: KronosDataSourceWrapper, showTable: Boolean, showAlias: Boolean
     ): String {
         return getFunctionSql(field, dataSource, showTable, showAlias)
     }
 
     private fun getFunctionSql(
         field: FunctionField, dataSource: KronosDataSourceWrapper, showTable: Boolean, showAlias: Boolean
     ): String {
         val alias = if (showAlias) field.name else ""
         return when (field.functionName) {
             "add", "sub", "mul", "div" -> {
                 val operator = when (field.functionName) {
                     "add" -> "+"
                     "sub" -> "-"
                     "mul" -> "*"
                     "div" -> "/"
                     else -> throw UnSupportedFunctionException(dataSource.dbType, field.functionName)
                 }
                 buildOperations(operator, alias, field.fields, dataSource, showTable)
             }
 
             else -> {
                 val functionName = when (field.functionName to dataSource.dbType) {
                     "ceil" to DBType.Mssql -> "CEILING"
                     "ln" to DBType.Mssql -> {
                         field.fields = listOf(
                             field.fields.first(), Pair(null, "EXP(1)")
                         )
                         "LOG"
                     }
 
                     "rand" to DBType.Oracle -> return buildAlias("DBMS_RANDOM.VALUE", alias)
                     "rand" to DBType.SQLite, "rand" to DBType.Postgres -> "RANDOM"
                     "trunc" to DBType.Mysql -> "TRUNCATE"
                     "trunc" to DBType.Mssql -> "ROUND"
                     else -> field.functionName
                 }
                 buildFields(functionName.uppercase(), alias, field.fields, dataSource, showTable)
             }
         }
     }
 
     fun buildField(it: Pair<Field?, Any?>, dataSource: KronosDataSourceWrapper, showTable: Boolean): String {
         return it.first?.quoted(
             dataSource, showTable
         ) ?: if (it.second is String) "'${it.second}'" else it.second.toString()
     }
 
     fun buildOperations(
         operator: String,
         alias: String,
         fields: List<Pair<Field?, Any?>>,
         dataSource: KronosDataSourceWrapper,
         showTable: Boolean
     ): String {
         return buildAlias("(${
             fields.joinToString(" $operator ") {
                 buildField(it, dataSource, showTable)
             }
         })", alias)
     }
 
     fun buildFields(
         functionName: String,
         alias: String,
         fields: List<Pair<Field?, Any?>>,
         dataSource: KronosDataSourceWrapper,
         showTable: Boolean
     ): String {
         return buildAlias("${functionName}(${
             fields.joinToString(", ") {
                 buildField(it, dataSource, showTable)
             }
         })", alias)
     }
 
     fun buildAlias(field: String, alias: String): String {
         return "$field${
             if (alias.isNotEmpty()) " AS $alias" else ""
         }"
     }
 
 }