]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Added TRIM, LTRIM, and RTRIM functions. (CVS 3698)
authordrh <drh@noemail.net>
Sat, 17 Mar 2007 17:52:42 +0000 (17:52 +0000)
committerdrh <drh@noemail.net>
Sat, 17 Mar 2007 17:52:42 +0000 (17:52 +0000)
FossilOrigin-Name: 6fe13eeade4fc7099fbda1e6520640927c08debc

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

index a20b20afb256db1061101b081e9fb0a0e6fcb3f7..87e95a1b83cd28d6cc0e66767f52f1a469a64576 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C First\scut\sat\san\simplementation\sof\sthe\sREPLACE()\sfunction.\s\sWe\smight\syet\nmake\sthis\sa\scompile-time\soption\sor\smove\sit\sinto\sa\sseparate\ssource\sfile.\s(CVS\s3697)
-D 2007-03-17T13:27:55
+C Added\sTRIM,\sLTRIM,\sand\sRTRIM\sfunctions.\s(CVS\s3698)
+D 2007-03-17T17:52:42
 F Makefile.in 1fe3d0b46e40fd684e1e61f8e8056cefed16de9f
 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -67,7 +67,7 @@ F src/date.c 393c73fc027597e008dcd81454544659e978b05c
 F src/delete.c 151d08386bf9c9e7f92f6b9106c71efec2def184
 F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b
 F src/expr.c b5c65202c7ada4b1ec24f0f010bb73c92ab44e6d
-F src/func.c 683c2d66a2ea386372f01afcd595bb1f026f1c12
+F src/func.c 94372fe3cf26b81d4dcdc15f98ff240c37c8c708
 F src/hash.c 449f3d6620193aa557f5d86cbc5cc6b87702b185
 F src/hash.h 1b3f7e2609141fd571f62199fc38687d262e9564
 F src/insert.c 72cb64b698796f2005c0158e098124d9490868bb
@@ -221,7 +221,7 @@ F test/fts2g.test c69a8ab43ec77d123976ba6cf9422d647ae63032
 F test/fts2h.test 223af921323b409d4b5b18ff4e51619541b174bb
 F test/fts2i.test 1b22451d1f13f7c509baec620dc3a4a754885dd6
 F test/fts2j.test f68d7611f76309bc8b94170f3740d9fbbc061d9b
-F test/func.test 86c3322c9209be36354981354278553b3491393f
+F test/func.test 019d706b2458dfdf239c74cc31143446de1ee44a
 F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a
 F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d
 F test/index.test e65df12bed94b2903ee89987115e1578687e9266
@@ -437,7 +437,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P b0f8203dbbf1036418a2dcc480f352f761826194
-R 1f5e4827e9584f1f16dd1e58366ed649
+P c2fe746ea782f84e850aaf3af7f5536b027a19a1
+R 56fe7aceb3f39a745b09a268ce41fa61
 U drh
-Z 93e91ca49be183cbbf3f15fa9aa6c1e3
+Z 1b019529be28ae82cd90d8e8442d2374
index cd8aadf2f979c98395d3260855de4161447830c9..87d238b0b0a59411fa193f1d4d98991a055b6c88 100644 (file)
@@ -1 +1 @@
-c2fe746ea782f84e850aaf3af7f5536b027a19a1
\ No newline at end of file
+6fe13eeade4fc7099fbda1e6520640927c08debc
\ No newline at end of file
index cfd377eeb79d9e96f238c88d15d0696224ebd85f..7850e22a150aa37cfcc8f6d5d8d57bad62a1927a 100644 (file)
@@ -16,7 +16,7 @@
 ** sqliteRegisterBuildinFunctions() found at the bottom of the file.
 ** All other code has file scope.
 **
-** $Id: func.c,v 1.137 2007/03/17 13:27:55 drh Exp $
+** $Id: func.c,v 1.138 2007/03/17 17:52:42 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -697,12 +697,12 @@ static void replaceFunc(
       sqlite3_value_type(argv[2])==SQLITE_NULL ){
     return;
   }
-  nStr = sqlite3_value_bytes(argv[0]);
   zStr = sqlite3_value_text(argv[0]);
