1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-09 14:21:03 +03:00

Change the way the fuzzer (test_fuzzer.c) works so that it loads its configuration from a database table.

FossilOrigin-Name: 90b7b957f8933047fd2878048dfa3ec4891988b8
This commit is contained in:
dan
2012-02-20 20:03:48 +00:00
parent 75f8f75f1e
commit a8ab692fa2
4 changed files with 433 additions and 304 deletions

View File

@@ -1,5 +1,5 @@
C Fix\sa\scase\sin\stest_fuzzer.c\scausing\stransformations\sfrom\sthe\swrong\sruleset\sto\sbe\sapplied\sin\ssome\scases. C Change\sthe\sway\sthe\sfuzzer\s(test_fuzzer.c)\sworks\sso\sthat\sit\sloads\sits\sconfiguration\sfrom\sa\sdatabase\stable.
D 2012-02-20T19:36:09.428 D 2012-02-20T20:03:48.835
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 3f79a373e57c3b92dabf76f40b065e719d31ac34 F Makefile.in 3f79a373e57c3b92dabf76f40b065e719d31ac34
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -206,7 +206,7 @@ F src/test_config.c a036a69b550ebc477ab9ca2b37269201f888436e
F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094 F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_func.c 6232d722a4ddb193035aa13a03796bf57d6c12fd F src/test_func.c 6232d722a4ddb193035aa13a03796bf57d6c12fd
F src/test_fuzzer.c 5c34fdb55c4fa3090d076886139b1f633327a2c5 F src/test_fuzzer.c 010ee3d4122fd955d6f0db598f68d62f95d15fa9
F src/test_hexio.c c4773049603151704a6ab25ac5e936b5109caf5a F src/test_hexio.c c4773049603151704a6ab25ac5e936b5109caf5a
F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a
F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99 F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
@@ -504,7 +504,7 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167
F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5 F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
F test/fuzzer1.test d5638894ffd89cb01e1276e2a52707b0b1261fe2 F test/fuzzer1.test 50a480932b91df9d61dd089f338e448991ab771e
F test/hook.test 5f3749de6462a6b87b4209b74adf7df5ac2df639 F test/hook.test 5f3749de6462a6b87b4209b74adf7df5ac2df639
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
F test/in.test a7b8a0f43da81cd08645b7a710099ffe9ad1126b F test/in.test a7b8a0f43da81cd08645b7a710099ffe9ad1126b
@@ -989,7 +989,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
P 760e009adc6d0fffb8e6f64c7ec283938a417a77 P cb5f5ebc563b8d3e47bc30b6dbb374bb91efd3ef
R 2f925ccdeb58b2c1514de71ba4c4df79 R 15ed6cfb8d0ad7f6ea31fbc888768f7f
U dan U dan
Z 2c1d8a850f278c0cc6fb31de180d043b Z d7a1a4dc789683dc31afc336a51e56bf

View File

@@ -1 +1 @@
cb5f5ebc563b8d3e47bc30b6dbb374bb91efd3ef 90b7b957f8933047fd2878048dfa3ec4891988b8

View File

