-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
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
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
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
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
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
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
-5716fc2341ddd8cf64139e7168597f864da4e10b
\ No newline at end of file
+6db7052eeefafdbf26b3153bc38600fecfb53ae6
\ No newline at end of file
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.
**
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 ),
/*
** 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.
** 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 */
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 */
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;
/* 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;
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{
for(idx=0; idx<ArraySize(fmtinfo); idx++){
if( c==fmtinfo[idx].fmttype ){
infop = &fmtinfo[idx];
- if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
+ if( useIntern || (infop->flags & FLAG_INTERN)==0 ){
xtype = infop->type;
}else{
return;
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);
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);
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
#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:
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<precision; idx++) buf[idx] = (char)c;
break;
case etSTRING:
case etDYNSTRING:
- bufpt = va_arg(ap,char*);
+ if( bArgList ){
+ bufpt = getTextArg(pArgList);
+ }else{
+ bufpt = va_arg(ap,char*);
+ }
if( bufpt==0 ){
bufpt = "";
- }else if( xtype==etDYNSTRING ){
+ }else if( xtype==etDYNSTRING && !bArgList ){
zExtra = bufpt;
}
if( precision>=0 ){
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;
}
case etTOKEN: {
Token *pToken = va_arg(ap, Token*);
+ assert( bArgList==0 );
if( pToken && pToken->n ){
sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
}
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 && k<pSrc->nSrc );
if( pItem->zDatabase ){
sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase);
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;
}
#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
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;
# 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*,...);
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
while( nOut<pVar->n && (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( nOut<pVar->n ) sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
+ if( nOut<pVar->n ){
+ 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 );
if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT;
#endif
for(i=0; i<nOut; i++){
- sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
+ sqlite3XPrintf(&out, 0, "%02x", pVar->z[i]&0xff);
}
sqlite3StrAccumAppend(&out, "'", 1);
#ifdef SQLITE_TRACE_SIZE_LIMIT
- if( nOut<pVar->n ) sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
+ if( nOut<pVar->n ){
+ sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut);
+ }
#endif
}
}
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);
}
}
--- /dev/null
+# 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