using System; using System.Diagnostics; using System.Text; using i64 = System.Int64; using u8 = System.Byte; namespace Community.CsharpSqlite { #if TCLSH using tcl.lang; using sqlite3_int64 = System.Int64; using sqlite_int64 = System.Int64; using sqlite3_stmt = Sqlite3.Vdbe; using sqlite3_value = Sqlite3.Mem; using Tcl_Interp = tcl.lang.Interp; using Tcl_Obj = tcl.lang.TclObject; using ClientData = System.Object; using fuzzer_cost = System.Int32; public partial class Sqlite3 { /* ** 2011 March 24 ** ** 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. ** ************************************************************************* ** ** Code for demonstartion virtual table that generates variations ** on an input word at increasing edit distances from the original. ** ** A fuzzer virtual table is created like this: ** ** CREATE VIRTUAL TABLE temp.f USING fuzzer; ** ** The name of the new virtual table in the example above is "f". ** Note that all fuzzer virtual tables must be TEMP tables. The ** "temp." prefix in front of the table name is required when the ** table is being created. The "temp." prefix can be omitted when ** using the table as long as the name is unambiguous. ** ** Before being used, the fuzzer needs to be programmed by giving it ** character transformations and a cost associated with each transformation. ** Examples: ** ** INSERT INTO f(cFrom,cTo,Cost) VALUES('','a',100); ** ** The above statement says that the cost of inserting a letter 'a' is ** 100. (All costs are integers. We recommend that costs be scaled so ** that the average cost is around 100.) ** ** INSERT INTO f(cFrom,cTo,Cost) VALUES('b','',87); ** ** The above statement says that the cost of deleting a single letter ** 'b' is 87. ** ** INSERT INTO f(cFrom,cTo,Cost) VALUES('o','oe',38); ** INSERT INTO f(cFrom,cTo,Cost) VALUES('oe','o',40); ** ** 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. ** ** After all the transformation costs have been set, the fuzzer table ** can be queried as follows: ** ** SELECT word, distance FROM f ** WHERE word MATCH 'abcdefg' ** AND distance<200; ** ** This first query outputs the string "abcdefg" and all strings that ** can be derived from that string by appling the specified transformations. ** The strings are output together with their total transformation cost ** (called "distance") and appear in order of increasing cost. No string ** is output more than once. If there are multiple ways to transform the ** target string into the output string then the lowest cost transform is ** the one that is returned. In the example, the search is limited to ** strings with a total distance of less than 200. ** ** It is important to put some kind of a limit on the fuzzer output. This ** can be either in the form of a LIMIT clause at the end of the query, ** or better, a "distance //#include //#include //#include #if !SQLITE_OMIT_VIRTUALTABLE /* ** Forward declaration of objects used by this implementation */ //typedef struct fuzzer_vtab fuzzer_vtab; //typedef struct fuzzer_cursor fuzzer_cursor; //typedef struct fuzzer_rule fuzzer_rule; //typedef struct fuzzer_seen fuzzer_seen; //typedef struct fuzzer_stem fuzzer_stem; /* ** Type of the "cost" of an edit operation. Might be changed to ** "float" or "double" or "sqlite3_int64" in the future. */ //typedef int fuzzer_cost; /* ** Each transformation rule is stored as an instance of this object. ** All rules are kept on a linked list sorted by rCost. */ class fuzzer_rule { public fuzzer_rule pNext; /* Next rule in order of increasing rCost */ public fuzzer_cost rCost; /* Cost of this transformation */ public int nFrom, nTo; /* Length of the zFrom and zTo strings */ public string zFrom; /* Transform from */ public string zTo = " "; /* Transform to (extra space appended) */ }; /* ** A stem object is used to generate variants. It is also used to record ** previously generated outputs. ** ** Every stem is added to a hash table as it is output. Generation of ** duplicate stems is suppressed. ** ** Active stems (those that might generate new outputs) are kepts on a linked ** list sorted by increasing cost. The cost is the sum of rBaseCost and ** pRule.rCost. */ class fuzzer_stem { public string zBasis; /* Word being fuzzed */ public int nBasis; /* Length of the zBasis string */ public fuzzer_rule pRule; /* Current rule to apply */ public int n; /* Apply pRule at this character offset */ public fuzzer_cost rBaseCost; /* Base cost of getting to zBasis */ public fuzzer_cost rCostX; /* Precomputed rBaseCost + pRule.rCost */ public fuzzer_stem pNext; /* Next stem in rCost order */ public fuzzer_stem pHash; /* Next stem with same hash on zBasis */ }; /* ** A fuzzer virtual-table object */ class fuzzer_vtab : sqlite3_vtab { //sqlite3_vtab base; /* Base class - must be first */ public string zClassName; /* Name of this class. Default: "fuzzer" */ public fuzzer_rule pRule; /* All active rules in this fuzzer */ public fuzzer_rule pNewRule; /* New rules to add when last cursor expires */ public int nCursor; /* Number of active cursors */ }; //#define FUZZER_HASH 4001 /* Hash table size */ const int FUZZER_HASH = 4001; //#define FUZZER_NQUEUE 20 /* Number of slots on the stem queue */ const int FUZZER_NQUEUE = 20; /* A fuzzer cursor object */ class fuzzer_cursor : sqlite3_vtab_cursor { //sqlite3_vtab_cursor base; /* Base class - must be first */ public sqlite3_int64 iRowid; /* The rowid of the current word */ public new fuzzer_vtab pVtab; /* The virtual table this cursor belongs to */ public fuzzer_cost rLimit; /* Maximum cost of any term */ public fuzzer_stem pStem; /* Stem with smallest rCostX */ public fuzzer_stem pDone; /* Stems already processed to completion */ public fuzzer_stem[] aQueue = new fuzzer_stem[FUZZER_NQUEUE]; /* Queue of stems with higher rCostX */ public int mxQueue; /* Largest used index in aQueue[] */ public string zBuf; /* Temporary use buffer */ public int nBuf; /* Bytes allocated for zBuf */ public int nStem; /* Number of stems allocated */ public fuzzer_rule nullRule = new fuzzer_rule(); /* Null rule used first */ public fuzzer_stem[] apHash = new fuzzer_stem[FUZZER_HASH]; /* Hash of previously generated terms */ }; /* Methods for the fuzzer module */ static int fuzzerConnect( sqlite3 db, object pAux, int argc, string[] argv, out sqlite3_vtab ppVtab, out string pzErr ) { fuzzer_vtab pNew; int n; if ( !argv[1].StartsWith( "temp", StringComparison.CurrentCultureIgnoreCase ) ) { pzErr = sqlite3_mprintf( "%s virtual tables must be TEMP", argv[0] ); ppVtab = null; return SQLITE_ERROR; } //n = strlen(argv[0]) + 1; pNew = new fuzzer_vtab();//sqlite3_malloc( sizeof(pNew) + n ); //if( pNew==0 ) return SQLITE_NOMEM; //pNew.zClassName = (char*)&pNew[1]; pNew.zClassName = argv[0];//memcpy(pNew.zClassName, argv[0], n); sqlite3_declare_vtab( db, "CREATE TABLE x(word,distance,cFrom,cTo,cost)" ); //memset(pNew, 0, sizeof(pNew)); pzErr = ""; ppVtab = pNew; return SQLITE_OK; } /* Note that for this virtual table, the xCreate and xConnect ** methods are identical. */ static int fuzzerDisconnect( ref object pVtab ) { fuzzer_vtab p = (fuzzer_vtab)pVtab; Debug.Assert( p.nCursor == 0 ); do { while ( p.pRule != null ) { fuzzer_rule pRule = p.pRule; p.pRule = pRule.pNext; pRule = null;//sqlite3_free(pRule); } p.pRule = p.pNewRule; p.pNewRule = null; } while ( p.pRule != null ); pVtab = null;//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 ** cost. Merge them together into a single list, sorted by cost, and ** return a pointer to the head of that list. */ static fuzzer_rule fuzzerMergeRules( fuzzer_rule pA, fuzzer_rule pB ) { fuzzer_rule head = new fuzzer_rule(); fuzzer_rule pTail; pTail = head; while ( pA != null && pB != null ) { if ( pA.rCost <= pB.rCost ) { pTail.pNext = pA; pTail = pA; pA = pA.pNext; } else { pTail.pNext = pB; pTail = pB; pB = pB.pNext; } } if ( pA == null ) { pTail.pNext = pB; } else { pTail.pNext = pA; } return head.pNext; } /* ** Open a new fuzzer cursor. */ static int fuzzerOpen( sqlite3_vtab pVTab, out sqlite3_vtab_cursor ppCursor ) { fuzzer_vtab p = (fuzzer_vtab)pVTab; fuzzer_cursor pCur; pCur = new fuzzer_cursor();//= sqlite3_malloc( sizeof(pCur) ); ///if( pCur==0 ) return SQLITE_NOMEM; //memset(pCur, 0, sizeof(pCur)); pCur.pVtab = p; ppCursor = pCur; if ( p.nCursor == 0 && p.pNewRule != null ) { uint i; fuzzer_rule pX; fuzzer_rule[] a = new fuzzer_rule[15]; //for(i=0; i>29) ^ *(z++); } for ( int i = 0; i < z.Length; i++ ) h = ( h << 3 ) ^ ( h >> 29 ) ^ (byte)z[i]; return h % FUZZER_HASH; } /* ** Current cost of a stem */ static fuzzer_cost fuzzerCost( fuzzer_stem pStem ) { return pStem.rCostX = pStem.rBaseCost + pStem.pRule.rCost; } #if FALSE /* ** Print a description of a fuzzer_stem on stderr. */ static void fuzzerStemPrint( const char *zPrefix, fuzzer_stem pStem, const char *zSuffix ){ if( pStem.n<0 ){ fprintf(stderr, "%s[%s](%d)-.self%s", zPrefix, pStem.zBasis, pStem.rBaseCost, zSuffix ); }else{ char *zBuf = 0; int nBuf = 0; if( fuzzerRender(pStem, &zBuf, &nBuf)!=SQLITE_OK ) return; fprintf(stderr, "%s[%s](%d)-.{%s}(%d)%s", zPrefix, pStem.zBasis, pStem.rBaseCost, zBuf, pStem., zSuffix ); sqlite3_free(zBuf); } } #endif /* ** Return 1 if the string to which the cursor is point has already ** been emitted. Return 0 if not. Return -1 on a memory allocation ** failures. */ static int fuzzerSeen( fuzzer_cursor pCur, fuzzer_stem pStem ) { uint h; fuzzer_stem pLookup; if ( fuzzerRender( pStem, ref pCur.zBuf, ref pCur.nBuf ) == SQLITE_NOMEM ) { return -1; } h = fuzzerHash( pCur.zBuf ); pLookup = pCur.apHash[h]; while ( pLookup != null && pLookup.zBasis.CompareTo( pCur.zBuf ) != 0 ) { pLookup = pLookup.pHash; } return pLookup != null ? 1 : 0; } /* ** Advance a fuzzer_stem to its next value. Return 0 if there are ** no more values that can be generated by this fuzzer_stem. Return ** -1 on a memory allocation failure. */ static int fuzzerAdvance( fuzzer_cursor pCur, fuzzer_stem pStem ) { fuzzer_rule pRule; while ( ( pRule = pStem.pRule ) != null ) { while ( pStem.n < pStem.nBasis - pRule.nFrom ) { pStem.n++; if ( pRule.nFrom == 0 || memcmp( pStem.zBasis.Substring( pStem.n ), pRule.zFrom, pRule.nFrom ) == 0//|| memcmp( &pStem.zBasis[pStem.n], pRule.zFrom, pRule.nFrom ) == 0 ) { /* Found a rewrite case. Make sure it is not a duplicate */ int rc = fuzzerSeen( pCur, pStem ); if ( rc < 0 ) return -1; if ( rc == 0 ) { fuzzerCost( pStem ); return 1; } } } pStem.n = -1; pStem.pRule = pRule.pNext; if ( pStem.pRule != null && fuzzerCost( pStem ) > pCur.rLimit ) pStem.pRule = null; } return 0; } /* ** The two input stem lists are both sorted in order of increasing ** rCostX. Merge them together into a single list, sorted by rCostX, and ** return a pointer to the head of that new list. */ static fuzzer_stem fuzzerMergeStems( fuzzer_stem pA, fuzzer_stem pB ) { fuzzer_stem head = new fuzzer_stem(); fuzzer_stem pTail; pTail = head; while ( pA != null && pB != null ) { if ( pA.rCostX <= pB.rCostX ) { pTail.pNext = pA; pTail = pA; pA = pA.pNext; } else { pTail.pNext = pB; pTail = pB; pB = pB.pNext; } } if ( pA == null ) { pTail.pNext = pB; } else { pTail.pNext = pA; } return head.pNext; } /* ** Load pCur.pStem with the lowest-cost stem. Return a pointer ** to the lowest-cost stem. */ static fuzzer_stem fuzzerLowestCostStem( fuzzer_cursor pCur ) { fuzzer_stem pBest, pX; int iBest; int i; if ( pCur.pStem == null ) { iBest = -1; pBest = null; for ( i = 0; i <= pCur.mxQueue; i++ ) { pX = pCur.aQueue[i]; if ( pX == null ) continue; if ( pBest == null || pBest.rCostX > pX.rCostX ) { pBest = pX; iBest = i; } } if ( pBest != null ) { pCur.aQueue[iBest] = pBest.pNext; pBest.pNext = null; pCur.pStem = pBest; } } return pCur.pStem; } /* ** Insert pNew into queue of pending stems. Then find the stem ** with the lowest rCostX and move it into pCur.pStem. ** list. The insert is done such the pNew is in the correct order ** according to fuzzer_stem.zBaseCost+fuzzer_stem.pRule.rCost. */ static fuzzer_stem fuzzerInsert( fuzzer_cursor pCur, fuzzer_stem pNew ) { fuzzer_stem pX; int i; /* If pCur.pStem exists and is greater than pNew, then make pNew ** the new pCur.pStem and insert the old pCur.pStem instead. */ if ( ( pX = pCur.pStem ) != null && pX.rCostX > pNew.rCostX ) { pNew.pNext = null; pCur.pStem = pNew; pNew = pX; } /* Insert the new value */ pNew.pNext = null; pX = pNew; for ( i = 0; i <= pCur.mxQueue; i++ ) { if ( pCur.aQueue[i] != null ) { pX = fuzzerMergeStems( pX, pCur.aQueue[i] ); pCur.aQueue[i] = null; } else { pCur.aQueue[i] = pX; break; } } if ( i > pCur.mxQueue ) { if ( i < FUZZER_NQUEUE ) { pCur.mxQueue = i; pCur.aQueue[i] = pX; } else { Debug.Assert( pCur.mxQueue == FUZZER_NQUEUE - 1 ); pX = fuzzerMergeStems( pX, pCur.aQueue[FUZZER_NQUEUE - 1] ); pCur.aQueue[FUZZER_NQUEUE - 1] = pX; } } //for ( i = 0; i <= pCur.mxQueue; i++ ) //{ // if (pCur.aQueue[i] == 0) // fprintf (stderr, "%d null", i); // else // fprintf (stderr, "%d %s %d %d",i, pCur.aQueue[i].zBasis, pCur.aQueue[i].n, pCur.aQueue[i].rCostX ); //} // fprintf (stderr, " (lowest %d )\n", fuzzerLowestCostStem(pCur).rCostX); return fuzzerLowestCostStem( pCur ); } /* ** Allocate a new fuzzer_stem. Add it to the hash table but do not ** link it into either the pCur.pStem or pCur.pDone lists. */ static fuzzer_stem fuzzerNewStem( fuzzer_cursor pCur, string zWord, fuzzer_cost rBaseCost ) { fuzzer_stem pNew; uint h; pNew = new fuzzer_stem();//= sqlite3_malloc( sizeof(pNew) + strlen(zWord) + 1 ); //if( pNew==0 ) return 0; //memset(pNew, 0, sizeof(pNew)); //pNew.zBasis = (char*)&pNew[1]; pNew.nBasis = zWord.Length;//strlen( zWord ); pNew.zBasis = zWord;//memcpy(pNew.zBasis, zWord, pNew.nBasis+1); pNew.pRule = pCur.pVtab.pRule; pNew.n = -1; pNew.rBaseCost = pNew.rCostX = rBaseCost; h = fuzzerHash( pNew.zBasis ); pNew.pHash = pCur.apHash[h]; pCur.apHash[h] = pNew; pCur.nStem++; return pNew; } /* ** Advance a cursor to its next row of output */ static int fuzzerNext( sqlite3_vtab_cursor cur ) { fuzzer_cursor pCur = (fuzzer_cursor)cur; int rc; fuzzer_stem pStem, pNew; pCur.iRowid++; /* Use the element the cursor is currently point to to create ** a new stem and insert the new stem into the priority queue. */ pStem = pCur.pStem; if ( pStem.rCostX > 0 ) { rc = fuzzerRender( pStem, ref pCur.zBuf, ref pCur.nBuf ); if ( rc == SQLITE_NOMEM ) return SQLITE_NOMEM; pNew = fuzzerNewStem( pCur, pCur.zBuf, pStem.rCostX ); if ( pNew != null ) { if ( fuzzerAdvance( pCur, pNew ) == 0 ) { pNew.pNext = pCur.pDone; pCur.pDone = pNew; } else { if ( fuzzerInsert( pCur, pNew ) == pNew ) { return SQLITE_OK; } } } else { return SQLITE_NOMEM; } } /* Adjust the priority queue so that the first element of the ** stem list is the next lowest cost word. */ while ( ( pStem = pCur.pStem ) != null ) { if ( fuzzerAdvance( pCur, pStem ) != 0 ) { pCur.pStem = null; pStem = fuzzerInsert( pCur, pStem ); if ( ( rc = fuzzerSeen( pCur, pStem ) ) != 0 ) { if ( rc < 0 ) return SQLITE_NOMEM; continue; } return SQLITE_OK; /* New word found */ } pCur.pStem = null; pStem.pNext = pCur.pDone; pCur.pDone = pStem; if ( fuzzerLowestCostStem( pCur ) != null ) { rc = fuzzerSeen( pCur, pCur.pStem ); if ( rc < 0 ) return SQLITE_NOMEM; if ( rc == 0 ) { return SQLITE_OK; } } } /* Reach this point only if queue has been exhausted and there is ** nothing left to be output. */ pCur.rLimit = (fuzzer_cost)0; return SQLITE_OK; } /* ** Called to "rewind" a cursor back to the beginning so that ** it starts its output over again. Always called at least once ** prior to any fuzzerColumn, fuzzerRowid, or fuzzerEof call. */ static int fuzzerFilter( sqlite3_vtab_cursor pVtabCursor, int idxNum, string idxStr, int argc, sqlite3_value[] argv ) { fuzzer_cursor pCur = (fuzzer_cursor)pVtabCursor; string zWord = ""; fuzzer_stem pStem; fuzzerClearCursor( pCur, 1 ); pCur.rLimit = 2147483647; if ( idxNum == 1 ) { zWord = (string)sqlite3_value_text( argv[0] ); } else if ( idxNum == 2 ) { pCur.rLimit = (fuzzer_cost)sqlite3_value_int( argv[0] ); } else if ( idxNum == 3 ) { zWord = (string)sqlite3_value_text( argv[0] ); pCur.rLimit = (fuzzer_cost)sqlite3_value_int( argv[1] ); } if ( zWord == null ) zWord = ""; pCur.pStem = pStem = fuzzerNewStem( pCur, zWord, (fuzzer_cost)0 ); if ( pStem == null ) return SQLITE_NOMEM; pCur.nullRule.pNext = pCur.pVtab.pRule; pCur.nullRule.rCost = 0; pCur.nullRule.nFrom = 0; pCur.nullRule.nTo = 0; pCur.nullRule.zFrom = ""; pStem.pRule = pCur.nullRule; pStem.n = pStem.nBasis; pCur.iRowid = 1; return SQLITE_OK; } /* ** Only the word and distance columns have values. All other columns ** return NULL */ static int fuzzerColumn( sqlite3_vtab_cursor cur, sqlite3_context ctx, int i ) { fuzzer_cursor pCur = (fuzzer_cursor)cur; if ( i == 0 ) { /* the "word" column */ if ( fuzzerRender( pCur.pStem, ref pCur.zBuf, ref pCur.nBuf ) == SQLITE_NOMEM ) { return SQLITE_NOMEM; } sqlite3_result_text( ctx, pCur.zBuf, -1, SQLITE_TRANSIENT ); } else if ( i == 1 ) { /* the "distance" column */ sqlite3_result_int( ctx, pCur.pStem.rCostX ); } else { /* All other columns are NULL */ sqlite3_result_null( ctx ); } return SQLITE_OK; } /* ** The rowid. */ static int fuzzerRowid( sqlite3_vtab_cursor cur, out sqlite_int64 pRowid ) { fuzzer_cursor pCur = (fuzzer_cursor)cur; pRowid = pCur.iRowid; return SQLITE_OK; } /* ** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal ** that the cursor has nothing more to output. */ static int fuzzerEof( sqlite3_vtab_cursor cur ) { fuzzer_cursor pCur = (fuzzer_cursor)cur; return pCur.rLimit <= (fuzzer_cost)0 ? 1 : 0; } /* ** Search for terms of these forms: ** ** word MATCH $str ** distance < $value ** distance <= $value ** ** The distance< and distance<= are both treated as distance<=. ** The query plan number is as follows: ** ** 0: None of the terms above are found ** 1: There is a "word MATCH" term with $str in filter.argv[0]. ** 2: There is a "distance<" term with $value in filter.argv[0]. ** 3: Both "word MATCH" and "distance<" with $str in argv[0] and ** $value in argv[1]. */ static int fuzzerBestIndex( sqlite3_vtab tab, ref sqlite3_index_info pIdxInfo ) { int iPlan = 0; int iDistTerm = -1; int i; sqlite3_index_constraint pConstraint; //pConstraint = pIdxInfo.aConstraint; for ( i = 0; i < pIdxInfo.nConstraint; i++ )//, pConstraint++) { pConstraint = pIdxInfo.aConstraint[i]; if ( pConstraint.usable == false ) continue; if ( ( iPlan & 1 ) == 0 && pConstraint.iColumn == 0 && pConstraint.op == SQLITE_INDEX_CONSTRAINT_MATCH ) { iPlan |= 1; pIdxInfo.aConstraintUsage[i].argvIndex = 1; pIdxInfo.aConstraintUsage[i].omit = true; } if ( ( iPlan & 2 ) == 0 && pConstraint.iColumn == 1 && ( pConstraint.op == SQLITE_INDEX_CONSTRAINT_LT || pConstraint.op == SQLITE_INDEX_CONSTRAINT_LE ) ) { iPlan |= 2; iDistTerm = i; } } if ( iPlan == 2 ) { pIdxInfo.aConstraintUsage[iDistTerm].argvIndex = 1; } else if ( iPlan == 3 ) { pIdxInfo.aConstraintUsage[iDistTerm].argvIndex = 2; } pIdxInfo.idxNum = iPlan; if ( pIdxInfo.nOrderBy == 1 && pIdxInfo.aOrderBy[0].iColumn == 1 && pIdxInfo.aOrderBy[0].desc == false ) { pIdxInfo.orderByConsumed = true; } pIdxInfo.estimatedCost = (double)10000; 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, out sqlite_int64 pRowid ) { fuzzer_vtab p = (fuzzer_vtab)pVTab; fuzzer_rule pRule; string zFrom; int nFrom; string zTo; int nTo; fuzzer_cost rCost; if ( argc != 7 ) { //sqlite3_free( pVTab.zErrMsg ); pVTab.zErrMsg = sqlite3_mprintf( "cannot delete from a %s virtual table", p.zClassName ); pRowid = 0; 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 ); pRowid = 0; return SQLITE_CONSTRAINT; } zFrom = sqlite3_value_text( argv[4] ); if ( zFrom == null ) zFrom = ""; zTo = sqlite3_value_text( argv[5] ); if ( zTo == null ) zTo = ""; if ( zFrom.CompareTo( zTo ) == 0 )//strcmp(zFrom,zTo)==0 ) { /* Silently ignore null transformations */ pRowid = 0; return SQLITE_OK; } rCost = sqlite3_value_int( argv[6] ); if ( rCost <= 0 ) { //sqlite3_free(pVTab.zErrMsg); pVTab.zErrMsg = sqlite3_mprintf( "cost must be positive" ); pRowid = 0; return SQLITE_CONSTRAINT; } nFrom = zFrom.Length;//strlen( zFrom ); nTo = zTo.Length;//strlen(zTo); pRule = new fuzzer_rule();// sqlite3_malloc( sizeof( pRule ) + nFrom + nTo ); //if ( pRule == null ) //{ // return SQLITE_NOMEM; //} //pRule.zFrom = pRule.zTo[nTo + 1]; pRule.nFrom = nFrom; pRule.zFrom = zFrom;//memcpy( pRule.zFrom, zFrom, nFrom + 1 ); pRule.zTo = zTo;//memcpy( pRule.zTo, zTo, nTo + 1 ); pRule.nTo = nTo; pRule.rCost = rCost; pRule.pNext = p.pNewRule; p.pNewRule = pRule; pRowid = 0; return SQLITE_OK; } /* ** A virtual table module that provides read-only access to a ** Tcl global variable namespace. */ static sqlite3_module fuzzerModule = new sqlite3_module( 0, /* iVersion */ fuzzerConnect, fuzzerConnect, fuzzerBestIndex, fuzzerDisconnect, fuzzerDisconnect, fuzzerOpen, /* xOpen - open a cursor */ fuzzerClose, /* xClose - close a cursor */ fuzzerFilter, /* xFilter - configure scan constraints */ fuzzerNext, /* xNext - advance a cursor */ fuzzerEof, /* xEof - check for end of scan */ fuzzerColumn, /* xColumn - read data */ fuzzerRowid, /* xRowid - read data */ fuzzerUpdate, /* xUpdate - INSERT */ null, /* xBegin */ null, /* xSync */ null, /* xCommit */ null, /* xRollback */ null, /* xFindMethod */ null /* xRename */ ); #endif ///* SQLITE_OMIT_VIRTUALTABLE */ /* ** Register the fuzzer virtual table */ static int fuzzer_register( sqlite3 db ) { int rc = SQLITE_OK; #if !SQLITE_OMIT_VIRTUALTABLE rc = sqlite3_create_module( db, "fuzzer", fuzzerModule, null ); #endif return rc; } #if SQLITE_TEST //#include /* ** Decode a pointer to an sqlite3 object. */ //extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 *ppDb); /* ** Register the echo virtual table module. */ static int register_fuzzer_module( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj[] objv /* Command arguments */ ) { sqlite3 db; if ( objc != 2 ) { TCL.Tcl_WrongNumArgs( interp, 1, objv, "DB" ); return TCL.TCL_ERROR; } if ( getDbPointer( interp, TCL.Tcl_GetString( objv[1] ), out db ) != 0 ) return TCL.TCL_ERROR; fuzzer_register( db ); return TCL.TCL_OK; } /* ** Register commands with the TCL interpreter. */ /* ** Register commands with the TCL interpreter. */ public static int Sqlitetestfuzzer_Init( Tcl_Interp interp ) { //static struct { // string zName; // Tcl_ObjCmdProc *xProc; // void *clientData; //} _aObjCmd[] aObjCmd = new _aObjCmd[] { new _aObjCmd( "register_fuzzer_module", register_fuzzer_module, 0 ), }; int i; for ( i = 0; i < aObjCmd.Length; i++ )//sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++) { TCL.Tcl_CreateObjCommand( interp, aObjCmd[i].zName, aObjCmd[i].xProc, aObjCmd[i].clientData, null ); } return TCL.TCL_OK; } #endif //* SQLITE_TEST */ } #endif // TCLSH }