239 lines
5.9 KiB
C
239 lines
5.9 KiB
C
|
|
||
|
|
||
|
/*
|
||
|
** This file contains tests related to the explicit rollback of database
|
||
|
** transactions and sub-transactions.
|
||
|
*/
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Repeat 2000 times (until the db contains 100,000 entries):
|
||
|
**
|
||
|
** 1. Open a transaction and insert 500 rows, opening a nested
|
||
|
** sub-transaction each 100 rows.
|
||
|
**
|
||
|
** 2. Roll back to each sub-transaction savepoint. Check the database
|
||
|
** checksum looks Ok.
|
||
|
**
|
||
|
** 3. Every second iteration, roll back the main transaction. Check the
|
||
|
** db checksum is correct. Every other iteration, commit the main
|
||
|
** transaction (increasing the size of the db by 100 rows).
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include "lsmtest.h"
|
||
|
|
||
|
struct CksumDb {
|
||
|
int nFirst;
|
||
|
int nLast;
|
||
|
int nStep;
|
||
|
char **azCksum;
|
||
|
};
|
||
|
|
||
|
CksumDb *testCksumArrayNew(
|
||
|
Datasource *pData,
|
||
|
int nFirst,
|
||
|
int nLast,
|
||
|
int nStep
|
||
|
){
|
||
|
TestDb *pDb;
|
||
|
CksumDb *pRet;
|
||
|
int i;
|
||
|
int nEntry;
|
||
|
int rc = 0;
|
||
|
|
||
|
assert( nLast>=nFirst && ((nLast-nFirst)%nStep)==0 );
|
||
|
|
||
|
pRet = malloc(sizeof(CksumDb));
|
||
|
memset(pRet, 0, sizeof(CksumDb));
|
||
|
pRet->nFirst = nFirst;
|
||
|
pRet->nLast = nLast;
|
||
|
pRet->nStep = nStep;
|
||
|
nEntry = 1 + ((nLast - nFirst) / nStep);
|
||
|
|
||
|
/* Allocate space so that azCksum is an array of nEntry pointers to
|
||
|
** buffers each TEST_CKSUM_BYTES in size. */
|
||
|
pRet->azCksum = (char **)malloc(nEntry * (sizeof(char *) + TEST_CKSUM_BYTES));
|
||
|
for(i=0; i<nEntry; i++){
|
||
|
char *pStart = (char *)(&pRet->azCksum[nEntry]);
|
||
|
pRet->azCksum[i] = &pStart[i * TEST_CKSUM_BYTES];
|
||
|
}
|
||
|
|
||
|
tdb_open("lsm", "tempdb.lsm", 1, &pDb);
|
||
|
testWriteDatasourceRange(pDb, pData, 0, nFirst, &rc);
|
||
|
for(i=0; i<nEntry; i++){
|
||
|
testCksumDatabase(pDb, pRet->azCksum[i]);
|
||
|
if( i==nEntry ) break;
|
||
|
testWriteDatasourceRange(pDb, pData, nFirst+i*nStep, nStep, &rc);
|
||
|
}
|
||
|
|
||
|
tdb_close(pDb);
|
||
|
|
||
|
return pRet;
|
||
|
}
|
||
|
|
||
|
char *testCksumArrayGet(CksumDb *p, int nRow){
|
||
|
int i;
|
||
|
assert( nRow>=p->nFirst );
|
||
|
assert( nRow<=p->nLast );
|
||
|
assert( ((nRow-p->nFirst) % p->nStep)==0 );
|
||
|
|
||
|
i = (nRow - p->nFirst) / p->nStep;
|
||
|
return p->azCksum[i];
|
||
|
}
|
||
|
|
||
|
void testCksumArrayFree(CksumDb *p){
|
||
|
free(p->azCksum);
|
||
|
memset(p, 0x55, sizeof(*p));
|
||
|
free(p);
|
||
|
}
|
||
|
|
||
|
/* End of CksumDb code.
|
||
|
**************************************************************************/
|
||
|
|
||
|
/*
|
||
|
** Test utility function. Write key-value pair $i from datasource pData
|
||
|
** into database pDb.
|
||
|
*/
|
||
|
void testWriteDatasource(TestDb *pDb, Datasource *pData, int i, int *pRc){
|
||
|
void *pKey; int nKey;
|
||
|
void *pVal; int nVal;
|
||
|
testDatasourceEntry(pData, i, &pKey, &nKey, &pVal, &nVal);
|
||
|
testWrite(pDb, pKey, nKey, pVal, nVal, pRc);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Test utility function. Delete datasource pData key $i from database pDb.
|
||
|
*/
|
||
|
void testDeleteDatasource(TestDb *pDb, Datasource *pData, int i, int *pRc){
|
||
|
void *pKey; int nKey;
|
||
|
testDatasourceEntry(pData, i, &pKey, &nKey, 0, 0);
|
||
|
testDelete(pDb, pKey, nKey, pRc);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** This function inserts nWrite key/value pairs into database pDb - the
|
||
|
** nWrite key value pairs starting at iFirst from data source pData.
|
||
|
*/
|
||
|
void testWriteDatasourceRange(
|
||
|
TestDb *pDb, /* Database to write to */
|
||
|
Datasource *pData, /* Data source to read values from */
|
||
|
int iFirst, /* Index of first key/value pair */
|
||
|
int nWrite, /* Number of key/value pairs to write */
|
||
|
int *pRc /* IN/OUT: Error code */
|
||
|
){
|
||
|
int i;
|
||
|
for(i=0; i<nWrite; i++){
|
||
|
testWriteDatasource(pDb, pData, iFirst+i, pRc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void testDeleteDatasourceRange(
|
||
|
TestDb *pDb, /* Database to write to */
|
||
|
Datasource *pData, /* Data source to read keys from */
|
||
|
int iFirst, /* Index of first key */
|
||
|
int nWrite, /* Number of keys to delete */
|
||
|
int *pRc /* IN/OUT: Error code */
|
||
|
){
|
||
|
int i;
|
||
|
for(i=0; i<nWrite; i++){
|
||
|
testDeleteDatasource(pDb, pData, iFirst+i, pRc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static char *getName(const char *zSystem){
|
||
|
char *zRet;
|
||
|
zRet = testMallocPrintf("rollback.%s", zSystem);
|
||
|
return zRet;
|
||
|
}
|
||
|
|
||
|
static int rollback_test_1(
|
||
|
const char *zSystem,
|
||
|
Datasource *pData
|
||
|
){
|
||
|
const int nRepeat = 100;
|
||
|
|
||
|
TestDb *pDb;
|
||
|
int rc;
|
||
|
int i;
|
||
|
CksumDb *pCksum;
|
||
|
char *zName;
|
||
|
|
||
|
zName = getName(zSystem);
|
||
|
testCaseStart(&rc, zName);
|
||
|
testFree(zName);
|
||
|
|
||
|
pCksum = testCksumArrayNew(pData, 0, nRepeat*100, 100);
|
||
|
pDb = 0;
|
||
|
rc = tdb_open(zSystem, 0, 1, &pDb);
|
||
|
if( pDb && tdb_transaction_support(pDb)==0 ){
|
||
|
testCaseSkip();
|
||
|
goto skip_rollback_test;
|
||
|
}
|
||
|
|
||
|
for(i=0; i<nRepeat && rc==0; i++){
|
||
|
char zCksum[TEST_CKSUM_BYTES];
|
||
|
int nCurrent = (((i+1)/2) * 100);
|
||
|
int nDbRow;
|
||
|
int iTrans;
|
||
|
|
||
|
/* Check that the database is the expected size. */
|
||
|
nDbRow = testCountDatabase(pDb);
|
||
|
testCompareInt(nCurrent, nDbRow, &rc);
|
||
|
|
||
|
for(iTrans=2; iTrans<=6 && rc==0; iTrans++){
|
||
|
tdb_begin(pDb, iTrans);
|
||
|
testWriteDatasourceRange(pDb, pData, nCurrent, 100, &rc);
|
||
|
nCurrent += 100;
|
||
|
}
|
||
|
|
||
|
testCksumDatabase(pDb, zCksum);
|
||
|
testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc);
|
||
|
|
||
|
for(iTrans=6; iTrans>2 && rc==0; iTrans--){
|
||
|
tdb_rollback(pDb, iTrans);
|
||
|
nCurrent -= 100;
|
||
|
testCksumDatabase(pDb, zCksum);
|
||
|
testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc);
|
||
|
}
|
||
|
|
||
|
if( i%2 ){
|
||
|
tdb_rollback(pDb, 0);
|
||
|
nCurrent -= 100;
|
||
|
testCksumDatabase(pDb, zCksum);
|
||
|
testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc);
|
||
|
}else{
|
||
|
tdb_commit(pDb, 0);
|
||
|
}
|
||
|
}
|
||
|
testCaseFinish(rc);
|
||
|
|
||
|
skip_rollback_test:
|
||
|
tdb_close(pDb);
|
||
|
testCksumArrayFree(pCksum);
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
void test_rollback(
|
||
|
const char *zSystem,
|
||
|
const char *zPattern,
|
||
|
int *pRc
|
||
|
){
|
||
|
if( *pRc==0 ){
|
||
|
int bRun = 1;
|
||
|
|
||
|
if( zPattern ){
|
||
|
char *zName = getName(zSystem);
|
||
|
bRun = testGlobMatch(zPattern, zName);
|
||
|
testFree(zName);
|
||
|
}
|
||
|
|
||
|
if( bRun ){
|
||
|
DatasourceDefn defn = { TEST_DATASOURCE_RANDOM, 10, 15, 50, 100 };
|
||
|
Datasource *pData = testDatasourceNew(&defn);
|
||
|
*pRc = rollback_test_1(zSystem, pData);
|
||
|
testDatasourceFree(pData);
|
||
|
}
|
||
|
}
|
||
|
}
|