aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/printf.c12
-rw-r--r--src/sqliteInt.h3
-rw-r--r--src/vdbe.c4
-rw-r--r--src/vdbe.h3
-rw-r--r--src/vdbetrace.c132
5 files changed, 153 insertions, 1 deletions
diff --git a/src/printf.c b/src/printf.c
index fdd1222c2..17edb0907 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -957,3 +957,15 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
fflush(stdout);
}
#endif
+
+#ifndef SQLITE_OMIT_TRACE
+/*
+** variable-argument wrapper around sqlite3VXPrintf().
+*/
+void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
+ va_list ap;
+ va_start(ap,zFormat);
+ sqlite3VXPrintf(p, 1, zFormat, ap);
+ va_end(ap);
+}
+#endif
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index a3980a071..3adba1c62 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -2517,6 +2517,9 @@ void sqlite3StatusSet(int, int);
int sqlite3IsNaN(double);
void sqlite3VXPrintf(StrAccum*, int, const char*, va_list);
+#ifndef SQLITE_OMIT_TRACE
+void sqlite3XPrintf(StrAccum*, const char*, ...);
+#endif
char *sqlite3MPrintf(sqlite3*,const char*, ...);
char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
diff --git a/src/vdbe.c b/src/vdbe.c
index 35916ea47..97a98b316 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -5632,7 +5632,9 @@ case OP_Trace: {
zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql);
if( zTrace ){
if( db->xTrace ){
- db->xTrace(db->pTraceArg, zTrace);
+ char *z = sqlite3VdbeExpandSql(p, zTrace);
+ db->xTrace(db->pTraceArg, z);
+ sqlite3DbFree(db, z);
}
#ifdef SQLITE_DEBUG
if( (db->flags & SQLITE_SqlTrace)!=0 ){
diff --git a/src/vdbe.h b/src/vdbe.h
index 3ac85eafd..f67e903b8 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -203,6 +203,9 @@ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*);
void sqlite3VdbeProgramDelete(sqlite3 *, SubProgram *, int);
sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8);
void sqlite3VdbeSetVarmask(Vdbe*, int);
+#ifndef SQLITE_OMIT_TRACE
+ char *sqlite3VdbeExpandSql(Vdbe*, const char*);
+#endif
UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,char*,int);
void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*);
diff --git a/src/vdbetrace.c b/src/vdbetrace.c
new file mode 100644
index 000000000..f43fe5532
--- /dev/null
+++ b/src/vdbetrace.c
@@ -0,0 +1,132 @@
+/*
+** 2009 November 25
+**
+** 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 contains code used to insert the values of host parameters
+** (aka "wildcards") into the SQL text output by sqlite3_trace().
+*/
+#include "sqliteInt.h"
+#include "vdbeInt.h"
+
+#ifndef SQLITE_OMIT_TRACE
+
+/*
+** zSql is a zero-terminated string of UTF-8 SQL text. Return the number of
+** bytes in this text up to but excluding the first character in
+** a host parameter. If the text contains no host parameters, return
+** the total number of bytes in the text.
+*/
+static int findNextHostParameter(const char *zSql){
+ int tokenType;
+ int nTotal = 0;
+ int n;
+
+ while( zSql[0] ){
+ n = sqlite3GetToken((u8*)zSql, &tokenType);
+ assert( n>0 && tokenType!=TK_ILLEGAL );
+ if( tokenType==TK_VARIABLE ) break;
+ nTotal += n;
+ zSql += n;
+ }
+ return nTotal;
+}
+
+/*
+** Return a pointer to a string in memory obtained form sqlite3Malloc() which
+** holds a copy of zRawSql but with host parameters expanded to their
+** current values.
+**
+** The calling function is responsible for making sure the memory returned
+** is eventually freed.
+**
+** ALGORITHM: Scan the input string looking for host parameters in any of
+** these forms: ?, ?N, $A, @A, :A. Take care to avoid text within
+** string literals, quoted identifier names, and comments. For text forms,
+** the host parameter index is found by scanning the perpared
+** statement for the corresponding OP_Variable opcode. Once the host
+** parameter index is known, locate the value in p->aVar[]. Then render
+** the value as a literal in place of the host parameter name.
+*/
+char *sqlite3VdbeExpandSql(
+ Vdbe *p, /* The prepared statement being evaluated */
+ const char *zRawSql /* Raw text of the SQL statement */
+){
+ sqlite3 *db; /* The database connection */
+ int idx; /* Index of a host parameter */
+ int nextIndex = 1; /* Index of next ? host parameter */
+ int n; /* Length of a token prefix */
+ int i; /* Loop counter */
+ int dummy; /* For holding a unused return value */
+ Mem *pVar; /* Value of a host parameter */
+ VdbeOp *pOp; /* For looping over opcodes */
+ StrAccum out; /* Accumulate the output here */
+ char zBase[100]; /* Initial working space */
+
+ db = p->db;
+ sqlite3StrAccumInit(&out, zBase, sizeof(zBase),
+ db->aLimit[SQLITE_LIMIT_LENGTH]);
+ out.db = db;
+ while( zRawSql[0] ){
+ n = findNextHostParameter(zRawSql);
+ assert( n>0 );
+ sqlite3StrAccumAppend(&out, zRawSql, n);
+ zRawSql += n;
+ if( zRawSql[0]==0 ) break;
+ if( zRawSql[0]=='?' ){
+ zRawSql++;
+ if( sqlite3Isdigit(zRawSql[0]) ){
+ idx = 0;
+ while( sqlite3Isdigit(zRawSql[0]) ){
+ idx = idx*10 + zRawSql[0] - '0';
+ zRawSql++;
+ }
+ }else{
+ idx = nextIndex;
+ }
+ }else{
+ assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' );
+ n = sqlite3GetToken((u8*)zRawSql, &dummy);
+ idx = 0;
+ for(i=0, pOp=p->aOp; ALWAYS(i<p->nOp); i++, pOp++){
+ if( pOp->opcode!=OP_Variable ) continue;
+ if( pOp->p3>1 ) continue;
+ if( memcmp(pOp->p4.z, zRawSql, n)==0 && pOp->p4.z[n]==0 ){
+ idx = pOp->p1;
+ break;
+ }
+ }
+ assert( idx>0 );
+ zRawSql += n;
+ }
+ nextIndex = idx + 1;
+ assert( idx>0 && idx<=p->nVar );
+ pVar = &p->aVar[idx-1];
+ if( pVar->flags & MEM_Null ){
+ sqlite3StrAccumAppend(&out, "NULL", 4);
+ }else if( pVar->flags & MEM_Int ){
+ sqlite3XPrintf(&out, "%lld", pVar->u.i);
+ }else if( pVar->flags & MEM_Real ){
+ sqlite3XPrintf(&out, "%!.15g", pVar->r);
+ }else if( pVar->flags & MEM_Str ){
+ sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z);
+ }else{
+ assert( pVar->flags & MEM_Blob );
+ sqlite3StrAccumAppend(&out, "x'", 2);
+ for(i=0; i<pVar->n; i++){
+ sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
+ }
+ sqlite3StrAccumAppend(&out, "'", 1);
+ }
+ }
+ return sqlite3StrAccumFinish(&out);
+}
+
+#endif /* #ifndef SQLITE_OMIT_TRACE */