Skip to content

Commit

Permalink
feat!: enable new math functions
Browse files Browse the repository at this point in the history
use the new math functions introduced in sqlite 3.35.0
this replaces some of the existing functions

BREAKING-CHANGE: previously log() computed the natural logarithm, now it computes a base-10 logarithm
  • Loading branch information
gotson committed Nov 21, 2022
1 parent 5d90c1f commit 0f41f46
Show file tree
Hide file tree
Showing 27 changed files with 136 additions and 23 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ $(SQLITE_OUT)/sqlite3.o : $(SQLITE_UNPACKED)
-DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_STAT4 \
-DSQLITE_ENABLE_DBSTAT_VTAB \
-DSQLITE_ENABLE_MATH_FUNCTIONS \
-DSQLITE_THREADSAFE=1 \
-DSQLITE_DEFAULT_MEMSTATUS=0 \
-DSQLITE_DEFAULT_FILE_PERMISSIONS=0666 \
Expand Down
47 changes: 39 additions & 8 deletions src/main/ext/extension-functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ static void name(sqlite3_context *context, int argc, sqlite3_value **argv){\
}\


#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
/*
** Example of GEN_MATH_WRAP_DOUBLE_1 usage
** this creates function sqrtFunc to wrap the math.h standard function sqrt(x)=x^0.5
Expand All @@ -365,6 +366,7 @@ GEN_MATH_WRAP_DOUBLE_1(sqrtFunc, sqrt)
GEN_MATH_WRAP_DOUBLE_1(acosFunc, acos)
GEN_MATH_WRAP_DOUBLE_1(asinFunc, asin)
GEN_MATH_WRAP_DOUBLE_1(atanFunc, atan)
#endif

/*
** Many of systems don't have inverse hyperbolic trig functions so this will emulate
Expand All @@ -378,23 +380,29 @@ static double acosh(double x){
}
#endif

#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
GEN_MATH_WRAP_DOUBLE_1(acoshFunc, acosh)
#endif

#ifndef HAVE_ASINH
static double asinh(double x){
return log(x + sqrt(x*x + 1.0));
}
#endif

#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
GEN_MATH_WRAP_DOUBLE_1(asinhFunc, asinh)
#endif

#ifndef HAVE_ATANH
static double atanh(double x){
return (1.0/2.0)*log((1+x)/(1-x)) ;
}
#endif

#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
GEN_MATH_WRAP_DOUBLE_1(atanhFunc, atanh)
#endif

/*
** math.h doesn't require cot (cotangent) so it's defined here
Expand All @@ -403,9 +411,12 @@ static double cot(double x){
return 1.0/tan(x);
}

#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
GEN_MATH_WRAP_DOUBLE_1(sinFunc, sin)
GEN_MATH_WRAP_DOUBLE_1(cosFunc, cos)
GEN_MATH_WRAP_DOUBLE_1(tanFunc, tan)
#endif

GEN_MATH_WRAP_DOUBLE_1(cotFunc, cot)

static double coth(double x){
Expand All @@ -422,23 +433,29 @@ static double sinh(double x){
}
#endif

#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
GEN_MATH_WRAP_DOUBLE_1(sinhFunc, sinh)
#endif

#ifndef HAVE_COSH
static double cosh(double x){
return (exp(x)+exp(-x))/2.0;
}
#endif

#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
GEN_MATH_WRAP_DOUBLE_1(coshFunc, cosh)
#endif

#ifndef HAVE_TANH
static double tanh(double x){
return sinh(x)/cosh(x);
}
#endif

#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
GEN_MATH_WRAP_DOUBLE_1(tanhFunc, tanh)
#endif

GEN_MATH_WRAP_DOUBLE_1(cothFunc, coth)

Expand All @@ -456,9 +473,11 @@ static double log10(double x){
}
#endif

#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
GEN_MATH_WRAP_DOUBLE_1(logFunc, log)
GEN_MATH_WRAP_DOUBLE_1(log10Func, log10)
GEN_MATH_WRAP_DOUBLE_1(expFunc, exp)
#endif

/*
** Fallback for systems where math.h doesn't define M_PI
Expand All @@ -472,6 +491,7 @@ GEN_MATH_WRAP_DOUBLE_1(expFunc, exp)
#define M_PI 3.14159265358979323846
#endif

#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
/* Convert Degrees into Radians */
static double deg2rad(double x){
return x*M_PI/180.0;
Expand All @@ -484,11 +504,14 @@ static double rad2deg(double x){

GEN_MATH_WRAP_DOUBLE_1(rad2degFunc, rad2deg)
GEN_MATH_WRAP_DOUBLE_1(deg2radFunc, deg2rad)
#endif

#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
/* constant function that returns the value of PI=3.1415... */
static void piFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
sqlite3_result_double(context, M_PI);
}
#endif

/*
** Implements the sqrt function, it has the peculiarity of returning an integer when the
Expand Down Expand Up @@ -517,6 +540,7 @@ static void squareFunc(sqlite3_context *context, int argc, sqlite3_value **argv)
}
}

#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
/*
** Wraps the pow math.h function
** When both the base and the exponent are integers the result should be integer
Expand Down Expand Up @@ -546,6 +570,7 @@ static void powerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
}
}
}
#endif

/*
** atan2 wrapper
Expand Down Expand Up @@ -1703,45 +1728,51 @@ int RegisterExtensionFunctions(sqlite3 *db){
u8 needCollSeq;
void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
} aFuncs[] = {
#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
/* math.h */
{ "acos", 1, 0, SQLITE_UTF8, 0, acosFunc },
{ "asin", 1, 0, SQLITE_UTF8, 0, asinFunc },
{ "atan", 1, 0, SQLITE_UTF8, 0, atanFunc },
{ "atn2", 2, 0, SQLITE_UTF8, 0, atn2Func },
/* XXX alias */
{ "atan2", 2, 0, SQLITE_UTF8, 0, atn2Func },
{ "acosh", 1, 0, SQLITE_UTF8, 0, acoshFunc },
{ "asinh", 1, 0, SQLITE_UTF8, 0, asinhFunc },
{ "atanh", 1, 0, SQLITE_UTF8, 0, atanhFunc },

{ "difference", 2, 0, SQLITE_UTF8, 0, differenceFunc},
{ "degrees", 1, 0, SQLITE_UTF8, 0, rad2degFunc },
{ "radians", 1, 0, SQLITE_UTF8, 0, deg2radFunc },

{ "cos", 1, 0, SQLITE_UTF8, 0, cosFunc },
{ "sin", 1, 0, SQLITE_UTF8, 0, sinFunc },
{ "tan", 1, 0, SQLITE_UTF8, 0, tanFunc },
{ "cot", 1, 0, SQLITE_UTF8, 0, cotFunc },

{ "cosh", 1, 0, SQLITE_UTF8, 0, coshFunc },
{ "sinh", 1, 0, SQLITE_UTF8, 0, sinhFunc },
{ "tanh", 1, 0, SQLITE_UTF8, 0, tanhFunc },
{ "coth", 1, 0, SQLITE_UTF8, 0, cothFunc },

{ "exp", 1, 0, SQLITE_UTF8, 0, expFunc },
{ "log", 1, 0, SQLITE_UTF8, 0, logFunc },
{ "log10", 1, 0, SQLITE_UTF8, 0, log10Func },
{ "power", 2, 0, SQLITE_UTF8, 0, powerFunc },
#if SQLITE_VERSION_NUMBER < 3035000
{ "sign", 1, 0, SQLITE_UTF8, 0, signFunc },
#endif

{ "sqrt", 1, 0, SQLITE_UTF8, 0, sqrtFunc },
{ "square", 1, 0, SQLITE_UTF8, 0, squareFunc },

{ "ceil", 1, 0, SQLITE_UTF8, 0, ceilFunc },
{ "floor", 1, 0, SQLITE_UTF8, 0, floorFunc },

{ "pi", 0, 0, SQLITE_UTF8, 1, piFunc },
#endif
{ "atn2", 2, 0, SQLITE_UTF8, 0, atn2Func },

{ "difference", 2, 0, SQLITE_UTF8, 0, differenceFunc},

{ "cot", 1, 0, SQLITE_UTF8, 0, cotFunc },
{ "coth", 1, 0, SQLITE_UTF8, 0, cothFunc },

#if SQLITE_VERSION_NUMBER < 3035000
{ "sign", 1, 0, SQLITE_UTF8, 0, signFunc },
#endif
{ "square", 1, 0, SQLITE_UTF8, 0, squareFunc },

/* string */
{ "replicate", 2, 0, SQLITE_UTF8, 0, replicateFunc },
Expand Down
Binary file not shown.
Binary file modified src/main/resources/org/sqlite/native/FreeBSD/x86/libsqlitejdbc.so
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified src/main/resources/org/sqlite/native/Linux/arm/libsqlitejdbc.so
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified src/main/resources/org/sqlite/native/Linux/x86/libsqlitejdbc.so
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified src/main/resources/org/sqlite/native/Windows/x86/sqlitejdbc.dll
Binary file not shown.
Binary file not shown.
93 changes: 78 additions & 15 deletions src/test/java/org/sqlite/MathFunctionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -173,30 +173,68 @@ public void floor() throws Exception {
}

@Test
// this actually performs ln()
public void log() throws Exception {
Utils.assumeJdbcExtensionsOrMathFunctions(conn);
boolean isLogLn = Utils.getCompileOptions(conn).contains("JDBC_EXTENSIONS");

// with the old math extension functions, log would perform ln instead of log10
public void logAsLn() throws Exception {
Utils.assumeJdbcExtensionsWithoutMathFunctions(conn);
ResultSet rs = stat.executeQuery("select log(2)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isCloseTo(0.30102999566398114, offset(0.000000000000001));
rs.close();
}

double ln = 0.693147180559945;
double log = 0.30102999566398114;
@Test
public void ln() throws Exception {
Utils.assumeMathFunctions(conn);
ResultSet rs = stat.executeQuery("select ln(2)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isCloseTo(0.693147180559945, offset(0.000000000000001));
rs.close();
}

assertThat(rs.getDouble(1)).isCloseTo(isLogLn ? ln : log, offset(0.000000000000001));
@Test
public void logBase() throws Exception {
Utils.assumeMathFunctions(conn);
ResultSet rs = stat.executeQuery("select log(3,3)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isCloseTo(1, offset(0.000000000000001));
rs.close();
}

@Test
public void log10() throws Exception {
Utils.assumeJdbcExtensionsOrMathFunctions(conn);
ResultSet rs = stat.executeQuery("select log10(10)");
public void log2() throws Exception {
Utils.assumeMathFunctions(conn);
ResultSet rs = stat.executeQuery("select log2(2)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isCloseTo(1, offset(0.000000000000001));
rs.close();
}

@Test
public void log10() throws Exception {
Utils.assumeMathFunctions(conn);
{
ResultSet rs = stat.executeQuery("select log10(10)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isCloseTo(1, offset(0.000000000000001));
rs.close();
}
{
ResultSet rs = stat.executeQuery("select log(10)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isCloseTo(1, offset(0.000000000000001));
rs.close();
}
}

@Test
public void mod() throws Exception {
Utils.assumeMathFunctions(conn);
ResultSet rs = stat.executeQuery("select mod(11,3.5)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isEqualTo(0.5);
rs.close();
}

@Test
public void pi() throws Exception {
Utils.assumeJdbcExtensionsOrMathFunctions(conn);
Expand All @@ -212,10 +250,18 @@ public void pi() throws Exception {
@Test
public void power() throws Exception {
Utils.assumeJdbcExtensionsOrMathFunctions(conn);
ResultSet rs = stat.executeQuery("select power(10,2)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isCloseTo(100, offset(0.000000000000001));
rs.close();
{
ResultSet rs = stat.executeQuery("select pow(10,2)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isCloseTo(100, offset(0.000000000000001));
rs.close();
}
{
ResultSet rs = stat.executeQuery("select power(10,2)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isCloseTo(100, offset(0.000000000000001));
rs.close();
}
}

@Test
Expand Down Expand Up @@ -280,4 +326,21 @@ public void tanh() throws Exception {
assertThat(rs.getDouble(1)).isCloseTo(0.46211715726001, offset(0.000000000000001));
rs.close();
}

@Test
public void trunc() throws Exception {
Utils.assumeMathFunctions(conn);
{
ResultSet rs = stat.executeQuery("select trunc(1.5)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isCloseTo(1, offset(0.000000000000001));
rs.close();
}
{
ResultSet rs = stat.executeQuery("select trunc(-1.5)");
assertThat(rs.next()).isTrue();
assertThat(rs.getDouble(1)).isCloseTo(-1, offset(0.000000000000001));
rs.close();
}
}
}
18 changes: 18 additions & 0 deletions src/test/java/org/sqlite/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ public static void assumeJdbcExtensions(Connection conn) throws SQLException {
.contains("JDBC_EXTENSIONS");
}

public static void assumeMathFunctions(Connection conn) throws SQLException {
assumeThat(getCompileOptions(conn))
.as("SQLite has to be compiled with SQLITE_ENABLE_MATH_FUNCTIONS")
.contains("ENABLE_MATH_FUNCTIONS");
}

public static void assumeJdbcExtensionsOrMathFunctions(Connection conn) throws SQLException {
List<String> compileOptions = getCompileOptions(conn);
boolean expected =
Expand All @@ -38,4 +44,16 @@ public static void assumeJdbcExtensionsOrMathFunctions(Connection conn) throws S
"SQLite has to be compiled with JDBC Extensions or SQLITE_ENABLE_MATH_FUNCTIONS")
.isTrue();
}

public static void assumeJdbcExtensionsWithoutMathFunctions(Connection conn)
throws SQLException {
List<String> compileOptions = getCompileOptions(conn);
boolean expected =
compileOptions.contains("JDBC_EXTENSIONS")
&& !compileOptions.contains("ENABLE_MATH_FUNCTIONS");
assumeThat(expected)
.as(
"SQLite has to be compiled with JDBC Extensions and without SQLITE_ENABLE_MATH_FUNCTIONS")
.isTrue();
}
}

0 comments on commit 0f41f46

Please sign in to comment.