]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Many more math functions. Semantics follows PG wherever possible.
authordrh <drh@noemail.net>
Mon, 7 Dec 2020 21:13:06 +0000 (21:13 +0000)
committerdrh <drh@noemail.net>
Mon, 7 Dec 2020 21:13:06 +0000 (21:13 +0000)
FossilOrigin-Name: 6b93627b5d9819abf179a3e4a82e7afe17cbcafdabbd5f058de9ed114c9d477f

manifest
manifest.uuid
src/func.c
test/func7.test

index 9347e389ecba4aa09b04e1c0c103b7be852fd89f..dccb95c603e2e8e856e061e30f425ec9bf7b6369 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Begin\sadding\snew\sSQL\sfunctions\sthat\sdepend\son\s-lm:\s\sceil(),\sceiling(),\nfloor(),\sln(),\slog(),\sand\slog10()\sso\sfar.\s\sMore\sto\sfollow.
-D 2020-12-07T17:15:32.150
+C Many\smore\smath\sfunctions.\s\sSemantics\sfollows\sPG\swherever\spossible.
+D 2020-12-07T21:13:06.475
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -495,7 +495,7 @@ F src/delete.c 927cf8f900583e79aca8f1a321979e0a8f053babd9a690b44b38f79de2cc09fe
 F src/expr.c 0d196ed5a2ebf96be7e8df88add4fabfad0dce16c0fed81a4b8f6a26e259797f
 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
 F src/fkey.c 83372403298e6a7dd989a47aaacdbaa5b4307b5199dbd56e07d4896066b3de72
-F src/func.c d3113a23625daeb54331752421442af772abd851123550459dec96fedbe068e7
+F src/func.c 7c3a41213179cce249d46283fc2237bc3424a041343866dbdb31914fed0edddc
 F src/global.c ed55af196a9b66e198aaeda3f5454c3aa7d7d050c6c938181fd044b70d180a81
 F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
@@ -1026,7 +1026,7 @@ F test/func3.test 2bb0f31ab7baaed690b962a88544d7be6b34fa389364bc36a44e441ed3e3f1
 F test/func4.test 2285fb5792d593fef442358763f0fd9de806eda47dbc7a5934df57ffdc484c31
 F test/func5.test 863e6d1bd0013d09c17236f8a13ea34008dd857d87d85a13a673960e4c25d82a
 F test/func6.test 90e42b64c4f9fb6f04f44cb8a1da586c8542502e926b19c76504fe74ff2a9b7c
-F test/func7.test 7414f1ffb7aba86b6bcfdf0c3739a4e4717026ff583acd8b43ab21189300bcdc
+F test/func7.test bb05a77daedf0e3f8764f323a49bc3b8d98f280a0bc6a370387117f4596bde05
 F test/fuzz-oss1.test e58330d01cbbd8215ee636b17a03fe220b37dbfa
 F test/fuzz.test 96083052bf5765e4518c1ba686ce2bab785670d1
 F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1
@@ -1888,7 +1888,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 1db7c751912beb57a697ac8e85b9c29e30da7b6c89207e9828bf08e56c58242f
-R 6662031cc76e6cb57f52d5a4ee0696ea
+P 4db5f2f7875f6df78630a7816fc018141a6eee2e295b44fc7627eb66d07881ea
+R 5f3694d6742f321a9a8686c5ca8182de
 U drh
-Z 99632da1aa8e30b2d9d751e33b863567
+Z 1fdfc343d2601875f5f91cade0f6a5bf
index 7ffa11f57e25384faaffb0f973d0690a9a9eeaa1..acf82a7c34ffccd0529a03f2f7f634efee3a83eb 100644 (file)
@@ -1 +1 @@
-4db5f2f7875f6df78630a7816fc018141a6eee2e295b44fc7627eb66d07881ea
\ No newline at end of file
+6b93627b5d9819abf179a3e4a82e7afe17cbcafdabbd5f058de9ed114c9d477f
\ No newline at end of file
index 09e44345dcbe91830e5f70741e43e55613c00654..7bfd5bd6a732723e864a82e6e77fc4f2d797c894 100644 (file)
@@ -1904,6 +1904,18 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
   return 1;
 }
 
+/* Mathematical Constants */
+#ifndef M_PI
+# define M_PI   3.141592653589793238462643383279502884
+#endif
+#ifndef M_LN10
+# define M_LN10 2.302585092994045684017991454684364208
+#endif
+#ifndef M_LN2
+# define M_LN2  0.693147180559945309417232121458176568
+#endif
+
+
 /* Extra math functions that require linking with -lm
 */
 #ifdef SQLITE_ENABLE_MATH_FUNCTIONS
