HHH-15516 add two-arg form of log() to HQL

This commit is contained in:
Gavin King 2022-09-15 22:39:32 +02:00
parent 1b5935e66d
commit 3ddfa3f47c
8 changed files with 75 additions and 5 deletions

View File

@ -1023,6 +1023,7 @@ Next, functions for working with numeric values:
| `power()` | Exponentiation | `power(x,y)` | Universal in SQL dialects | `power()` | Exponentiation | `power(x,y)` | Universal in SQL dialects
| `ln()` | Natural logarithm | `ln(x)` | Very common in SQL dialects | `ln()` | Natural logarithm | `ln(x)` | Very common in SQL dialects
| `log10()` | Base-10 logarithm | `log10(x)` | Very common in SQL dialects | `log10()` | Base-10 logarithm | `log10(x)` | Very common in SQL dialects
| `log()` | Arbitrary-base logarithm | `log(b,x)` | Very common in SQL dialects
| `pi` | π | `pi` | Very common in SQL dialects | `pi` | π | `pi` | Very common in SQL dialects
| `sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()` | Basic trigonometric functions | `sin(theta)`, `cos(theta)`, `atan2(opposite, adjacent)` | Very common in SQL dialects | `sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()` | Basic trigonometric functions | `sin(theta)`, `cos(theta)`, `atan2(opposite, adjacent)` | Very common in SQL dialects
| `round()` | Numeric rounding | As usual: `round(number, places)` | Very common in SQL dialects | `round()` | Numeric rounding | As usual: `round(number, places)` | Very common in SQL dialects

View File

@ -127,8 +127,8 @@ public abstract class AbstractTransactSQLDialect extends Dialect {
CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine); CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine);
functionFactory.cot(); functionFactory.cot();
functionFactory.log();
functionFactory.ln_log(); functionFactory.ln_log();
functionFactory.log_loglog();
functionFactory.log10(); functionFactory.log10();
functionFactory.atan2_atn2(); functionFactory.atan2_atn2();
functionFactory.mod_operator(); functionFactory.mod_operator();

View File

