/ Check-in [6614cfcb]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Change to the session module to use user-defined primary keys instead of rowids when collecting changes.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: 6614cfcb9c41da71ddec3c44a3de0d4d849e1cdd
User & Date: dan 2011-03-17 19:20:27
Context
2011-03-18
12:35
Merge all the latest trunk enhancements into the sessions branch. check-in: 94fd5bb6 user: drh tags: sessions
2011-03-17
19:20
Change to the session module to use user-defined primary keys instead of rowids when collecting changes. check-in: 6614cfcb user: dan tags: sessions
2011-03-16
19:59
Add the sqlite3_preupdate_new() API, for retrieving the new.* values from within a pre-update callback. check-in: 526545c4 user: dan tags: sessions
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/session/sqlite3session.c.

     4      4   #include "sqlite3session.h"
     5      5   #include <assert.h>
     6      6   #include <string.h>
     7      7   
     8      8   #include "sqliteInt.h"
     9      9   #include "vdbeInt.h"
    10     10   
    11         -typedef struct RowChange RowChange;
    12     11   typedef struct SessionTable SessionTable;
    13     12   typedef struct SessionChange SessionChange;
    14     13   typedef struct SessionBuffer SessionBuffer;
    15     14   
    16     15   /*
    17     16   ** Session handle structure.
    18     17   */