-  nPattern = sqlite3_value_bytes(argv[1]);
+  nStr = sqlite3_value_bytes(argv[0]);
   zPattern = sqlite3_value_text(argv[1]);
-  nRep = sqlite3_value_bytes(argv[2]);
+  nPattern = sqlite3_value_bytes(argv[1]);
   zRep = sqlite3_value_text(argv[2]);
+  nRep = sqlite3_value_bytes(argv[2]);
   if( nPattern>=nRep ){
     nOut = nStr;
   }else{
@@ -727,6 +727,55 @@ static void replaceFunc(
   sqlite3_result_text(context, (char*)zOut, j, sqlite3_free);
 }
 
+/*
+** Implementation of the TRIM(), LTRIM(), and RTRIM() functions.
+** The userdata is 0x1 for left trim, 0x2 for right trim, 0x3 for both.
+*/
+static void trimFunc(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  const unsigned char *zIn;         /* Input string */
+  const unsigned char *zCharSet;    /* Set of characters to trim */
+  int nIn;                          /* Number of bytes in input */
+  int flags;
+  int i;
+  unsigned char cFirst, cNext;
+  if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
+    return;
+  }
+  zIn = sqlite3_value_text(argv[0]);
+  nIn = sqlite3_value_bytes(argv[0]);
+  if( argc==1 ){
+    static const unsigned char zSpace[] = " ";
+    zCharSet = zSpace;
+  }else if( sqlite3_value_type(argv[1])==SQLITE_NULL ){
+    return;
+  }else{
+    zCharSet = sqlite3_value_text(argv[1]);
+  }
+  cFirst = zCharSet[0];
+  if( cFirst ){
+    flags = (int)sqlite3_user_data(context);
+    if( flags & 1 ){
+      for(; nIn>0; nIn--, zIn++){
+        if( cFirst==zIn[0] ) continue;
+        for(i=1; zCharSet[i] && zCharSet[i]!=zIn[0]; i++){}
+        if( zCharSet[i]==0 ) break;
+      }
+    }
+    if( flags & 2 ){
+      for(; nIn>0; nIn--){
+        cNext = zIn[nIn-1];
+        if( cFirst==cNext ) continue;
+        for(i=1; zCharSet[i] && zCharSet[i]!=cNext; i++){}
+        if( zCharSet[i]==0 ) break;
+      }
+    }
+  }
+  sqlite3_result_text(context, (char*)zIn, nIn, SQLITE_TRANSIENT);
+}
 
 #ifdef SQLITE_SOUNDEX
 /*
@@ -1079,7 +1128,7 @@ static void minmaxStep(sqlite3_context *context, int argc, sqlite3_value **argv)
     ** Therefore the next statement sets variable 'max' to 1 for the max()
     ** aggregate, or 0 for min().
     */
