1 
2 
3 /*
4 ** This file contains tests related to the explicit rollback of database
5 ** transactions and sub-transactions.
6 */
7 
8 
9 /*
10 ** Repeat 2000 times (until the db contains 100,000 entries):
11 **
12 **   1. Open a transaction and insert 500 rows, opening a nested
13 **      sub-transaction each 100 rows.
14 **
15 **   2. Roll back to each sub-transaction savepoint. Check the database
16 **      checksum looks Ok.
17 **
18 **   3. Every second iteration, roll back the main transaction. Check the
19 **      db checksum is correct. Every other iteration, commit the main
20 **      transaction (increasing the size of the db by 100 rows).
21 */
22 
23 
24 #include "lsmtest.h"
25 
26 struct CksumDb {
27   int nFirst;
28   int nLast;
29   int nStep;
30   char **azCksum;
31 };
32 
testCksumArrayNew(Datasource * pData,int nFirst,int nLast,int nStep)33 CksumDb *testCksumArrayNew(
34   Datasource *pData,
35   int nFirst,
36   int nLast,
37   int nStep
38 ){
39   TestDb *pDb;
40   CksumDb *pRet;
41   int i;
42   int nEntry;
43   int rc = 0;
44 
45   assert( nLast>=nFirst && ((nLast-nFirst)%nStep)==0 );
46 
47   pRet = malloc(sizeof(CksumDb));
48   memset(pRet, 0, sizeof(CksumDb));
49   pRet->nFirst = nFirst;
50   pRet->nLast = nLast;
51   pRet->nStep = nStep;
52   nEntry = 1 + ((nLast - nFirst) / nStep);
53 
54   /* Allocate space so that azCksum is an array of nEntry pointers to
55   ** buffers each TEST_CKSUM_BYTES in size.  */
56   pRet->azCksum = (char **)malloc(nEntry * (sizeof(char *) + TEST_CKSUM_BYTES));
57   for(i=0; i<nEntry; i++){
58     char *pStart = (char *)(&pRet->azCksum[nEntry]);
59     pRet->azCksum[i] = &pStart[i * TEST_CKSUM_BYTES];
60   }
61 
62   tdb_open("lsm", "tempdb.lsm", 1, &pDb);
63   testWriteDatasourceRange(pDb, pData, 0, nFirst, &rc);
64   for(i=0; i<nEntry; i++){
65     testCksumDatabase(pDb, pRet->azCksum[i]);
66     if( i==nEntry ) break;
67     testWriteDatasourceRange(pDb, pData, nFirst+i*nStep, nStep, &rc);
68   }
69 
70   tdb_close(pDb);
71 
72   return pRet;
73 }
74 
testCksumArrayGet(CksumDb * p,int nRow)75 char *testCksumArrayGet(CksumDb *p, int nRow){
76   int i;
77   assert( nRow>=p->nFirst );
78   assert( nRow<=p->nLast );
79   assert( ((nRow-p->nFirst) % p->nStep)==0 );
80 
81   i = (nRow - p->nFirst) / p->nStep;
82   return p->azCksum[i];
83 }
84 
testCksumArrayFree(CksumDb * p)85 void testCksumArrayFree(CksumDb *p){
86   free(p->azCksum);
87   memset(p, 0x55, sizeof(*p));
88   free(p);
89 }
90 
91 /* End of CksumDb code.
92 **************************************************************************/
93 
94 /*
95 ** Test utility function. Write key-value pair $i from datasource pData
96 ** into database pDb.
97 */
testWriteDatasource(TestDb * pDb,Datasource * pData,int i,int * pRc)98 void testWriteDatasource(TestDb *pDb, Datasource *pData, int i, int *pRc){
99   void *pKey; int nKey;
100   void *pVal; int nVal;
101   testDatasourceEntry(pData, i, &pKey, &nKey, &pVal, &nVal);
102   testWrite(pDb, pKey, nKey, pVal, nVal, pRc);
103 }
104 
105 /*
106 ** Test utility function. Delete datasource pData key $i from database pDb.
107 */
testDeleteDatasource(TestDb * pDb,Datasource * pData,int i,int * pRc)108 void testDeleteDatasource(TestDb *pDb, Datasource *pData, int i, int *pRc){
109   void *pKey; int nKey;
110   testDatasourceEntry(pData, i, &pKey, &nKey, 0, 0);
111   testDelete(pDb, pKey, nKey, pRc);
112 }
113 
114 /*
115 ** This function inserts nWrite key/value pairs into database pDb - the
116 ** nWrite key value pairs starting at iFirst from data source pData.
117 */
testWriteDatasourceRange(TestDb * pDb,Datasource * pData,int iFirst,int nWrite,int * pRc)118 void testWriteDatasourceRange(
119   TestDb *pDb,                    /* Database to write to */
120   Datasource *pData,              /* Data source to read values from */
121   int iFirst,                     /* Index of first key/value pair */
122   int nWrite,                     /* Number of key/value pairs to write */
123   int *pRc                        /* IN/OUT: Error code */
124 ){
125   int i;
126   for(i=0; i<nWrite; i++){
127     testWriteDatasource(pDb, pData, iFirst+i, pRc);
128   }
129 }
130 
testDeleteDatasourceRange(TestDb * pDb,Datasource * pData,int iFirst,int nWrite,int * pRc)131 void testDeleteDatasourceRange(
132   TestDb *pDb,                    /* Database to write to */
133   Datasource *pData,              /* Data source to read keys from */
134   int iFirst,                     /* Index of first key */
135   int nWrite,                     /* Number of keys to delete */
136   int *pRc                        /* IN/OUT: Error code */
137 ){
138   int i;
139   for(i=0; i<nWrite; i++){
140     testDeleteDatasource(pDb, pData, iFirst+i, pRc);
141   }
142 }
143 
getName(const char * zSystem)144 static char *getName(const char *zSystem){
145   char *zRet;
146   zRet = testMallocPrintf("rollback.%s", zSystem);
147   return zRet;
148 }
149 
rollback_test_1(const char * zSystem,Datasource * pData)150 static int rollback_test_1(
151   const char *zSystem,
152   Datasource *pData
153 ){
154   const int nRepeat = 100;
155 
156   TestDb *pDb;
157   int rc;
158   int i;
159   CksumDb *pCksum;
160   char *zName;
161 
162   zName = getName(zSystem);
163   testCaseStart(&rc, zName);
164   testFree(zName);
165 
166   pCksum = testCksumArrayNew(pData, 0, nRepeat*100, 100);
167   pDb = 0;
168   rc = tdb_open(zSystem, 0, 1, &pDb);
169   if( pDb && tdb_transaction_support(pDb)==0 ){
170     testCaseSkip();
171     goto skip_rollback_test;
172   }
173 
174   for(i=0; i<nRepeat && rc==0; i++){
175     char zCksum[TEST_CKSUM_BYTES];
176     int nCurrent = (((i+1)/2) * 100);
177     int nDbRow;
178     int iTrans;
179 
180     /* Check that the database is the expected size. */
181     nDbRow = testCountDatabase(pDb);
182     testCompareInt(nCurrent, nDbRow, &rc);
183 
184     for(iTrans=2; iTrans<=6 && rc==0; iTrans++){
185       tdb_begin(pDb, iTrans);
186       testWriteDatasourceRange(pDb, pData, nCurrent, 100, &rc);
187       nCurrent += 100;
188     }
189 
190     testCksumDatabase(pDb, zCksum);
191     testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc);
192 
193     for(iTrans=6; iTrans>2 && rc==0; iTrans--){
194       tdb_rollback(pDb, iTrans);
195       nCurrent -= 100;
196       testCksumDatabase(pDb, zCksum);
197       testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc);
198     }
199 
200     if( i%2 ){
201       tdb_rollback(pDb, 0);
202       nCurrent -= 100;
203       testCksumDatabase(pDb, zCksum);
204       testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc);
205     }else{
206       tdb_commit(pDb, 0);
207     }
208   }
209   testCaseFinish(rc);
210 
211  skip_rollback_test:
212   tdb_close(pDb);
213   testCksumArrayFree(pCksum);
214   return rc;
215 }
216 
test_rollback(const char * zSystem,const char * zPattern,int * pRc)217 void test_rollback(
218   const char *zSystem,
219   const char *zPattern,
220   int *pRc
221 ){
222   if( *pRc==0 ){
223     int bRun = 1;
224 
225     if( zPattern ){
226       char *zName = getName(zSystem);
227       bRun = testGlobMatch(zPattern, zName);
228       testFree(zName);
229     }
230 
231     if( bRun ){
232       DatasourceDefn defn = { TEST_DATASOURCE_RANDOM, 10, 15, 50, 100 };
233       Datasource *pData = testDatasourceNew(&defn);
234       *pRc = rollback_test_1(zSystem, pData);
235       testDatasourceFree(pData);
236     }
237   }
238 }
239