................................................................................
    53     52   ** a subset of the initial values that the modified row contained at the
    54     53   ** start of the session. Or no initial values if the row was inserted.
    55     54   */
    56     55   struct SessionTable {
    57     56     SessionTable *pNext;
    58     57     char *zName;                    /* Local name of table */
    59     58     int nCol;                       /* Number of columns in table zName */
           59  +  const char **azCol;             /* Column names */
           60  +  u8 *abPK;                       /* Array of primary key flags */
    60     61     int nEntry;                     /* Total number of entries in hash table */
    61     62     int nChange;                    /* Size of apChange[] array */
    62     63     SessionChange **apChange;       /* Hash table buckets */
    63     64   };
    64     65   
    65     66   /* 
    66     67   ** RECORD FORMAT:
................................................................................
   124    125   */
   125    126   
   126    127   /*
   127    128   ** For each row modified during a session, there exists a single instance of
   128    129   ** this structure stored in a SessionTable.aChange[] hash table.
   129    130   */
   130    131   struct SessionChange {
   131         -  sqlite3_int64 iKey;             /* Key value */
          132  +  int bInsert;                    /* True if row was inserted this session */
   132    133     int nRecord;                    /* Number of bytes in buffer aRecord[] */
   133    134     u8 *aRecord;                    /* Buffer containing old.* record */
   134    135     SessionChange *pNext;           /* For hash-table collisions */
   135    136   };
   136    137   
   137    138   /*
   138    139   ** Instances of this structure are used to build strings or binary records.
................................................................................
   260    261       }
   261    262     }
   262    263   
   263    264     *pnWrite += nByte;
   264    265     return SQLITE_OK;
   265    266   }
   266    267   
   267         -/*
   268         -** Return the hash of iKey, assuming there are nBucket hash buckets in
   269         -** the hash table.
   270         -*/
   271         -static int sessionKeyhash(int nBucket, sqlite3_int64 iKey){
   272         -  return (iKey % nBucket);
          268  +#define HASH_APPEND(hash, add) ((hash) << 3) ^ (hash) ^ (int)(add)
          269  +static int sessionHashAppendI64(int h, i64 i){
          270  +  h = HASH_APPEND(h, i & 0xFFFFFFFF);
          271  +  return HASH_APPEND(h, (i>>32)&0xFFFFFFFF);
          272  +}
          273  +static int sessionHashAppendBlob(int h, int n, const u8 *z){
          274  +  int i;
          275  +  for(i=0; i<n; i++) h = HASH_APPEND(h, z[i]);
          276  +  return h;
          277  +}
          278  +
          279  +/*
          280  +** This function calculates a hash based on the primary key values of
          281  +** the old.* or new.* row currently available.
          282  +*/
          283  +static int sessionPreupdateHash(
          284  +  sqlite3 *db,                    /* Database handle */
          285  +  SessionTable *pTab,             /* Session table handle */
          286  +  int bNew,                       /* True to hash the new.* PK */
          287  +  int *piHash                     /* OUT: Hash value */
          288  +){
          289  +  int h = 0;
          290  +  int i;
          291  +
          292  +  assert( pTab->nCol==sqlite3_preupdate_count(db) );
          293  +  for(i=0; i<pTab->nCol; i++){
          294  +    if( pTab->abPK[i] ){
          295  +      int rc;
          296  +      int eType;
          297  +      sqlite3_value *pVal;
          298  +
          299  +      if( bNew ){
          300  +        rc = sqlite3_preupdate_new(db, i, &pVal);
          301  +      }else{
          302  +        rc = sqlite3_preupdate_old(db, i, &pVal);
          303  +      }
          304  +
          305  +      eType = sqlite3_value_type(pVal);
          306  +      h = HASH_APPEND(h, eType);
          307  +      switch( eType ){
          308  +        case SQLITE_INTEGER: 
          309  +        case SQLITE_FLOAT: {
          310  +          i64 iVal;
          311  +          if( eType==SQLITE_INTEGER ){
          312  +            iVal = sqlite3_value_int64(pVal);
          313  +          }else{
          314  +            double rVal = sqlite3_value_double(pVal);
          315  +            assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
          316  +            memcpy(&iVal, &rVal, 8);
          317  +          }
          318  +          h = sessionHashAppendI64(h, iVal);
          319  +          break;
          320  +        }
          321  +
          322  +        case SQLITE_TEXT: 
          323  +        case SQLITE_BLOB: {
          324  +          int n = sqlite3_value_bytes(pVal);
          325  +          const u8 *z = eType==SQLITE_TEXT ?
          326  +            sqlite3_value_text(pVal) : sqlite3_value_blob(pVal);
          327  +          h = sessionHashAppendBlob(h, n, z);
          328  +          break;
          329  +        }
          330  +      }
          331  +    }
          332  +  }
          333  +
          334  +  *piHash = (h % pTab->nChange);
          335  +  return SQLITE_OK;
          336  +}
          337  +
          338  +static int sessionChangeHash(
          339  +  sqlite3 *db,
          340  +  SessionTable *pTab,
          341  +  SessionChange *pChange,
          342  +  int nBucket
          343  +){
          344  +  int h = 0;
          345  +  int i;
          346  +  u8 *a = pChange->aRecord;
          347  +
          348  +  for(i=0; i<pTab->nCol; i++){
          349  +    int eType = *a++;
          350  +    int isPK = pTab->abPK[i];
          351  +
          352  +    if( isPK ) h = HASH_APPEND(h, eType);
          353  +    switch( eType ){
          354  +      case SQLITE_INTEGER:
          355  +      case SQLITE_FLOAT: {
          356  +        if( isPK ){
          357  +          i64 iVal = sessionGetI64(a);
          358  +          h = sessionHashAppendI64(h, iVal);
          359  +        }
          360  +        a += 8;
          361  +        break;
          362  +      }
          363  +      case SQLITE_TEXT:
          364  +      case SQLITE_BLOB: {
          365  +        int n;
          366  +        a += sessionVarintGet(a, &n);
          367  +        if( isPK ){
          368  +          h = sessionHashAppendBlob(h, n, a);
          369  +        }
          370  +        a += n;
          371  +        break;
          372  +      }
          373  +    }
          374  +  }
          375  +  return (h % nBucket);
          376  +}
          377  +
          378  +static int sessionPreupdateEqual(
          379  +  sqlite3 *db,
          380  +  SessionTable *pTab,
          381  +  SessionChange *pChange,
          382  +  int bNew,
          383  +  int *pbEqual
          384  +){
          385  +  int i;
          386  +  u8 *a = pChange->aRecord;
          387  +
          388  +  *pbEqual = 0;
          389  +
          390  +  for(i=0; i<pTab->nCol; i++){
          391  +    int eType = *a++;
          392  +    if( !pTab->abPK[i] ){
          393  +      switch( eType ){
          394  +        case SQLITE_INTEGER: 
          395  +        case SQLITE_FLOAT:
          396  +          a += 8;
          397  +          break;
          398  +
          399  +        case SQLITE_TEXT: 
          400  +        case SQLITE_BLOB: {
          401  +          int n;
          402  +          a += sessionVarintGet(a, &n);
          403  +          a += n;
          404  +          break;
          405  +        }
          406  +      }
          407  +    }else{
          408  +      sqlite3_value *pVal;
          409  +      int rc;
          410  +      if( bNew ){
          411  +        rc = sqlite3_preupdate_new(db, i, &pVal);
          412  +      }else{
          413  +        rc = sqlite3_preupdate_old(db, i, &pVal);
          414  +      }
          415  +      if( rc!=SQLITE_OK || sqlite3_value_type(pVal)!=eType ) return rc;
          416  +
          417  +      switch( eType ){
          418  +        case SQLITE_INTEGER:
          419  +        case SQLITE_FLOAT: {
          420  +          i64 iVal = sessionGetI64(a);
          421  +          a += 8;
          422  +          if( eType==SQLITE_INTEGER ){
          423  +            if( sqlite3_value_int64(pVal)!=iVal ) return SQLITE_OK;
          424  +          }else{
          425  +            double rVal;
          426  +            assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
          427  +            memcpy(&rVal, &iVal, 8);
          428  +            if( sqlite3_value_double(pVal)!=rVal ) return SQLITE_OK;
          429  +          }
          430  +          break;
          431  +        }
          432  +        case SQLITE_TEXT:
          433  +        case SQLITE_BLOB: {
          434  +          int n;
          435  +          const u8 *z;
          436  +          a += sessionVarintGet(a, &n);
          437  +          if( sqlite3_value_bytes(pVal)!=n ) return SQLITE_OK;
          438  +          z = eType==SQLITE_TEXT ? 
          439  +            sqlite3_value_text(pVal) : sqlite3_value_blob(pVal);
          440  +          if( memcmp(a, z, n) ) return SQLITE_OK;
          441  +          a += n;
          442  +          break;
          443  +        }
          444  +      }
          445  +    }
          446  +  }
          447  +
          448  +  *pbEqual = 1;
          449  +  return SQLITE_OK;
   273    450   }
   274    451   
   275    452   /*
   276    453   ** If required, grow the hash table used to store changes on table pTab 
   277    454   ** (part of the session pSession). If a fatal OOM error occurs, set the
   278    455   ** session object to failed and return SQLITE_ERROR. Otherwise, return
   279    456   ** SQLITE_OK.
................................................................................
   299    476       }
   300    477       memset(apNew, 0, sizeof(SessionChange *) * nNew);
   301    478   
   302    479       for(i=0; i<pTab->nChange; i++){
   303    480         SessionChange *p;
   304    481         SessionChange *pNext;
   305    482         for(p=pTab->apChange[i]; p; p=pNext){
   306         -        int iHash = sessionKeyhash(nNew, p->iKey);
          483  +        int iHash = sessionChangeHash(pSession->db, pTab, p, nNew);
   307    484           pNext = p->pNext;
   308    485           p->pNext = apNew[iHash];
   309    486           apNew[iHash] = p;
   310    487         }
   311    488       }
   312    489   
   313    490       sqlite3_free(pTab->apChange);
   314    491       pTab->nChange = nNew;
   315    492       pTab->apChange = apNew;
   316    493     }
   317    494   
   318    495     return SQLITE_OK;
   319    496   }
          497  +
          498  +/*
          499  +** This function queries the database for the names of the columns of table
          500  +** zThis, in schema zDb. It is expected that the table has nCol columns. If
          501  +** not, SQLITE_SCHEMA is returned and none of the output variables are
          502  +** populated.
          503  +**
          504  +** Otherwise, if it is not NULL, variable *pzTab is set to point to a
          505  +** nul-terminated copy of the table name. *pazCol (if not NULL) is set to
          506  +** point to an array of pointers to column names. And *pabPK (again, if not
          507  +** NULL) is set to point to an array of booleans - true if the corresponding
          508  +** column is part of the primary key.
          509  +**
          510  +** For example, if the table is declared as:
          511  +**
          512  +**     CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z));
          513  +**
          514  +** Then the three output variables are populated as follows:
          515  +**
          516  +**     *pzTab  = "tbl1"
          517  +**     *pazCol = {"w", "x", "y", "z"}
          518  +**     *pabPK  = {1, 0, 0, 1}
          519  +**
          520  +** All returned buffers are part of the same single allocation, which must
          521  +** be freed using sqlite3_free() by the caller. If pazCol was not NULL, then
          522  +** pointer *pazCol should be freed to release all memory. Otherwise, pointer
          523  +** *pabPK. It is illegal for both pazCol and pabPK to be NULL.
          524  +*/
          525  +static int sessionTableInfo(
          526  +  sqlite3 *db,                    /* Database connection */
          527  +  const char *zDb,                /* Name of attached database (e.g. "main") */
          528  +  const char *zThis,              /* Table name */
          529  +  int nCol,                       /* Expected number of columns */
          530  +  const char **pzTab,             /* OUT: Copy of zThis */
          531  +  const char ***pazCol,           /* OUT: Array of column names for table */
          532  +  u8 **pabPK                      /* OUT: Array of booleans - true for PK col */
          533  +){
          534  +  char *zPragma;
          535  +  sqlite3_stmt *pStmt;
          536  +  int rc;
          537  +  int nByte;
          538  +  int nDbCol = 0;
          539  +  int nThis;
          540  +  int i;
          541  +  u8 *pAlloc;
          542  +  u8 *pFree = 0;
          543  +  char **azCol;
          544  +  u8 *abPK;
          545  +
          546  +  assert( pazCol || pabPK );
          547  +
          548  +  nThis = strlen(zThis);
          549  +  zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
          550  +  if( !zPragma ) return SQLITE_NOMEM;
          551  +
          552  +  rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
          553  +  sqlite3_free(zPragma);
          554  +  if( rc!=SQLITE_OK ) return rc;
          555  +
          556  +  nByte = nThis + 1;
          557  +  while( SQLITE_ROW==sqlite3_step(pStmt) ){
          558  +    nByte += sqlite3_column_bytes(pStmt, 1);
          559  +    nDbCol++;
          560  +  }
          561  +  rc = sqlite3_reset(pStmt);
          562  +
          563  +  if( nDbCol!=nCol ){
          564  +    rc = SQLITE_SCHEMA;
          565  +  }
          566  +  if( rc==SQLITE_OK ){
          567  +    nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
          568  +    pAlloc = sqlite3_malloc(nByte);
          569  +    if( pAlloc==0 ){
          570  +      rc = SQLITE_NOMEM;
          571  +    }
          572  +  }
          573  +  if( rc==SQLITE_OK ){
          574  +    pFree = pAlloc;
          575  +    if( pazCol ){
          576  +      azCol = (char **)pAlloc;
          577  +      pAlloc = (u8 *)&azCol[nCol];
          578  +    }
          579  +    if( pabPK ){
          580  +      abPK = (u8 *)pAlloc;
          581  +      pAlloc = &abPK[nCol];
          582  +    }
          583  +    if( pzTab ){
          584  +      memcpy(pAlloc, zThis, nThis+1);
          585  +      *pzTab = (char *)pAlloc;
          586  +      pAlloc += nThis+1;
          587  +    }
          588  +  
          589  +    i = 0;
          590  +    while( SQLITE_ROW==sqlite3_step(pStmt) ){
          591  +      int nName = sqlite3_column_bytes(pStmt, 1);
          592  +      const unsigned char *zName = sqlite3_column_text(pStmt, 1);
          593  +      if( zName==0 ) break;
          594  +      if( pazCol ){
          595  +        memcpy(pAlloc, zName, nName+1);
          596  +        azCol[i] = (char *)pAlloc;
          597  +        pAlloc += nName+1;
          598  +      }
          599  +      if( pabPK ) abPK[i] = sqlite3_column_int(pStmt, 5);
          600  +      i++;
          601  +    }
          602  +    rc = sqlite3_reset(pStmt);
          603  +  
          604  +  }
          605  +
          606  +  /* If successful, populate the output variables. Otherwise, zero them and
          607  +  ** free any allocation made. An error code will be returned in this case.
          608  +  */
          609  +  if( rc==SQLITE_OK ){
          610  +    if( pazCol ) *pazCol = (const char **)azCol;
          611  +    if( pabPK ) *pabPK = abPK;
          612  +  }else{
          613  +    if( pazCol ) *pazCol = 0;
          614  +    if( pabPK ) *pabPK = 0;
          615  +    if( pzTab ) *pzTab = 0;
          616  +    sqlite3_free(pFree);
          617  +  }
          618  +  sqlite3_finalize(pStmt);
          619  +  return rc;
          620  +}
   320    621   
   321    622   /*
   322    623   ** This function is only called from within a pre-update handler for a
   323    624   ** write to table pTab, part of session pSession. If this is the first
   324    625   ** write to this table, set the SessionTable.nCol variable to the number
   325    626   ** of columns in the table.
   326    627   **
................................................................................
   332    633   ** was recorded, set the session error-code to SQLITE_SCHEMA and return
   333    634   ** non-zero. Users are not allowed to change the number of columns in a table
   334    635   ** for which changes are being recorded by the session module. If they do so, 
   335    636   ** it is an error.
   336    637   */
   337    638   static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
   338    639     if( pTab->nCol==0 ){
          640  +    assert( pTab->azCol==0 || pTab->abPK==0 );
   339    641       pTab->nCol = sqlite3_preupdate_count(pSession->db);
          642  +    pSession->rc = sessionTableInfo(pSession->db, pSession->zDb, 
          643  +        pTab->zName, pTab->nCol, 0, &pTab->azCol, &pTab->abPK
          644  +    );
   340    645     }else if( pTab->nCol!=sqlite3_preupdate_count(pSession->db) ){
   341    646       pSession->rc = SQLITE_SCHEMA;
   342         -    return SQLITE_ERROR;
   343    647     }
   344         -  return SQLITE_OK;
          648  +  return pSession->rc;
          649  +}
          650  +
          651  +static void sessionPreupdateOneChange(
          652  +  int op,
          653  +  sqlite3_session *pSession,
          654  +  SessionTable *pTab
          655  +){
          656  +  sqlite3 *db = pSession->db;
          657  +  SessionChange *pChange;
          658  +  SessionChange *pC;
          659  +  int iHash; 
          660  +  int rc = SQLITE_OK;
          661  +
          662  +  if( pSession->rc ) return;
          663  +
          664  +  /* Load table details if required */
          665  +  if( sessionInitTable(pSession, pTab) ) return;
          666  +
          667  +  /* Grow the hash table if required */
          668  +  if( sessionGrowHash(pSession, pTab) ) return;
          669  +
          670  +  /* Search the hash table for an existing entry for rowid=iKey2. If
          671  +   ** one is found, store a pointer to it in pChange and unlink it from
          672  +   ** the hash table. Otherwise, set pChange to NULL.
          673  +   */
          674  +  rc = sessionPreupdateHash(db, pTab, op==SQLITE_INSERT, &iHash);
          675  +  for(pC=pTab->apChange[iHash]; rc==SQLITE_OK && pC; pC=pC->pNext){
          676  +    int bEqual;
          677  +    rc = sessionPreupdateEqual(db, pTab, pC, op==SQLITE_INSERT, &bEqual);
          678  +    if( bEqual ) break;
          679  +  }
          680  +  if( pC==0 ){
          681  +    /* Create a new change object containing all the old values (if
          682  +     ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK
          683  +     ** values (if this is an INSERT). */
          684  +    int nByte;              /* Number of bytes to allocate */
          685  +    int i;                  /* Used to iterate through columns */
          686  +
          687  +    pTab->nEntry++;
          688  +
          689  +    /* Figure out how large an allocation is required */
          690  +    nByte = sizeof(SessionChange);
          691  +    for(i=0; i<pTab->nCol && rc==SQLITE_OK; i++){
          692  +      sqlite3_value *p = 0;
          693  +      if( op!=SQLITE_INSERT ){
          694  +        rc = sqlite3_preupdate_old(pSession->db, i, &p);
          695  +      }else if( 1 || pTab->abPK[i] ){
          696  +        rc = sqlite3_preupdate_new(pSession->db, i, &p);
          697  +      }
          698  +      if( p && rc==SQLITE_OK ){
          699  +        rc = sessionSerializeValue(0, p, &nByte);
          700  +      }
          701  +    }
          702  +
          703  +    /* Allocate the change object */
          704  +    pChange = (SessionChange *)sqlite3_malloc(nByte);
          705  +    if( !pChange ){
          706  +      rc = SQLITE_NOMEM;
          707  +    }else{
          708  +      memset(pChange, 0, sizeof(SessionChange));
          709  +      pChange->aRecord = (u8 *)&pChange[1];
          710  +    }
          711  +
          712  +    /* Populate the change object */
          713  +    nByte = 0;
          714  +    for(i=0; i<pTab->nCol && rc==SQLITE_OK; i++){
          715  +      sqlite3_value *p = 0;
          716  +      if( op!=SQLITE_INSERT ){
          717  +        rc = sqlite3_preupdate_old(pSession->db, i, &p);
          718  +      }else if( 1 || pTab->abPK[i] ){
          719  +        rc = sqlite3_preupdate_new(pSession->db, i, &p);
          720  +      }
          721  +      if( p && rc==SQLITE_OK ){
          722  +        rc = sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
          723  +      }
          724  +    }
          725  +    pChange->nRecord = nByte;
          726  +
          727  +    /* If an error has occurred, mark the session object as failed. */
          728  +    if( rc!=SQLITE_OK ){
          729  +      sqlite3_free(pChange);
          730  +      pSession->rc = rc;
          731  +    }else{
          732  +      /* Add the change back to the hash-table */
          733  +      pChange->bInsert = (op==SQLITE_INSERT);
          734  +      pChange->pNext = pTab->apChange[iHash];
          735  +      pTab->apChange[iHash] = pChange;
          736  +    }
          737  +  }
   345    738   }
   346    739   
   347    740   /*
   348    741   ** The 'pre-update' hook registered by this module with SQLite databases.
   349    742   */
   350    743   static void xPreUpdate(
   351    744     void *pCtx,                     /* Copy of third arg to preupdate_hook() */
................................................................................
   359    752     sqlite3_session *pSession;
   360    753     int nDb = strlen(zDb);
   361    754     int nName = strlen(zDb);
   362    755    
   363    756     for(pSession=(sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){
   364    757       SessionTable *pTab;
   365    758   
   366         -    /* If this session is already in the error-state, or if it is attached
   367         -    ** to a different database ("main", "temp" etc.), or if it is not 
   368         -    ** currently enabled, there is nothing to do. Skip to the next session
   369         -    ** object attached to this database. */
          759  +    /* If this session is attached to a different database ("main", "temp" 
          760  +    ** etc.), or if it is not currently enabled, there is nothing to do. Skip 
          761  +    ** to the next session object attached to this database. */
   370    762       if( pSession->bEnable==0 ) continue;
   371    763       if( pSession->rc ) continue;
   372    764       if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue;
   373    765   
   374    766       for(pTab=pSession->pTable; pTab; pTab=pTab->pNext){
   375    767         if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ){
   376         -        SessionChange *pChange;
   377         -        SessionChange *pC;
   378         -        int iHash; 
   379         -        int rc = SQLITE_OK;
   380         -
   381         -        /* Load table details if required */
   382         -        if( sessionInitTable(pSession, pTab) ) return;
   383         -
   384         -        /* Grow the hash table if required */
   385         -        if( sessionGrowHash(pSession, pTab) ) return;
   386         -
   387         -        /* Search the hash table for an existing entry for rowid=iKey2. If
   388         -        ** one is found, store a pointer to it in pChange and unlink it from
   389         -        ** the hash table. Otherwise, set pChange to NULL.
   390         -        */
   391         -        iHash = sessionKeyhash(pTab->nChange, iKey2);
   392         -        for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){
   393         -          if( pC->iKey==iKey2 ) break;
          768  +        sessionPreupdateOneChange(op, pSession, pTab);
          769  +        if( op==SQLITE_UPDATE ){
          770  +          sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab);
   394    771           }
   395         -        if( pC ) continue;
   396         -
   397         -        pTab->nEntry++;
   398         -
   399         -        /* Create a new change object containing all the old values (if
   400         -        ** this is an SQLITE_UPDATE or SQLITE_DELETE), or no record at
   401         -        ** all (if this is an INSERT). */
   402         -        if( op==SQLITE_INSERT ){
   403         -          pChange = (SessionChange *)sqlite3_malloc(sizeof(SessionChange));
   404         -          if( pChange ){
   405         -            memset(pChange, 0, sizeof(SessionChange));
   406         -          }
   407         -        }else{
   408         -          int nByte;              /* Number of bytes to allocate */
   409         -          int i;                  /* Used to iterate through columns */
   410         -
   411         -          /* Figure out how large an allocation is required */
   412         -          nByte = sizeof(SessionChange);
   413         -          for(i=0; i<pTab->nCol && rc==SQLITE_OK; i++){
   414         -            sqlite3_value *p;     /* old.* value */
   415         -            rc = sqlite3_preupdate_old(pSession->db, i, &p);
   416         -            if( rc==SQLITE_OK ){
   417         -              rc = sessionSerializeValue(0, p, &nByte);
   418         -            }
   419         -          }
   420         -
   421         -          /* Allocate the change object */
   422         -          pChange = (SessionChange *)sqlite3_malloc(nByte);
   423         -          if( !pChange ){
   424         -            rc = SQLITE_NOMEM;
   425         -          }else{
   426         -            memset(pChange, 0, sizeof(SessionChange));
   427         -            pChange->aRecord = (u8 *)&pChange[1];
   428         -          }
   429         -
   430         -          /* Populate the change object */
   431         -          nByte = 0;
   432         -          for(i=0; i<pTab->nCol && rc==SQLITE_OK; i++){
   433         -            sqlite3_value *p;     /* old.* value */
   434         -            rc = sqlite3_preupdate_old(pSession->db, i, &p);
   435         -            if( rc==SQLITE_OK ){
   436         -              rc = sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
   437         -            }
   438         -          }
   439         -          pChange->nRecord = nByte;
   440         -        }
   441         -
   442         -        /* If an error has occurred, mark the session object as failed. */
   443         -        if( rc!=SQLITE_OK ){
   444         -          sqlite3_free(pChange);
   445         -          pSession->rc = rc;
   446         -          return;
   447         -        }
   448         -
   449         -        /* Add the change back to the hash-table */
   450         -        pChange->iKey = iKey2;
   451         -        pChange->pNext = pTab->apChange[iHash];
   452         -        pTab->apChange[iHash] = pChange;
   453    772           break;
   454    773         }
   455    774       }
   456    775     }
   457    776   }
   458    777   
   459    778   /*
................................................................................
   520    839         SessionChange *p;
   521    840         SessionChange *pNext;
   522    841         for(p=pTab->apChange[i]; p; p=pNext){
   523    842           pNext = p->pNext;
   524    843           sqlite3_free(p);
   525    844         }
   526    845       }
          846  +    sqlite3_free(pTab->azCol);
   527    847       sqlite3_free(pTab->apChange);
   528    848       sqlite3_free(pTab);
   529    849     }
   530    850   
   531    851     /* Free the session object itself. */
   532    852     sqlite3_free(pSession);
   533    853   }
