diff options
author | dan <dan@noemail.net> | 2018-11-07 17:52:29 +0000 |
---|---|---|
committer | dan <dan@noemail.net> | 2018-11-07 17:52:29 +0000 |
commit | 7844d21536bb6d847bd7d3b6d3c74e14edcbe761 (patch) | |
tree | 234be6c109a9e6864bdb0d24919d9abd30ba1282 /ext/session/changesetfuzz.c | |
parent | 005d4c61c177b2e108b6178c97c139af904e2beb (diff) | |
download | sqlite-7844d21536bb6d847bd7d3b6d3c74e14edcbe761.tar.gz sqlite-7844d21536bb6d847bd7d3b6d3c74e14edcbe761.zip |
Update the "changesetfuzz" program to work with patchsets as well as
changesets.
FossilOrigin-Name: 75b00fbe884d4bd8ba099d7c0a2e8af2f40fedfdef2d9b624dd51cb66952611c
Diffstat (limited to 'ext/session/changesetfuzz.c')
-rw-r--r-- | ext/session/changesetfuzz.c | 289 |
1 files changed, 180 insertions, 109 deletions
diff --git a/ext/session/changesetfuzz.c b/ext/session/changesetfuzz.c index 4ea61e1c1..22458c430 100644 --- a/ext/session/changesetfuzz.c +++ b/ext/session/changesetfuzz.c @@ -86,11 +86,14 @@ ** value is appended to all INSERT, DELETE and UPDATE old.* records. ** An "undefined" is appended to new.* UPDATE records. ** -** 14. A column may be removed from a table. In this case the corresponding +** 14. A column may be removed from a table, provided that it is not the +** only PRIMARY KEY column in the table. In this case the corresponding ** field is removed from all records. In cases where this leaves an UPDATE -** with no non-PK, non-undefined fields, the entire change is removed. If -** the table has more than on PK column, the column removed may be part of -** the PK. +** with no non-PK, non-undefined fields, the entire change is removed. +** +** PATCHSETS +** +** As well as changesets, this program can also dump and fuzz patchsets. */ #include "sqlite3.h" @@ -118,13 +121,6 @@ #define FUZZ_COLUMN_ADDPK 13 /* Add PK column to table definition */ #define FUZZ_COLUMN_DEL 14 /* Remove column from table definition */ -#if 0 -#define FUZZ_COLUMN_ADD 1 /* Add column to table definition */ -#define FUZZ_PK_ADD 3 /* Add a PK column */ -#define FUZZ_PK_DEL 4 /* Delete a PK column */ -#define FUZZ_NAME_CHANGE 5 /* Change a table name */ -#endif - typedef unsigned char u8; @@ -172,6 +168,10 @@ static void fuzzReadFile(const char *zFilename, int *pSz, void **ppBuf){ *ppBuf = pBuf; } +/* +** Write the contents of buffer pBuf, size nBuf bytes, into file zFilename +** on disk. zFilename, if it already exists, is clobbered. +*/ static void fuzzWriteFile(const char *zFilename, void *pBuf, int nBuf){ FILE *f; f = fopen(zFilename, "wb"); @@ -289,7 +289,6 @@ static void fuzzRandomSeed(unsigned int iSeed){ sqlite3Prng.s[i+3] ^= ((iSeed >> 0) & 0xFF); } } - /* ** End of code for generating pseudo-random values. *************************************************************************/ @@ -298,12 +297,11 @@ typedef struct FuzzChangeset FuzzChangeset; typedef struct FuzzChangesetGroup FuzzChangesetGroup; typedef struct FuzzChange FuzzChange; -#define FUZZER_AVAL_SZ 512 - /* ** Object containing partially parsed changeset. */ struct FuzzChangeset { + int bPatchset; /* True for a patchset */ FuzzChangesetGroup **apGroup; /* Array of groups in changeset */ int nGroup; /* Number of items in list pGroup */ u8 **apVal; /* Array of all values in changeset */ @@ -312,6 +310,10 @@ struct FuzzChangeset { int nUpdate; /* Number of UPDATE changes in changeset */ }; +/* +** There is one object of this type for each change-group (table header) +** in the input changeset. +*/ struct FuzzChangesetGroup { const char *zTab; /* Name of table */ int nCol; /* Number of columns in table */ @@ -329,12 +331,15 @@ struct FuzzChange { int iChange; /* Change or UPDATE to modify */ int iGroup; /* Group to modify */ int iDelete; /* Field to remove (FUZZ_COLUMN_DEL) */ - u8 *pSub1; - u8 *pSub2; - u8 aSub[128]; /* Substitute value */ + u8 *pSub1; /* Replace this value with pSub2 */ + u8 *pSub2; /* And this one with pSub1 */ + u8 aSub[128]; /* Buffer for substitute value */ int iCurrent; /* Current change number */ }; +/* +** Allocate and return nByte bytes of zeroed memory. +*/ static void *fuzzMalloc(int nByte){ void *pRet = sqlite3_malloc(nByte); if( pRet ){ @@ -343,10 +348,20 @@ static void *fuzzMalloc(int nByte){ return pRet; } +/* +** Free the buffer indicated by the first argument. This function is used +** to free buffers allocated by fuzzMalloc(). +*/ static void fuzzFree(void *p){ sqlite3_free(p); } +/* +** Argument p points to a buffer containing an SQLite varint that, assuming the +** input is not corrupt, may be between 0 and 0x7FFFFFFF, inclusive. Before +** returning, this function sets (*pnVal) to the value of that varint, and +** returns the number of bytes of space that it takes up. +*/ static int fuzzGetVarint(u8 *p, int *pnVal){ int i; sqlite3_uint64 nVal = 0; @@ -361,6 +376,11 @@ static int fuzzGetVarint(u8 *p, int *pnVal){ return i; } +/* +** Write value nVal into the buffer indicated by argument p as an SQLite +** varint. nVal is guaranteed to be between 0 and (2^21-1), inclusive. +** Return the number of bytes written to buffer p. +*/ static int fuzzPutVarint(u8 *p, int nVal){ assert( nVal>0 && nVal<2097152 ); if( nVal<128 ){ @@ -379,20 +399,26 @@ static int fuzzPutVarint(u8 *p, int nVal){ return 3; } -/* Load an unaligned and unsigned 32-bit integer */ -#define FUZZ_UINT32(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3]) - /* ** Read a 64-bit big-endian integer value from buffer aRec[]. Return ** the value read. */ -static sqlite3_int64 fuzzGetI64(u8 *aRec){ - u64 x = FUZZ_UINT32(aRec); - u32 y = FUZZ_UINT32(aRec+4); - x = (x<<32) + y; - return (sqlite3_int64)x; +static i64 fuzzGetI64(u8 *aRec){ + return (i64)( + (((u64)aRec[0]) << 56) + + (((u64)aRec[1]) << 48) + + (((u64)aRec[2]) << 40) + + (((u64)aRec[3]) << 32) + + (((u64)aRec[4]) << 24) + + (((u64)aRec[5]) << 16) + + (((u64)aRec[6]) << 8) + + (((u64)aRec[7]) << 0) + ); } +/* +** Write value iVal to buffer aRec[] as an unsigned 64-bit big-endian integer. +*/ static void fuzzPutU64(u8 *aRec, u64 iVal){ aRec[0] = (iVal>>56) & 0xFF; aRec[1] = (iVal>>48) & 0xFF; @@ -404,9 +430,15 @@ static void fuzzPutU64(u8 *aRec, u64 iVal){ aRec[7] = (iVal) & 0xFF; } -static int fuzzParseHeader(u8 **ppHdr, u8 *pEnd, FuzzChangesetGroup **ppGrp){ +static int fuzzParseHeader( + FuzzChangeset *pParse, + u8 **ppHdr, + u8 *pEnd, + FuzzChangesetGroup **ppGrp +){ int rc = SQLITE_OK; FuzzChangesetGroup *pGrp; + u8 cHdr = (pParse->bPatchset ? 'P' : 'T'); assert( pEnd>(*ppHdr) ); pGrp = (FuzzChangesetGroup*)fuzzMalloc(sizeof(FuzzChangesetGroup)); @@ -414,7 +446,7 @@ static int fuzzParseHeader(u8 **ppHdr, u8 *pEnd, FuzzChangesetGroup **ppGrp){ rc = SQLITE_NOMEM; }else{ u8 *p = *ppHdr; - if( p[0]!='T' ){ + if( p[0]!=cHdr ){ rc = fuzzCorrupt(); }else{ p++; @@ -468,26 +500,33 @@ static int fuzzChangeSize(u8 *p, int *pSz){ return SQLITE_OK; } -static int fuzzParseRecord(u8 **ppRec, u8 *pEnd, FuzzChangeset *pParse){ +static int fuzzParseRecord( + u8 **ppRec, /* IN/OUT: Iterator */ + u8 *pEnd, /* One byte after end of input data */ + FuzzChangeset *pParse, + int bPkOnly +){ int rc = SQLITE_OK; - int nCol = pParse->apGroup[pParse->nGroup-1]->nCol; + FuzzChangesetGroup *pGrp = pParse->apGroup[pParse->nGroup-1]; int i; u8 *p = *ppRec; - for(i=0; rc==SQLITE_OK && i<nCol && p<pEnd; i++){ - int sz; - if( (pParse->nVal & (pParse->nVal-1))==0 ){ - int nNew = pParse->nVal ? pParse->nVal*2 : 4; - u8 **apNew = (u8**)sqlite3_realloc(pParse->apVal, nNew*sizeof(u8*)); - if( apNew==0 ) return SQLITE_NOMEM; - pParse->apVal = apNew; + for(i=0; rc==SQLITE_OK && i<pGrp->nCol && p<pEnd; i++){ + if( bPkOnly==0 || pGrp->aPK[i] ){ + int sz; + if( (pParse->nVal & (pParse->nVal-1))==0 ){ + int nNew = pParse->nVal ? pParse->nVal*2 : 4; + u8 **apNew = (u8**)sqlite3_realloc(pParse->apVal, nNew*sizeof(u8*)); + if( apNew==0 ) return SQLITE_NOMEM; + pParse->apVal = apNew; + } + pParse->apVal[pParse->nVal++] = p; + rc = fuzzChangeSize(p, &sz); + p += sz; } - pParse->apVal[pParse->nVal++] = p; - rc = fuzzChangeSize(p, &sz); - p += sz; } - if( rc==SQLITE_OK && i<nCol ){ + if( rc==SQLITE_OK && i<pGrp->nCol ){ rc = fuzzCorrupt(); } @@ -496,24 +535,28 @@ static int fuzzParseRecord(u8 **ppRec, u8 *pEnd, FuzzChangeset *pParse){ } static int fuzzParseChanges(u8 **ppData, u8 *pEnd, FuzzChangeset *pParse){ + u8 cHdr = (pParse->bPatchset ? 'P' : 'T'); FuzzChangesetGroup *pGrp = pParse->apGroup[pParse->nGroup-1]; int rc = SQLITE_OK; u8 *p = *ppData; pGrp->aChange = p; - while( rc==SQLITE_OK && p<pEnd && p[0]!='T' ){ + while( rc==SQLITE_OK && p<pEnd && p[0]!=cHdr ){ u8 eOp = p[0]; u8 bIndirect = p[1]; p += 2; if( eOp==SQLITE_UPDATE ){ pParse->nUpdate++; - rc = fuzzParseRecord(&p, pEnd, pParse); + if( pParse->bPatchset==0 ){ + rc = fuzzParseRecord(&p, pEnd, pParse, 0); + } }else if( eOp!=SQLITE_INSERT && eOp!=SQLITE_DELETE ){ rc = fuzzCorrupt(); } if( rc==SQLITE_OK ){ - rc = fuzzParseRecord(&p, pEnd, pParse); + int bPkOnly = (eOp==SQLITE_DELETE && pParse->bPatchset); + rc = fuzzParseRecord(&p, pEnd, pParse, bPkOnly); } pGrp->nChange++; pParse->nChange++; @@ -534,16 +577,19 @@ static int fuzzParseChangeset( int rc = SQLITE_OK; memset(pParse, 0, sizeof(FuzzChangeset)); + if( nChangeset>0 ){ + pParse->bPatchset = (pChangeset[0]=='P'); + } while( rc==SQLITE_OK && p<pEnd ){ FuzzChangesetGroup *pGrp = 0; /* Read a table-header from the changeset */ - rc = fuzzParseHeader(&p, pEnd, &pGrp); + rc = fuzzParseHeader(pParse, &p, pEnd, &pGrp); assert( (rc==SQLITE_OK)==(pGrp!=0) ); - /* If the table-header was successfully parsed, link the new change-group - ** into the linked list and parse the associated array of changes. */ + /* If the table-header was successfully parsed, add the new change-group + ** to the array and parse the associated changes. */ if( rc==SQLITE_OK ){ FuzzChangesetGroup **apNew = (FuzzChangesetGroup**)sqlite3_realloc( pParse->apGroup, sizeof(FuzzChangesetGroup*)*(pParse->nGroup+1) @@ -562,65 +608,67 @@ static int fuzzParseChangeset( return rc; } -static int fuzzPrintRecord(FuzzChangesetGroup *pGrp, u8 **ppRec){ +static int fuzzPrintRecord(FuzzChangesetGroup *pGrp, u8 **ppRec, int bPKOnly){ int rc = SQLITE_OK; u8 *p = *ppRec; int i; const char *zPre = " ("; for(i=0; i<pGrp->nCol; i++){ - u8 eType = p++[0]; - switch( eType ){ - case 0x00: /* undefined */ - printf("%sn/a", zPre); - break; + if( bPKOnly==0 || pGrp->aPK[i] ){ + u8 eType = p++[0]; + switch( eType ){ + case 0x00: /* undefined */ + printf("%sn/a", zPre); + break; - case 0x01: { /* integer */ - sqlite3_int64 iVal = 0; - iVal = fuzzGetI64(p); - printf("%s%lld", zPre, iVal); - p += 8; - break; - } + case 0x01: { /* integer */ + sqlite3_int64 iVal = 0; + iVal = fuzzGetI64(p); + printf("%s%lld", zPre, iVal); + p += 8; + break; + } - case 0x02: { /* real */ - sqlite3_int64 iVal = 0; - double fVal = 0.0; - iVal = fuzzGetI64(p); - memcpy(&fVal, &iVal, 8); - printf("%s%f", zPre, fVal); - p += 8; - break; - } + case 0x02: { /* real */ + sqlite3_int64 iVal = 0; + double fVal = 0.0; + iVal = fuzzGetI64(p); + memcpy(&fVal, &iVal, 8); + printf("%s%f", zPre, fVal); + p += 8; + break; + } - case 0x03: /* text */ - case 0x04: { /* blob */ - int nTxt; - int sz; - int i; - p += fuzzGetVarint(p, &nTxt); - printf("%s%s", zPre, eType==0x03 ? "'" : "X'"); - for(i=0; i<nTxt; i++){ - if( eType==0x03 ){ - printf("%c", p[i]); - }else{ - char aHex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' - }; - printf("%c", aHex[ p[i]>>4 ]); - printf("%c", aHex[ p[i] & 0x0F ]); + case 0x03: /* text */ + case 0x04: { /* blob */ + int nTxt; + int sz; + int i; + p += fuzzGetVarint(p, &nTxt); + printf("%s%s", zPre, eType==0x03 ? "'" : "X'"); + for(i=0; i<nTxt; i++){ + if( eType==0x03 ){ + printf("%c", p[i]); + }else{ + char aHex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + printf("%c", aHex[ p[i]>>4 ]); + printf("%c", aHex[ p[i] & 0x0F ]); + } } + printf("'"); + p += nTxt; + break; } - printf("'"); - p += nTxt; - break; - } - case 0x05: /* null */ - printf("%sNULL", zPre); - break; + case 0x05: /* null */ + printf("%sNULL", zPre); + break; + } + zPre = ", "; } - zPre = ", "; } printf(")"); @@ -628,7 +676,7 @@ static int fuzzPrintRecord(FuzzChangesetGroup *pGrp, u8 **ppRec){ return rc; } -static int fuzzPrintGroup(FuzzChangesetGroup *pGrp){ +static int fuzzPrintGroup(FuzzChangeset *pParse, FuzzChangesetGroup *pGrp){ int i; u8 *p; @@ -651,10 +699,10 @@ static int fuzzPrintGroup(FuzzChangesetGroup *pGrp){ ); p += 2; - if( eType==SQLITE_UPDATE ){ - fuzzPrintRecord(pGrp, &p); + if( pParse->bPatchset==0 && eType==SQLITE_UPDATE ){ + fuzzPrintRecord(pGrp, &p, 0); } - fuzzPrintRecord(pGrp, &p); + fuzzPrintRecord(pGrp, &p, eType==SQLITE_DELETE && pParse->bPatchset); printf("\n"); } } @@ -663,7 +711,7 @@ static int fuzzSelectChange(FuzzChangeset *pParse, FuzzChange *pChange){ int iSub; memset(pChange, 0, sizeof(FuzzChange)); - pChange->eType = fuzzRandomInt(14) + FUZZ_VALUE_SUB; + pChange->eType = fuzzRandomInt(FUZZ_COLUMN_DEL) + 1; assert( pChange->eType==FUZZ_VALUE_SUB || pChange->eType==FUZZ_VALUE_MOD @@ -795,12 +843,13 @@ static int fuzzCopyChange( FuzzChange *pFuzz, u8 **pp, u8 **ppOut /* IN/OUT: Input and output pointers */ ){ + int bPS = pParse->bPatchset; FuzzChangesetGroup *pGrp = pParse->apGroup[iGrp]; u8 *p = *pp; u8 *pOut = *ppOut; u8 eType = p++[0]; int iRec; - int nRec = (eType==SQLITE_UPDATE ? 2 : 1); + int nRec = ((eType==SQLITE_UPDATE && !bPS) ? 2 : 1); int iUndef = -1; int nUpdate = 0; @@ -833,6 +882,7 @@ static int fuzzCopyChange( } if( nDef<=1 ) return -1; nDef = fuzzRandomInt(nDef); + pCsr = p+1; for(i=0; i<pGrp->nCol; i++){ if( pCsr[0] && pGrp->aPK[i]==0 ){ if( nDef==0 ) iUndef = i; @@ -855,10 +905,30 @@ static int fuzzCopyChange( for(iRec=0; iRec<nRec; iRec++){ int i; + + /* Copy the next record from the output to the input. + */ for(i=0; i<pGrp->nCol; i++){ int sz; u8 *pCopy = p; + /* If this is a patchset, and the input is a DELETE, then the only + ** fields present are the PK fields. So, if this is not a PK, skip to + ** the next column. If the current fuzz is FUZZ_CHANGE_TYPE, then + ** write a randomly selected value to the output. */ + if( bPS && eType==SQLITE_DELETE && pGrp->aPK[i]==0 ){ + if( eType!=eNew ){ + assert( eNew==SQLITE_UPDATE ); + do { + pCopy = pParse->apVal[fuzzRandomInt(pParse->nVal)]; + }while( pCopy[0]==0x00 ); + fuzzChangeSize(pCopy, &sz); + memcpy(pOut, pCopy, sz); + pOut += sz; + } + continue; + } + if( p==pFuzz->pSub1 ){ pCopy = pFuzz->pSub2; }else if( p==pFuzz->pSub2 ){ @@ -877,13 +947,14 @@ static int fuzzCopyChange( if( pGrp->aPK[i]>0 && pCopy[0]==0x05 ) return -1; } - if( pFuzz->iGroup!=iGrp || i!=pFuzz->iDelete ){ - if( eNew==eType || eType!=SQLITE_UPDATE || iRec==0 ){ - fuzzChangeSize(pCopy, &sz); - memcpy(pOut, pCopy, sz); - pOut += sz; - nUpdate += (pGrp->aPK[i]==0 && pCopy[0]!=0x00); - } + if( (pFuzz->iGroup!=iGrp || i!=pFuzz->iDelete) + && (eNew==eType || eType!=SQLITE_UPDATE || iRec==0) + && (eNew==eType || eNew!=SQLITE_DELETE || !bPS || pGrp->aPK[i]) + ){ + fuzzChangeSize(pCopy, &sz); + memcpy(pOut, pCopy, sz); + pOut += sz; + nUpdate += (pGrp->aPK[i]==0 && pCopy[0]!=0x00); } fuzzChangeSize(p, &sz); @@ -892,7 +963,7 @@ static int fuzzCopyChange( if( iGrp==pFuzz->iGroup ){ if( pFuzz->eType==FUZZ_COLUMN_ADD ){ - *(pOut++) = 0x05; + if( !bPS || eType!=SQLITE_DELETE ) *(pOut++) = 0x05; }else if( pFuzz->eType==FUZZ_COLUMN_ADDPK ){ if( iRec==1 ){ *(pOut++) = 0x00; @@ -920,7 +991,7 @@ static int fuzzCopyChange( if( pFuzz->eType==FUZZ_CHANGE_DEL ){ pOut = *ppOut; } - if( eNew!=eType && eNew==SQLITE_UPDATE ){ + if( eNew!=eType && eNew==SQLITE_UPDATE && !bPS ){ int i; u8 *pCsr = (*ppOut) + 2; for(i=0; i<pGrp->nCol; i++){ @@ -1003,7 +1074,7 @@ static int fuzzDoOneFuzz( } /* Output a table header */ - pOut++[0] = 'T'; + pOut++[0] = pParse->bPatchset ? 'P' : 'T'; pOut += fuzzPutVarint(pOut, nCol); for(i=0; i<pGrp->nCol; i++){ @@ -1063,7 +1134,7 @@ int main(int argc, char **argv){ if( rc==SQLITE_OK ){ if( argc==2 ){ for(i=0; i<changeset.nGroup; i++){ - fuzzPrintGroup(changeset.apGroup[i]); + fuzzPrintGroup(&changeset, changeset.apGroup[i]); } }else{ pBuf = (u8*)fuzzMalloc(nChangeset*2 + 1024); |