Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix some missing comments and other issues with session module code. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | sessions |
Files: | files | file ages | folders |
SHA1: |
20d7c280235201e519f296372f269e7c |
User & Date: | dan 2011-04-18 12:05:03.122 |
Context
2011-04-18
| ||
15:47 | Fix further missing comments and other minor issues in the session module code. (check-in: 99f0f35092 user: dan tags: sessions) | |
12:05 | Fix some missing comments and other issues with session module code. (check-in: 20d7c28023 user: dan tags: sessions) | |
07:36 | Further coverage tests for the session module. (check-in: 69a01c708b user: dan tags: sessions) | |
Changes
Changes to ext/session/sqlite3session.c.
︙ | ︙ | |||
132 133 134 135 136 137 138 | */ /* ** For each row modified during a session, there exists a single instance of ** this structure stored in a SessionTable.aChange[] hash table. */ struct SessionChange { | | | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | */ /* ** For each row modified during a session, there exists a single instance of ** this structure stored in a SessionTable.aChange[] hash table. */ struct SessionChange { int op; /* One of UPDATE, DELETE, INSERT */ int bIndirect; /* True if this change is "indirect" */ int nRecord; /* Number of bytes in buffer aRecord[] */ u8 *aRecord; /* Buffer containing old.* record */ SessionChange *pNext; /* For hash-table collisions */ }; /* |
︙ | ︙ | |||
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | if( aBuf ) aBuf[0] = '\0'; } *pnWrite += nByte; return SQLITE_OK; } #define HASH_APPEND(hash, add) ((hash) << 3) ^ (hash) ^ (unsigned int)(add) static unsigned int sessionHashAppendI64(unsigned int h, i64 i){ h = HASH_APPEND(h, i & 0xFFFFFFFF); return HASH_APPEND(h, (i>>32)&0xFFFFFFFF); } static unsigned int sessionHashAppendBlob(unsigned int h, int n, const u8 *z){ int i; for(i=0; i<n; i++) h = HASH_APPEND(h, z[i]); return h; } /* ** This function may only be called from within a pre-update callback. ** It calculates a hash based on the primary key values of the old.* or | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > | | | | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | if( aBuf ) aBuf[0] = '\0'; } *pnWrite += nByte; return SQLITE_OK; } /* ** This macro is used to calculate hash key values for data structures. In ** order to use this macro, the entire data structure must be represented ** as a series of unsigned integers. In order to calculate a hash-key value ** for a data structure represented as three such integers, the macro may ** then be used as follows: ** ** int hash_key_value; ** hash_key_value = HASH_APPEND(0, <value 1>); ** hash_key_value = HASH_APPEND(hash_key_value, <value 2>); ** hash_key_value = HASH_APPEND(hash_key_value, <value 3>); ** ** In practice, the data structures this macro is used for are the primary ** key values of modified rows. */ #define HASH_APPEND(hash, add) ((hash) << 3) ^ (hash) ^ (unsigned int)(add) /* ** Append the hash of the 64-bit integer passed as the second argument to the ** hash-key value passed as the first. Return the new hash-key value. */ static unsigned int sessionHashAppendI64(unsigned int h, i64 i){ h = HASH_APPEND(h, i & 0xFFFFFFFF); return HASH_APPEND(h, (i>>32)&0xFFFFFFFF); } /* ** Append the hash of the blob passed via the second and third arguments to ** the hash-key value passed as the first. Return the new hash-key value. */ static unsigned int sessionHashAppendBlob(unsigned int h, int n, const u8 *z){ int i; for(i=0; i<n; i++) h = HASH_APPEND(h, z[i]); return h; } /* ** Append the hash of the data type passed as the second argument to the ** hash-key value passed as the first. Return the new hash-key value. */ static unsigned int sessionHashAppendType(unsigned int h, int eType){ return HASH_APPEND(h, eType); } /* ** This function may only be called from within a pre-update callback. ** It calculates a hash based on the primary key values of the old.* or ** new.* row currently available and, assuming no error occurs, writes it to ** *piHash before returning. If the primary key contains one or more NULL ** values, *pbNullPK is set to true before returning. ** ** If an error occurs, an SQLite error code is returned and the final values ** of *piHash asn *pbNullPK are undefined. Otherwise, SQLITE_OK is returned ** and the output variables are set as described above. */ static int sessionPreupdateHash( sqlite3 *db, /* Database handle */ SessionTable *pTab, /* Session table handle */ int bNew, /* True to hash the new.* PK */ int *piHash, /* OUT: Hash value */ int *pbNullPK /* OUT: True if there are NULL values in PK */ ){ unsigned int h = 0; /* Hash value to return */ int i; /* Used to iterate through columns */ assert( *pbNullPK==0 ); assert( pTab->nCol==sqlite3_preupdate_count(db) ); for(i=0; i<pTab->nCol; i++){ if( pTab->abPK[i] ){ int rc; int eType; sqlite3_value *pVal; if( bNew ){ rc = sqlite3_preupdate_new(db, i, &pVal); }else{ rc = sqlite3_preupdate_old(db, i, &pVal); } if( rc!=SQLITE_OK ) return rc; eType = sqlite3_value_type(pVal); h = sessionHashAppendType(h, eType); if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ i64 iVal; if( eType==SQLITE_INTEGER ){ iVal = sqlite3_value_int64(pVal); }else{ double rVal = sqlite3_value_double(pVal); assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); |
︙ | ︙ | |||
352 353 354 355 356 357 358 | z = (const u8 *)sqlite3_value_blob(pVal); } if( !z ) return SQLITE_NOMEM; h = sessionHashAppendBlob(h, sqlite3_value_bytes(pVal), z); }else{ assert( eType==SQLITE_NULL ); *pbNullPK = 1; | < | 390 391 392 393 394 395 396 397 398 399 400 401 402 403 | z = (const u8 *)sqlite3_value_blob(pVal); } if( !z ) return SQLITE_NOMEM; h = sessionHashAppendBlob(h, sqlite3_value_bytes(pVal), z); }else{ assert( eType==SQLITE_NULL ); *pbNullPK = 1; } } } *piHash = (h % pTab->nChange); return SQLITE_OK; } |
︙ | ︙ | |||
377 378 379 380 381 382 383 | if( e==SQLITE_NULL ) return 1; if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9; return sessionVarintGet(&a[1], &n) + 1 + n; } /* ** Based on the primary key values stored in change aRecord, calculate a | | | 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | if( e==SQLITE_NULL ) return 1; if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9; return sessionVarintGet(&a[1], &n) + 1 + n; } /* ** Based on the primary key values stored in change aRecord, calculate a ** hash key. Assume the has table has nBucket buckets. The hash keys ** calculated by this function are compatible with those calculated by ** sessionPreupdateHash(). */ static unsigned int sessionChangeHash( SessionTable *pTab, /* Table handle */ u8 *aRecord, /* Change record */ int nBucket /* Assume this many buckets in hash table */ |
︙ | ︙ | |||
405 406 407 408 409 410 411 | || eType==SQLITE_TEXT || eType==SQLITE_BLOB || eType==SQLITE_NULL || eType==0 ); assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) ); if( isPK ){ a++; | | > > > > > > | | | | | | > > > > > > > > > > > > > < > | | | | | > > > > > > > > > > > > > > | | | | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 | || eType==SQLITE_TEXT || eType==SQLITE_BLOB || eType==SQLITE_NULL || eType==0 ); assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) ); if( isPK ){ a++; h = sessionHashAppendType(h, eType); if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ h = sessionHashAppendI64(h, sessionGetI64(a)); a += 8; }else{ int n; a += sessionVarintGet(a, &n); h = sessionHashAppendBlob(h, n, a); a += n; } }else{ a += sessionSerialLen(a); } } return (h % nBucket); } /* ** Arguments aLeft and aRight are pointers to change records for table pTab. ** This function returns true if the two records apply to the same row (i.e. ** have the same values stored in the primary key columns), or false ** otherwise. */ static int sessionChangeEqual( SessionTable *pTab, /* Table used for PK definition */ u8 *aLeft, /* Change record */ u8 *aRight /* Change record */ ){ u8 *a1 = aLeft; /* Cursor to iterate through aLeft */ u8 *a2 = aRight; /* Cursor to iterate through aRight */ int iCol; /* Used to iterate through table columns */ for(iCol=0; iCol<pTab->nCol; iCol++){ int n1 = sessionSerialLen(a1); int n2 = sessionSerialLen(a2); if( pTab->abPK[iCol] && (n1!=n2 || memcmp(a1, a2, n1)) ){ return 0; } a1 += n1; a2 += n2; } return 1; } /* ** Arguments aLeft and aRight both point to buffers containing change ** records with nCol columns. This function "merges" the two records into ** a single records which is written to the buffer at *paOut. *paOut is ** then set to point to one byte after the last byte written before ** returning. ** ** The merging of records is done as follows: For each column, if the ** aRight record contains a value for the column, copy the value from ** their. Otherwise, if aLeft contains a value, copy it. If neither ** record contains a value for a given column, then neither does the ** output record. */ static void sessionMergeRecord( u8 **paOut, int nCol, u8 *aLeft, u8 *aRight ){ u8 *a1 = aLeft; /* Cursor used to iterate through aLeft */ u8 *a2 = aRight; /* Cursor used to iterate through aRight */ u8 *aOut = *paOut; /* Output cursor */ int iCol; /* Used to iterate from 0 to nCol */ for(iCol=0; iCol<nCol; iCol++){ int n1 = sessionSerialLen(a1); int n2 = sessionSerialLen(a2); if( *a2 ){ memcpy(aOut, a2, n2); aOut += n2; }else{ memcpy(aOut, a1, n1); aOut += n1; } a1 += n1; a2 += n2; } *paOut = aOut; } /* ** This is a helper function used by sessionMergeUpdate(). ** ** When this function is called, both *paOne and *paTwo point to a value ** within a change record. Before it returns, both have been advanced so ** as to point to the next value in the record. ** ** If, when this function is called, *paTwo points to a valid value (i.e. ** *paTwo[0] is not 0x00 - the "no value" placeholder), a copy of the *paOne ** pointer is returned and *pnVal is set to the number of bytes in the ** serialized value. Otherwise, a copy of *paOne is returned and *pnVal ** set to the number of bytes in the value at *paOne. If *paOne points ** to the "no value" placeholder, *pnVal is set to 1. */ static u8 *sessionMergeValue( u8 **paOne, /* IN/OUT: Left-hand buffer pointer */ u8 **paTwo, /* IN/OUT: Right-hand buffer pointer */ int *pnVal /* OUT: Bytes in returned value */ ){ u8 *a1 = *paOne; u8 *a2 = *paTwo; u8 *pRet = 0; int n1; assert( a1 ); |
︙ | ︙ | |||
503 504 505 506 507 508 509 510 | pRet = a1; } *paOne = &a1[n1]; return pRet; } static int sessionMergeUpdate( | > > > > | | | | | | | 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 | pRet = a1; } *paOne = &a1[n1]; return pRet; } /* ** This function is used by changeset_concat() to merge two UPDATE changes ** on the same row. */ static int sessionMergeUpdate( u8 **paOut, /* IN/OUT: Pointer to output buffer */ SessionTable *pTab, /* Table change pertains to */ u8 *aOldRecord1, /* old.* record for first change */ u8 *aOldRecord2, /* old.* record for second change */ u8 *aNewRecord1, /* new.* record for first change */ u8 *aNewRecord2 /* new.* record for second change */ ){ u8 *aOld1 = aOldRecord1; u8 *aOld2 = aOldRecord2; u8 *aNew1 = aNewRecord1; u8 *aNew2 = aNewRecord2; u8 *aOut = *paOut; |
︙ | ︙ | |||
580 581 582 583 584 585 586 | ){ int i; u8 *a = pChange->aRecord; *pbEqual = 0; for(i=0; i<pTab->nCol; i++){ | < < < < < < < < < < | < < < < > | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 | ){ int i; u8 *a = pChange->aRecord; *pbEqual = 0; for(i=0; i<pTab->nCol; i++){ if( !pTab->abPK[i] ){ a += sessionSerialLen(a); }else{ sqlite3_value *pVal; /* Value returned by preupdate_new/old */ int rc; /* Error code from preupdate_new/old */ int eType = *a++; /* Type of value from change record */ /* The following calls to preupdate_new() and preupdate_old() can not ** fail. This is because they cache their return values, and by the ** time control flows to here they have already been called once from ** within sessionPreupdateHash(). The first two asserts below verify ** this (that the method has already been called). */ if( bNew ){ |
︙ | ︙ | |||
938 939 940 941 942 943 944 | } /* Add the change to the hash-table */ if( pSession->bIndirect || sqlite3_preupdate_depth(pSession->db) ){ pChange->bIndirect = 1; } pChange->nRecord = nByte; | | | 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 | } /* Add the change to the hash-table */ if( pSession->bIndirect || sqlite3_preupdate_depth(pSession->db) ){ pChange->bIndirect = 1; } pChange->nRecord = nByte; pChange->op = op; pChange->pNext = pTab->apChange[iHash]; pTab->apChange[iHash] = pChange; }else if( pC->bIndirect ){ /* If the existing change is considered "indirect", but this current ** change is "direct", mark the change object as direct. */ if( sqlite3_preupdate_depth(pSession->db)==0 && pSession->bIndirect==0 ){ |
︙ | ︙ | |||
1602 1603 1604 1605 1606 1607 1608 | for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){ SessionChange *p; /* Used to iterate through changes */ for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){ rc = sessionSelectBind(pSel, nCol, abPK, p); if( rc!=SQLITE_OK ) continue; if( sqlite3_step(pSel)==SQLITE_ROW ){ | > | < | | 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 | for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){ SessionChange *p; /* Used to iterate through changes */ for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){ rc = sessionSelectBind(pSel, nCol, abPK, p); if( rc!=SQLITE_OK ) continue; if( sqlite3_step(pSel)==SQLITE_ROW ){ if( p->op==SQLITE_INSERT ){ int iCol; sessionAppendByte(&buf, SQLITE_INSERT, &rc); sessionAppendByte(&buf, p->bIndirect, &rc); for(iCol=0; iCol<nCol; iCol++){ sessionAppendCol(&buf, pSel, iCol, &rc); } }else{ rc = sessionAppendUpdate(&buf, pSel, p, abPK); } }else if( p->op!=SQLITE_INSERT ){ /* A DELETE change */ sessionAppendByte(&buf, SQLITE_DELETE, &rc); sessionAppendByte(&buf, p->bIndirect, &rc); sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc); } if( rc==SQLITE_OK ){ rc = sqlite3_reset(pSel); |
︙ | ︙ | |||
2780 2781 2782 2783 2784 2785 2786 | if( !pExist ){ pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange)); if( !pNew ){ return SQLITE_NOMEM; } memset(pNew, 0, sizeof(SessionChange)); | | | | 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 | if( !pExist ){ pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange)); if( !pNew ){ return SQLITE_NOMEM; } memset(pNew, 0, sizeof(SessionChange)); pNew->op = op2; pNew->bIndirect = bIndirect; pNew->nRecord = nRec; pNew->aRecord = aRec; }else{ int op1 = pExist->op; /* ** op1=INSERT, op2=INSERT -> Unsupported. Discard op2. ** op1=INSERT, op2=UPDATE -> INSERT. ** op1=INSERT, op2=DELETE -> (none) ** ** op1=UPDATE, op2=INSERT -> Unsupported. Discard op2. |
︙ | ︙ | |||
2826 2827 2828 2829 2830 2831 2832 | memset(pNew, 0, sizeof(SessionChange)); pNew->bIndirect = (bIndirect && pExist->bIndirect); aCsr = pNew->aRecord = (u8 *)&pNew[1]; if( op1==SQLITE_INSERT ){ /* INSERT + UPDATE */ u8 *a1 = aRec; assert( op2==SQLITE_UPDATE ); | | | | | | | | 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 | memset(pNew, 0, sizeof(SessionChange)); pNew->bIndirect = (bIndirect && pExist->bIndirect); aCsr = pNew->aRecord = (u8 *)&pNew[1]; if( op1==SQLITE_INSERT ){ /* INSERT + UPDATE */ u8 *a1 = aRec; assert( op2==SQLITE_UPDATE ); pNew->op = SQLITE_INSERT; sessionReadRecord(&a1, pTab->nCol, 0); sessionMergeRecord(&aCsr, pTab->nCol, pExist->aRecord, a1); }else if( op1==SQLITE_DELETE ){ /* DELETE + INSERT */ assert( op2==SQLITE_INSERT ); pNew->op = SQLITE_UPDATE; if( 0==sessionMergeUpdate(&aCsr, pTab, pExist->aRecord, 0, aRec, 0) ){ sqlite3_free(pNew); pNew = 0; } }else if( op2==SQLITE_UPDATE ){ /* UPDATE + UPDATE */ assert( op1==SQLITE_UPDATE ); u8 *a1 = pExist->aRecord; u8 *a2 = aRec; sessionReadRecord(&a1, pTab->nCol, 0); sessionReadRecord(&a2, pTab->nCol, 0); pNew->op = SQLITE_UPDATE; if( 0==sessionMergeUpdate(&aCsr, pTab, aRec, pExist->aRecord, a1, a2) ){ sqlite3_free(pNew); pNew = 0; } }else{ /* UPDATE + DELETE */ assert( op1==SQLITE_UPDATE && op2==SQLITE_DELETE ); pNew->op = SQLITE_DELETE; sessionMergeRecord(&aCsr, pTab->nCol, aRec, pExist->aRecord); } if( pNew ){ pNew->nRecord = (aCsr - pNew->aRecord); } sqlite3_free(pExist); } |
︙ | ︙ | |||
3002 3003 3004 3005 3006 3007 3008 | int i; if( pTab->nEntry==0 ) continue; sessionAppendTableHdr(&buf, pTab, &rc); for(i=0; i<pTab->nChange; i++){ SessionChange *p; for(p=pTab->apChange[i]; p; p=p->pNext){ | | | 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 | int i; if( pTab->nEntry==0 ) continue; sessionAppendTableHdr(&buf, pTab, &rc); for(i=0; i<pTab->nChange; i++){ SessionChange *p; for(p=pTab->apChange[i]; p; p=p->pNext){ sessionAppendByte(&buf, p->op, &rc); sessionAppendByte(&buf, p->bIndirect, &rc); sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc); } } } if( rc==SQLITE_OK ){ |
︙ | ︙ |