@@ -1923,17 +1935,19 @@ static void ceilingFunc(
   sqlite3_value **argv
 ){
   assert( argc==1 );
-  switch( sqlite3_value_type(argv[0]) ){
-    case SQLITE_INTEGER:
+  switch( sqlite3_value_numeric_type(argv[0]) ){
+    case SQLITE_INTEGER: {
        sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
        break;
-    case SQLITE_NULL:
-       break;
-    default: {
+    }
+    case SQLITE_FLOAT: {
        double (*x)(double) = (double(*)(double))sqlite3_user_data(context);
        sqlite3_result_double(context, x(sqlite3_value_double(argv[0])));
        break;
     }
+    default: {
+       break;
+    }
   }
 }
 
@@ -1941,38 +1955,142 @@ static void ceilingFunc(
 ** Implementation of SQL functions:
 **
 **   ln(X)       - natural logarithm
-**   log(X)      - log base 10
-**   log10(X)    - log base 10
-**   log(X,Y)    - log base Y
+**   log(X)      - log base 10
+**   log10(X)    - log base 10
+**   log(B,X)    - log X base B
 */
 static void logFunc(
   sqlite3_context *context,
   int argc,
   sqlite3_value **argv
 ){
-  double x, y, ans;
+  double x, b, ans;
   assert( argc==1 || argc==2 );
-  if( sqlite3_value_type(argv[0])==SQLITE_NULL
-   || (x = sqlite3_value_double(argv[0]))<0.0
-  ){
-    return;  /* Return NULL for a domain error */
+  switch( sqlite3_value_numeric_type(argv[0]) ){
+    case SQLITE_INTEGER:
+    case SQLITE_FLOAT:
+      x = sqlite3_value_double(argv[0]);
+      if( x<0.0 ) return;
+      break;
+    default:
+      return;
   }
-  ans = log(x);
   if( argc==2 ){
-    if( sqlite3_value_type(argv[1])==SQLITE_NULL
-     || (y = sqlite3_value_double(argv[1]))<0.0
-    ){
-      return;  /* Return NULL for a domain error */
+    switch( sqlite3_value_numeric_type(argv[0]) ){
+      case SQLITE_INTEGER:
+      case SQLITE_FLOAT:
+        b = x;
+        x = sqlite3_value_double(argv[1]);
+        if( x<0.0 ) return;
+        break;
+     default:
+        return;
+    }
+    ans = log(x)/log(b);
+  }else{
+    ans = log(x);
+    switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){
+      case 1:
+        /* Convert from natural logarithm to log base 10 */
+        ans *= 1.0/M_LN10;
+        break;
+      case 2:
+        /* Convert from natural logarithm to log base 2 */
+        ans *= 1.0/M_LN2;
+        break;
+      default:
+        break;
     }
-    ans /= log(y);
-  }else if( sqlite3_user_data(context)!=0 ){
-    /* Convert from natural logarithm to log base 10 */
-    ans *= 0.43429448190325178672;
   }
   sqlite3_result_double(context, ans);
 }
+
+/*
+** Functions to converts degrees to radians and radians to degrees.
+*/
+static double degToRad(double x){ return x*(M_PI/180.0); }
+static double radToDeg(double x){ return x*(180.0/M_PI); }
+
+/*
+** Implementation of 1-argument SQL math functions:
+**
+**   exp(X)  - Compute e to the X-th power
+*/
+static void math1Func(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  assert( argc==2 );
+  int type0;
+  double v0, ans;
+  double (*x)(double);
+  type0 = sqlite3_value_numeric_type(argv[0]);
+  if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
+  v0 = sqlite3_value_double(argv[0]);
+  x = (double(*)(double))sqlite3_user_data(context);
+  ans = x(v0);
+  sqlite3_result_double(context, ans);
+}
+
+/*
+** Implementation of 2-argument SQL math functions:
+**
+**   power(X,Y)  - Compute X to the Y-th power
+*/
+static void math2Func(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  assert( argc==2 );
+  int type0, type1;
+  double v0, v1, ans;
+  double (*x)(double,double);
+  type0 = sqlite3_value_numeric_type(argv[0]);
+  if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
+  type1 = sqlite3_value_numeric_type(argv[1]);
+  if( type1!=SQLITE_INTEGER && type1!=SQLITE_FLOAT ) return;
+  v0 = sqlite3_value_double(argv[0]);
+  v1 = sqlite3_value_double(argv[1]);
+  x = (double(*)(double,double))sqlite3_user_data(context);
+  ans = x(v0, v1);
+  sqlite3_result_double(context, ans);
+}
+
+/*
+** Implementation of 2-argument SQL math functions:
+**
+**   power(X,Y)  - Compute X to the Y-th power
+*/
+static void piFunc(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  assert( argc==0 );
+  sqlite3_result_double(context, M_PI);
+}
+
 #endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
 
+/*
+** Implementation of sign(X) function.
+*/
+static void signFunc(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  assert( argc==1 );
+  int type0;
+  double x;
+  type0 = sqlite3_value_numeric_type(argv[0]);
+  if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
+  x = sqlite3_value_double(argv[0]);
+  sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0);
+}
+
 /*
 ** All of the FuncDef structures in the aBuiltinFunc[] array above
 ** to the global function hash table.  This occurs at start-time (as
@@ -2091,17 +2209,41 @@ void sqlite3RegisterBuiltinFunctions(void){
 #endif
     FUNCTION(coalesce,           1, 0, 0, 0                ),
     FUNCTION(coalesce,           0, 0, 0, 0                ),
-    INLINE_FUNC(coalesce,       -1, INLINEFUNC_coalesce, 0 ),
-    INLINE_FUNC(iif,             3, INLINEFUNC_iif,      0 ),
 #ifdef SQLITE_ENABLE_MATH_FUNCTIONS
     MFUNCTION(ceil,              1, ceil,      ceilingFunc ),
     MFUNCTION(ceiling,           1, ceil,      ceilingFunc ),
     MFUNCTION(floor,             1, floor,     ceilingFunc ),
+    MFUNCTION(trunc,             1, trunc,     ceilingFunc ),
     FUNCTION(ln,                 1, 0, 0,      logFunc     ),
     FUNCTION(log,                1, 1, 0,      logFunc     ),
     FUNCTION(log10,              1, 1, 0,      logFunc     ),
+    FUNCTION(log2,               1, 2, 0,      logFunc     ),
     FUNCTION(log,                2, 0, 0,      logFunc     ),
+    MFUNCTION(exp,               1, exp,       math1Func   ),
+    MFUNCTION(pow,               2, pow,       math2Func   ),
+    MFUNCTION(power,             2, pow,       math2Func   ),
+    MFUNCTION(mod,               2, fmod,      math2Func   ),
+    MFUNCTION(acos,              1, acos,      math1Func   ),
+    MFUNCTION(asin,              1, asin,      math1Func   ),
+    MFUNCTION(atan,              1, atan,      math1Func   ),
+    MFUNCTION(atan2,             2, atan2,     math2Func   ),
+    MFUNCTION(cos,               1, cos,       math1Func   ),
+    MFUNCTION(sin,               1, sin,       math1Func   ),
+    MFUNCTION(tan,               1, tan,       math1Func   ),
+    MFUNCTION(cosh,              1, cosh,      math1Func   ),
+    MFUNCTION(sinh,              1, sinh,      math1Func   ),
+    MFUNCTION(tanh,              1, tanh,      math1Func   ),
+    MFUNCTION(acosh,             1, acosh,     math1Func   ),
+    MFUNCTION(asinh,             1, asinh,     math1Func   ),
+    MFUNCTION(atanh,             1, atanh,     math1Func   ),
+    MFUNCTION(sqrt,              1, sqrt,      math1Func   ),
+    MFUNCTION(radians,           1, degToRad,  math1Func   ),
+    MFUNCTION(degrees,           1, radToDeg,  math1Func   ),
+    FUNCTION(pi,                 0, 0, 0,      piFunc      ),
 #endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
+    FUNCTION(sign,               1, 0, 0,      signFunc    ),
+    INLINE_FUNC(coalesce,       -1, INLINEFUNC_coalesce, 0 ),
+    INLINE_FUNC(iif,             3, INLINEFUNC_iif,      0 ),
   };
 #ifndef SQLITE_OMIT_ALTERTABLE
   sqlite3AlterFunctions();
index c7fce79dd6dfcb100147c83476af205c0747d304..536f7eb414b0c61fa1c04d199e325b901db48c8f 100644 (file)
@@ -25,11 +25,221 @@ do_execsql_test func7-110 {
   SELECT quote(ceil(NULL)), ceil('-99.99');
 } {NULL -99.0}
 do_execsql_test func7-200 {
-  SELECT round(ln(5),2), log(100.0), log(100), log('256',2);
+  SELECT round(ln(5),2), log(100.0), log(100), log(2,'256');
 } {1.61 2.0 2.0 8.0}
 do_execsql_test func7-210 {
-  SELECT ln(-5), log(100.0,-5);
+  SELECT ln(-5), log(-5,100.0);
 } {{} {}}
 
+# Test cases derived from PostgreSQL documentation
+#
+do_execsql_test func7-pg-100 {
+  SELECT abs(-17.4)
+} {17.4}
+do_execsql_test func7-pg-110 {
+  SELECT ceil(42.2)
+} {43.0}
+do_execsql_test func7-pg-120 {
+  SELECT ceil(-42.2)
+} {-42.0}
+do_execsql_test func7-pg-130 {
+  SELECT round(exp(1.0),7)
+} {2.7182818}
+do_execsql_test func7-pg-140 {
+  SELECT floor(42.8)
+} {42.0}
+do_execsql_test func7-pg-150 {
+  SELECT floor(-42.8)
+} {-43.0}
+do_execsql_test func7-pg-160 {
+  SELECT round(ln(2.0),7)
+} {0.6931472}
+do_execsql_test func7-pg-170 {
+  SELECT log(100.0)
+} {2.0}
+do_execsql_test func7-pg-180 {
+  SELECT log10(1000.0)
+} {3.0}
+do_execsql_test func7-pg-190 {
+  SELECT log(2.0, 64.0)
+} {6.0}
+do_execsql_test func7-pg-200 {
+   SELECT mod(9,4);
+} {1.0}
+do_execsql_test func7-pg-210 {
+   SELECT round(pi(),7);
+} {3.1415927}
+do_execsql_test func7-pg-220 {
+   SELECT power(9,3);
+} {729.0}
+do_execsql_test func7-pg-230 {
+   SELECT round(radians(45.0),7);
+} {0.7853982}
+do_execsql_test func7-pg-240 {
+   SELECT round(42.4);
+} {42.0}
+do_execsql_test func7-pg-250 {
+   SELECT round(42.4382,2);
+} {42.44}
+do_execsql_test func7-pg-260 {
+   SELECT sign(-8.4);
+} {-1}
+do_execsql_test func7-pg-270 {
+   SELECT round( sqrt(2), 7);
+} {1.4142136}
+do_execsql_test func7-pg-280 {
+   SELECT trunc(42.8), trunc(-42.8);
+} {42.0 -42.0}
+do_execsql_test func7-pg-300 {
+   SELECT acos(1);
+} {0.0}
+do_execsql_test func7-pg-301 {
+   SELECT degrees(acos(0.5));
+} {60.0}
+do_execsql_test func7-pg-310 {
+   SELECT round( asin(1), 7);
+} {1.5707963}
+do_execsql_test func7-pg-311 {
+   SELECT degrees( asin(0.5) );
+} {30.0}
+do_execsql_test func7-pg-320 {
+   SELECT round( atan(1), 7);
+} {0.7853982}
+do_execsql_test func7-pg-321 {
+   SELECT degrees( atan(1) );
+} {45.0}
+do_execsql_test func7-pg-330 {
+   SELECT round( atan2(1,0), 7);
+} {1.5707963}
+do_execsql_test func7-pg-331 {
+   SELECT degrees( atan2(1,0) );
+} {90.0}
+do_execsql_test func7-pg-400 {
+   SELECT cos(0);
+} {1.0}
+do_execsql_test func7-pg-401 {
+   SELECT cos( radians(60.0) );
+} {0.5}
+do_execsql_test func7-pg-400 {
+   SELECT cos(0);
+} {1.0}
+do_execsql_test func7-pg-410 {
+   SELECT round( sin(1), 7);
+} {0.841471}
+do_execsql_test func7-pg-411 {
+   SELECT sin( radians(30) );
+} {0.5}
+do_execsql_test func7-pg-420 {
+   SELECT round( tan(1), 7);
+} {1.5574077}
+do_execsql_test func7-pg-421 {
+   SELECT tan( radians(45) );
+} {1.0}
+do_execsql_test func7-pg-500 {
+   SELECT round( sinh(1), 7);
+} {1.1752012}
+do_execsql_test func7-pg-510 {
+   SELECT round( cosh(0), 7);
+} {1.0}
+do_execsql_test func7-pg-520 {
+   SELECT round( tanh(1), 7);
+} {0.7615942}
+do_execsql_test func7-pg-530 {
+   SELECT round( asinh(1), 7);
+} {0.8813736}
+do_execsql_test func7-pg-540 {
+   SELECT round( acosh(1), 7);
+} {0.0}
+do_execsql_test func7-pg-550 {
+   SELECT round( atanh(0.5), 7);
+} {0.5493061}
+
+# Test cases derived from MySQL documentation
+#
+do_execsql_test func7-mysql-100 {
+   SELECT acos(1);
+} {0.0}
+do_execsql_test func7-mysql-110 {
+   SELECT acos(1.0001);
+} {{}}
+do_execsql_test func7-mysql-120 {
+   SELECT round( acos(0.0), 7);
+} {1.5707963}
+do_execsql_test func7-mysql-130 {
+   SELECT round( asin(0.2), 7);
+} {0.2013579}
+do_execsql_test func7-mysql-140 {
+   SELECT asin('foo');
+} {{}}  ;# Note: MySQL returns 0 here, not NULL.
+         # SQLite deliberately returns NULL.
+         # SQLServer and Oracle throw an error.
+do_execsql_test func7-mysql-150 {
+   SELECT round( atan(2), 7), round( atan(-2), 7);
+} {1.1071487 -1.1071487}
+do_execsql_test func7-mysql-160 {
+   SELECT round( atan2(-2,2), 7), round( atan2(pi(),0), 7);
+} {-0.7853982 1.5707963}
+do_execsql_test func7-mysql-170 {
+   SELECT ceiling(1.23), ceiling(-1.23);
+} {2.0 -1.0}
+do_execsql_test func7-mysql-180 {
+   SELECT cos(pi());
+} {-1.0}
+do_execsql_test func7-mysql-190 {
+   SELECT degrees(pi()), degrees(pi()/2);
+} {180.0 90.0}
+do_execsql_test func7-mysql-190 {
+   SELECT round( exp(2), 7), round( exp(-2), 7), exp(0);
+} {7.3890561 0.1353353 1.0}
+do_execsql_test func7-mysql-200 {
+   SELECT floor(1.23), floor(-1.23);
+} {1.0 -2.0}
+do_execsql_test func7-mysql-210 {
+   SELECT round(ln(2),7), quote(ln(-2));
+} {0.6931472 NULL}
+#do_execsql_test func7-mysql-220 {
+#   SELECT round(log(2),7), log(-2);
+#} {0.6931472 NULL}
+# log() means natural logarithm in MySQL
+do_execsql_test func7-mysql-230 {
+   SELECT log(2,65536), log(10,100), quote(log(1,100));
+} {16.0 2.0 Inf}
+do_execsql_test func7-mysql-240 {
+   SELECT log2(65536), quote(log2(-100));
+} {16.0 NULL}
+do_execsql_test func7-mysql-250 {
+   SELECT round(log10(2),7), log10(100), quote(log10(-100));
+} {0.30103 2.0 NULL}
+do_execsql_test func7-mysql-260 {
+   SELECT mod(234,10), 253%7, mod(29,9), 29%9;
+} {4.0 1 2.0 2}
+do_execsql_test func7-mysql-270 {
+   SELECT mod(34.5,3);
+} {1.5}
+do_execsql_test func7-mysql-280 {
+   SELECT pow(2,2), pow(2,-2);
+} {4.0 0.25}
+do_execsql_test func7-mysql-281 {
+   SELECT power(2,2), power(2,-2);
+} {4.0 0.25}
+do_execsql_test func7-mysql-290 {
+   SELECT round(radians(90),7);
+} {1.5707963}
+do_execsql_test func7-mysql-300 {
+   SELECT sign(-32), sign(0), sign(234);
+} {-1 0 1}
+do_execsql_test func7-mysql-310 {
+   SELECT sin(pi()) BETWEEN -1.0e-15 AND 1.0e-15;
+} {1}
+do_execsql_test func7-mysql-320 {
+   SELECT sqrt(4), round(sqrt(20),7), quote(sqrt(-16));
+} {2.0 4.472136 NULL}
+do_execsql_test func7-mysql-330 {
+   SELECT tan(pi()) BETWEEN -1.0e-15 AND 1.0e-15;
+} {1}
+do_execsql_test func7-mysql-331 {
+   SELECT round(tan(pi()+1),7);
+} {1.5574077}
+
 
 finish_test