................................................................................
   848   1168       }else{
   849   1169         sessionAppendBlob(pBuf, buf2.aBuf, buf2.nBuf, pRc);
   850   1170         sqlite3_free(buf2.aBuf);
   851   1171       }
   852   1172     }
   853   1173   }
   854   1174   
   855         -/*
   856         -** This function queries the database for the names of the columns of table
   857         -** zThis, in schema zDb. It is expected that the table has nCol columns. If
   858         -** not, SQLITE_SCHEMA is returned and none of the output variables are
   859         -** populated.
   860         -**
   861         -** Otherwise, if it is not NULL, variable *pzTab is set to point to a
   862         -** nul-terminated copy of the table name. *pazCol (if not NULL) is set to
   863         -** point to an array of pointers to column names. And *pabPK (again, if not
   864         -** NULL) is set to point to an array of booleans - true if the corresponding
   865         -** column is part of the primary key.
   866         -**
   867         -** For example, if the table is declared as:
   868         -**
   869         -**     CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z));
   870         -**
   871         -** Then the three output variables are populated as follows:
   872         -**
   873         -**     *pzTab  = "tbl1"
   874         -**     *pazCol = {"w", "x", "y", "z"}
   875         -**     *pabPK  = {1, 0, 0, 1}
   876         -**
   877         -** All returned buffers are part of the same single allocation, which must
   878         -** be freed using sqlite3_free() by the caller. If pazCol was not NULL, then
   879         -** pointer *pazCol should be freed to release all memory. Otherwise, pointer
   880         -** *pabPK. It is illegal for both pazCol and pabPK to be NULL.
   881         -*/
   882         -static int sessionTableInfo(
   883         -  sqlite3 *db,                    /* Database connection */
   884         -  const char *zDb,                /* Name of attached database (e.g. "main") */
   885         -  const char *zThis,              /* Table name */
   886         -  int nCol,                       /* Expected number of columns */
   887         -  const char **pzTab,             /* OUT: Copy of zThis */
   888         -  const char ***pazCol,           /* OUT: Array of column names for table */
   889         -  u8 **pabPK                      /* OUT: Array of booleans - true for PK col */
         1175  +static int sessionSelectStmt(
         1176  +  sqlite3 *db,                    /* Database handle */
         1177  +  const char *zTab,               /* Table name */
         1178  +  int nCol,
         1179  +  const char **azCol,
         1180  +  u8 *abPK,
         1181  +  sqlite3_stmt **ppStmt
   890   1182   ){
   891         -  char *zPragma;
   892         -  sqlite3_stmt *pStmt;
   893         -  int rc;
   894         -  int nByte;
   895         -  int nDbCol = 0;
   896         -  int nThis;
         1183  +  int rc = SQLITE_OK;
   897   1184     int i;
   898         -  u8 *pAlloc;
   899         -  u8 *pFree = 0;
   900         -  char **azCol;
   901         -  u8 *abPK;
         1185  +  const char *zSep = "";
         1186  +  SessionBuffer buf = {0, 0, 0};
   902   1187   
   903         -  assert( pazCol || pabPK );
   904         -
   905         -  nThis = strlen(zThis);
   906         -  zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
   907         -  if( !zPragma ) return SQLITE_NOMEM;
   908         -
   909         -  rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
   910         -  sqlite3_free(zPragma);
   911         -  if( rc!=SQLITE_OK ) return rc;
   912         -
   913         -  nByte = nThis + 1;
   914         -  while( SQLITE_ROW==sqlite3_step(pStmt) ){
   915         -    nByte += sqlite3_column_bytes(pStmt, 1);
   916         -    nDbCol++;
   917         -  }
   918         -  rc = sqlite3_reset(pStmt);
   919         -
   920         -  if( nDbCol!=nCol ){
   921         -    rc = SQLITE_SCHEMA;
   922         -  }
   923         -  if( rc==SQLITE_OK ){
   924         -    nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
   925         -    pAlloc = sqlite3_malloc(nByte);
   926         -    if( pAlloc==0 ){
   927         -      rc = SQLITE_NOMEM;
         1188  +  sessionAppendStr(&buf, "SELECT * FROM ", &rc);
         1189  +  sessionAppendIdent(&buf, zTab, &rc);
         1190  +  sessionAppendStr(&buf, " WHERE ", &rc);
         1191  +  for(i=0; i<nCol; i++){
         1192  +    if( abPK[i] ){
         1193  +      sessionAppendStr(&buf, zSep, &rc);
         1194  +      sessionAppendIdent(&buf, azCol[i], &rc);
         1195  +      sessionAppendStr(&buf, " = ?", &rc);
         1196  +      sessionAppendInteger(&buf, i+1, &rc);
         1197  +      zSep = " AND ";
   928   1198       }
   929   1199     }
   930   1200     if( rc==SQLITE_OK ){
   931         -    pFree = pAlloc;
   932         -    if( pazCol ){
   933         -      azCol = (char **)pAlloc;
   934         -      pAlloc = (u8 *)&azCol[nCol];
   935         -    }
   936         -    if( pabPK ){
   937         -      abPK = (u8 *)pAlloc;
   938         -      pAlloc = &abPK[nCol];
   939         -    }
   940         -    if( pzTab ){
   941         -      memcpy(pAlloc, zThis, nThis+1);
   942         -      *pzTab = (char *)pAlloc;
   943         -      pAlloc += nThis+1;
   944         -    }
   945         -  
   946         -    i = 0;
   947         -    while( SQLITE_ROW==sqlite3_step(pStmt) ){
   948         -      int nName = sqlite3_column_bytes(pStmt, 1);
   949         -      const unsigned char *zName = sqlite3_column_text(pStmt, 1);
   950         -      if( zName==0 ) break;
   951         -      if( pazCol ){
   952         -        memcpy(pAlloc, zName, nName+1);
   953         -        azCol[i] = (char *)pAlloc;
   954         -        pAlloc += nName+1;
   955         -      }
   956         -      if( pabPK ) abPK[i] = sqlite3_column_int(pStmt, 5);
   957         -      i++;
   958         -    }
   959         -    rc = sqlite3_reset(pStmt);
   960         -  
   961         -  }
   962         -
   963         -  /* If successful, populate the output variables. Otherwise, zero them and
   964         -  ** free any allocation made. An error code will be returned in this case.
   965         -  */
   966         -  if( rc==SQLITE_OK ){
   967         -    if( pazCol ) *pazCol = (const char **)azCol;
   968         -    if( pabPK ) *pabPK = abPK;
   969         -  }else{
   970         -    if( pazCol ) *pazCol = 0;
   971         -    if( pabPK ) *pabPK = 0;
   972         -    if( pzTab ) *pzTab = 0;
   973         -    sqlite3_free(pFree);
   974         -  }
   975         -  sqlite3_finalize(pStmt);
         1201  +    rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, ppStmt, 0);
         1202  +  }
         1203  +  sqlite3_free(buf.aBuf);
         1204  +  return rc;
         1205  +}
         1206  +
         1207  +static int sessionSelectBind(
         1208  +  sqlite3_stmt *pSelect,
         1209  +  int nCol,
         1210  +  u8 *abPK,
         1211  +  u8 *aRecord,
         1212  +  int nRecord
         1213  +){
         1214  +  int i;
         1215  +  int rc = SQLITE_OK;
         1216  +  u8 *a = aRecord;
         1217  +
         1218  +  for(i=0; i<nCol && rc==SQLITE_OK; i++){
         1219  +    int eType = *a++;
         1220  +
         1221  +    switch( eType ){
         1222  +      case SQLITE_NULL:
         1223  +        if( abPK[i] ) rc = sqlite3_bind_null(pSelect, i+1);
         1224  +        break;
         1225  +
         1226  +      case SQLITE_INTEGER: {
         1227  +        if( abPK[i] ){
         1228  +          i64 iVal = sessionGetI64(a);
         1229  +          rc = sqlite3_bind_int64(pSelect, i+1, iVal);
         1230  +        }
         1231  +        a += 8;
         1232  +        break;
         1233  +      }
         1234  +
         1235  +      case SQLITE_FLOAT: {
         1236  +        if( abPK[i] ){
         1237  +          double rVal;
         1238  +          i64 iVal = sessionGetI64(a);
         1239  +          memcpy(&rVal, &iVal, 8);
         1240  +          rc = sqlite3_bind_int64(pSelect, i+1, rVal);
         1241  +        }
         1242  +        a += 8;
         1243  +        break;
         1244  +      }
         1245  +
         1246  +      case SQLITE_TEXT: {
         1247  +        int n;
         1248  +        a += sessionVarintGet(a, &n);
         1249  +        if( abPK[i] ){
         1250  +          rc = sqlite3_bind_text(pSelect, i+1, (char *)a, n, SQLITE_TRANSIENT);
         1251  +        }
         1252  +        a += n;
         1253  +        break;
         1254  +      }
         1255  +
         1256  +      case SQLITE_BLOB: {
         1257  +        int n;
         1258  +        a += sessionVarintGet(a, &n);
         1259  +        if( abPK[i] ){
         1260  +          rc = sqlite3_bind_blob(pSelect, i+1, a, n, SQLITE_TRANSIENT);
         1261  +        }
         1262  +        a += n;
         1263  +        break;
         1264  +      }
         1265  +    }
         1266  +  }
         1267  +
   976   1268     return rc;
   977   1269   }
   978   1270   
   979   1271   /*
   980   1272   ** Obtain a changeset object containing all changes recorded by the 
   981   1273   ** session object passed as the first argument.
   982   1274   **
................................................................................
   998   1290     ** this call will be a no-op.  */
   999   1291     *pnChangeset = 0;
  1000   1292     *ppChangeset = 0;
  1001   1293     rc = pSession->rc;
  1002   1294   
  1003   1295     for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
  1004   1296       if( pTab->nEntry ){
         1297  +      int nCol = pTab->nCol;      /* Local copy of member variable */
         1298  +      u8 *abPK = pTab->abPK;      /* Local copy of member variable */
  1005   1299         int i;
  1006   1300         sqlite3_stmt *pStmt = 0;
  1007   1301         int bNoop = 1;
  1008   1302         int nRewind = buf.nBuf;
  1009         -      u8 *abPK = 0;
  1010   1303   
  1011   1304         /* Write a table header */
  1012   1305         sessionAppendByte(&buf, 'T', &rc);
  1013         -      sessionAppendVarint(&buf, pTab->nCol, &rc);
         1306  +      sessionAppendVarint(&buf, nCol, &rc);
  1014   1307         sessionAppendBlob(&buf, (u8 *)pTab->zName, strlen(pTab->zName)+1, &rc);
  1015   1308   
  1016   1309         /* Build and compile a statement to execute: */
  1017   1310         if( rc==SQLITE_OK ){
  1018         -        char *zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q WHERE _rowid_ = ?", 
  1019         -            pSession->zDb, pTab->zName
  1020         -        );
  1021         -        if( !zSql ){
  1022         -          rc = SQLITE_NOMEM;
  1023         -        }else{
  1024         -          rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
  1025         -        }
  1026         -        sqlite3_free(zSql);
         1311  +        rc = sessionSelectStmt(db, pTab->zName, nCol, pTab->azCol, abPK,&pStmt);
  1027   1312         }
  1028   1313   
  1029         -      if( rc==SQLITE_OK && pTab->nCol!=sqlite3_column_count(pStmt) ){
         1314  +      if( rc==SQLITE_OK && nCol!=sqlite3_column_count(pStmt) ){
  1030   1315           rc = SQLITE_SCHEMA;
  1031   1316         }
  1032   1317   
  1033         -      if( rc==SQLITE_OK ){
  1034         -        rc = sessionTableInfo(
  1035         -            db, pSession->zDb, pTab->zName, pTab->nCol, 0, 0, &abPK);
  1036         -      }
  1037         -
  1038   1318         for(i=0; i<pTab->nChange; i++){
  1039         -        SessionChange *p;
         1319  +        SessionChange *p;         /* Used to iterate through changes */
         1320  +
  1040   1321           for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
  1041         -          sqlite3_bind_int64(pStmt, 1, p->iKey);
  1042         -          if( sqlite3_step(pStmt)==SQLITE_ROW ){
  1043         -            int iCol;
  1044         -            if( p->aRecord ){
  1045         -              sessionAppendUpdate(&buf, pStmt, p, abPK, &rc);
  1046         -            }else{
  1047         -              sessionAppendByte(&buf, SQLITE_INSERT, &rc);
  1048         -              for(iCol=0; iCol<pTab->nCol; iCol++){
  1049         -                sessionAppendCol(&buf, pStmt, iCol, &rc);
         1322  +          rc = sessionSelectBind(pStmt, nCol, abPK, p->aRecord, p->nRecord);
         1323  +          if( rc==SQLITE_OK ){
         1324  +            if( sqlite3_step(pStmt)==SQLITE_ROW ){
         1325  +              int iCol;
         1326  +              if( p->bInsert ){
         1327  +                sessionAppendByte(&buf, SQLITE_INSERT, &rc);
         1328  +                for(iCol=0; iCol<nCol; iCol++){
         1329  +                  sessionAppendCol(&buf, pStmt, iCol, &rc);
         1330  +                }
         1331  +              }else{
         1332  +                sessionAppendUpdate(&buf, pStmt, p, abPK, &rc);
  1050   1333                 }
         1334  +              bNoop = 0;
         1335  +            }else if( !p->bInsert ){
         1336  +              /* A DELETE change */
         1337  +              sessionAppendByte(&buf, SQLITE_DELETE, &rc);
         1338  +              sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
         1339  +              bNoop = 0;
  1051   1340               }
  1052         -            bNoop = 0;
  1053         -          }else if( p->aRecord ){
  1054         -            /* A DELETE change */
  1055         -            sessionAppendByte(&buf, SQLITE_DELETE, &rc);
  1056         -            sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
  1057         -            bNoop = 0;
         1341  +            rc = sqlite3_reset(pStmt);
  1058   1342             }
  1059         -          rc = sqlite3_reset(pStmt);
  1060   1343           }
  1061   1344         }
  1062   1345   
  1063   1346         sqlite3_finalize(pStmt);
  1064         -      sqlite3_free(abPK);
  1065   1347   
  1066   1348         if( bNoop ){
  1067   1349           buf.nBuf = nRewind;
  1068   1350         }
  1069   1351       }
  1070   1352     }
  1071   1353   
................................................................................
  1628   1910   ** pointing to the prepared version of the SQL statement.
  1629   1911   */
  1630   1912   static int sessionSelectRow(
  1631   1913     sqlite3 *db,                    /* Database handle */
  1632   1914     const char *zTab,               /* Table name */
  1633   1915     SessionApplyCtx *p              /* Session changeset-apply context */
  1634   1916   ){
  1635         -  int rc = SQLITE_OK;
  1636         -  int i;
  1637         -  const char *zSep = "";
  1638         -  SessionBuffer buf = {0, 0, 0};
  1639         -
  1640         -  sessionAppendStr(&buf, "SELECT * FROM ", &rc);
  1641         -  sessionAppendIdent(&buf, zTab, &rc);
  1642         -  sessionAppendStr(&buf, " WHERE ", &rc);
  1643         -  for(i=0; i<p->nCol; i++){
  1644         -    if( p->abPK[i] ){
  1645         -      sessionAppendStr(&buf, zSep, &rc);
  1646         -      sessionAppendIdent(&buf, p->azCol[i], &rc);
  1647         -      sessionAppendStr(&buf, " = ?", &rc);
  1648         -      sessionAppendInteger(&buf, i+1, &rc);
  1649         -      zSep = " AND ";
  1650         -    }
  1651         -  }
  1652         -  if( rc==SQLITE_OK ){
  1653         -    rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pSelect, 0);
  1654         -  }
  1655         -  sqlite3_free(buf.aBuf);
  1656         -  return rc;
         1917  +  return sessionSelectStmt(db, zTab, p->nCol, p->azCol, p->abPK, &p->pSelect);
  1657   1918   }
  1658   1919   
  1659   1920   /*
  1660   1921   ** Formulate and prepare an INSERT statement to add a record to table zTab.
  1661   1922   ** For example:
  1662   1923   **
  1663   1924   **     INSERT INTO main."zTab" VALUES(?1, ?2, ?3 ...);

Changes to test/session1.test.

    33     33       set changeset [sqlite3changeset_invert [$session changeset]]
    34     34       sqlite3session_foreach c [set changeset] { lappend x [set c] }
    35     35       set x
    36     36     }]] [list $r]
    37     37   }
    38     38   
    39     39   do_execsql_test 1.0 {
    40         -  CREATE TABLE t1(x, y);
           40  +  CREATE TABLE t1(x PRIMARY KEY, y);
    41     41     INSERT INTO t1 VALUES('abc', 'def');
    42     42   }
    43     43   
    44     44   #-------------------------------------------------------------------------
    45     45   # Test creating, attaching tables to and deleting session objects.
    46     46   #
    47     47   do_test 1.1 { sqlite3session S db main } {S}
................................................................................
   109    109     {INSERT t1 {} {i 1 t Sukhothai}}
   110    110     {INSERT t1 {} {i 2 t Ayutthaya}}
   111    111     {INSERT t1 {} {i 3 t Thonburi}}
   112    112   }
   113    113   do_test 2.2.4 { S delete } {}
   114    114   
   115    115   do_test 2.3.1 {
          116  +  execsql { DELETE FROM t1 }
   116    117     sqlite3session S db main
   117    118     execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') }
   118    119     execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') }
   119    120     execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
   120    121     S attach t1
   121    122     execsql { 
   122    123       UPDATE t1 SET x = 10 WHERE x = 1;
   123    124       UPDATE t1 SET y = 'Surin' WHERE x = 2;
   124    125       UPDATE t1 SET x = 20, y = 'Thapae' WHERE x = 3;
   125    126     }
   126    127   } {}
   127    128   
   128    129   do_changeset_test 2.3.2 S {
   129         -  {UPDATE t1 {i 1 {} {}} {i 10 {} {}}} 
   130         -  {UPDATE t1 {{} {} t Ayutthaya} {{} {} t Surin}} 
   131         -  {UPDATE t1 {i 3 t Thonburi} {i 20 t Thapae}}
          130  +  {INSERT t1 {} {i 10 t Sukhothai}} 
          131  +  {DELETE t1 {i 1 t Sukhothai} {}} 
          132  +  {UPDATE t1 {i 2 t Ayutthaya} {{} {} t Surin}} 
          133  +  {DELETE t1 {i 3 t Thonburi} {}} 
          134  +  {INSERT t1 {} {i 20 t Thapae}} 
   132    135   }
          136  +
   133    137   do_changeset_invert_test 2.3.3 S {
   134         -  {UPDATE t1 {i 10 {} {}} {i 1 {} {}}} 
   135         -  {UPDATE t1 {{} {} t Surin} {{} {} t Ayutthaya}} 
   136         -  {UPDATE t1 {i 20 t Thapae} {i 3 t Thonburi}}
          138  +  {DELETE t1 {i 10 t Sukhothai} {}} 
          139  +  {INSERT t1 {} {i 1 t Sukhothai}} 
          140  +  {UPDATE t1 {{} {} t Surin} {i 2 t Ayutthaya}} 
          141  +  {INSERT t1 {} {i 3 t Thonburi}} 
          142  +  {DELETE t1 {i 20 t Thapae} {}}
   137    143   }
   138    144   do_test 2.3.4 { S delete } {}
   139    145   
   140    146   do_test 2.4.1 {
   141    147     sqlite3session S db main
   142    148     S attach t1
   143    149     execsql { INSERT INTO t1 VALUES(100, 'Bangkok') }
................................................................................
   205    211     execsql { 
   206    212       CREATE TABLE t1(a PRIMARY KEY, b);
   207    213       INSERT INTO t1 VALUES(1, 'one');
   208    214       INSERT INTO t1 VALUES(2, 'two');
   209    215     } db 
   210    216   } {}
   211    217   do_db2_test 3.1.1 "INSERT INTO t1 VALUES(6, 'VI')"
   212         -do_conflict_test 3.2.2 -tables t1 -sql {
          218  +do_conflict_test 3.1.2 -tables t1 -sql {
   213    219     INSERT INTO t1 VALUES(3, 'three');
   214    220     INSERT INTO t1 VALUES(4, 'four');
   215    221     INSERT INTO t1 VALUES(5, 'five');
   216    222     INSERT INTO t1 VALUES(6, 'six');
   217    223     INSERT INTO t1 VALUES(7, 'seven');
   218    224     INSERT INTO t1 VALUES(8, NULL);
   219    225   } -conflicts {
   220         -  {INSERT t1 CONFLICT {i 6 t six} {i 6 t VI}}
   221    226     {INSERT t1 CONSTRAINT {i 8 n {}}}
          227  +  {INSERT t1 CONFLICT {i 6 t six} {i 6 t VI}}
   222    228   }
   223         -do_db2_test 3.1.2 "SELECT * FROM t1" {
          229  +
          230  +do_db2_test 3.1.3 "SELECT * FROM t1" {
   224    231     6 VI 3 three 4 four 5 five 7 seven
   225    232   }
   226         -do_execsql_test 3.1.3 "SELECT * FROM t1" {
          233  +do_execsql_test 3.1.4 "SELECT * FROM t1" {
   227    234     1 one 2 two 3 three 4 four 5 five 6 six 7 seven 8 {}
   228    235   }
   229    236   
   230    237   # Test DELETE changesets.
   231    238   #
   232    239   do_execsql_test 3.2.1 {
   233    240     PRAGMA foreign_keys = on;
................................................................................
   278    285   }
   279    286   do_conflict_test 3.3.3 -tables t4 -sql {
   280    287     UPDATE t4 SET a = -1 WHERE b = 2;
   281    288     UPDATE t4 SET a = -1 WHERE b = 5;
   282    289     UPDATE t4 SET a = NULL WHERE c = 9;
   283    290     UPDATE t4 SET a = 'x' WHERE b = 11;
   284    291   } -conflicts {
          292  +  {UPDATE t4 CONSTRAINT {i 7 i 8 i 9} {n {} {} {} {} {}}}
   285    293     {UPDATE t4 DATA {i 1 i 2 i 3} {i -1 {} {} {} {}} {i 0 i 2 i 3}}
   286    294     {UPDATE t4 NOTFOUND {i 4 i 5 i 6} {i -1 {} {} {} {}}}
   287         -  {UPDATE t4 CONSTRAINT {i 7 i 8 i 9} {n {} {} {} {} {}}}
   288    295   }
   289    296   do_db2_test     3.3.4 { SELECT * FROM t4 } {0 2 3 4 5 7 7 8 9 x 11 12}
   290    297   do_execsql_test 3.3.5 { SELECT * FROM t4 } {-1 2 3 -1 5 6 {} 8 9 x 11 12}
   291    298   
   292    299   #-------------------------------------------------------------------------
   293    300   # This next block of tests verifies that values returned by the conflict
   294    301   # handler are intepreted correctly.
................................................................................
   304    311   
   305    312   proc xConflict {args} {
   306    313     lappend ::xConflict $args
   307    314     return $::conflict_return
   308    315   }
   309    316   
   310    317   foreach {tn conflict_return after} {
   311         -  1 OMIT      {1 2 value1   4 5 7       7 8 9   10 x x}
   312         -  2 REPLACE   {1 2 value1   4 5 value2          10 8 9}
          318  +  1 OMIT      {1 2 value1   4 5 7       10 x x}
          319  +  2 REPLACE   {1 2 value1   4 5 value2  10 8 9}
   313    320   } {
   314    321     test_reset
   315    322   
   316    323     do_test 4.$tn.1 {
   317    324       foreach db {db db2} {
   318    325         execsql { 
   319    326           CREATE TABLE t1(a, b, c, PRIMARY KEY(a));
................................................................................
   329    336     } {}
   330    337   
   331    338     do_conflict_test 4.$tn.2 -tables t1 -sql {
   332    339       UPDATE t1 SET c = 'value1' WHERE a = 1;       -- no conflict
   333    340       UPDATE t1 SET c = 'value2' WHERE a = 4;       -- DATA conflict
   334    341       UPDATE t1 SET a = 10 WHERE a = 7;             -- CONFLICT conflict
   335    342     } -conflicts {
          343  +    {INSERT t1 CONFLICT {i 10 i 8 i 9} {i 10 t x t x}}
   336    344       {UPDATE t1 DATA {i 4 {} {} i 6} {{} {} {} {} t value2} {i 4 i 5 i 7}}
   337         -    {UPDATE t1 CONFLICT {i 7 {} {} {} {}} {i 10 {} {} {} {}} {i 10 t x t x}}
   338    345     }
   339    346   
   340    347     do_db2_test 4.$tn.3 "SELECT * FROM t1 ORDER BY a" $after
   341    348   }
   342    349   
   343    350   foreach {tn conflict_return} {
   344    351     1 OMIT