Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -634,10 +634,16 @@ /* Delete any foreign keys attached to this table. */ sqlite3FkDelete(db, pTable); /* Delete the Table structure itself. */ +#ifdef SQLITE_ENABLE_NORMALIZE + if( pTable->pColHash ){ + sqlite3HashClear(pTable->pColHash); + sqlite3_free(pTable->pColHash); + } +#endif sqlite3DeleteColumnNames(db, pTable); sqlite3DbFree(db, pTable->zName); sqlite3DbFree(db, pTable->zColAff); sqlite3SelectDelete(db, pTable->pSelect); sqlite3ExprListDelete(db, pTable->pCheck); Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -708,10 +708,100 @@ sqlite3_mutex_leave(db->mutex); return rc; } #ifdef SQLITE_ENABLE_NORMALIZE +/* +** Checks if the specified token is a table, column, or function name, +** based on the databases associated with the statement being prepared. +** If the function fails, zero is returned and pRc is filled with the +** error code. +*/ +static int shouldTreatAsIdentifier( + sqlite3 *db, /* Database handle. */ + const char *zToken, /* Pointer to start of token to be checked */ + int nToken, /* Length of token to be checked */ + int *pRc /* Pointer to error code upon failure */ +){ + int bFound = 0; /* Non-zero if token is an identifier name. */ + int i, j; /* Database and column loop indexes. */ + Schema *pSchema; /* Schema for current database. */ + Hash *pHash; /* Hash table of tables for current database. */ + HashElem *e; /* Hash element for hash table iteration. */ + Table *pTab; /* Database table for columns being checked. */ + char *zId; /* Zero terminated name of the identifier */ + char zSpace[65]; /* Static space for the zero-terminated name */ + + if( nToken0 ){ + int hash = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zToken[0]], nToken); + if( sqlite3FunctionSearch(hash, zId) ){ + bFound = 1; + goto done1; + } + } + assert( db!=0 ); + sqlite3_mutex_enter(db->mutex); + sqlite3BtreeEnterAll(db); + for(i=0; inDb; i++){ + pHash = &db->aFunc; + if( sqlite3HashFind(pHash, zId) ){ + bFound = 1; + break; + } + pSchema = db->aDb[i].pSchema; + if( pSchema==0 ) continue; + pHash = &pSchema->tblHash; + if( sqlite3HashFind(pHash, zId) ){ + bFound = 1; + break; + } + for(e=sqliteHashFirst(pHash); e; e=sqliteHashNext(e)){ + pTab = sqliteHashData(e); + if( pTab==0 ) continue; + pHash = pTab->pColHash; + if( pHash==0 ){ + pTab->pColHash = pHash = sqlite3_malloc(sizeof(Hash)); + if( pHash ){ + sqlite3HashInit(pHash); + for(j=0; jnCol; j++){ + Column *pCol = &pTab->aCol[j]; + sqlite3HashInsert(pHash, pCol->zName, pCol); + } + }else{ + *pRc = SQLITE_NOMEM_BKPT; + bFound = 0; + goto done2; + } + } + if( pHash && sqlite3HashFind(pHash, zId) ){ + bFound = 1; + goto done2; + } + } + } +done2: + sqlite3BtreeLeaveAll(db); + sqlite3_mutex_leave(db->mutex); +done1: + if( zId!=zSpace ) sqlite3DbFree(db, zId); + return bFound; +} /* ** 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 @@ -780,11 +870,12 @@ ** 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 */ + int nSql, /* Size of the input string in bytes */ + u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */ ){ 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[] */ @@ -883,10 +974,20 @@ } if( tokenType==TK_ID ){ int i2 = i, n2 = n; if( nParen==nParenAtIN ) iStartIN = 0; if( flags&SQLITE_TOKEN_QUOTED ){ i2++; n2-=2; } + if( (prepFlags & SQLITE_PREPARE_CHKIDENTS)!=0 ){ + int rc = SQLITE_OK; + if( shouldTreatAsIdentifier(db, zSql+i2, n2, &rc)==0 ){ + if( rc!=SQLITE_OK ) goto normalizeError; + if( sqlite3_keyword_check(zSql+i2, n2)==0 ){ + z[j++] = '?'; + break; + } + } + } } copyNormalizedToken(zSql, i, n, flags, z, &j); break; } } Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -3639,10 +3639,11 @@ ** placeholders. ** */ #define SQLITE_PREPARE_PERSISTENT 0x01 #define SQLITE_PREPARE_NORMALIZE 0x02 +#define SQLITE_PREPARE_CHKIDENTS 0x04 /* ** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} ** METHOD: sqlite3 Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -1955,10 +1955,13 @@ ** by an instance of the following structure. */ struct Table { char *zName; /* Name of the table or view */ Column *aCol; /* Information about each column */ +#ifdef SQLITE_ENABLE_NORMALIZE + Hash *pColHash; /* All columns indexed by name */ +#endif Index *pIndex; /* List of SQL indexes on this table. */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ ExprList *pCheck; /* All CHECK constraints */ @@ -4414,11 +4417,11 @@ sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*); int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); void sqlite3ParserReset(Parse*); #ifdef SQLITE_ENABLE_NORMALIZE -char *sqlite3Normalize(Vdbe*, const char*, int); +char *sqlite3Normalize(Vdbe*, const char*, int, u8); #endif int sqlite3Reprepare(Vdbe*); void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); int sqlite3TempInMemory(const sqlite3*); Index: src/vdbeapi.c ================================================================== --- src/vdbeapi.c +++ src/vdbeapi.c @@ -1713,11 +1713,11 @@ const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe *)pStmt; if( p==0 ) return 0; if( p->zNormSql==0 && p->zSql!=0 ){ sqlite3_mutex_enter(p->db->mutex); - p->zNormSql = sqlite3Normalize(p, p->zSql, sqlite3Strlen30(p->zSql)); + p->zNormSql = sqlite3Normalize(p, p->zSql, sqlite3Strlen30(p->zSql), 0); sqlite3_mutex_leave(p->db->mutex); } return p->zNormSql; } #endif /* SQLITE_ENABLE_NORMALIZE */ Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -65,11 +65,11 @@ assert( p->zSql==0 ); p->zSql = sqlite3DbStrNDup(p->db, z, n); #ifdef SQLITE_ENABLE_NORMALIZE assert( p->zNormSql==0 ); if( p->zSql && (prepFlags & SQLITE_PREPARE_NORMALIZE)!=0 ){ - p->zNormSql = sqlite3Normalize(p, p->zSql, n); + p->zNormSql = sqlite3Normalize(p, p->zSql, n, prepFlags); assert( p->zNormSql!=0 || p->db->mallocFailed ); } #endif } Index: test/normalize.test ================================================================== --- test/normalize.test +++ test/normalize.test @@ -207,10 +207,15 @@ 430 {SELECT "a" FROM t1 WHERE "x" IN ("1","2",'3');} 0x2 {0 {SELECT"a"FROM t1 WHERE"x"IN("1","2",?);}} + 431 + {SELECT "a" FROM t1 WHERE "x" IN ("1","2",'3');} + 0x6 + {0 {SELECT"a"FROM t1 WHERE"x"IN(?,?,?);}} + 440 {SELECT 'a' FROM t1 WHERE 'x';} 0x2 {0 {SELECT?FROM t1 WHERE?;}}