blob: a31d04ff4eaa763316be828dfb5404295fd32da7 [file] [log] [blame]
danc18d3042016-08-22 20:49:06 +00001
2#if !defined(SQLITE_TEST) || (defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK))
3
4#include "sqlite3session.h"
5#include "sqlite3changebatch.h"
6
7#include <assert.h>
8#include <string.h>
9
10typedef struct BatchTable BatchTable;
11typedef struct BatchIndex BatchIndex;
12typedef struct BatchIndexEntry BatchIndexEntry;
13typedef struct BatchHash BatchHash;
14
15struct sqlite3_changebatch {
16 sqlite3 *db; /* Database handle used to read schema */
17 BatchTable *pTab; /* First in linked list of tables */
18 int iChangesetId; /* Current changeset id */
19 int iNextIdxId; /* Next available index id */
20 int nEntry; /* Number of entries in hash table */
21 int nHash; /* Number of hash buckets */
22 BatchIndexEntry **apHash; /* Array of hash buckets */
23};
24
25struct BatchTable {
26 BatchIndex *pIdx; /* First in linked list of UNIQUE indexes */
27 BatchTable *pNext; /* Next table */
28 char zTab[1]; /* Table name */
29};
30
31struct BatchIndex {
32 BatchIndex *pNext; /* Next index on same table */
33 int iId; /* Index id (assigned internally) */
34 int bPk; /* True for PK index */
35 int nCol; /* Size of aiCol[] array */
36 int *aiCol; /* Array of columns that make up index */
37};
38
39struct BatchIndexEntry {
40 BatchIndexEntry *pNext; /* Next colliding hash table entry */
41 int iChangesetId; /* Id of associated changeset */
42 int iIdxId; /* Id of index this key is from */
43 int szRecord;
44 char aRecord[1];
45};
46
47/*
48** Allocate and zero a block of nByte bytes. Must be freed using cbFree().
49*/
50static void *cbMalloc(int *pRc, int nByte){
51 void *pRet;
52
53 if( *pRc ){
54 pRet = 0;
55 }else{
56 pRet = sqlite3_malloc(nByte);
57 if( pRet ){
58 memset(pRet, 0, nByte);
59 }else{
60 *pRc = SQLITE_NOMEM;
61 }
62 }
63
64 return pRet;
65}
66
67/*
68** Free an allocation made by cbMalloc().
69*/
70static void cbFree(void *p){
71 sqlite3_free(p);
72}
73
74/*
75** Return the hash bucket that pEntry belongs in.
76*/
77static int cbHash(sqlite3_changebatch *p, BatchIndexEntry *pEntry){
78 unsigned int iHash = (unsigned int)pEntry->iIdxId;
79 unsigned char *pEnd = (unsigned char*)&pEntry->aRecord[pEntry->szRecord];
80 unsigned char *pIter;
81
drh7d931b92016-08-23 18:09:37 +000082 for(pIter=(unsigned char*)pEntry->aRecord; pIter<pEnd; pIter++){
danc18d3042016-08-22 20:49:06 +000083 iHash += (iHash << 7) + *pIter;
84 }
85
86 return (int)(iHash % p->nHash);
87}
88
89/*
90** Resize the hash table.
91*/
92static int cbHashResize(sqlite3_changebatch *p){
93 int rc = SQLITE_OK;
94 BatchIndexEntry **apNew;
95 int nNew = (p->nHash ? p->nHash*2 : 512);
96 int i;
97
98 apNew = cbMalloc(&rc, sizeof(BatchIndexEntry*) * nNew);
99 if( rc==SQLITE_OK ){
100 int nHash = p->nHash;
101 p->nHash = nNew;
102 for(i=0; i<nHash; i++){
103 BatchIndexEntry *pEntry;
drh7d931b92016-08-23 18:09:37 +0000104 while( (pEntry=p->apHash[i])!=0 ){
danc18d3042016-08-22 20:49:06 +0000105 int iHash = cbHash(p, pEntry);
106 p->apHash[i] = pEntry->pNext;
107 pEntry->pNext = apNew[iHash];
108 apNew[iHash] = pEntry;
109 }
110 }
111
112 cbFree(p->apHash);
113 p->apHash = apNew;
114 }
115
116 return rc;
117}
118
119
120/*
121** Allocate a new sqlite3_changebatch object.
122*/
123int sqlite3changebatch_new(sqlite3 *db, sqlite3_changebatch **pp){
124 sqlite3_changebatch *pRet;
125 int rc = SQLITE_OK;
126 *pp = pRet = (sqlite3_changebatch*)cbMalloc(&rc, sizeof(sqlite3_changebatch));
127 if( pRet ){
128 pRet->db = db;
129 }
130 return rc;
131}
132
133/*
134** Add a BatchIndex entry for index zIdx to table pTab.
135*/
136static int cbAddIndex(
137 sqlite3_changebatch *p,
138 BatchTable *pTab,
139 const char *zIdx,
140 int bPk
141){
142 int nCol = 0;
143 sqlite3_stmt *pIndexInfo = 0;
144 BatchIndex *pNew = 0;
145 int rc;
146 char *zIndexInfo;
147
148 zIndexInfo = (char*)sqlite3_mprintf("PRAGMA main.index_info = %Q", zIdx);
149 if( zIndexInfo ){
150 rc = sqlite3_prepare_v2(p->db, zIndexInfo, -1, &pIndexInfo, 0);
151 sqlite3_free(zIndexInfo);
152 }else{
153 rc = SQLITE_NOMEM;
154 }
155
156 if( rc==SQLITE_OK ){
danc18d3042016-08-22 20:49:06 +0000157 while( SQLITE_ROW==sqlite3_step(pIndexInfo) ){ nCol++; }
dana8dee8d2016-08-23 19:02:21 +0000158 rc = sqlite3_reset(pIndexInfo);
danc18d3042016-08-22 20:49:06 +0000159 }
160
161 pNew = (BatchIndex*)cbMalloc(&rc, sizeof(BatchIndex) + sizeof(int) * nCol);
162 if( rc==SQLITE_OK ){
163 int rc2;
164 pNew->nCol = nCol;
165 pNew->bPk = bPk;
166 pNew->aiCol = (int*)&pNew[1];
167 pNew->iId = p->iNextIdxId++;
168 while( SQLITE_ROW==sqlite3_step(pIndexInfo) ){
169 int i = sqlite3_column_int(pIndexInfo, 0);
170 int j = sqlite3_column_int(pIndexInfo, 1);
171 pNew->aiCol[i] = j;
172 }
dana8dee8d2016-08-23 19:02:21 +0000173 rc = sqlite3_reset(pIndexInfo);
danc18d3042016-08-22 20:49:06 +0000174 }
175
176 if( rc==SQLITE_OK ){
177 pNew->pNext = pTab->pIdx;
178 pTab->pIdx = pNew;
179 }else{
180 cbFree(pNew);
181 }
182 sqlite3_finalize(pIndexInfo);
183
184 return rc;
185}
186
187/*
dana8dee8d2016-08-23 19:02:21 +0000188** Free the object passed as the first argument.
189*/
190static void cbFreeTable(BatchTable *pTab){
191 BatchIndex *pIdx;
192 BatchIndex *pIdxNext;
193 for(pIdx=pTab->pIdx; pIdx; pIdx=pIdxNext){
194 pIdxNext = pIdx->pNext;
195 cbFree(pIdx);
196 }
197 cbFree(pTab);
198}
199
200/*
danc18d3042016-08-22 20:49:06 +0000201** Find or create the BatchTable object named zTab.
202*/
203static int cbFindTable(
204 sqlite3_changebatch *p,
205 const char *zTab,
206 BatchTable **ppTab
207){
208 BatchTable *pRet = 0;
209 int rc = SQLITE_OK;
210
211 for(pRet=p->pTab; pRet; pRet=pRet->pNext){
212 if( 0==sqlite3_stricmp(zTab, pRet->zTab) ) break;
213 }
214
215 if( pRet==0 ){
216 int nTab = strlen(zTab);
217 pRet = (BatchTable*)cbMalloc(&rc, nTab + sizeof(BatchTable));
218 if( pRet ){
219 sqlite3_stmt *pIndexList = 0;
220 char *zIndexList = 0;
221 int rc2;
222 memcpy(pRet->zTab, zTab, nTab);
223
224 zIndexList = sqlite3_mprintf("PRAGMA main.index_list = %Q", zTab);
225 if( zIndexList==0 ){
226 rc = SQLITE_NOMEM;
227 }else{
228 rc = sqlite3_prepare_v2(p->db, zIndexList, -1, &pIndexList, 0);
229 sqlite3_free(zIndexList);
230 }
231
232 while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pIndexList) ){
233 if( sqlite3_column_int(pIndexList, 2) ){
234 const char *zIdx = (const char*)sqlite3_column_text(pIndexList, 1);
235 const char *zTyp = (const char*)sqlite3_column_text(pIndexList, 3);
236 rc = cbAddIndex(p, pRet, zIdx, (zTyp[0]=='p'));
237 }
238 }
239 rc2 = sqlite3_finalize(pIndexList);
240 if( rc==SQLITE_OK ) rc = rc2;
241
242 if( rc==SQLITE_OK ){
243 pRet->pNext = p->pTab;
244 p->pTab = pRet;
dana8dee8d2016-08-23 19:02:21 +0000245 }else{
246 cbFreeTable(pRet);
247 pRet = 0;
danc18d3042016-08-22 20:49:06 +0000248 }
249 }
250 }
251
252 *ppTab = pRet;
253 return rc;
254}
255
dana8dee8d2016-08-23 19:02:21 +0000256/*
257** Extract value iVal from the changeset iterator passed as the first
258** argument. Set *ppVal to point to the value before returning.
259**
260** This function attempts to extract the value using function xVal
261** (which is always either sqlite3changeset_new or sqlite3changeset_old).
262** If the call returns SQLITE_OK but does not supply an sqlite3_value*
263** pointer, an attempt to extract the value is made using the xFallback
264** function.
265*/
dan8bbf5442016-08-23 17:02:28 +0000266static int cbGetChangesetValue(
267 sqlite3_changeset_iter *pIter,
268 int (*xVal)(sqlite3_changeset_iter*,int,sqlite3_value**),
269 int (*xFallback)(sqlite3_changeset_iter*,int,sqlite3_value**),
270 int iVal,
271 sqlite3_value **ppVal
272){
273 int rc = xVal(pIter, iVal, ppVal);
274 if( rc==SQLITE_OK && *ppVal==0 && xFallback ){
275 rc = xFallback(pIter, iVal, ppVal);
276 }
277 return rc;
278}
279
danc18d3042016-08-22 20:49:06 +0000280static int cbAddToHash(
281 sqlite3_changebatch *p,
282 sqlite3_changeset_iter *pIter,
283 BatchIndex *pIdx,
284 int (*xVal)(sqlite3_changeset_iter*,int,sqlite3_value**),
dan8bbf5442016-08-23 17:02:28 +0000285 int (*xFallback)(sqlite3_changeset_iter*,int,sqlite3_value**),
danc18d3042016-08-22 20:49:06 +0000286 int *pbConf
287){
288 BatchIndexEntry *pNew;
289 int sz = pIdx->nCol;
290 int i;
291 int iOut = 0;
292 int rc = SQLITE_OK;
293
294 for(i=0; rc==SQLITE_OK && i<pIdx->nCol; i++){
295 sqlite3_value *pVal;
dan8bbf5442016-08-23 17:02:28 +0000296 rc = cbGetChangesetValue(pIter, xVal, xFallback, pIdx->aiCol[i], &pVal);
danc18d3042016-08-22 20:49:06 +0000297 if( rc==SQLITE_OK ){
298 int eType = 0;
dan8bbf5442016-08-23 17:02:28 +0000299 if( pVal ) eType = sqlite3_value_type(pVal);
danc18d3042016-08-22 20:49:06 +0000300 switch( eType ){
301 case 0:
302 case SQLITE_NULL:
303 return SQLITE_OK;
304
305 case SQLITE_INTEGER:
306 sz += 8;
307 break;
308 case SQLITE_FLOAT:
309 sz += 8;
310 break;
311
312 default:
313 assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
314 sz += sqlite3_value_bytes(pVal);
315 break;
316 }
317 }
318 }
319
320 pNew = cbMalloc(&rc, sizeof(BatchIndexEntry) + sz);
321 if( pNew ){
322 pNew->iChangesetId = p->iChangesetId;
323 pNew->iIdxId = pIdx->iId;
324 pNew->szRecord = sz;
325
dana8dee8d2016-08-23 19:02:21 +0000326 for(i=0; i<pIdx->nCol; i++){
327 int eType;
danc18d3042016-08-22 20:49:06 +0000328 sqlite3_value *pVal;
dan8bbf5442016-08-23 17:02:28 +0000329 rc = cbGetChangesetValue(pIter, xVal, xFallback, pIdx->aiCol[i], &pVal);
dana8dee8d2016-08-23 19:02:21 +0000330 if( rc!=SQLITE_OK ) break; /* coverage: condition is never true */
331 eType = sqlite3_value_type(pVal);
332 pNew->aRecord[iOut++] = eType;
333 switch( eType ){
334 case SQLITE_INTEGER: {
335 sqlite3_int64 i64 = sqlite3_value_int64(pVal);
336 memcpy(&pNew->aRecord[iOut], &i64, 8);
337 iOut += 8;
338 break;
339 }
340 case SQLITE_FLOAT: {
341 double d64 = sqlite3_value_double(pVal);
342 memcpy(&pNew->aRecord[iOut], &d64, sizeof(double));
343 iOut += sizeof(double);
344 break;
345 }
danc18d3042016-08-22 20:49:06 +0000346
dana8dee8d2016-08-23 19:02:21 +0000347 default: {
348 int nByte = sqlite3_value_bytes(pVal);
349 const char *z = (const char*)sqlite3_value_blob(pVal);
350 memcpy(&pNew->aRecord[iOut], z, nByte);
351 iOut += nByte;
352 break;
danc18d3042016-08-22 20:49:06 +0000353 }
354 }
355 }
356 }
357
358 if( rc==SQLITE_OK && p->nEntry>=(p->nHash/2) ){
359 rc = cbHashResize(p);
360 }
361
362 if( rc==SQLITE_OK ){
363 BatchIndexEntry *pIter;
364 int iHash = cbHash(p, pNew);
365
366 assert( iHash>=0 && iHash<p->nHash );
367 for(pIter=p->apHash[iHash]; pIter; pIter=pIter->pNext){
368 if( pNew->szRecord==pIter->szRecord
369 && 0==memcmp(pNew->aRecord, pIter->aRecord, pNew->szRecord)
370 ){
371 if( pNew->iChangesetId!=pIter->iChangesetId ){
372 *pbConf = 1;
373 }
374 cbFree(pNew);
375 pNew = 0;
376 break;
377 }
378 }
379
380 if( pNew ){
381 pNew->pNext = p->apHash[iHash];
382 p->apHash[iHash] = pNew;
383 p->nEntry++;
384 }
dana8dee8d2016-08-23 19:02:21 +0000385 }else{
386 cbFree(pNew);
danc18d3042016-08-22 20:49:06 +0000387 }
388
danc18d3042016-08-22 20:49:06 +0000389 return rc;
390}
391
392
393/*
394** Add a changeset to the current batch.
395*/
396int sqlite3changebatch_add(sqlite3_changebatch *p, void *pBuf, int nBuf){
397 sqlite3_changeset_iter *pIter; /* Iterator opened on pBuf/nBuf */
398 int rc; /* Return code */
399 int bConf = 0; /* Conflict was detected */
400
401 rc = sqlite3changeset_start(&pIter, nBuf, pBuf);
402 if( rc==SQLITE_OK ){
403 int rc2;
404 for(rc2 = sqlite3changeset_next(pIter);
405 rc2==SQLITE_ROW;
406 rc2 = sqlite3changeset_next(pIter)
407 ){
408 BatchTable *pTab;
409 BatchIndex *pIdx;
410 const char *zTab; /* Table this change applies to */
411 int nCol; /* Number of columns in table */
412 int op; /* UPDATE, INSERT or DELETE */
413
414 sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
415 assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
416
417 rc = cbFindTable(p, zTab, &pTab);
dana8dee8d2016-08-23 19:02:21 +0000418 assert( pTab || rc!=SQLITE_OK );
419 if( pTab ){
420 for(pIdx=pTab->pIdx; pIdx && rc==SQLITE_OK; pIdx=pIdx->pNext){
421 if( op==SQLITE_UPDATE && pIdx->bPk ) continue;
422 if( op==SQLITE_UPDATE || op==SQLITE_DELETE ){
423 rc = cbAddToHash(p, pIter, pIdx, sqlite3changeset_old, 0, &bConf);
424 }
425 if( op==SQLITE_UPDATE || op==SQLITE_INSERT ){
426 rc = cbAddToHash(p, pIter, pIdx,
427 sqlite3changeset_new, sqlite3changeset_old, &bConf
428 );
429 }
danc18d3042016-08-22 20:49:06 +0000430 }
431 }
432 if( rc!=SQLITE_OK ) break;
433 }
434
435 rc2 = sqlite3changeset_finalize(pIter);
436 if( rc==SQLITE_OK ) rc = rc2;
437 }
438
439 if( rc==SQLITE_OK && bConf ){
440 rc = SQLITE_CONSTRAINT;
441 }
dana8dee8d2016-08-23 19:02:21 +0000442 p->iChangesetId++;
danc18d3042016-08-22 20:49:06 +0000443 return rc;
444}
445
446/*
447** Zero an existing changebatch object.
448*/
449void sqlite3changebatch_zero(sqlite3_changebatch *p){
450 int i;
451 for(i=0; i<p->nHash; i++){
452 BatchIndexEntry *pEntry;
453 BatchIndexEntry *pNext;
454 for(pEntry=p->apHash[i]; pEntry; pEntry=pNext){
455 pNext = pEntry->pNext;
456 cbFree(pEntry);
457 }
458 }
459 cbFree(p->apHash);
460 p->nHash = 0;
461 p->apHash = 0;
462}
463
464/*
465** Delete a changebatch object.
466*/
467void sqlite3changebatch_delete(sqlite3_changebatch *p){
468 BatchTable *pTab;
469 BatchTable *pTabNext;
470
471 sqlite3changebatch_zero(p);
472 for(pTab=p->pTab; pTab; pTab=pTabNext){
danc18d3042016-08-22 20:49:06 +0000473 pTabNext = pTab->pNext;
dana8dee8d2016-08-23 19:02:21 +0000474 cbFreeTable(pTab);
danc18d3042016-08-22 20:49:06 +0000475 }
476 cbFree(p);
477}
478
479#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */