Added TRIM, LTRIM, and RTRIM functions. (CVS 3698)

FossilOrigin-Name: 6fe13eeade4fc7099fbda1e6520640927c08debc
This commit is contained in:
drh 2007-03-17 17:52:42 +00:00
parent 26b6d90d74
commit 309b338673
4 changed files with 141 additions and 40 deletions

View File

@ -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) C Added\sTRIM,\sLTRIM,\sand\sRTRIM\sfunctions.\s(CVS\s3698)
D 2007-03-17T13:27:55 D 2007-03-17T17:52:42
F Makefile.in 1fe3d0b46e40fd684e1e61f8e8056cefed16de9f F Makefile.in 1fe3d0b46e40fd684e1e61f8e8056cefed16de9f
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@ -67,7 +67,7 @@ F src/date.c 393c73fc027597e008dcd81454544659e978b05c
F src/delete.c 151d08386bf9c9e7f92f6b9106c71efec2def184 F src/delete.c 151d08386bf9c9e7f92f6b9106c71efec2def184
F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b
F src/expr.c b5c65202c7ada4b1ec24f0f010bb73c92ab44e6d F src/expr.c b5c65202c7ada4b1ec24f0f010bb73c92ab44e6d
F src/func.c 683c2d66a2ea386372f01afcd595bb1f026f1c12 F src/func.c 94372fe3cf26b81d4dcdc15f98ff240c37c8c708
F src/hash.c 449f3d6620193aa557f5d86cbc5cc6b87702b185 F src/hash.c 449f3d6620193aa557f5d86cbc5cc6b87702b185
F src/hash.h 1b3f7e2609141fd571f62199fc38687d262e9564 F src/hash.h 1b3f7e2609141fd571f62199fc38687d262e9564
F src/insert.c 72cb64b698796f2005c0158e098124d9490868bb F src/insert.c 72cb64b698796f2005c0158e098124d9490868bb
@ -221,7 +221,7 @@ F test/fts2g.test c69a8ab43ec77d123976ba6cf9422d647ae63032
F test/fts2h.test 223af921323b409d4b5b18ff4e51619541b174bb F test/fts2h.test 223af921323b409d4b5b18ff4e51619541b174bb
F test/fts2i.test 1b22451d1f13f7c509baec620dc3a4a754885dd6 F test/fts2i.test 1b22451d1f13f7c509baec620dc3a4a754885dd6
F test/fts2j.test f68d7611f76309bc8b94170f3740d9fbbc061d9b F test/fts2j.test f68d7611f76309bc8b94170f3740d9fbbc061d9b
F test/func.test 86c3322c9209be36354981354278553b3491393f F test/func.test 019d706b2458dfdf239c74cc31143446de1ee44a
F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a
F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d
F test/index.test e65df12bed94b2903ee89987115e1578687e9266 F test/index.test e65df12bed94b2903ee89987115e1578687e9266
@ -437,7 +437,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
P b0f8203dbbf1036418a2dcc480f352f761826194 P c2fe746ea782f84e850aaf3af7f5536b027a19a1
R 1f5e4827e9584f1f16dd1e58366ed649 R 56fe7aceb3f39a745b09a268ce41fa61
U drh U drh
Z 93e91ca49be183cbbf3f15fa9aa6c1e3 Z 1b019529be28ae82cd90d8e8442d2374

View File

@ -1 +1 @@
c2fe746ea782f84e850aaf3af7f5536b027a19a1 6fe13eeade4fc7099fbda1e6520640927c08debc

View File

