From: drh Date: Tue, 17 Dec 2013 15:03:06 +0000 (+0000) Subject: Add the printf() SQL function. X-Git-Tag: version-3.8.3~93^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a5c1416d64b4b857721f085258b6ef1dcaeb6f5b;p=thirdparty%2Fsqlite.git Add the printf() SQL function. FossilOrigin-Name: 6db7052eeefafdbf26b3153bc38600fecfb53ae6 --- diff --git a/manifest b/manifest index 92e7b53f9f..04c30af9ed 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\sthe\sSQLITE_DETERMINISTIC\sflag\sto\sbe\sORed\sinto\sthe\spreferred\stext\sencoding\nof\sapplication-defined\sfunctions,\sto\smark\sthe\sfunction\sas\sdeterministic. -D 2013-12-14T13:44:22.886 +C Add\sthe\sprintf()\sSQL\sfunction. +D 2013-12-17T15:03:06.814 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -178,7 +178,7 @@ F src/delete.c b36db1f79ee50eaca979660c9dd36437f5410b93 F src/expr.c 31a2b65339f6c3795d4cfa5e99798cd72f9fdfdf F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 2ab0f5384b70594468ef3ac5c7ed8ca24bfd17d5 -F src/func.c fed87f35cf4da4a798b726d84abefc209b48d831 +F src/func.c 6325ac2ec10833ccf4d5c36d323709221d37ea19 F src/global.c 1d7bb7ea8254ae6a68ed9bfaf65fcb3d1690b486 F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4 F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22 @@ -215,7 +215,7 @@ F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222 F src/pcache1.c 57fee9a9a617218f5037afbbe49b09da65bde56b F src/pragma.c 5ab7279d132143feb77f773688a24ab05da75fd7 F src/prepare.c 359d1a1e9c9bd4488e4dd3a1aaaf2d2ebb9bb768 -F src/printf.c ba8b28e9d4ce984430e9f33f6ef1c85a1826d1dd +F src/printf.c 0c0cb58e43410d6237afe0f2751f265fc62eac59 F src/random.c 0b2dbc37fdfbfa6bd455b091dfcef5bdb32dba68 F src/resolve.c 7eda9097b29fcf3d2b42fdc17d1de672134e09b6 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 @@ -224,7 +224,7 @@ F src/shell.c 18924f6ccfa70da98bf9e388bab512c0fd1e792e F src/sqlite.h.in 4ef56464aeaa3785a2c5ca37fb3a0fb229d68b2e F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h 3c1c14a551b019c94e1addcb67d92dd14a62e058 +F src/sqliteInt.h b7e9da87740488671cfe4c70038a8eef3a1d317e F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -288,7 +288,7 @@ F src/vdbeaux.c a22cbd91b24503b82690cb03324ffec9f4ef63f6 F src/vdbeblob.c 8cd05a5630e6d5563ad017bf82edaf812b28acde F src/vdbemem.c 0e69351b2c6ff7d8b638688c0ae336a26befa6b2 F src/vdbesort.c 9d83601f9d6243fe70dd0169a2820c5ddfd48147 -F src/vdbetrace.c f7eb148eb3b4fa3401b20024630dcb43d322e73c +F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767 F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 @@ -739,6 +739,7 @@ F test/permutations.test af3278cbea3a19e025d5169be8193ff48dc3f862 F test/pragma.test e882183ecd21d064cec5c7aaea174fbd36293429 F test/pragma2.test aea7b3d82c76034a2df2b38a13745172ddc0bc13 F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552 +F test/printf2.test 7b1c2c27826702723ad2b1fcd92bce2ffc9f45f3 F test/progress.test a282973d1d17f08071bc58a77d6b80f2a81c354d F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca @@ -1146,7 +1147,10 @@ F tool/vdbe-compress.tcl 0cf56e9263a152b84da86e75a5c0cdcdb7a47891 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P bc5febef921bd12ca7760e9d07d3be0e67140320 -R 08fd6b0f6ffb77c364a2bec4efb57810 +P 5716fc2341ddd8cf64139e7168597f864da4e10b +R 2b8d4659fa3fad9afbe93191f7aa6089 +T *branch * printf-sql-function +T *sym-printf-sql-function * +T -sym-trunk * U drh -Z d2313ea9b793ebbfa6428f7e8b022d58 +Z 77c6ba366806353f9c00efecee393357 diff --git a/manifest.uuid b/manifest.uuid index 0d64591d7b..ce69d4e663 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5716fc2341ddd8cf64139e7168597f864da4e10b \ No newline at end of file +6db7052eeefafdbf26b3153bc38600fecfb53ae6 \ No newline at end of file diff --git a/src/func.c b/src/func.c index 951af97b35..1d6ec9f6ee 100644 --- a/src/func.c +++ b/src/func.c @@ -218,6 +218,32 @@ static void instrFunc( sqlite3_result_int(context, N); } +/* +** Implementation of the printf() function. +*/ +static void printfFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + PrintfArguments x; + StrAccum str; + const char *zFormat; + int n; + + if( argc>=1 && (zFormat = (const char*)sqlite3_value_text(argv[0]))!=0 ){ + x.nArg = argc-1; + x.nUsed = 0; + x.apArg = argv+1; + sqlite3StrAccumInit(&str, 0, 0, SQLITE_MAX_LENGTH); + str.db = sqlite3_context_db_handle(context); + sqlite3XPrintf(&str, SQLITE_PRINTF_SQLFUNC, zFormat, &x); + n = str.nChar; + sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n, + SQLITE_DYNAMIC); + } +} + /* ** Implementation of the substr() function. ** @@ -1648,6 +1674,7 @@ void sqlite3RegisterGlobalFunctions(void){ FUNCTION(instr, 2, 0, 0, instrFunc ), FUNCTION(substr, 2, 0, 0, substrFunc ), FUNCTION(substr, 3, 0, 0, substrFunc ), + FUNCTION(printf, -1, 0, 0, printfFunc ), FUNCTION(unicode, 1, 0, 0, unicodeFunc ), FUNCTION(char, -1, 0, 0, charFunc ), FUNCTION(abs, 1, 0, 0, absFunc ), diff --git a/src/printf.c b/src/printf.c index 3279a54f21..da118818bb 100644 --- a/src/printf.c +++ b/src/printf.c @@ -151,11 +151,28 @@ void sqlite3AppendSpace(StrAccum *pAccum, int N){ /* ** Set the StrAccum object to an error mode. */ -void setStrAccumError(StrAccum *p, u8 eError){ +static void setStrAccumError(StrAccum *p, u8 eError){ p->accError = eError; p->nAlloc = 0; } +/* +** Extra argument values from a PrintfArguments object +*/ +static sqlite3_int64 getIntArg(PrintfArguments *p){ + if( p->nArg<=p->nUsed ) return 0; + return sqlite3_value_int64(p->apArg[p->nUsed++]); +} +static double getDoubleArg(PrintfArguments *p){ + if( p->nArg<=p->nUsed ) return 0.0; + return sqlite3_value_double(p->apArg[p->nUsed++]); +} +static char *getTextArg(PrintfArguments *p){ + if( p->nArg<=p->nUsed ) return 0; + return (char*)sqlite3_value_text(p->apArg[p->nUsed++]); +} + + /* ** On machines with a small stack size, you can redefine the ** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired. @@ -169,10 +186,10 @@ void setStrAccumError(StrAccum *p, u8 eError){ ** Render a string given by "fmt" into the StrAccum object. */ void sqlite3VXPrintf( - StrAccum *pAccum, /* Accumulate results here */ - int useExtended, /* Allow extended %-conversions */ - const char *fmt, /* Format string */ - va_list ap /* arguments */ + StrAccum *pAccum, /* Accumulate results here */ + u32 bFlags, /* SQLITE_PRINTF_* flags */ + const char *fmt, /* Format string */ + va_list ap /* arguments */ ){ int c; /* Next character in the format string */ char *bufpt; /* Pointer to the conversion buffer */ @@ -190,6 +207,8 @@ void sqlite3VXPrintf( etByte flag_longlong; /* True if the "ll" flag is present */ etByte done; /* Loop termination flag */ etByte xtype = 0; /* Conversion paradigm */ + u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ + u8 useIntern; /* Ok to use internal conversions (ex: %T) */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ sqlite_uint64 longvalue; /* Value for integer types */ LONGDOUBLE_TYPE realvalue; /* Value for real types */ @@ -204,9 +223,18 @@ void sqlite3VXPrintf( etByte flag_dp; /* True if decimal point should be shown */ etByte flag_rtz; /* True if trailing zeros should be removed */ #endif + PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ char buf[etBUFSIZE]; /* Conversion buffer */ bufpt = 0; + if( bFlags ){ + if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){ + pArgList = va_arg(ap, PrintfArguments*); + } + useIntern = bFlags & SQLITE_PRINTF_INTERNAL; + }else{ + bArgList = useIntern = 0; + } for(; (c=(*fmt))!=0; ++fmt){ if( c!='%' ){ int amt; @@ -238,7 +266,11 @@ void sqlite3VXPrintf( /* Get the field width */ width = 0; if( c=='*' ){ - width = va_arg(ap,int); + if( bArgList ){ + width = (int)getIntArg(pArgList); + }else{ + width = va_arg(ap,int); + } if( width<0 ){ flag_leftjustify = 1; width = -width; @@ -255,7 +287,11 @@ void sqlite3VXPrintf( precision = 0; c = *++fmt; if( c=='*' ){ - precision = va_arg(ap,int); + if( bArgList ){ + precision = (int)getIntArg(pArgList); + }else{ + precision = va_arg(ap,int); + } if( precision<0 ) precision = -precision; c = *++fmt; }else{ @@ -286,7 +322,7 @@ void sqlite3VXPrintf( for(idx=0; idxflags & FLAG_INTERN)==0 ){ + if( useIntern || (infop->flags & FLAG_INTERN)==0 ){ xtype = infop->type; }else{ return; @@ -326,7 +362,9 @@ void sqlite3VXPrintf( case etRADIX: if( infop->flags & FLAG_SIGNED ){ i64 v; - if( flag_longlong ){ + if( bArgList ){ + v = getIntArg(pArgList); + }else if( flag_longlong ){ v = va_arg(ap,i64); }else if( flag_long ){ v = va_arg(ap,long int); @@ -347,7 +385,9 @@ void sqlite3VXPrintf( else prefix = 0; } }else{ - if( flag_longlong ){ + if( bArgList ){ + longvalue = (u64)getIntArg(pArgList); + }else if( flag_longlong ){ longvalue = va_arg(ap,u64); }else if( flag_long ){ longvalue = va_arg(ap,unsigned long int); @@ -407,7 +447,11 @@ void sqlite3VXPrintf( case etFLOAT: case etEXP: case etGENERIC: - realvalue = va_arg(ap,double); + if( bArgList ){ + realvalue = getDoubleArg(pArgList); + }else{ + realvalue = va_arg(ap,double); + } #ifdef SQLITE_OMIT_FLOATING_POINT length = 0; #else @@ -562,7 +606,7 @@ void sqlite3VXPrintf( #endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */ break; case etSIZE: - *(va_arg(ap,int*)) = pAccum->nChar; + if( !bArgList ) *(va_arg(ap,int*)) = pAccum->nChar; length = width = 0; break; case etPERCENT: @@ -571,7 +615,11 @@ void sqlite3VXPrintf( length = 1; break; case etCHARX: - c = va_arg(ap,int); + if( bArgList ){ + c = (int)getIntArg(pArgList); + }else{ + c = va_arg(ap,int); + } buf[0] = (char)c; if( precision>=0 ){ for(idx=1; idx=0 ){ @@ -602,7 +654,13 @@ void sqlite3VXPrintf( int needQuote; char ch; char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ - char *escarg = va_arg(ap,char*); + char *escarg; + + if( bArgList ){ + escarg = getTextArg(pArgList); + }else{ + escarg = va_arg(ap,char*); + } isnull = escarg==0; if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); k = precision; @@ -637,6 +695,7 @@ void sqlite3VXPrintf( } case etTOKEN: { Token *pToken = va_arg(ap, Token*); + assert( bArgList==0 ); if( pToken && pToken->n ){ sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n); } @@ -647,6 +706,7 @@ void sqlite3VXPrintf( SrcList *pSrc = va_arg(ap, SrcList*); int k = va_arg(ap, int); struct SrcList_item *pItem = &pSrc->a[k]; + assert( bArgList==0 ); assert( k>=0 && knSrc ); if( pItem->zDatabase ){ sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase); @@ -810,7 +870,7 @@ char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){ sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), db->aLimit[SQLITE_LIMIT_LENGTH]); acc.db = db; - sqlite3VXPrintf(&acc, 1, zFormat, ap); + sqlite3VXPrintf(&acc, SQLITE_PRINTF_INTERNAL, zFormat, ap); z = sqlite3StrAccumFinish(&acc); if( acc.accError==STRACCUM_NOMEM ){ db->mallocFailed = 1; @@ -966,14 +1026,12 @@ void sqlite3DebugPrintf(const char *zFormat, ...){ } #endif -#ifndef SQLITE_OMIT_TRACE /* ** variable-argument wrapper around sqlite3VXPrintf(). */ -void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){ +void sqlite3XPrintf(StrAccum *p, u32 bFlags, const char *zFormat, ...){ va_list ap; va_start(ap,zFormat); - sqlite3VXPrintf(p, 1, zFormat, ap); + sqlite3VXPrintf(p, bFlags, zFormat, ap); va_end(ap); } -#endif diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 8e1436efd3..af018edf01 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -742,6 +742,7 @@ typedef struct LookasideSlot LookasideSlot; typedef struct Module Module; typedef struct NameContext NameContext; typedef struct Parse Parse; +typedef struct PrintfArguments PrintfArguments; typedef struct RowSet RowSet; typedef struct Savepoint Savepoint; typedef struct Select Select; @@ -2764,10 +2765,20 @@ void sqlite3StatusSet(int, int); # define sqlite3IsNaN(X) 0 #endif -void sqlite3VXPrintf(StrAccum*, int, const char*, va_list); -#ifndef SQLITE_OMIT_TRACE -void sqlite3XPrintf(StrAccum*, const char*, ...); -#endif +/* +** An instance of the following structure holds information about SQL +** functions arguments that are the parameters to the printf() function. +*/ +struct PrintfArguments { + int nArg; /* Total number of arguments */ + int nUsed; /* Number of arguments used so far */ + sqlite3_value **apArg; /* The argument values */ +}; + +#define SQLITE_PRINTF_INTERNAL 0x01 +#define SQLITE_PRINTF_SQLFUNC 0x02 +void sqlite3VXPrintf(StrAccum*, u32, const char*, va_list); +void sqlite3XPrintf(StrAccum*, u32, const char*, ...); char *sqlite3MPrintf(sqlite3*,const char*, ...); char *sqlite3VMPrintf(sqlite3*,const char*, va_list); char *sqlite3MAppendf(sqlite3*,char*,const char*,...); diff --git a/src/vdbetrace.c b/src/vdbetrace.c index a7ff0a685d..4a39e26521 100644 --- a/src/vdbetrace.c +++ b/src/vdbetrace.c @@ -125,9 +125,9 @@ char *sqlite3VdbeExpandSql( if( pVar->flags & MEM_Null ){ sqlite3StrAccumAppend(&out, "NULL", 4); }else if( pVar->flags & MEM_Int ){ - sqlite3XPrintf(&out, "%lld", pVar->u.i); + sqlite3XPrintf(&out, 0, "%lld", pVar->u.i); }else if( pVar->flags & MEM_Real ){ - sqlite3XPrintf(&out, "%!.15g", pVar->r); + sqlite3XPrintf(&out, 0, "%!.15g", pVar->r); }else if( pVar->flags & MEM_Str ){ int nOut; /* Number of bytes of the string text to include in output */ #ifndef SQLITE_OMIT_UTF16 @@ -148,15 +148,17 @@ char *sqlite3VdbeExpandSql( while( nOutn && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; } } #endif - sqlite3XPrintf(&out, "'%.*q'", nOut, pVar->z); + sqlite3XPrintf(&out, 0, "'%.*q'", nOut, pVar->z); #ifdef SQLITE_TRACE_SIZE_LIMIT - if( nOutn ) sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut); + if( nOutn ){ + sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut); + } #endif #ifndef SQLITE_OMIT_UTF16 if( enc!=SQLITE_UTF8 ) sqlite3VdbeMemRelease(&utf8); #endif }else if( pVar->flags & MEM_Zero ){ - sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero); + sqlite3XPrintf(&out, 0, "zeroblob(%d)", pVar->u.nZero); }else{ int nOut; /* Number of bytes of the blob to include in output */ assert( pVar->flags & MEM_Blob ); @@ -166,11 +168,13 @@ char *sqlite3VdbeExpandSql( if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT; #endif for(i=0; iz[i]&0xff); + sqlite3XPrintf(&out, 0, "%02x", pVar->z[i]&0xff); } sqlite3StrAccumAppend(&out, "'", 1); #ifdef SQLITE_TRACE_SIZE_LIMIT - if( nOutn ) sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut); + if( nOutn ){ + sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut); + } #endif } } @@ -229,7 +233,7 @@ void sqlite3ExplainPrintf(Vdbe *pVdbe, const char *zFormat, ...){ sqlite3AppendSpace(&p->str, p->aIndent[n-1]); } va_start(ap, zFormat); - sqlite3VXPrintf(&p->str, 1, zFormat, ap); + sqlite3VXPrintf(&p->str, SQLITE_PRINTF_INTERNAL, zFormat, ap); va_end(ap); } } diff --git a/test/printf2.test b/test/printf2.test new file mode 100644 index 0000000000..6a017f152e --- /dev/null +++ b/test/printf2.test @@ -0,0 +1,56 @@ +# 2013-12-17 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the printf() SQL function. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_execsql_test printf2-1.1 { + SELECT printf(); +} {{}} +do_execsql_test printf2-1.2 { + SELECT printf('hello'); +} {hello} +do_execsql_test printf2-1.3 { + SELECT printf('%d,%d,%d',55,-11,3421); +} {55,-11,3421} +do_execsql_test printf2-1.4 { + SELECT printf('%d,%d,%d',55,'-11',3421); +} {55,-11,3421} +do_execsql_test printf2-1.5 { + SELECT printf('%d,%d,%d,%d',55,'-11',3421); +} {55,-11,3421,0} +do_execsql_test printf2-1.6 { + SELECT printf('%.2f',3.141592653); +} {3.14} +do_execsql_test printf2-1.7 { + SELECT printf('%.*f',2,3.141592653); +} {3.14} +do_execsql_test printf2-1.8 { + SELECT printf('%*.*f',5,2,3.141592653); +} {{ 3.14}} +do_execsql_test printf2-1.9 { + SELECT printf('%d',314159.2653); +} {314159} +do_execsql_test printf2-1.10 { + SELECT printf('%lld',314159.2653); +} {314159} +do_execsql_test printf2-1.11 { + SELECT printf('%lld%n',314159.2653,'hi'); +} {314159} +do_execsql_test printf2-1.12 { + SELECT printf('%.*z',5,'abcdefghijklmnop'); +} {abcde} + + +finish_test