@@ -10,43 +10,56 @@
** **
************************************************************************* *************************************************************************
** **
** Code for demonstartion virtual table that generates variations ** Code for a demonstration virtual table that generates variations
** on an input word at increasing edit distances from the original. ** on an input word at increasing edit distances from the original.
** **
** A fuzzer virtual table is created like this: ** A fuzzer virtual table is created like this:
** **
** CREATE VIRTUAL TABLE temp.f USING fuzzer; ** CREATE VIRTUAL TABLE f USING fuzzer(<fuzzer-data-table>);
** **
** The name of the new virtual table in the example above is "f". ** When it is created, the new fuzzer table must be supplied with the
** Note that all fuzzer virtual tables must be TEMP tables. The ** name of a "fuzzer data table", which must reside in the same database
** "temp." prefix in front of the table name is required when the ** file as the new fuzzer table. The fuzzer data table contains the various
** table is being created. The "temp." prefix can be omitted when ** transformations and their costs that the fuzzer logic uses to generate
** using the table as long as the name is unambiguous. ** variations.
** **
** Before being used, the fuzzer needs to be programmed by giving it ** The fuzzer data table must contain exactly four columns (more precisely,
** character transformations and a cost associated with each transformation. ** the statement "SELECT * FROM <fuzzer_data_table>" must return records
** Examples: ** that consist of four columns). It does not matter what the columns are
** named.
** **
** INSERT INTO f(cFrom,cTo,Cost) VALUES('','a',100); ** Each row in the fuzzer table represents a single character transformation.
** The left most column of the row (column 0) contains an integer value -
** the identifier of the ruleset to which the transformation rule belongs
** (see "MULTIPLE RULE SETS" below). The second column of the row (column 0)
** contains the input character or characters. The third column contains the
** output character or characters. And the fourth column contains the integer
** cost of making the transformation. For example:
** **
** The above statement says that the cost of inserting a letter 'a' is ** CREATE TABLE f_data(ruleset, cFrom, cTo, Cost);
** 100. (All costs are integers. We recommend that costs be scaled so ** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, '', 'a', 100);
** that the average cost is around 100.) ** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'b', '', 87);
** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'o', 'oe', 38);
** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'oe', 'o', 40);
** **
** INSERT INTO f(cFrom,cTo,Cost) VALUES('b','',87); ** The first row inserted into the fuzzer data table by the SQL script
** ** above indicates that the cost of inserting a letter 'a' is 100. (All
** The above statement says that the cost of deleting a single letter ** costs are integers. We recommend that costs be scaled so that the
** 'b' is 87. ** average cost is around 100.) The second INSERT statement creates a rule
** ** that the cost of that the cost of deleting a single letter 'b' is 87.
** INSERT INTO f(cFrom,cTo,Cost) VALUES('o','oe',38); ** The third and fourth INSERT statements mean that the cost of transforming
** INSERT INTO f(cFrom,cTo,Cost) VALUES('oe','o',40); ** a single letter "o" into the two-letter sequence "oe" is 38 and that the
**
** This third example says that the cost of transforming the single
** letter "o" into the two-letter sequence "oe" is 38 and that the
** cost of transforming "oe" back into "o" is 40. ** cost of transforming "oe" back into "o" is 40.
** **
** After all the transformation costs have been set, the fuzzer table ** The contents of the fuzzer data table are loaded into main memory when
** can be queried as follows: ** a fuzzer table is first created, and may be internally reloaded by the
** system at any subsequent time. Therefore, the fuzzer data table should be
** populated before the fuzzer table is created and not modified thereafter.
** If you do need to modify the contents of the fuzzer data table, it is
** recommended that the associated fuzzer table be dropped, the fuzzer data
** table edited, and the fuzzer table recreated within a single transaction.
**
** Once it has been created, the fuzzer table can be queried as follows:
** **
** SELECT word, distance FROM f ** SELECT word, distance FROM f
** WHERE word MATCH 'abcdefg' ** WHERE word MATCH 'abcdefg'
@@ -96,19 +109,16 @@
** **
** MULTIPLE RULE SETS ** MULTIPLE RULE SETS
** **
** An enhancement as of 2012-02-14 allows multiple rule sets to coexist in ** Normally, the "ruleset" value associated with all character transformations
** the same fuzzer. This allows, for example, the fuzzer to operate in ** in the fuzzer data table is zero. However, if required, the fuzzer table
** allows multiple rulesets to be defined. Each query uses only a single
** ruleset. This allows, for example, a single fuzzer table to support
** multiple languages. ** multiple languages.
** **
** A new column "ruleset" is added to the table. This column must have a ** By default, only the rules from ruleset 0 are used. To specify an
** value between 0 and 49. The default value for the ruleset is 0. But ** alternative ruleset, a "ruleset = ?" expression must be added to the
** alternative values can be specified. For example: ** WHERE clause of a SELECT, where ? is the identifier of the desired
** ** ruleset. For example:
** INSERT INTO f(ruleset,cFrom,cTo,Cost) VALUES(1,'qu','k',100);
**
** Only one ruleset will be used at a time. When running a MATCH query,
** specify the desired ruleset using a "ruleset=N" term in the WHERE clause.
** For example:
** **
** SELECT vocabulary.w FROM f, vocabulary ** SELECT vocabulary.w FROM f, vocabulary
** WHERE f.word MATCH $word ** WHERE f.word MATCH $word
@@ -117,7 +127,8 @@
** AND f.ruleset=1 -- Specify the ruleset to use here ** AND f.ruleset=1 -- Specify the ruleset to use here
** LIMIT 20 ** LIMIT 20
** **
** If no ruleset is specified in the WHERE clause, ruleset 0 is used. ** If no "ruleset = ?" constraint is specified in the WHERE clause, ruleset
** 0 is used.
*/ */
#include "sqlite3.h" #include "sqlite3.h"
#include <stdlib.h> #include <stdlib.h>
@@ -199,7 +210,6 @@ struct fuzzer_vtab {
sqlite3_vtab base; /* Base class - must be first */ sqlite3_vtab base; /* Base class - must be first */
char *zClassName; /* Name of this class. Default: "fuzzer" */ char *zClassName; /* Name of this class. Default: "fuzzer" */
fuzzer_rule *pRule; /* All active rules in this fuzzer */ fuzzer_rule *pRule; /* All active rules in this fuzzer */
fuzzer_rule *pNewRule; /* New rules to add when last cursor expires */
int nCursor; /* Number of active cursors */ int nCursor; /* Number of active cursors */
}; };
@@ -224,51 +234,6 @@ struct fuzzer_cursor {
fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */ fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */
}; };
/* Methods for the fuzzer module */
static int fuzzerConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
fuzzer_vtab *pNew;
int n;
if( strcmp(argv[1],"temp")!=0 ){
*pzErr = sqlite3_mprintf("%s virtual tables must be TEMP", argv[0]);
return SQLITE_ERROR;
}
n = strlen(argv[0]) + 1;
pNew = sqlite3_malloc( sizeof(*pNew) + n );
if( pNew==0 ) return SQLITE_NOMEM;
pNew->zClassName = (char*)&pNew[1];
memcpy(pNew->zClassName, argv[0], n);
sqlite3_declare_vtab(db,
"CREATE TABLE x(word,distance,ruleset,cFrom,cTo,cost)");
memset(pNew, 0, sizeof(*pNew));
*ppVtab = &pNew->base;
return SQLITE_OK;
}
/* Note that for this virtual table, the xCreate and xConnect
** methods are identical. */
static int fuzzerDisconnect(sqlite3_vtab *pVtab){
fuzzer_vtab *p = (fuzzer_vtab*)pVtab;
assert( p->nCursor==0 );
do{
while( p->pRule ){
fuzzer_rule *pRule = p->pRule;
p->pRule = pRule->pNext;
sqlite3_free(pRule);
}
p->pRule = p->pNewRule;
p->pNewRule = 0;
}while( p->pRule );
sqlite3_free(p);
return SQLITE_OK;
}
/* The xDisconnect and xDestroy methods are also the same */
/* /*
** The two input rule lists are both sorted in order of increasing ** The two input rule lists are both sorted in order of increasing
** cost. Merge them together into a single list, sorted by cost, and ** cost. Merge them together into a single list, sorted by cost, and
@@ -298,6 +263,218 @@ static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){
return head.pNext; return head.pNext;
} }
/*
** Statement pStmt currently points to a row in the fuzzer data table. This
** function allocates and populates a fuzzer_rule structure according to
** the content of the row.
**
** If successful, *ppRule is set to point to the new object and SQLITE_OK
** is returned. Otherwise, *ppRule is zeroed, *pzErr may be set to point
** to an error message and an SQLite error code returned.
*/
static int fuzzerLoadOneRule(
fuzzer_vtab *p, /* Fuzzer virtual table handle */
sqlite3_stmt *pStmt, /* Base rule on statements current row */
fuzzer_rule **ppRule, /* OUT: New rule object */
char **pzErr /* OUT: Error message */
){
int iRuleset = sqlite3_column_int(pStmt, 0);
const char *zFrom = (const char *)sqlite3_column_text(pStmt, 1);
const char *zTo = (const char *)sqlite3_column_text(pStmt, 2);
int nCost = sqlite3_column_int(pStmt, 3);
int rc = SQLITE_OK; /* Return code */
int nFrom; /* Size of string zFrom, in bytes */
int nTo; /* Size of string zTo, in bytes */
fuzzer_rule *pRule = 0; /* New rule object to return */
if( zFrom==0 ) zFrom = "";
if( zTo==0 ) zTo = "";
nFrom = strlen(zFrom);
nTo = strlen(zTo);
/* Silently ignore null transformations */
if( strcmp(zFrom, zTo)==0 ){
*ppRule = 0;
return SQLITE_OK;
}
if( nCost<=0 || nCost>FUZZER_MX_COST ){
*pzErr = sqlite3_mprintf("cost must be between 1 and %d", FUZZER_MX_COST);
rc = SQLITE_ERROR;
}else
if( nFrom>FUZZER_MX_LENGTH || nTo>FUZZER_MX_LENGTH ){
*pzErr = sqlite3_mprintf("maximum string length is %d", FUZZER_MX_LENGTH);
rc = SQLITE_ERROR;
}else
if( iRuleset<0 || iRuleset>FUZZER_MX_RULEID ){
*pzErr = sqlite3_mprintf(
"ruleset must be between 0 and %d", FUZZER_MX_RULEID);
rc = SQLITE_ERROR;
}else{
pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
if( pRule==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pRule, 0, sizeof(*pRule));
pRule->zFrom = &pRule->zTo[nTo+1];
pRule->nFrom = nFrom;
memcpy(pRule->zFrom, zFrom, nFrom+1);
memcpy(pRule->zTo, zTo, nTo+1);
pRule->nTo = nTo;
pRule->rCost = nCost;
pRule->iRuleset = iRuleset;
}
}
*ppRule = pRule;
return rc;
}
/*
** Load the content of the fuzzer data table into memory.
*/
static int fuzzerLoadRules(
sqlite3 *db, /* Database handle */
fuzzer_vtab *p, /* Virtual fuzzer table to configure */
const char *zDb, /* Database containing rules data */
const char *zData, /* Table containing rules data */
char **pzErr /* OUT: Error message */
){
int rc = SQLITE_OK; /* Return code */
char *zSql; /* SELECT used to read from rules table */
fuzzer_rule *pHead = 0;
zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zData);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
int rc2; /* finalize() return code */
sqlite3_stmt *pStmt = 0;
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("%s: %s", p->zClassName, sqlite3_errmsg(db));
}else if( sqlite3_column_count(pStmt)!=4 ){
*pzErr = sqlite3_mprintf("%s: %s has %d columns, expected 4",
p->zClassName, zData, sqlite3_column_count(pStmt)
);
rc = SQLITE_ERROR;
}else{
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
fuzzer_rule *pRule = 0;
rc = fuzzerLoadOneRule(p, pStmt, &pRule, pzErr);
if( pRule ){
pRule->pNext = pHead;
pHead = pRule;
}
}
}
rc2 = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ) rc = rc2;
}
sqlite3_free(zSql);
/* All rules are now in a singly linked list starting at pHead. This
** block sorts them by cost and then sets fuzzer_vtab.pRule to point to
** point to the head of the sorted list.
*/
if( rc==SQLITE_OK ){
unsigned int i;
fuzzer_rule *pX;
fuzzer_rule *a[15];
for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
while( (pX = pHead)!=0 ){
pHead = pX->pNext;
pX->pNext = 0;
for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
pX = fuzzerMergeRules(a[i], pX);
a[i] = 0;
}
a[i] = fuzzerMergeRules(a[i], pX);
}
for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
pX = fuzzerMergeRules(a[i], pX);
}
p->pRule = fuzzerMergeRules(p->pRule, pX);
}else{
/* An error has occurred. Setting p->pRule to point to the head of the
** allocated list ensures that the list will be cleaned up in this case.
*/
assert( p->pRule==0 );
p->pRule = pHead;
}
return rc;
}
/*
** xConnect/xCreate method for the fuzzer module. Arguments are:
**
** argv[0] -> module name ("fuzzer")
** argv[1] -> database name
** argv[2] -> table name
** argv[3] -> fuzzer rule table name
*/
static int fuzzerConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
int rc = SQLITE_OK; /* Return code */
fuzzer_vtab *pNew = 0; /* New virtual table */
const char *zModule = argv[0];
const char *zDb = argv[1];
if( argc!=4 ){
*pzErr = sqlite3_mprintf(
"%s: wrong number of CREATE VIRTUAL TABLE arguments", zModule
);
rc = SQLITE_ERROR;
}else{
int nModule; /* Length of zModule, in bytes */
nModule = strlen(zModule);
pNew = sqlite3_malloc( sizeof(*pNew) + nModule + 1);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pNew, 0, sizeof(*pNew));
pNew->zClassName = (char*)&pNew[1];
memcpy(pNew->zClassName, zModule, nModule+1);
rc = fuzzerLoadRules(db, pNew, zDb, argv[3], pzErr);
if( rc==SQLITE_OK ){
sqlite3_declare_vtab(db, "CREATE TABLE x(word, distance,ruleset)");
}else{
sqlite3_free(pNew);
pNew = 0;
}
}
}
*ppVtab = (sqlite3_vtab *)pNew;
return rc;
}
/* Note that for this virtual table, the xCreate and xConnect
** methods are identical. */
static int fuzzerDisconnect(sqlite3_vtab *pVtab){
fuzzer_vtab *p = (fuzzer_vtab*)pVtab;
assert( p->nCursor==0 );
while( p->pRule ){
fuzzer_rule *pRule = p->pRule;
p->pRule = pRule->pNext;
sqlite3_free(pRule);
}
sqlite3_free(p);
return SQLITE_OK;
}
/* The xDisconnect and xDestroy methods are also the same */
/* /*
** Open a new fuzzer cursor. ** Open a new fuzzer cursor.
@@ -310,25 +487,6 @@ static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
memset(pCur, 0, sizeof(*pCur)); memset(pCur, 0, sizeof(*pCur));
pCur->pVtab = p; pCur->pVtab = p;
*ppCursor = &pCur->base; *ppCursor = &pCur->base;
if( p->nCursor==0 && p->pNewRule ){
unsigned int i;
fuzzer_rule *pX;
fuzzer_rule *a[15];
for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
while( (pX = p->pNewRule)!=0 ){
p->pNewRule = pX->pNext;
pX->pNext = 0;
for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
pX = fuzzerMergeRules(a[i], pX);
a[i] = 0;
}
a[i] = fuzzerMergeRules(a[i], pX);
}
for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
pX = fuzzerMergeRules(a[i], pX);
}
p->pRule = fuzzerMergeRules(p->pRule, pX);
}
p->nCursor++; p->nCursor++;
return SQLITE_OK; return SQLITE_OK;
} }
@@ -479,6 +637,7 @@ static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){
*/ */
static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){ static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
const fuzzer_rule *pRule; const fuzzer_rule *pRule;
const int iSet = pCur->iRuleset;
while( (pRule = pStem->pRule)!=0 ){ while( (pRule = pStem->pRule)!=0 ){
assert( pRule==&pCur->nullRule || pRule->iRuleset==pCur->iRuleset ); assert( pRule==&pCur->nullRule || pRule->iRuleset==pCur->iRuleset );
while( pStem->n < pStem->nBasis - pRule->nFrom ){ while( pStem->n < pStem->nBasis - pRule->nFrom ){
@@ -498,7 +657,7 @@ static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
pStem->n = -1; pStem->n = -1;
do{ do{
pRule = pRule->pNext; pRule = pRule->pNext;
}while( pRule && pRule->iRuleset!=pCur->iRuleset ); }while( pRule && pRule->iRuleset!=iSet );
pStem->pRule = pRule; pStem->pRule = pRule;
if( pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0; if( pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
} }
@@ -864,86 +1023,6 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
return SQLITE_OK; return SQLITE_OK;
} }
/*
** Disallow all attempts to DELETE or UPDATE. Only INSERTs are allowed.
**
** On an insert, the cFrom, cTo, and cost columns are used to construct
** a new rule. All other columns are ignored. The rule is ignored
** if cFrom and cTo are identical. A NULL value for cFrom or cTo is
** interpreted as an empty string. The cost must be positive.
*/
static int fuzzerUpdate(
sqlite3_vtab *pVTab,
int argc,
sqlite3_value **argv,
sqlite_int64 *pRowid
){
fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
fuzzer_rule *pRule;
const char *zFrom;
int nFrom;
const char *zTo;
int nTo;
fuzzer_cost rCost;
int rulesetId;
if( argc!=8 ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf("cannot delete from a %s virtual table",
p->zClassName);
return SQLITE_CONSTRAINT;
}
if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf("cannot update a %s virtual table",
p->zClassName);
return SQLITE_CONSTRAINT;
}
zFrom = (char*)sqlite3_value_text(argv[5]);
if( zFrom==0 ) zFrom = "";
zTo = (char*)sqlite3_value_text(argv[6]);
if( zTo==0 ) zTo = "";
if( strcmp(zFrom,zTo)==0 ){
/* Silently ignore null transformations */
return SQLITE_OK;
}
rCost = sqlite3_value_int(argv[7]);
if( rCost<=0 || rCost>FUZZER_MX_COST ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf("cost must be between 1 and %d",
FUZZER_MX_COST);
return SQLITE_CONSTRAINT;
}
nFrom = strlen(zFrom);
nTo = strlen(zTo);
if( nFrom>FUZZER_MX_LENGTH || nTo>FUZZER_MX_LENGTH ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf("maximum string length is %d",
FUZZER_MX_LENGTH);
return SQLITE_CONSTRAINT;
}
rulesetId = sqlite3_value_int(argv[4]);
if( rulesetId<0 || rulesetId>FUZZER_MX_RULEID ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf("rulesetid must be between 0 and %d",
FUZZER_MX_RULEID);
return SQLITE_CONSTRAINT;
}
pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
if( pRule==0 ){
return SQLITE_NOMEM;
}
pRule->zFrom = &pRule->zTo[nTo+1];
pRule->nFrom = nFrom;
memcpy(pRule->zFrom, zFrom, nFrom+1);
memcpy(pRule->zTo, zTo, nTo+1);
pRule->nTo = nTo;
pRule->rCost = rCost;
pRule->pNext = p->pNewRule;
pRule->iRuleset = rulesetId;
p->pNewRule = pRule;
return SQLITE_OK;
}
/* /*
** A virtual table module that provides read-only access to a ** A virtual table module that provides read-only access to a
** Tcl global variable namespace. ** Tcl global variable namespace.
@@ -962,7 +1041,7 @@ static sqlite3_module fuzzerModule = {
fuzzerEof, /* xEof - check for end of scan */ fuzzerEof, /* xEof - check for end of scan */
fuzzerColumn, /* xColumn - read data */ fuzzerColumn, /* xColumn - read data */
fuzzerRowid, /* xRowid - read data */ fuzzerRowid, /* xRowid - read data */
fuzzerUpdate, /* xUpdate - INSERT */ 0, /* xUpdate */
0, /* xBegin */ 0, /* xBegin */
0, /* xSync */ 0, /* xSync */
0, /* xCommit */ 0, /* xCommit */

View File

@@ -23,48 +23,96 @@ ifcapable !vtab {
} }
register_fuzzer_module db register_fuzzer_module db
do_test fuzzer1-1.0 {
catchsql {CREATE VIRTUAL TABLE fault1 USING fuzzer;}
} {1 {fuzzer virtual tables must be TEMP}}
do_test fuzzer1-1.1 { # Check configuration errors.
db eval {CREATE VIRTUAL TABLE temp.f1 USING fuzzer;} #
} {} do_catchsql_test fuzzer1-1.1 {
do_test fuzzer1-1.2 { CREATE VIRTUAL TABLE f USING fuzzer;
db eval { } {1 {fuzzer: wrong number of CREATE VIRTUAL TABLE arguments}}
INSERT INTO f1(cfrom, cto, cost) VALUES('e','a',1);
INSERT INTO f1(cfrom, cto, cost) VALUES('a','e',10); do_catchsql_test fuzzer1-1.2 {
INSERT INTO f1(cfrom, cto, cost) VALUES('e','o',100); CREATE VIRTUAL TABLE f USING fuzzer(one, two);
} } {1 {fuzzer: wrong number of CREATE VIRTUAL TABLE arguments}}
do_catchsql_test fuzzer1-1.3 {
CREATE VIRTUAL TABLE f USING fuzzer(nosuchtable);
} {1 {fuzzer: no such table: main.nosuchtable}}
do_catchsql_test fuzzer1-1.4 {
CREATE TEMP TABLE nosuchtable(a, b, c, d);
CREATE VIRTUAL TABLE f USING fuzzer(nosuchtable);
} {1 {fuzzer: no such table: main.nosuchtable}}
do_catchsql_test fuzzer1-1.5 {
DROP TABLE temp.nosuchtable;
CREATE TABLE nosuchtable(a, b, c, d);
CREATE VIRTUAL TABLE temp.f USING fuzzer(nosuchtable);
} {1 {fuzzer: no such table: temp.nosuchtable}}
do_catchsql_test fuzzer1-1.6 {
DROP TABLE IF EXISTS f_rules;
CREATE TABLE f_rules(a, b, c);
CREATE VIRTUAL TABLE f USING fuzzer(f_rules);
} {1 {fuzzer: f_rules has 3 columns, expected 4}}
do_catchsql_test fuzzer1-1.7 {
DROP TABLE IF EXISTS f_rules;
CREATE TABLE f_rules(a, b, c, d, e);
CREATE VIRTUAL TABLE f USING fuzzer(f_rules);
} {1 {fuzzer: f_rules has 5 columns, expected 4}}
do_execsql_test fuzzer1-2.1 {
CREATE TABLE f1_rules(ruleset DEFAULT 0, cfrom, cto, cost);
INSERT INTO f1_rules(cfrom, cto, cost) VALUES('e','a',1);
INSERT INTO f1_rules(cfrom, cto, cost) VALUES('a','e',10);
INSERT INTO f1_rules(cfrom, cto, cost) VALUES('e','o',100);
CREATE VIRTUAL TABLE f1 USING fuzzer(f1_rules);
} {} } {}
do_test fuzzer1-1.3 { do_execsql_test fuzzer1-2.1 {
db eval {
SELECT word, distance FROM f1 WHERE word MATCH 'abcde' SELECT word, distance FROM f1 WHERE word MATCH 'abcde'
} {
abcde 0 abcda 1 ebcde 10
ebcda 11 abcdo 100 ebcdo 110
obcde 110 obcda 111 obcdo 210
} }
} {abcde 0 abcda 1 ebcde 10 ebcda 11 abcdo 100 ebcdo 110 obcde 110 obcda 111 obcdo 210}
do_test fuzzer1-1.4 { do_execsql_test fuzzer1-2.4 {
db eval { INSERT INTO f1_rules(ruleset, cfrom, cto, cost) VALUES(1,'b','x',1);
INSERT INTO f1(ruleset, cfrom, cto, cost) VALUES(1,'b','x',1); INSERT INTO f1_rules(ruleset, cfrom, cto, cost) VALUES(1,'d','y',10);
INSERT INTO f1(ruleset, cfrom, cto, cost) VALUES(1,'d','y',10); INSERT INTO f1_rules(ruleset, cfrom, cto, cost) VALUES(1,'y','z',100);
INSERT INTO f1(ruleset, cfrom, cto, cost) VALUES(1,'y','z',100);
} DROP TABLE f1;
CREATE VIRTUAL TABLE f1 USING fuzzer(f1_rules);
} {} } {}
do_test fuzzer1-1.5 {
db eval { do_execsql_test fuzzer1-2.5 {
SELECT word, distance FROM f1 WHERE word MATCH 'abcde' SELECT word, distance FROM f1 WHERE word MATCH 'abcde'
} {
abcde 0 abcda 1 ebcde 10
ebcda 11 abcdo 100 ebcdo 110
obcde 110 obcda 111 obcdo 210
} }
} {abcde 0 abcda 1 ebcde 10 ebcda 11 abcdo 100 ebcdo 110 obcde 110 obcda 111 obcdo 210}
do_test fuzzer1-1.6 { do_execsql_test fuzzer1-2.6 {
db eval {
SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND ruleset=0 SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND ruleset=0
} {
abcde 0 abcda 1 ebcde 10
ebcda 11 abcdo 100 ebcdo 110
obcde 110 obcda 111 obcdo 210
} }
} {abcde 0 abcda 1 ebcde 10 ebcda 11 abcdo 100 ebcdo 110 obcde 110 obcda 111 obcdo 210}
do_test fuzzer1-1.7 { do_execsql_test fuzzer1-2.7 {
db eval {
SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND ruleset=1 SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND ruleset=1
} {
abcde 0 axcde 1 abcye 10
axcye 11 abcze 110 axcze 111
} }
} {abcde 0 axcde 1 abcye 10 axcye 11 abcze 110 axcze 111}
do_test fuzzer1-1.8 { do_test fuzzer1-1.8 {
db eval { db eval {
SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND distance<100 SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND distance<100
@@ -102,77 +150,78 @@ do_test fuzzer1-1.13 {
do_test fuzzer1-2.0 { do_test fuzzer1-2.0 {
execsql { execsql {
CREATE VIRTUAL TABLE temp.f2 USING fuzzer;
-- costs based on English letter frequencies -- costs based on English letter frequencies
INSERT INTO f2(cFrom,cTo,cost) VALUES('a','e',24); CREATE TEMP TABLE f2_rules(ruleset, cFrom, cTo, cost);
INSERT INTO f2(cFrom,cTo,cost) VALUES('a','o',47); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('a','e',24);
INSERT INTO f2(cFrom,cTo,cost) VALUES('a','u',50); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('a','o',47);
INSERT INTO f2(cFrom,cTo,cost) VALUES('e','a',23); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('a','u',50);
INSERT INTO f2(cFrom,cTo,cost) VALUES('e','i',33); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('e','a',23);
INSERT INTO f2(cFrom,cTo,cost) VALUES('e','o',37); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('e','i',33);
INSERT INTO f2(cFrom,cTo,cost) VALUES('i','e',33); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('e','o',37);
INSERT INTO f2(cFrom,cTo,cost) VALUES('i','y',33); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('i','e',33);
INSERT INTO f2(cFrom,cTo,cost) VALUES('o','a',41); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('i','y',33);
INSERT INTO f2(cFrom,cTo,cost) VALUES('o','e',46); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('o','a',41);
INSERT INTO f2(cFrom,cTo,cost) VALUES('o','u',57); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('o','e',46);
INSERT INTO f2(cFrom,cTo,cost) VALUES('u','o',58); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('o','u',57);
INSERT INTO f2(cFrom,cTo,cost) VALUES('y','i',33); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('u','o',58);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('y','i',33);
INSERT INTO f2(cFrom,cTo,cost) VALUES('t','th',70); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('t','th',70);
INSERT INTO f2(cFrom,cTo,cost) VALUES('th','t',66); INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('th','t',66);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('a','',84);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','b',106);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('b','',106);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','c',94);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('c','',94);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','d',89);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('d','',89);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','e',83);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('e','',83);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','f',97);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('f','',97);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','g',99);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('g','',99);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','h',86);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('h','',86);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','i',85);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('i','',85);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','j',120);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('j','',120);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','k',120);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('k','',120);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','l',89);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('l','',89);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','m',96);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('m','',96);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','n',85);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('n','',85);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','o',85);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('o','',85);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','p',100);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('p','',100);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','q',120);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('q','',120);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','r',86);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('r','',86);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','s',86);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('s','',86);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','t',84);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('t','',84);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','u',94);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('u','',94);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','v',120);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('v','',120);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','w',96);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('w','',96);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','x',120);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('x','',120);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','y',100);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('y','',100);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','z',120);
INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('z','',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('a','',84); CREATE VIRTUAL TABLE temp.f2 USING fuzzer(f2_rules);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','b',106);
INSERT INTO f2(cFrom,cTo,cost) VALUES('b','',106);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','c',94);
INSERT INTO f2(cFrom,cTo,cost) VALUES('c','',94);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','d',89);
INSERT INTO f2(cFrom,cTo,cost) VALUES('d','',89);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','e',83);
INSERT INTO f2(cFrom,cTo,cost) VALUES('e','',83);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','f',97);
INSERT INTO f2(cFrom,cTo,cost) VALUES('f','',97);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','g',99);
INSERT INTO f2(cFrom,cTo,cost) VALUES('g','',99);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','h',86);
INSERT INTO f2(cFrom,cTo,cost) VALUES('h','',86);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','i',85);
INSERT INTO f2(cFrom,cTo,cost) VALUES('i','',85);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','j',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('j','',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','k',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('k','',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','l',89);
INSERT INTO f2(cFrom,cTo,cost) VALUES('l','',89);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','m',96);
INSERT INTO f2(cFrom,cTo,cost) VALUES('m','',96);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','n',85);
INSERT INTO f2(cFrom,cTo,cost) VALUES('n','',85);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','o',85);
INSERT INTO f2(cFrom,cTo,cost) VALUES('o','',85);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','p',100);
INSERT INTO f2(cFrom,cTo,cost) VALUES('p','',100);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','q',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('q','',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','r',86);
INSERT INTO f2(cFrom,cTo,cost) VALUES('r','',86);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','s',86);
INSERT INTO f2(cFrom,cTo,cost) VALUES('s','',86);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','t',84);
INSERT INTO f2(cFrom,cTo,cost) VALUES('t','',84);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','u',94);
INSERT INTO f2(cFrom,cTo,cost) VALUES('u','',94);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','v',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('v','',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','w',96);
INSERT INTO f2(cFrom,cTo,cost) VALUES('w','',96);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','x',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('x','',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','y',100);
INSERT INTO f2(cFrom,cTo,cost) VALUES('y','',100);
INSERT INTO f2(cFrom,cTo,cost) VALUES('','z',120);
INSERT INTO f2(cFrom,cTo,cost) VALUES('z','',120);
-- Street names for the 28269 ZIPCODE. -- Street names for the 28269 ZIPCODE.
-- --
@@ -1435,12 +1484,13 @@ do_test fuzzer1-2.3 {
} }
} {{tyler finley} trailer taymouth steelewood tallia tallu talwyn thelema} } {{tyler finley} trailer taymouth steelewood tallia tallu talwyn thelema}
forcedelete test.db2
do_execsql_test fuzzer1-3.1 { do_execsql_test fuzzer1-4.1 {
CREATE VIRTUAL TABLE temp.f3 USING fuzzer; ATTACH 'test.db2' AS aux;
CREATE TABLE f3(ruleset, cfrom, cto, cost); CREATE TABLE aux.f3_rules(ruleset, cfrom, cto, cost);
INSERT INTO f3(ruleset, cfrom, cto, cost) VALUES(0, 'x','y', 10); INSERT INTO f3_rules(ruleset, cfrom, cto, cost) VALUES(0, 'x','y', 10);
INSERT INTO f3(ruleset, cfrom, cto, cost) VALUES(1, 'a','b', 10); INSERT INTO f3_rules(ruleset, cfrom, cto, cost) VALUES(1, 'a','b', 10);
CREATE VIRTUAL TABLE aux.f3 USING fuzzer(f3_rules);
SELECT word FROM f3 WHERE word MATCH 'ax' SELECT word FROM f3 WHERE word MATCH 'ax'
} {ax ay} } {ax ay}