-    max = ((sqlite3_user_data(context)==(void *)-1)?1:0);
+    max = sqlite3_user_data(context)!=0;
     cmp = sqlite3MemCompare(pBest, pArg, pColl);
     if( (max && cmp<0) || (!max && cmp>0) ){
       sqlite3VdbeMemCopy(pBest, pArg);
@@ -1109,15 +1158,15 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
   static const struct {
      char *zName;
      signed char nArg;
-     u8 argType;           /* 0: none.  1: db  2: (-1) */
+     u8 argType;           /* ff: db   1: 0, 2: 1, 3: 2,...  N:  N-1. */
      u8 eTextRep;          /* 1: UTF-16.  0: UTF-8 */
      u8 needCollSeq;
      void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
   } aFuncs[] = {
     { "min",               -1, 0, SQLITE_UTF8,    1, minmaxFunc },
     { "min",                0, 0, SQLITE_UTF8,    1, 0          },
-    { "max",               -1, 2, SQLITE_UTF8,    1, minmaxFunc },
-    { "max",                0, 2, SQLITE_UTF8,    1, 0          },
+    { "max",               -1, 1, SQLITE_UTF8,    1, minmaxFunc },
+    { "max",                0, 1, SQLITE_UTF8,    1, 0          },
     { "typeof",             1, 0, SQLITE_UTF8,    0, typeofFunc },
     { "length",             1, 0, SQLITE_UTF8,    0, lengthFunc },
     { "substr",             3, 0, SQLITE_UTF8,    0, substrFunc },
@@ -1139,23 +1188,29 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
     { "nullif",             2, 0, SQLITE_UTF8,    1, nullifFunc },
     { "sqlite_version",     0, 0, SQLITE_UTF8,    0, versionFunc},
     { "quote",              1, 0, SQLITE_UTF8,    0, quoteFunc  },
-    { "last_insert_rowid",  0, 1, SQLITE_UTF8,    0, last_insert_rowid },
-    { "changes",            0, 1, SQLITE_UTF8,    0, changes           },
-    { "total_changes",      0, 1, SQLITE_UTF8,    0, total_changes     },
+    { "last_insert_rowid",  0, 0xff, SQLITE_UTF8, 0, last_insert_rowid },
+    { "changes",            0, 0xff, SQLITE_UTF8, 0, changes           },
+    { "total_changes",      0, 0xff, SQLITE_UTF8, 0, total_changes     },
     { "replace",            3, 0, SQLITE_UTF8,    0, replaceFunc       },
+    { "ltrim",              1, 1, SQLITE_UTF8,    0, trimFunc          },
+    { "ltrim",              2, 1, SQLITE_UTF8,    0, trimFunc          },
+    { "rtrim",              1, 2, SQLITE_UTF8,    0, trimFunc          },
+    { "rtrim",              2, 2, SQLITE_UTF8,    0, trimFunc          },
+    { "trim",               1, 3, SQLITE_UTF8,    0, trimFunc          },
+    { "trim",               2, 3, SQLITE_UTF8,    0, trimFunc          },
 #ifdef SQLITE_SOUNDEX
-    { "soundex",            1, 0, SQLITE_UTF8, 0, soundexFunc},
+    { "soundex",            1, 0, SQLITE_UTF8,    0, soundexFunc},
 #endif
 #ifndef SQLITE_OMIT_LOAD_EXTENSION
-    { "load_extension",     1, 1, SQLITE_UTF8,    0, loadExt },
-    { "load_extension",     2, 1, SQLITE_UTF8,    0, loadExt },
+    { "load_extension",     1, 0xff, SQLITE_UTF8, 0, loadExt },
+    { "load_extension",     2, 0xff, SQLITE_UTF8, 0, loadExt },
 #endif
 #ifdef SQLITE_TEST
-    { "randstr",               2, 0, SQLITE_UTF8, 0, randStr    },
-    { "test_destructor",       1, 1, SQLITE_UTF8, 0, test_destructor},
-    { "test_destructor_count", 0, 0, SQLITE_UTF8, 0, test_destructor_count},
-    { "test_auxdata",         -1, 0, SQLITE_UTF8, 0, test_auxdata},
-    { "test_error",            1, 0, SQLITE_UTF8, 0, test_error},
+    { "randstr",               2, 0,    SQLITE_UTF8, 0, randStr    },
+    { "test_destructor",       1, 0xff, SQLITE_UTF8, 0, test_destructor},
+    { "test_destructor_count", 0, 0,    SQLITE_UTF8, 0, test_destructor_count},
+    { "test_auxdata",         -1, 0,    SQLITE_UTF8, 0, test_auxdata},
+    { "test_error",            1, 0,    SQLITE_UTF8, 0, test_error},
 #endif
   };
   static const struct {
@@ -1167,7 +1222,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
     void (*xFinalize)(sqlite3_context*);
   } aAggs[] = {
     { "min",    1, 0, 1, minmaxStep,   minMaxFinalize },
-    { "max",    1, 2, 1, minmaxStep,   minMaxFinalize },
+    { "max",    1, 1, 1, minmaxStep,   minMaxFinalize },
     { "sum",    1, 0, 0, sumStep,      sumFinalize    },
     { "total",  1, 0, 0, sumStep,      totalFinalize    },
     { "avg",    1, 0, 0, sumStep,      avgFinalize    },
@@ -1177,10 +1232,12 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
   int i;
 
   for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
-    void *pArg = 0;
-    switch( aFuncs[i].argType ){
-      case 1: pArg = db; break;
-      case 2: pArg = (void *)(-1); break;
+    void *pArg;
+    u8 argType = aFuncs[i].argType;
+    if( argType==0xff ){
+      pArg = db;
+    }else{
+      pArg = (void*)(int)argType;
     }
     sqlite3CreateFunc(db, aFuncs[i].zName, aFuncs[i].nArg,
         aFuncs[i].eTextRep, pArg, aFuncs[i].xFunc, 0, 0);
@@ -1199,11 +1256,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
   sqlite3AttachFunctions(db);
 #endif
   for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
-    void *pArg = 0;
-    switch( aAggs[i].argType ){
-      case 1: pArg = db; break;
-      case 2: pArg = (void *)(-1); break;
-    }
+    void *pArg = (void*)(int)aAggs[i].argType;
     sqlite3CreateFunc(db, aAggs[i].zName, aAggs[i].nArg, SQLITE_UTF8, 
         pArg, 0, aAggs[i].xStep, aAggs[i].xFinalize);
     if( aAggs[i].needCollSeq ){
index 2f966cc44b3168a1c1b8ea1757d76282c36377f3..2f3fc314d8f95892ec2ba49833678749e8b228b4 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing built-in functions.
 #
-# $Id: func.test,v 1.58 2007/03/17 13:27:56 drh Exp $
+# $Id: func.test,v 1.59 2007/03/17 17:52:42 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -440,7 +440,6 @@ do_test func-12.7 {
 # Test that the auxdata API for scalar functions works. This test uses
 # a special user-defined function only available in test builds,
 # test_auxdata(). Function test_auxdata() takes any number of arguments.
-btree_breakpoint
 do_test func-13.1 {
   execsql {
     SELECT test_auxdata('hello world');
@@ -772,6 +771,55 @@ do_test func-21.8 {
   }
 } {0123456789012345678901234567890123456789012345678901234567890123456789}
 
-
+# Tests for the TRIM, LTRIM and RTRIM functions.
+#
+do_test func-22.1 {
+  catchsql {SELECT trim(1,2,3)}
+} {1 {wrong number of arguments to function trim()}}
+do_test func-22.2 {
+  catchsql {SELECT ltrim(1,2,3)}
+} {1 {wrong number of arguments to function ltrim()}}
+do_test func-22.3 {
+  catchsql {SELECT rtrim(1,2,3)}
+} {1 {wrong number of arguments to function rtrim()}}
+do_test func-22.4 {
+  execsql {SELECT trim('  hi  ');}
+} {hi}
+do_test func-22.5 {
+  execsql {SELECT ltrim('  hi  ');}
+} {{hi  }}
+do_test func-22.6 {
+  execsql {SELECT rtrim('  hi  ');}
+} {{  hi}}
+do_test func-22.7 {
+  execsql {SELECT trim('  hi  ','xyz');}
+} {{  hi  }}
+do_test func-22.8 {
+  execsql {SELECT ltrim('  hi  ','xyz');}
+} {{  hi  }}
+do_test func-22.9 {
+  execsql {SELECT rtrim('  hi  ','xyz');}
+} {{  hi  }}
+do_test func-22.10 {
+  execsql {SELECT trim('xyxzy  hi  zzzy','xyz');}
+} {{  hi  }}
+do_test func-22.11 {
+  execsql {SELECT ltrim('xyxzy  hi  zzzy','xyz');}
+} {{  hi  zzzy}}
+do_test func-22.12 {
+  execsql {SELECT rtrim('xyxzy  hi  zzzy','xyz');}
+} {{xyxzy  hi  }}
+do_test func-22.13 {
+  execsql {SELECT trim('  hi  ','');}
+} {{  hi  }}
+do_test func-22.20 {
+  execsql {SELECT typeof(trim(NULL));}
+} {null}
+do_test func-22.21 {
+  execsql {SELECT typeof(trim(NULL,'xyz'));}
+} {null}
+do_test func-22.22 {
+  execsql {SELECT typeof(trim('hello',NULL));}
+} {null}
 
 finish_test