Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -707,208 +707,10 @@ assert( (rc&db->errMask)==rc ); sqlite3_mutex_leave(db->mutex); return rc; } -#ifdef SQLITE_ENABLE_NORMALIZE - -/* -** Attempt to estimate the final output buffer size needed for the fully -** normalized version of the specified SQL string. This should take into -** account any potential expansion that could occur (e.g. via IN clauses -** being expanded, etc). This size returned is the total number of bytes -** including the NUL terminator. -*/ -static int estimateNormalizedSize( - const char *zSql, /* The original SQL string */ - int nSql /* Length of original SQL string */ -){ - int nOut = nSql + 4; - const char *z = zSql; - while( nOut0 ){ - zOut[j++] = '"'; - continue; - }else if( k==nToken-1 ){ - zOut[j++] = '"'; - continue; - } - } - if( bKeyword ){ - zOut[j++] = sqlite3Toupper(zSql[iIn+k]); - }else{ - zOut[j++] = sqlite3Tolower(zSql[iIn+k]); - } - } - *piOut = j; -} - -/* -** Compute a normalization of the SQL given by zSql[0..nSql-1]. Return -** the normalization in space obtained from sqlite3DbMalloc(). Or return -** NULL if anything goes wrong or if zSql is NULL. -*/ -char *sqlite3Normalize( - Vdbe *pVdbe, /* VM being reprepared */ - const char *zSql, /* The original SQL string */ - int nSql /* Size of the input string in bytes */ -){ - sqlite3 *db; /* Database handle. */ - char *z; /* The output string */ - int nZ; /* Size of the output string in bytes */ - int i; /* Next character to read from zSql[] */ - int j; /* Next character to fill in on z[] */ - int tokenType = 0; /* Type of the next token */ - int prevTokenType = 0; /* Type of the previous token, except spaces */ - int n; /* Size of the next token */ - int nParen = 0; /* Nesting level of parenthesis */ - int iStartIN = 0; /* Start of RHS of IN operator in z[] */ - int nParenAtIN = 0; /* Value of nParent at start of RHS of IN operator */ - - db = sqlite3VdbeDb(pVdbe); - assert( db!=0 ); - if( zSql==0 ) return 0; - nZ = estimateNormalizedSize(zSql, nSql); - z = sqlite3DbMallocRawNN(db, nZ); - if( z==0 ) goto normalizeError; - for(i=j=0; i0 && nParen==nParenAtIN ){ - assert( iStartIN+6=0 ); - assert( nZ-1-j=0 ); - /* Fall through */ - } - case TK_MINUS: - case TK_SEMI: - case TK_PLUS: - case TK_STAR: - case TK_SLASH: - case TK_REM: - case TK_EQ: - case TK_LE: - case TK_NE: - case TK_LSHIFT: - case TK_LT: - case TK_RSHIFT: - case TK_GT: - case TK_GE: - case TK_BITOR: - case TK_CONCAT: - case TK_COMMA: - case TK_BITAND: - case TK_BITNOT: - case TK_DOT: - case TK_IN: - case TK_IS: - case TK_NOT: - case TK_NULL: - case TK_ID: { - if( tokenType==TK_NULL ){ - if( prevTokenType==TK_IS || prevTokenType==TK_NOT ){ - /* NULL is a keyword in this case, not a literal value */ - }else{ - /* Here the NULL is a literal value */ - z[j++] = '?'; - break; - } - } - if( j>0 && sqlite3IsIdChar(z[j-1]) && sqlite3IsIdChar(zSql[i]) ){ - z[j++] = ' '; - } - if( tokenType==TK_ID ){ - if( zSql[i]=='"' - && sqlite3VdbeUsesDoubleQuotedString(db,pVdbe,zSql+i,n) - ){ - z[j++] = '?'; - break; - } - if( nParen==nParenAtIN ) iStartIN = 0; - } - copyNormalizedToken(zSql, i, n, flags, z, &j); - break; - } - } - } - assert( j0 && z[j-1]==' ' ){ j--; } - if( j>0 && z[j-1]!=';' ){ z[j++] = ';'; } - z[j] = 0; - assert( jrc!=SQLITE_OK ); return nErr; } + + +#ifdef SQLITE_ENABLE_NORMALIZE +/* +** Insert a single space character into pStr if the current string +** ends with an identifier +*/ +static void addSpaceSeparator(sqlite3_str *pStr){ + if( pStr->nChar && sqlite3IsIdChar(pStr->zText[pStr->nChar-1]) ){ + sqlite3_str_append(pStr, " ", 1); + } +} + +/* +** Compute a normalization of the SQL given by zSql[0..nSql-1]. Return +** the normalization in space obtained from sqlite3DbMalloc(). Or return +** NULL if anything goes wrong or if zSql is NULL. +*/ +char *sqlite3Normalize( + Vdbe *pVdbe, /* VM being reprepared */ + const char *zSql, /* The original SQL string */ + int nSql /* Size of the input string in bytes */ +){ + sqlite3 *db; /* The database connection */ + int i; /* Next unread byte of zSql[] */ + int n; /* length of current token */ + int tokenType; /* type of current token */ + int prevType; /* Previous non-whitespace token */ + int nParen; /* Number of nested levels of parentheses */ + int iStartIN; /* Start of RHS of IN operator in z[] */ + int nParenAtIN; /* Value of nParent at start of RHS of IN operator */ + int j; /* Bytes of normalized SQL generated so far */ + sqlite3_str *pStr; /* The normalized SQL string under construction */ + + if( zSql==0 || nSql==0 ) return 0; + db = sqlite3VdbeDb(pVdbe); + tokenType = -1; + nParen = iStartIN = nParenAtIN = 0; + pStr = sqlite3_str_new(db); + for(i=0; iaccError==0; i+=n){ + if( tokenType!=TK_SPACE ){ + prevType = tokenType; + } + n = sqlite3GetToken((unsigned char*)zSql+i, &tokenType); + if( NEVER(n<=0) ) break; + switch( tokenType ){ + case TK_SPACE: { + break; + } + case TK_NULL: { + if( prevType==TK_IS || prevType==TK_NOT ){ + sqlite3_str_append(pStr, " NULL", 5); + break; + } + /* Fall through */ + } + case TK_STRING: + case TK_INTEGER: + case TK_FLOAT: + case TK_VARIABLE: + case TK_BLOB: { + sqlite3_str_append(pStr, "?", 1); + break; + } + case TK_LP: { + nParen++; + if( prevType==TK_IN ){ + iStartIN = pStr->nChar; + nParenAtIN = nParen; + } + sqlite3_str_append(pStr, "(", 1); + break; + } + case TK_RP: { + if( iStartIN>0 && nParen==nParenAtIN ){ + assert( pStr->nChar>=iStartIN ); + pStr->nChar = iStartIN+1; + sqlite3_str_append(pStr, "?,?,?", 5); + iStartIN = 0; + } + nParen--; + sqlite3_str_append(pStr, ")", 1); + break; + } + case TK_ID: { + iStartIN = 0; + j = pStr->nChar; + if( sqlite3Isquote(zSql[i]) ){ + char *zId = sqlite3DbStrNDup(db, zSql+i, n); + int nId; + int eType = 0; + if( zId==0 ) break; + sqlite3Dequote(zId); + if( zSql[i]=='"' && sqlite3VdbeUsesDoubleQuotedString(pVdbe, zId) ){ + sqlite3_str_append(pStr, "?", 1); + sqlite3DbFree(db, zId); + break; + } + nId = sqlite3Strlen30(zId); + if( sqlite3GetToken((u8*)zId, &eType)==nId && eType==TK_ID ){ + addSpaceSeparator(pStr); + sqlite3_str_append(pStr, zId, nId); + }else{ + sqlite3_str_appendf(pStr, "\"%w\"", zId); + } + sqlite3DbFree(db, zId); + }else{ + addSpaceSeparator(pStr); + sqlite3_str_append(pStr, zSql+i, n); + } + while( jnChar ){ + pStr->zText[j] = sqlite3Tolower(pStr->zText[j]); + j++; + } + break; + } + case TK_SELECT: { + iStartIN = 0; + /* fall through */ + } + default: { + if( sqlite3IsIdChar(zSql[i]) ) addSpaceSeparator(pStr); + j = pStr->nChar; + sqlite3_str_append(pStr, zSql+i, n); + while( jnChar ){ + pStr->zText[j] = sqlite3Toupper(pStr->zText[j]); + j++; + } + break; + } + } + } + if( tokenType!=TK_SEMI ) sqlite3_str_append(pStr, ";", 1); + return sqlite3_str_finish(pStr); +} +#endif /* SQLITE_ENABLE_NORMALIZE */ Index: src/vdbe.h ================================================================== --- src/vdbe.h +++ src/vdbe.h @@ -251,11 +251,11 @@ sqlite3 *sqlite3VdbeDb(Vdbe*); u8 sqlite3VdbePrepareFlags(Vdbe*); void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, u8); #ifdef SQLITE_ENABLE_NORMALIZE void sqlite3VdbeAddDblquoteStr(sqlite3*,Vdbe*,const char*); -int sqlite3VdbeUsesDoubleQuotedString(sqlite3*,Vdbe*,const char*,int); +int sqlite3VdbeUsesDoubleQuotedString(Vdbe*,const char*); #endif void sqlite3VdbeSwap(Vdbe*,Vdbe*); VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8); void sqlite3VdbeSetVarmask(Vdbe*, int); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -95,30 +95,20 @@ /* ** zId of length nId is a double-quoted identifier. Check to see if ** that identifier is really used as a string literal. */ int sqlite3VdbeUsesDoubleQuotedString( - sqlite3 *db, /* Used for transient malloc */ Vdbe *pVdbe, /* The prepared statement */ - const char *zId, /* The double-quoted identifier */ - int nId /* Bytes in zId, which is not zero-terminated */ + const char *zId /* The double-quoted identifier, already dequoted */ ){ - char *z; DblquoteStr *pStr; assert( zId!=0 ); - assert( zId[0]=='"' ); - assert( nId>=2 ); - assert( zId[nId-1]=='"' ); if( pVdbe->pDblStr==0 ) return 0; - z = sqlite3DbStrNDup(db, zId, nId); - if( z==0 ) return 0; - sqlite3Dequote(z); for(pStr=pVdbe->pDblStr; pStr; pStr=pStr->pNextStr){ - if( strcmp(z, pStr->z)==0 ) break; + if( strcmp(zId, pStr->z)==0 ) return 1; } - sqlite3DbFree(db, z); - return pStr!=0; + return 0; } #endif /* ** Swap all content between two VDBE structures. Index: test/normalize.test ================================================================== --- test/normalize.test +++ test/normalize.test @@ -205,21 +205,21 @@ {0 {SELECT a FROM t1 WHERE x IN(?,?,?)AND hex8(?);}} 430 {SELECT "a" FROM t1 WHERE "x" IN ("1","2",'3');} 0x2 - {0 {SELECT"a"FROM t1 WHERE"x"IN(?,?,?);}} + {0 {SELECT a FROM t1 WHERE x IN(?,?,?);}} 440 {SELECT 'a' FROM t1 WHERE 'x';} 0x2 {0 {SELECT?FROM t1 WHERE?;}} 450 {SELECT [a] FROM t1 WHERE [x];} 0x2 - {0 {SELECT"a"FROM t1 WHERE"x";}} + {0 {SELECT a FROM t1 WHERE x;}} 460 {SELECT * FROM t1 WHERE x IN (x);} 0x2 {0 {SELECT*FROM t1 WHERE x IN(x);}} @@ -230,31 +230,31 @@ {0 {SELECT*FROM t1 WHERE x IN(x,a);}} 480 {SELECT * FROM t1 WHERE x IN ([x],"a");} 0x2 - {0 {SELECT*FROM t1 WHERE x IN("x","a");}} + {0 {SELECT*FROM t1 WHERE x IN(x,a);}} 500 {SELECT * FROM t1 WHERE x IN ([x],"a",'b',sqlite_version());} 0x2 - {0 {SELECT*FROM t1 WHERE x IN("x","a",?,sqlite_version());}} + {0 {SELECT*FROM t1 WHERE x IN(x,a,?,sqlite_version());}} 520 {SELECT * FROM t1 WHERE x IN (SELECT x FROM t1);} 0x2 {0 {SELECT*FROM t1 WHERE x IN(SELECT x FROM t1);}} 540 {SELECT * FROM t1 WHERE x IN ((SELECT x FROM t1));} 0x2 - {0 {SELECT*FROM t1 WHERE x IN(?,?,?);}} + {0 {SELECT*FROM t1 WHERE x IN((SELECT x FROM t1));}} 550 {SELECT a, a+1, a||'b', a+"b" FROM t1;} 0x2 - {0 {SELECT a,a+?,a||?,a+"b"FROM t1;}} + {0 {SELECT a,a+?,a||?,a+b FROM t1;}} 570 {SELECT * FROM t1 WHERE x IN (1);} 0x2 {0 {SELECT*FROM t1 WHERE x IN(?,?,?);}} @@ -314,11 +314,11 @@ {0 {SELECT"col f","col f"FROM t1;}} 680 {SELECT a, "col f" FROM t1 LEFT OUTER JOIN t2 ON [t1].[col f] == [t2].[col y];} 0x2 - {0 {SELECT a,"col f"FROM t1 LEFT OUTER JOIN t2 ON"t1"."col f"=="t2"."col y";}} + {0 {SELECT a,"col f"FROM t1 LEFT OUTER JOIN t2 ON t1."col f"==t2."col y";}} 690 {SELECT * FROM ( WITH x AS ( SELECT * FROM t1 WHERE x IN ( 1)) SELECT 10);} 0x2 {0 {SELECT*FROM(WITH x AS(SELECT*FROM t1 WHERE x IN(?,?,?))SELECT?);}} @@ -344,11 +344,11 @@ {0 {SELECT x FROM t1 WHERE x=?;}} 760 {SELECT x FROM t1 WHERE x IN ([x] IS NOT NULL, NULL, 1, 'a', "b", x'00');} 0x2 - {0 {SELECT x FROM t1 WHERE x IN("x"IS NOT NULL,?,?,?,"b",?);}} + {0 {SELECT x FROM t1 WHERE x IN(x IS NOT NULL,?,?,?,b,?);}} } { do_test $tnum { set code [catch { set STMT [sqlite3_prepare_v3 $DB $sql -1 $flags TAIL] sqlite3_normalized_sql $STMT