@ -207,7 +207,6 @@ public class DB2Dialect extends Dialect {
functionFactory.cot(); functionFactory.cot();
functionFactory.degrees(); functionFactory.degrees();
functionFactory.log();
functionFactory.log10(); functionFactory.log10();
functionFactory.radians(); functionFactory.radians();
functionFactory.rand(); functionFactory.rand();

View File

@ -735,6 +735,7 @@ public abstract class Dialect implements ConversionContext {
* <ul> * <ul>
* <li> <code>pi</code> * <li> <code>pi</code>
* <li> <code>log10(arg)</code> * <li> <code>log10(arg)</code>
* <li> <code>log(base, arg)</code>
* <li> <code>sign(arg)</code> * <li> <code>sign(arg)</code>
* <li> <code>sin(arg)</code> * <li> <code>sin(arg)</code>
* <li> <code>cos(arg)</code> * <li> <code>cos(arg)</code>
@ -817,6 +818,10 @@ public abstract class Dialect implements ConversionContext {
functionFactory.pi_acos(); functionFactory.pi_acos();
//log(base, arg) supported on most databases, but emulate it here
functionFactory.log_ln();
//coalesce() function, supported by most databases, must be emulated //coalesce() function, supported by most databases, must be emulated
//in terms of nvl() for platforms which don't support it natively //in terms of nvl() for platforms which don't support it natively

View File

@ -444,7 +444,6 @@ public class MySQLDialect extends Dialect {
functionFactory.log(); functionFactory.log();
functionFactory.log2(); functionFactory.log2();
functionFactory.log10(); functionFactory.log10();
functionFactory.pi();
functionFactory.trim2(); functionFactory.trim2();
functionFactory.octetLength(); functionFactory.octetLength();
functionFactory.reverse(); functionFactory.reverse();
@ -498,6 +497,12 @@ public class MySQLDialect extends Dialect {
.setUseParenthesesWhenNoArgs( false ) .setUseParenthesesWhenNoArgs( false )
.register(); .register();
queryEngine.getSqmFunctionRegistry().patternDescriptorBuilder( "pi", "cast(pi() as double)" )
.setInvariantType(basicTypeRegistry.resolve( StandardBasicTypes.DOUBLE ))
.setExactArgumentCount(0)
.setArgumentListSignature("")
.register();
// MySQL timestamp type defaults to precision 0 (seconds) but // MySQL timestamp type defaults to precision 0 (seconds) but
// we want the standard default precision of 6 (microseconds) // we want the standard default precision of 6 (microseconds)
functionFactory.sysdateExplicitMicros(); functionFactory.sysdateExplicitMicros();

View File

@ -265,6 +265,8 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function // AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
functionFactory.avg_castingNonDoubleArguments( this, SqlAstNodeRenderingMode.DEFAULT ); functionFactory.avg_castingNonDoubleArguments( this, SqlAstNodeRenderingMode.DEFAULT );
functionFactory.log_log();
functionFactory.truncate_round(); functionFactory.truncate_round();
functionFactory.everyAny_minMaxIif(); functionFactory.everyAny_minMaxIif();
functionFactory.octetLength_pattern( "datalength(?1)" ); functionFactory.octetLength_pattern( "datalength(?1)" );

View File

@ -88,6 +88,9 @@ public class CommonFunctionFactory {
.register(); .register();
} }
/**
* For databases where the first parameter is the base
*/
public void log() { public void log() {
functionRegistry.namedDescriptorBuilder( "log" ) functionRegistry.namedDescriptorBuilder( "log" )
.setArgumentCountBetween( 1, 2 ) .setArgumentCountBetween( 1, 2 )
@ -96,6 +99,42 @@ public class CommonFunctionFactory {
.register(); .register();
} }
public void log_ln() {
functionRegistry.patternDescriptorBuilder( "log", "ln(?2)/ln(?1)" )
.setExactArgumentCount( 2 )
.setParameterTypes(NUMERIC, NUMERIC)
.setInvariantType(doubleType)
.setArgumentListSignature("(NUMERIC base, NUMERIC arg)")
.register();
}
/**
* SQL Server defines parameters in reverse order
*/
public void log_log() {
functionRegistry.patternDescriptorBuilder( "log", "log(?2,?1)" )
.setExactArgumentCount( 2 )
.setParameterTypes(NUMERIC, NUMERIC)
.setInvariantType(doubleType)
.setArgumentListSignature("(NUMERIC base, NUMERIC arg)")
.register();
}
/**
* For Sybase
*/
public void log_loglog() {
functionRegistry.patternDescriptorBuilder( "log", "log(?2)/log(?1)" )
.setExactArgumentCount( 2 )
.setParameterTypes(NUMERIC, NUMERIC)
.setInvariantType(doubleType)
.setArgumentListSignature("(NUMERIC base, NUMERIC arg)")
.register();
}
/**
* For SQL Server and Sybase
*/
public void ln_log() { public void ln_log() {
functionRegistry.namedDescriptorBuilder( "ln", "log" ) functionRegistry.namedDescriptorBuilder( "ln", "log" )
.setInvariantType(doubleType) .setInvariantType(doubleType)
@ -385,6 +424,7 @@ public class CommonFunctionFactory {
functionRegistry.noArgsBuilder( "pi" ) functionRegistry.noArgsBuilder( "pi" )
.setInvariantType(doubleType) .setInvariantType(doubleType)
.setUseParenthesesWhenNoArgs( true ) .setUseParenthesesWhenNoArgs( true )
.setArgumentListSignature("")
.register(); .register();
} }
@ -392,6 +432,7 @@ public class CommonFunctionFactory {
functionRegistry.patternDescriptorBuilder( "pi", "acos(-1)" ) functionRegistry.patternDescriptorBuilder( "pi", "acos(-1)" )
.setInvariantType(doubleType) .setInvariantType(doubleType)
.setExactArgumentCount(0) .setExactArgumentCount(0)
.setArgumentListSignature("")
.register(); .register();
} }

View File

@ -12,6 +12,7 @@ import java.sql.Timestamp;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalTime; import java.time.LocalTime;
import org.hamcrest.number.IsCloseTo;
import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics; import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DialectFeatureChecks;
@ -913,8 +914,24 @@ public class StandardFunctionTests {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
assertThat( assertThat(
session.createQuery("select pi").getSingleResult(), session.createQuery("select pi", Double.class).getSingleResult(),
is( Math.PI ) IsCloseTo.closeTo( Math.PI, 1e-9 )
);
}
);
}
@Test
public void testLog(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
assertThat(
session.createQuery("select log(3,9)", Double.class).getSingleResult(),
IsCloseTo.closeTo( 2d, 1e-9 )
);
assertThat(
session.createQuery("select log(10,1e12)", Double.class).getSingleResult(),
IsCloseTo.closeTo( 12d, 1e-9 )
); );
} }
); );