@ -16,7 +16,7 @@
** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** sqliteRegisterBuildinFunctions() found at the bottom of the file.
** All other code has file scope. ** 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 "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@ -697,12 +697,12 @@ static void replaceFunc(
sqlite3_value_type(argv[2])==SQLITE_NULL ){ sqlite3_value_type(argv[2])==SQLITE_NULL ){
return; return;
} }
nStr = sqlite3_value_bytes(argv[0]);
zStr = sqlite3_value_text(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]); zPattern = sqlite3_value_text(argv[1]);
nRep = sqlite3_value_bytes(argv[2]); nPattern = sqlite3_value_bytes(argv[1]);
zRep = sqlite3_value_text(argv[2]); zRep = sqlite3_value_text(argv[2]);
nRep = sqlite3_value_bytes(argv[2]);
if( nPattern>=nRep ){ if( nPattern>=nRep ){
nOut = nStr; nOut = nStr;
}else{ }else{
@ -727,6 +727,55 @@ static void replaceFunc(
sqlite3_result_text(context, (char*)zOut, j, sqlite3_free); 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 #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() ** Therefore the next statement sets variable 'max' to 1 for the max()
** aggregate, or 0 for min(). ** 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); cmp = sqlite3MemCompare(pBest, pArg, pColl);
if( (max && cmp<0) || (!max && cmp>0) ){ if( (max && cmp<0) || (!max && cmp>0) ){
sqlite3VdbeMemCopy(pBest, pArg); sqlite3VdbeMemCopy(pBest, pArg);
@ -1109,15 +1158,15 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
static const struct { static const struct {
char *zName; char *zName;
signed char nArg; 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 eTextRep; /* 1: UTF-16. 0: UTF-8 */
u8 needCollSeq; u8 needCollSeq;
void (*xFunc)(sqlite3_context*,int,sqlite3_value **); void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
} aFuncs[] = { } aFuncs[] = {
{ "min", -1, 0, SQLITE_UTF8, 1, minmaxFunc }, { "min", -1, 0, SQLITE_UTF8, 1, minmaxFunc },
{ "min", 0, 0, SQLITE_UTF8, 1, 0 }, { "min", 0, 0, SQLITE_UTF8, 1, 0 },
{ "max", -1, 2, SQLITE_UTF8, 1, minmaxFunc }, { "max", -1, 1, SQLITE_UTF8, 1, minmaxFunc },
{ "max", 0, 2, SQLITE_UTF8, 1, 0 }, { "max", 0, 1, SQLITE_UTF8, 1, 0 },
{ "typeof", 1, 0, SQLITE_UTF8, 0, typeofFunc }, { "typeof", 1, 0, SQLITE_UTF8, 0, typeofFunc },
{ "length", 1, 0, SQLITE_UTF8, 0, lengthFunc }, { "length", 1, 0, SQLITE_UTF8, 0, lengthFunc },
{ "substr", 3, 0, SQLITE_UTF8, 0, substrFunc }, { "substr", 3, 0, SQLITE_UTF8, 0, substrFunc },
@ -1139,23 +1188,29 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
{ "nullif", 2, 0, SQLITE_UTF8, 1, nullifFunc }, { "nullif", 2, 0, SQLITE_UTF8, 1, nullifFunc },
{ "sqlite_version", 0, 0, SQLITE_UTF8, 0, versionFunc}, { "sqlite_version", 0, 0, SQLITE_UTF8, 0, versionFunc},
{ "quote", 1, 0, SQLITE_UTF8, 0, quoteFunc }, { "quote", 1, 0, SQLITE_UTF8, 0, quoteFunc },
{ "last_insert_rowid", 0, 1, SQLITE_UTF8, 0, last_insert_rowid }, { "last_insert_rowid", 0, 0xff, SQLITE_UTF8, 0, last_insert_rowid },
{ "changes", 0, 1, SQLITE_UTF8, 0, changes }, { "changes", 0, 0xff, SQLITE_UTF8, 0, changes },
{ "total_changes", 0, 1, SQLITE_UTF8, 0, total_changes }, { "total_changes", 0, 0xff, SQLITE_UTF8, 0, total_changes },
{ "replace", 3, 0, SQLITE_UTF8, 0, replaceFunc }, { "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 #ifdef SQLITE_SOUNDEX
{ "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc}, { "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc},
#endif #endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION #ifndef SQLITE_OMIT_LOAD_EXTENSION
{ "load_extension", 1, 1, SQLITE_UTF8, 0, loadExt }, { "load_extension", 1, 0xff, SQLITE_UTF8, 0, loadExt },
{ "load_extension", 2, 1, SQLITE_UTF8, 0, loadExt }, { "load_extension", 2, 0xff, SQLITE_UTF8, 0, loadExt },
#endif #endif
#ifdef SQLITE_TEST #ifdef SQLITE_TEST
{ "randstr", 2, 0, SQLITE_UTF8, 0, randStr }, { "randstr", 2, 0, SQLITE_UTF8, 0, randStr },
{ "test_destructor", 1, 1, SQLITE_UTF8, 0, test_destructor}, { "test_destructor", 1, 0xff, SQLITE_UTF8, 0, test_destructor},
{ "test_destructor_count", 0, 0, SQLITE_UTF8, 0, test_destructor_count}, { "test_destructor_count", 0, 0, SQLITE_UTF8, 0, test_destructor_count},
{ "test_auxdata", -1, 0, SQLITE_UTF8, 0, test_auxdata}, { "test_auxdata", -1, 0, SQLITE_UTF8, 0, test_auxdata},
{ "test_error", 1, 0, SQLITE_UTF8, 0, test_error}, { "test_error", 1, 0, SQLITE_UTF8, 0, test_error},
#endif #endif
}; };
static const struct { static const struct {
@ -1167,7 +1222,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
void (*xFinalize)(sqlite3_context*); void (*xFinalize)(sqlite3_context*);
} aAggs[] = { } aAggs[] = {
{ "min", 1, 0, 1, minmaxStep, minMaxFinalize }, { "min", 1, 0, 1, minmaxStep, minMaxFinalize },
{ "max", 1, 2, 1, minmaxStep, minMaxFinalize }, { "max", 1, 1, 1, minmaxStep, minMaxFinalize },
{ "sum", 1, 0, 0, sumStep, sumFinalize }, { "sum", 1, 0, 0, sumStep, sumFinalize },
{ "total", 1, 0, 0, sumStep, totalFinalize }, { "total", 1, 0, 0, sumStep, totalFinalize },
{ "avg", 1, 0, 0, sumStep, avgFinalize }, { "avg", 1, 0, 0, sumStep, avgFinalize },
@ -1177,10 +1232,12 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
int i; int i;
for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){ for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
void *pArg = 0; void *pArg;
switch( aFuncs[i].argType ){ u8 argType = aFuncs[i].argType;
case 1: pArg = db; break; if( argType==0xff ){
case 2: pArg = (void *)(-1); break; pArg = db;
}else{
pArg = (void*)(int)argType;
} }
sqlite3CreateFunc(db, aFuncs[i].zName, aFuncs[i].nArg, sqlite3CreateFunc(db, aFuncs[i].zName, aFuncs[i].nArg,
aFuncs[i].eTextRep, pArg, aFuncs[i].xFunc, 0, 0); aFuncs[i].eTextRep, pArg, aFuncs[i].xFunc, 0, 0);
@ -1199,11 +1256,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
sqlite3AttachFunctions(db); sqlite3AttachFunctions(db);
#endif #endif
for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){ for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
void *pArg = 0; void *pArg = (void*)(int)aAggs[i].argType;
switch( aAggs[i].argType ){
case 1: pArg = db; break;
case 2: pArg = (void *)(-1); break;
}
sqlite3CreateFunc(db, aAggs[i].zName, aAggs[i].nArg, SQLITE_UTF8, sqlite3CreateFunc(db, aAggs[i].zName, aAggs[i].nArg, SQLITE_UTF8,
pArg, 0, aAggs[i].xStep, aAggs[i].xFinalize); pArg, 0, aAggs[i].xStep, aAggs[i].xFinalize);
if( aAggs[i].needCollSeq ){ if( aAggs[i].needCollSeq ){

View File

@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The # This file implements regression tests for SQLite library. The
# focus of this file is testing built-in functions. # 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] set testdir [file dirname $argv0]
source $testdir/tester.tcl 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 # Test that the auxdata API for scalar functions works. This test uses
# a special user-defined function only available in test builds, # a special user-defined function only available in test builds,
# test_auxdata(). Function test_auxdata() takes any number of arguments. # test_auxdata(). Function test_auxdata() takes any number of arguments.
btree_breakpoint
do_test func-13.1 { do_test func-13.1 {
execsql { execsql {
SELECT test_auxdata('hello world'); SELECT test_auxdata('hello world');
@ -772,6 +771,55 @@ do_test func-21.8 {
} }
} {0123456789012345678901234567890123456789012345678901234567890123456789} } {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 finish_test