SQLite4
Check-in [81fab25002]
Not logged in

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

Overview
Comment:Further fixes to things.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | primary-keys
Files: files | file ages | folders
SHA1: 81fab250025d4c12a2014c4a8ca20ccafa1ac2f8
User & Date: dan 2012-04-12 19:52:31
Context
2012-04-13
05:50
Ensure that kvmemSeek() does not come to rest on a deleted node. check-in: 21991a932a user: dan tags: primary-keys
2012-04-12
19:52
Further fixes to things. check-in: 81fab25002 user: dan tags: primary-keys
11:49
Fix a problem in kvmemRemoveNode. check-in: f3c424ddf3 user: dan tags: primary-keys
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/build.c.

2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
    sqlite4HaltConstraint(
        pParse, OE_Abort, "indexed columns are not unique", P4_STATIC
    );
  }else{
    addr2 = sqlite4VdbeCurrentAddr(v);
  }
  sqlite4VdbeAddOp2(v, OP_SorterData, iSorter, regRecord);
  sqlite4VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);  ***** busted
  sqlite4VdbeChangeP5(v, OPFLAG_USESEEKRESULT | OPFLAG_APPENDBIAS);
#else
  regIdxKey = sqlite4GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1, iIdx);
  addr2 = addr1 + 1;
  if( pIndex->onError!=OE_None ){
    const int regRowid = regIdxKey + pIndex->nColumn;
    const int j2 = sqlite4VdbeCurrentAddr(v) + 2;







|







2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
    sqlite4HaltConstraint(
        pParse, OE_Abort, "indexed columns are not unique", P4_STATIC
    );
  }else{
    addr2 = sqlite4VdbeCurrentAddr(v);
  }
  sqlite4VdbeAddOp2(v, OP_SorterData, iSorter, regRecord);
  sqlite4VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);  
  sqlite4VdbeChangeP5(v, OPFLAG_USESEEKRESULT | OPFLAG_APPENDBIAS);
#else
  regIdxKey = sqlite4GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1, iIdx);
  addr2 = addr1 + 1;
  if( pIndex->onError!=OE_None ){
    const int regRowid = regIdxKey + pIndex->nColumn;
    const int j2 = sqlite4VdbeCurrentAddr(v) + 2;

Changes to src/fkey.c.

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
...
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
...
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



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411






412
413
414
415
416
417
418
419
420
421
422
423
424
425
...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
...
758
759
760
761
762
763
764
765
766
767
768
769

770
771
772
773
774
775
776
777
778
779
780

781
782
783
784
785
786
787
...
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
**   Register (x+3):      3.1  (type real)
*/

/*
** A foreign key constraint requires that the key columns in the parent
** table are collectively subject to a UNIQUE or PRIMARY KEY constraint.
** Given that pParent is the parent table for foreign key constraint pFKey, 
** search the schema a unique index on the parent key columns. 
**
** If successful, zero is returned. If the parent key is an INTEGER PRIMARY 
** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx 
** is set to point to the unique index. 
** 
** If the parent key consists of a single column (the foreign key constraint
** is not a composite foreign key), output variable *paiCol is set to NULL.
** Otherwise, it is set to point to an allocated array of size N, where
** N is the number of columns in the parent key. The first element of the
** array is the index of the child table column that is mapped by the FK
** constraint to the parent table column stored in the left-most column
................................................................................
static int locateFkeyIndex(
  Parse *pParse,                  /* Parse context to store any error in */
  Table *pParent,                 /* Parent table of FK constraint pFKey */
  FKey *pFKey,                    /* Foreign key to find index for */
  Index **ppIdx,                  /* OUT: Unique index on parent table */
  int **paiCol                    /* OUT: Map of index columns in pFKey */
){
  Index *pIdx = 0;                    /* Value to return via *ppIdx */
  int *aiCol = 0;                     /* Value to return via *paiCol */
  int nCol = pFKey->nCol;             /* Number of columns in parent key */
  char *zKey = pFKey->aCol[0].zCol;   /* Name of left-most parent key column */

  /* The caller is responsible for zeroing output parameters. */
  assert( ppIdx && *ppIdx==0 );
  assert( !paiCol || *paiCol==0 );
  assert( pParse );

  /* If this is a non-composite (single column) foreign key, check if it 
  ** maps to the INTEGER PRIMARY KEY of table pParent. If so, leave *ppIdx 
  ** and *paiCol set to zero and return early. 
  **

  ** Otherwise, for a composite foreign key (more than one column), allocate
  ** space for the aiCol array (returned via output parameter *paiCol).
  ** Non-composite foreign keys do not require the aiCol array.
  */
  if( nCol==1 ){
    /* The FK maps to the IPK if any of the following are true:
    **
    **   1) There is an INTEGER PRIMARY KEY column and the FK is implicitly 
    **      mapped to the primary key of table pParent, or
    **   2) The FK is explicitly mapped to a column declared as INTEGER
    **      PRIMARY KEY.
    */
    if( pParent->iPKey>=0 ){
      if( !zKey ) return 0;
      if( !sqlite4StrICmp(pParent->aCol[pParent->iPKey].zName, zKey) ) return 0;
    }
  }else if( paiCol ){
    assert( nCol>1 );
    aiCol = (int *)sqlite4DbMallocRaw(pParse->db, nCol*sizeof(int));
    if( !aiCol ) return 1;
    *paiCol = aiCol;
  }

  for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){
    if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){ 
      /* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number
      ** of columns. If each indexed column corresponds to a foreign key
      ** column of pFKey, then this index is a winner.  */

      if( zKey==0 ){
        /* If zKey is NULL, then this foreign key is implicitly mapped to 
        ** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be 
        ** identified by the test (Index.autoIndex==2).  */
        if( pIdx->eIndexType==SQLITE_INDEX_PRIMARYKEY ){
          if( aiCol ){
            int i;
            for(i=0; i<nCol; i++) aiCol[i] = pFKey->aCol[i].iFrom;
          }
          break;
        }
      }else{
        /* If zKey is non-NULL, then this foreign key was declared to
        ** map to an explicit list of columns in table pParent. Check if this
        ** index matches those columns. Also, check that the index uses
        ** the default collation sequences for each column. */
        int i, j;
        for(i=0; i<nCol; i++){
          int iCol = pIdx->aiColumn[i];     /* Index of column in parent tbl */
          char *zDfltColl;                  /* Def. collation for column */
          char *zIdxCol;                    /* Name of indexed column */

          /* If the index uses a collation sequence that is different from
................................................................................
static void fkLookupParent(
  Parse *pParse,        /* Parse context */
  int iDb,              /* Index of database housing pTab */
  Table *pTab,          /* Parent table of FK pFKey */
  Index *pIdx,          /* Unique index on parent key columns in pTab */
  FKey *pFKey,          /* Foreign key constraint */
  int *aiCol,           /* Map from parent key columns to child table columns */
  int regData,          /* Address of array containing child table row */
  int nIncr,            /* Increment constraint counter by this */
  int isIgnore          /* If true, pretend pTab contains all NULL values */
){
  int i;                                    /* Iterator variable */
  Vdbe *v = sqlite4GetVdbe(pParse);         /* Vdbe to add code to */
  int iCur = pParse->nTab - 1;              /* Cursor number to use */
  int iOk = sqlite4VdbeMakeLabel(v);        /* jump here if parent key found */

  /* If nIncr is less than zero, then check at runtime if there are any
  ** outstanding constraints to resolve. If there are not, there is no need
  ** to check if deleting this row resolves any outstanding violations.
  **
  ** Check if any of the key columns in the child table row are NULL. If 
  ** any are, then the constraint is considered satisfied. No need to 
  ** search for a matching row in the parent table.  */
  if( nIncr<0 ){
    sqlite4VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, iOk);
  }




  for(i=0; i<pFKey->nCol; i++){
    int iReg = aiCol[i] + regData + 1;
    sqlite4VdbeAddOp2(v, OP_IsNull, iReg, iOk);
  }

  if( isIgnore==0 ){
    if( pIdx==0 ){
      /* If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY
      ** column of the parent table (table pTab).  */
      int iMustBeInt;               /* Address of MustBeInt instruction */
      int regTemp = sqlite4GetTempReg(pParse);
  
      /* Invoke MustBeInt to coerce the child key value to an integer (i.e. 
      ** apply the affinity of the parent key). If this fails, then there
      ** is no matching parent key. Before using MustBeInt, make a copy of
      ** the value. Otherwise, the value inserted into the child key column
      ** will have INTEGER affinity applied to it, which may not be correct.  */
      sqlite4VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp);
      iMustBeInt = sqlite4VdbeAddOp2(v, OP_MustBeInt, regTemp, 0);
  
      /* If the parent table is the same as the child table, and we are about
      ** to increment the constraint-counter (i.e. this is an INSERT operation),
      ** then check if the row being inserted matches itself. If so, do not
      ** increment the constraint-counter.  */
      if( pTab==pFKey->pFrom && nIncr==1 ){
        sqlite4VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp);
      }
  
      sqlite4OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
      sqlite4VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp);
      sqlite4VdbeAddOp2(v, OP_Goto, 0, iOk);
      sqlite4VdbeJumpHere(v, sqlite4VdbeCurrentAddr(v)-2);
      sqlite4VdbeJumpHere(v, iMustBeInt);
      sqlite4ReleaseTempReg(pParse, regTemp);
    }else{
      int nCol = pFKey->nCol;
      int regTemp = sqlite4GetTempRange(pParse, nCol);
      int regRec = sqlite4GetTempReg(pParse);
      KeyInfo *pKey = sqlite4IndexKeyinfo(pParse, pIdx);
  
      sqlite4VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb);
      sqlite4VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);



      for(i=0; i<nCol; i++){
        sqlite4VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i);
      }
  
      /* If the parent table is the same as the child table, and we are about
      ** to increment the constraint-counter (i.e. this is an INSERT operation),
      ** then check if the row being inserted matches itself. If so, do not
      ** increment the constraint-counter. 
      **
      ** If any of the parent-key values are NULL, then the row cannot match 
      ** itself. So set JUMPIFNULL to make sure we do the OP_Found if any
      ** of the parent-key values are NULL (at this point it is known that
      ** none of the child key values are).
      */
      if( pTab==pFKey->pFrom && nIncr==1 ){
        int iJump = sqlite4VdbeCurrentAddr(v) + nCol + 1;
        for(i=0; i<nCol; i++){
          int iChild = aiCol[i]+1+regData;
          int iParent = pIdx->aiColumn[i]+1+regData;
          assert( aiCol[i]!=pTab->iPKey );
          if( pIdx->aiColumn[i]==pTab->iPKey ){
            /* The parent key is a composite key that includes the IPK column */
            iParent = regData;
          }
          sqlite4VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent);
          sqlite4VdbeChangeP5(v, SQLITE_JUMPIFNULL);
        }
        sqlite4VdbeAddOp2(v, OP_Goto, 0, iOk);
      }
  






      sqlite4VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec);
      sqlite4VdbeChangeP4(v, -1, sqlite4IndexAffinityStr(v,pIdx), P4_TRANSIENT);
      sqlite4VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0);
  
      sqlite4ReleaseTempReg(pParse, regRec);
      sqlite4ReleaseTempRange(pParse, regTemp, nCol);
    }
  }

  if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){
    /* Special case: If this is an INSERT statement that will insert exactly
    ** one row into the table, raise a constraint immediately instead of
    ** incrementing a counter. This is necessary as the VM code is being
    ** generated for will not open a statement transaction.  */
................................................................................
    if( pLeft ){
      /* Set the collation sequence and affinity of the LHS of each TK_EQ
      ** expression to the parent key column defaults.  */
      if( pIdx ){
        Column *pCol;
        iCol = pIdx->aiColumn[i];
        pCol = &pTab->aCol[iCol];
        if( pTab->iPKey==iCol ) iCol = -1;
        pLeft->iTable = regData+iCol+1;
        pLeft->affinity = pCol->affinity;
        pLeft->pColl = sqlite4LocateCollSeq(pParse, pCol->zColl);
      }else{
        pLeft->iTable = regData;
        pLeft->affinity = SQLITE_AFF_INTEGER;
      }
................................................................................

    if( aiFree ){
      aiCol = aiFree;
    }else{
      iCol = pFKey->aCol[0].iFrom;
      aiCol = &iCol;
    }
    for(i=0; i<pFKey->nCol; i++){
      if( aiCol[i]==pTab->iPKey ){
        aiCol[i] = -1;
      }
#ifndef SQLITE_OMIT_AUTHORIZATION

      /* Request permission to read the parent key columns. If the 
      ** authorization callback returns SQLITE_IGNORE, behave as if any
      ** values read from the parent table are NULL. */
      if( db->xAuth ){
        int rcauth;
        char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName;
        rcauth = sqlite4AuthReadCol(pParse, pTo->zName, zCol, iDb);
        isIgnore = (rcauth==SQLITE_IGNORE);
      }
#endif
    }


    /* Take a shared-cache advisory read-lock on the parent table. Allocate 
    ** a cursor to use to search the unique index on the parent key columns 
    ** in the parent table.  */
    sqlite4TableLock(pParse, iDb, pTo->tnum, 0, pTo->zName);
    pParse->nTab++;

................................................................................
      FKey *p;

      /* Check if any child key columns are being modified. */
      for(p=pTab->pFKey; p; p=p->pNextFrom){
        for(i=0; i<p->nCol; i++){
          int iChildKey = p->aCol[i].iFrom;
          if( aChange[iChildKey]>=0 ) return 1;
          if( iChildKey==pTab->iPKey && chngRowid ) return 1;
        }
      }

      /* Check if any parent key columns are being modified. */
      for(p=sqlite4FkReferences(pTab); p; p=p->pNextTo){
        for(i=0; i<p->nCol; i++){
          char *zKey = p->aCol[i].zCol;
          int iKey;
          for(iKey=0; iKey<pTab->nCol; iKey++){
            Column *pCol = &pTab->aCol[iKey];
            if( (zKey ? !sqlite4StrICmp(pCol->zName, zKey) : pCol->isPrimKey) ){
              if( aChange[iKey]>=0 ) return 1;
              if( iKey==pTab->iPKey && chngRowid ) return 1;
            }
          }
        }
      }
    }
  }
  return 0;







|

|
<
|







 







|
|
|
|






|
<
<
<
>
|

|
<
<
<
<
<
<
<
<
<
<
<
<
<
|












|
<
<
<








|
|
|
|







 







|








|
|
|
|
|
|
<



>
>
>
>

|




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
|
<
|
<
<
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
|
|
|
|
<
<
<
<
<
|
|
|
|
|
|
>
>
>
>
>
>
|
|
|
|
|
|
<







 







<







 







<
<
<
<

>





|



<

>







 







<












<







138
139
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197



198
199
200
201













202
203
204
205
206
207
208
209
210
211
212
213
214
215



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
...
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
...
463
464
465
466
467
468
469

470
471
472
473
474
475
476
...
711
712
713
714
715
716
717




718
719
720
721
722
723
724
725
726
727
728

729
730
731
732
733
734
735
736
737
...
860
861
862
863
864
865
866

867
868
869
870
871
872
873
874
875
876
877
878

879
880
881
882
883
884
885
**   Register (x+3):      3.1  (type real)
*/

/*
** A foreign key constraint requires that the key columns in the parent
** table are collectively subject to a UNIQUE or PRIMARY KEY constraint.
** Given that pParent is the parent table for foreign key constraint pFKey, 
** search the schema for a unique index on the parent key columns. 
**
** If successful, zero is returned and *ppIdx is set to point to the 

** unique index.
** 
** If the parent key consists of a single column (the foreign key constraint
** is not a composite foreign key), output variable *paiCol is set to NULL.
** Otherwise, it is set to point to an allocated array of size N, where
** N is the number of columns in the parent key. The first element of the
** array is the index of the child table column that is mapped by the FK
** constraint to the parent table column stored in the left-most column
................................................................................
static int locateFkeyIndex(
  Parse *pParse,                  /* Parse context to store any error in */
  Table *pParent,                 /* Parent table of FK constraint pFKey */
  FKey *pFKey,                    /* Foreign key to find index for */
  Index **ppIdx,                  /* OUT: Unique index on parent table */
  int **paiCol                    /* OUT: Map of index columns in pFKey */
){
  Index *pIdx = 0;                /* Value to return via *ppIdx */
  int *aiCol = 0;                 /* Value to return via *paiCol */
  int nCol = pFKey->nCol;         /* Number of columns in parent key */
  int bImplicit;                  /* True if no explicit parent columns */

  /* The caller is responsible for zeroing output parameters. */
  assert( ppIdx && *ppIdx==0 );
  assert( !paiCol || *paiCol==0 );
  assert( pParse );

  bImplicit = (pFKey->aCol[0].zCol==0);




  /* If this is a composite foreign key (more than one column), allocate
  ** space for the aiCol array (returned via output parameter *paiCol).
  ** Non-composite foreign keys do not require the aiCol array.  */













  if( paiCol && nCol>1 ){
    assert( nCol>1 );
    aiCol = (int *)sqlite4DbMallocRaw(pParse->db, nCol*sizeof(int));
    if( !aiCol ) return 1;
    *paiCol = aiCol;
  }

  for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){
    if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){ 
      /* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number
      ** of columns. If each indexed column corresponds to a foreign key
      ** column of pFKey, then this index is a winner.  */

      if( bImplicit ){



        if( pIdx->eIndexType==SQLITE_INDEX_PRIMARYKEY ){
          if( aiCol ){
            int i;
            for(i=0; i<nCol; i++) aiCol[i] = pFKey->aCol[i].iFrom;
          }
          break;
        }
      }else{
        /* If this foreign key was declared to map to an explicit list of 
        ** columns in table pParent. Check if this index matches those 
        ** columns. Also, check that the index uses the default collation 
        ** sequences for each column. */
        int i, j;
        for(i=0; i<nCol; i++){
          int iCol = pIdx->aiColumn[i];     /* Index of column in parent tbl */
          char *zDfltColl;                  /* Def. collation for column */
          char *zIdxCol;                    /* Name of indexed column */

          /* If the index uses a collation sequence that is different from
................................................................................
static void fkLookupParent(
  Parse *pParse,        /* Parse context */
  int iDb,              /* Index of database housing pTab */
  Table *pTab,          /* Parent table of FK pFKey */
  Index *pIdx,          /* Unique index on parent key columns in pTab */
  FKey *pFKey,          /* Foreign key constraint */
  int *aiCol,           /* Map from parent key columns to child table columns */
  int regContent,       /* Address of array containing child table row */
  int nIncr,            /* Increment constraint counter by this */
  int isIgnore          /* If true, pretend pTab contains all NULL values */
){
  int i;                                    /* Iterator variable */
  Vdbe *v = sqlite4GetVdbe(pParse);         /* Vdbe to add code to */
  int iCur = pParse->nTab - 1;              /* Cursor number to use */
  int iOk = sqlite4VdbeMakeLabel(v);        /* jump here if parent key found */

  assert( pIdx );

  /* If nIncr is less than zero (this is a DELETE), then check at runtime if
  ** there are any outstanding constraints to resolve. If there are not, 
  ** there is no need to check if deleting this row resolves any outstanding 
  ** violations.  */

  if( nIncr<0 ){
    sqlite4VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, iOk);
  }

  /* Check if any of the key columns in the child table row are NULL. If 
  ** any are, then the constraint is considered satisfied. No need to 
  ** search for a matching row in the parent table.  */
  for(i=0; i<pFKey->nCol; i++){
    int iReg = aiCol[i] + regContent;
    sqlite4VdbeAddOp2(v, OP_IsNull, iReg, iOk);
  }

  if( isIgnore==0 ){





























    int nCol = pFKey->nCol;
    int regTemp = sqlite4GetTempRange(pParse, nCol);
    int regRec = sqlite4GetTempReg(pParse);




    sqlite4OpenIndex(pParse, iCur, iDb, pIdx, OP_OpenRead);

    /* Assemble the child key values in a contiguous array of registers */
    for(i=0; i<nCol; i++){
      sqlite4VdbeAddOp2(v, OP_Copy, aiCol[i]+regContent, regTemp+i);
    }

    /* If the parent table is the same as the child table, and we are about
    ** to increment the constraint-counter (i.e. this is an INSERT operation),
    ** then check if the row being inserted matches itself. If so, do not
    ** increment the constraint-counter. 
    **
    ** If any of the parent-key values are NULL, then the row cannot match 
    ** itself. So set JUMPIFNULL to make sure we do the OP_Found if any
    ** of the parent-key values are NULL (at this point it is known that
    ** none of the child key values are).  */

    if( pTab==pFKey->pFrom && nIncr==1 ){
      int iJump = sqlite4VdbeCurrentAddr(v) + nCol + 1;
      for(i=0; i<nCol; i++){
        int iChild = aiCol[i]+regContent;
        int iParent = pIdx->aiColumn[i]+regContent;





        sqlite4VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent);
        sqlite4VdbeChangeP5(v, SQLITE_JUMPIFNULL);
      }
      sqlite4VdbeAddOp2(v, OP_Goto, 0, iOk);
    }

    /* FIXME: Affinities... */
    sqlite4VdbeAddOp3(v, OP_MakeIdxKey, iCur, regTemp, regRec);
    /* sqlite4VdbeChangeP5(v, 1); */

    sqlite4VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0);

    sqlite4VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec);
    sqlite4VdbeChangeP4(v, -1, sqlite4IndexAffinityStr(v,pIdx), P4_TRANSIENT);
    sqlite4VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0);

    sqlite4ReleaseTempReg(pParse, regRec);
    sqlite4ReleaseTempRange(pParse, regTemp, nCol);

  }

  if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){
    /* Special case: If this is an INSERT statement that will insert exactly
    ** one row into the table, raise a constraint immediately instead of
    ** incrementing a counter. This is necessary as the VM code is being
    ** generated for will not open a statement transaction.  */
................................................................................
    if( pLeft ){
      /* Set the collation sequence and affinity of the LHS of each TK_EQ
      ** expression to the parent key column defaults.  */
      if( pIdx ){
        Column *pCol;
        iCol = pIdx->aiColumn[i];
        pCol = &pTab->aCol[iCol];

        pLeft->iTable = regData+iCol+1;
        pLeft->affinity = pCol->affinity;
        pLeft->pColl = sqlite4LocateCollSeq(pParse, pCol->zColl);
      }else{
        pLeft->iTable = regData;
        pLeft->affinity = SQLITE_AFF_INTEGER;
      }
................................................................................

    if( aiFree ){
      aiCol = aiFree;
    }else{
      iCol = pFKey->aCol[0].iFrom;
      aiCol = &iCol;
    }




#ifndef SQLITE_OMIT_AUTHORIZATION
    for(i=0; i<pFKey->nCol; i++){
      /* Request permission to read the parent key columns. If the 
      ** authorization callback returns SQLITE_IGNORE, behave as if any
      ** values read from the parent table are NULL. */
      if( db->xAuth ){
        int rcauth;
        char *zCol = pTo->aCol[pIdx->aiColumn[i]].zName;
        rcauth = sqlite4AuthReadCol(pParse, pTo->zName, zCol, iDb);
        isIgnore = (rcauth==SQLITE_IGNORE);
      }

    }
#endif

    /* Take a shared-cache advisory read-lock on the parent table. Allocate 
    ** a cursor to use to search the unique index on the parent key columns 
    ** in the parent table.  */
    sqlite4TableLock(pParse, iDb, pTo->tnum, 0, pTo->zName);
    pParse->nTab++;

................................................................................
      FKey *p;

      /* Check if any child key columns are being modified. */
      for(p=pTab->pFKey; p; p=p->pNextFrom){
        for(i=0; i<p->nCol; i++){
          int iChildKey = p->aCol[i].iFrom;
          if( aChange[iChildKey]>=0 ) return 1;

        }
      }

      /* Check if any parent key columns are being modified. */
      for(p=sqlite4FkReferences(pTab); p; p=p->pNextTo){
        for(i=0; i<p->nCol; i++){
          char *zKey = p->aCol[i].zCol;
          int iKey;
          for(iKey=0; iKey<pTab->nCol; iKey++){
            Column *pCol = &pTab->aCol[iKey];
            if( (zKey ? !sqlite4StrICmp(pCol->zName, zKey) : pCol->isPrimKey) ){
              if( aChange[iKey]>=0 ) return 1;

            }
          }
        }
      }
    }
  }
  return 0;

Changes to src/insert.c.

544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
...
561
562
563
564
565
566
567
568



569
570
571
572
573
574
575
576
577
578
579
580
581
....
1022
1023
1024
1025
1026
1027
1028

1029
1030
1031

1032

1033
1034
1035
1036
1037
1038
1039

  db = pParse->db;
  memset(&dest, 0, sizeof(dest));
  if( pParse->nErr || db->mallocFailed ){
    goto insert_cleanup;
  }

  /* Locate the table into which we will be inserting new information.
  */
  assert( pTabList->nSrc==1 );
  zTab = pTabList->a[0].zName;
  if( NEVER(zTab==0) ) goto insert_cleanup;
  pTab = sqlite4SrcListLookup(pParse, pTabList);
  if( pTab==0 ){
    goto insert_cleanup;
  }
................................................................................
  assert( iDb<db->nDb );
  pDb = &db->aDb[iDb];
  zDb = pDb->zName;
  if( sqlite4AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
    goto insert_cleanup;
  }

  /* Set bImplicitPK to true for an implicit PRIMARY KEY, or false otherwise */



  pPk = sqlite4FindPrimaryKey(pTab, &iPk);
  bImplicitPK = pPk->aiColumn[0]==-1;

  /* Figure out if we have any triggers and if the table being
  ** inserted into is a view
  */
#ifndef SQLITE_OMIT_TRIGGER
  pTrigger = sqlite4TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask);
  isView = pTab->pSelect!=0;
#else
# define pTrigger 0
# define tmask 0
# define isView 0
................................................................................
      sqlite4VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB);
      sqlite4VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
      sqlite4MayAbort(pParse);
    }else
#endif
    {
      int isReplace;    /* Set to true if constraints may cause a replace */

      sqlite4GenerateConstraintChecks(pParse, pTab, baseCur, 
          regContent, aRegIdx, keyColumn>=0, 0, onError, endOfLoop, &isReplace
      );

      sqlite4FkCheck(pParse, pTab, 0, regIns);

      sqlite4CompleteInsertion(pParse, pTab, baseCur, 
          regContent, aRegIdx, 0, appendFlag, isReplace==0
      );
    }
  }

  /* Update the count of rows that are inserted







|
<







 







|
>
>
>




|
<







 







>



>
|
>







544
545
546
547
548
549
550
551

552
553
554
555
556
557
558
...
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575

576
577
578
579
580
581
582
....
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043

  db = pParse->db;
  memset(&dest, 0, sizeof(dest));
  if( pParse->nErr || db->mallocFailed ){
    goto insert_cleanup;
  }

  /* Locate the table into which we will be inserting new information. */

  assert( pTabList->nSrc==1 );
  zTab = pTabList->a[0].zName;
  if( NEVER(zTab==0) ) goto insert_cleanup;
  pTab = sqlite4SrcListLookup(pParse, pTabList);
  if( pTab==0 ){
    goto insert_cleanup;
  }
................................................................................
  assert( iDb<db->nDb );
  pDb = &db->aDb[iDb];
  zDb = pDb->zName;
  if( sqlite4AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
    goto insert_cleanup;
  }

  /* Set bImplicitPK to true for an implicit PRIMARY KEY, or false otherwise.
  ** Also set pPk to point to the primary key, and iPk to the cursor offset
  ** of the primary key cursor (i.e. so that the cursor opened on the primary
  ** key index is VDBE cursor (baseCur+iPk).  */
  pPk = sqlite4FindPrimaryKey(pTab, &iPk);
  bImplicitPK = pPk->aiColumn[0]==-1;

  /* Figure out if we have any triggers and if the table being
  ** inserted into is a view. */

#ifndef SQLITE_OMIT_TRIGGER
  pTrigger = sqlite4TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask);
  isView = pTab->pSelect!=0;
#else
# define pTrigger 0
# define tmask 0
# define isView 0
................................................................................
      sqlite4VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB);
      sqlite4VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
      sqlite4MayAbort(pParse);
    }else
#endif
    {
      int isReplace;    /* Set to true if constraints may cause a replace */

      sqlite4GenerateConstraintChecks(pParse, pTab, baseCur, 
          regContent, aRegIdx, keyColumn>=0, 0, onError, endOfLoop, &isReplace
      );

      sqlite4FkCheck(pParse, pTab, 0, regContent);

      sqlite4CompleteInsertion(pParse, pTab, baseCur, 
          regContent, aRegIdx, 0, appendFlag, isReplace==0
      );
    }
  }

  /* Update the count of rows that are inserted

Changes to src/kvmem.c.

470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
...
677
678
679
680
681
682
683

684
685
686
687
688
689
690
** After this routine returns successfully, the transaction level will be
** equal to iLevel.
*/
static int kvmemRollback(KVStore *pKVStore, int iLevel){
  KVMem *p = (KVMem*)pKVStore;
  assert( p->iMagicKVMemBase==SQLITE_KVMEMBASE_MAGIC );
  assert( iLevel>=0 );
  assert( iLevel<p->base.iTransLevel );
  while( p->base.iTransLevel>iLevel && p->base.iTransLevel>1 ){
    KVMemChng *pChng, *pNext;
    for(pChng=p->apLog[p->base.iTransLevel-2]; pChng; pChng=pNext){
      KVMemNode *pNode = pChng->pNode;
      if( pChng->pData ){
        kvmemDataUnref(pNode->pData);
        pNode->pData = pChng->pData;
................................................................................
  if( pBest ){
    pCur->pNode = kvmemNodeRef(pBest);
    pCur->pData = kvmemDataRef(pBest->pData);
  }else{
    pCur->pNode = 0;
    pCur->pData = 0;
  }

  return rc;
}

/*
** Delete the entry that the cursor is pointing to.
**
** Though the entry is "deleted", it still continues to exist as a







<







 







>







470
471
472
473
474
475
476

477
478
479
480
481
482
483
...
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
** After this routine returns successfully, the transaction level will be
** equal to iLevel.
*/
static int kvmemRollback(KVStore *pKVStore, int iLevel){
  KVMem *p = (KVMem*)pKVStore;
  assert( p->iMagicKVMemBase==SQLITE_KVMEMBASE_MAGIC );
  assert( iLevel>=0 );

  while( p->base.iTransLevel>iLevel && p->base.iTransLevel>1 ){
    KVMemChng *pChng, *pNext;
    for(pChng=p->apLog[p->base.iTransLevel-2]; pChng; pChng=pNext){
      KVMemNode *pNode = pChng->pNode;
      if( pChng->pData ){
        kvmemDataUnref(pNode->pData);
        pNode->pData = pChng->pData;
................................................................................
  if( pBest ){
    pCur->pNode = kvmemNodeRef(pBest);
    pCur->pData = kvmemDataRef(pBest->pData);
  }else{
    pCur->pNode = 0;
    pCur->pData = 0;
  }
  assert( rc!=SQLITE_DONE );
  return rc;
}

/*
** Delete the entry that the cursor is pointing to.
**
** Though the entry is "deleted", it still continues to exist as a

Changes to src/sqliteInt.h.

18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
/*#define SQLITE_OMIT_BTREECOUNT 1*/
#define SQLITE_OMIT_WAL 1
#define SQLITE_OMIT_VACUUM 1
#define SQLITE_OMIT_AUTOVACUUM 1
#define SQLITE_OMIT_SHARED_CACHE 1
/*#define SQLITE_OMIT_PAGER_PRAGMAS 1*/
#define SQLITE_OMIT_PROGRESS_CALLBACK 1

#define SQLITE_OMIT_MERGE_SORT 1

/*
** These #defines should enable >2GB file support on POSIX if the
** underlying operating system supports it.  If the OS lacks
** large file support, or if the OS is windows, these should be no-ops.
**
** Ticket #2739:  The _LARGEFILE_SOURCE macro must appear before any







>
|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*#define SQLITE_OMIT_BTREECOUNT 1*/
#define SQLITE_OMIT_WAL 1
#define SQLITE_OMIT_VACUUM 1
#define SQLITE_OMIT_AUTOVACUUM 1
#define SQLITE_OMIT_SHARED_CACHE 1
/*#define SQLITE_OMIT_PAGER_PRAGMAS 1*/
#define SQLITE_OMIT_PROGRESS_CALLBACK 1

#define SQLITE_OMIT_MERGE_SORT 1 

/*
** These #defines should enable >2GB file support on POSIX if the
** underlying operating system supports it.  If the OS lacks
** large file support, or if the OS is windows, these should be no-ops.
**
** Ticket #2739:  The _LARGEFILE_SOURCE macro must appear before any

Changes to src/test_storage.c.

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51


52
53
54
55
56
57
58
*/
static void storageSetTclErrorName(Tcl_Interp *interp, int rc){
  extern const char *sqlite4TestErrorName(int);
  Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite4TestErrorName(rc), -1));
}

/*
** TCLCMD:    storage_open URI FLAGS
**
** Return a string that identifies the new storage object.
*/
static int test_storage_open(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  KVStore *pNew = 0;
  int rc;
  int flags;
  sqlite4 db;
  char zRes[50];
  if( objc!=3 ){
    Tcl_WrongNumArgs(interp, 2, objv, "URI FLAGS");
    return TCL_ERROR;
  }
  if( Tcl_GetIntFromObj(interp, objv[2], &flags) ) return TCL_ERROR;


  memset(&db, 0, sizeof(db));
  rc = sqlite4KVStoreOpen(&db, "test", Tcl_GetString(objv[1]), &pNew, flags);
  if( rc ){
    sqlite4KVStoreClose(pNew);
    storageSetTclErrorName(interp, rc);
    return TCL_ERROR;
  }







|











|


|
|


|
>
>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
*/
static void storageSetTclErrorName(Tcl_Interp *interp, int rc){
  extern const char *sqlite4TestErrorName(int);
  Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite4TestErrorName(rc), -1));
}

/*
** TCLCMD:    storage_open URI ?FLAGS?
**
** Return a string that identifies the new storage object.
*/
static int test_storage_open(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  KVStore *pNew = 0;
  int rc;
  int flags = 0;
  sqlite4 db;
  char zRes[50];
  if( objc!=2 && objc!=3 ){
    Tcl_WrongNumArgs(interp, 1, objv, "URI ?FLAGS?");
    return TCL_ERROR;
  }
  if( objc==3 && Tcl_GetIntFromObj(interp, objv[2], &flags) ){
    return TCL_ERROR;
  }
  memset(&db, 0, sizeof(db));
  rc = sqlite4KVStoreOpen(&db, "test", Tcl_GetString(objv[1]), &pNew, flags);
  if( rc ){
    sqlite4KVStoreClose(pNew);
    storageSetTclErrorName(interp, rc);
    return TCL_ERROR;
  }

Changes to src/vdbe.c.

2136
2137
2138
2139
2140
2141
2142

2143
2144
2145
2146
2147
2148
2149
....
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176


2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201

2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
....
2659
2660
2661
2662
2663
2664
2665

2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
....
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
....
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702

3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716

3717

3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
      sqlite4VdbeDestroyDecoder(pCodec);
    }
  }else{
    sqlite4VdbeMemSetNull(pDest);
  }
  UPDATE_MAX_BLOBSIZE(pDest);
  REGISTER_TRACE(pOp->p3, pDest);

  break;
}

/* Opcode: Affinity P1 P2 * P4 *
**
** Apply affinities to a range of P2 registers starting with P1.
**
................................................................................
    assert( memIsValid(pIn1) );
    applyAffinity(pIn1, cAff, encoding);
    pIn1++;
  }
  break;
}

/* Opcode: MakeIdxKey P1 P2 P3 P4 *
**
** P1 is an open cursor. P2 is the first register in a contiguous array
** of N registers containing values to encode into a database key. N is
** equal to the number of columns indexed by P1, plus the number of 
** trailing primary key columns (if any).


**
** This instruction encodes the N values into a database key and writes
** the result to register P3.
**
** If P4 is of type P4_INT32, then it is a register number. This instruction
** sets register P4 to an integer value - the number of bytes in the 
** generated index key not including any appended primary key column values.
*/
case OP_MakeIdxKey: {
  VdbeCursor *pC;
  KeyInfo *pKeyInfo;
  Mem *pData0;
  u8 *aRec;                       /* The constructed database key */
  int nRec;                       /* Size of aRec[] in bytes */
  int nShort;                     /* Size of aRec[] without PK values */
  Mem *pShort;                    /* Memory cell to write nShort to */
  
  pC = p->apCsr[pOp->p1];
  pKeyInfo = pC->pKeyInfo;
  pData0 = &aMem[pOp->p2];
  pOut = &aMem[pOp->p3];
  aRec = 0;

  memAboutToChange(p, pOut);


  rc = sqlite4VdbeEncodeKey(
    db, pData0, pKeyInfo->nField, pC->iRoot, pKeyInfo, &aRec, &nRec, &nShort
  );

  if( rc ){
    sqlite4DbFree(db, aRec);
  }else{
    if( pOp->p4type==P4_INT32 ){
      pShort = &aMem[pOp->p4.i];
      memAboutToChange(p, pShort);
      pShort->u.i = nShort;
      MemSetTypeFlag(pShort, MEM_Int);
      REGISTER_TRACE(pOp->p4.i, pShort);
    }
    rc = sqlite4VdbeMemSetStr(pOut, aRec, nRec, 0, SQLITE_DYNAMIC);
    REGISTER_TRACE(pOp->p3, pOut);
    UPDATE_MAX_BLOBSIZE(pOut);
  }

  break;
}
................................................................................
case OP_OpenEphemeral: {
  VdbeCursor *pCx;

  assert( pOp->p1>=0 );
  pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
  if( pCx==0 ) goto no_mem;
  pCx->nullRow = 1;

  rc = sqlite4KVStoreOpen(db, "ephm", ":memory:", &pCx->pTmpKV,
          SQLITE_KVOPEN_TEMPORARY | SQLITE_KVOPEN_NO_TRANSACTIONS
  );
  if( rc==SQLITE_OK ){
    rc = sqlite4KVStoreOpenCursor(pCx->pTmpKV, &pCx->pKVCur);
  }
  if( rc==SQLITE_OK ){
    rc = sqlite4KVStoreBegin(pCx->pTmpKV, 2);
  }
  pCx->pKeyInfo = pOp->p4.pKeyInfo;
  if( pCx->pKeyInfo ) pCx->pKeyInfo->enc = ENC(p->db);

  pCx->isIndex = !pCx->isTable;
  break;
}

/* Opcode: OpenSorter P1 P2 * P4 *
**
** This opcode works like OP_OpenEphemeral except that it opens
................................................................................
  pOut = &aMem[pOp->p2];
  pC = p->apCsr[pOp->p1];
  assert( pC!=0 );
#ifndef SQLITE_OMIT_MERGE_SORT
  assert( pC->isSorter );
  rc = sqlite4VdbeSorterRowkey(pC, pOut);
#else
  pOp->opcode = OP_RowKey;
  pc--;
#endif
  break;
}

/* Opcode: RowData P1 P2 * * *
**
................................................................................
    pC->nullRow = 1;
    rc = SQLITE_OK;
  }
  pC->rowidIsValid = 0;
  break;
}

/* Opcode: IdxInsert P1 P2 * * P5
**
** Register P2 holds an SQL index key made using the
** MakeRecord instructions.  This opcode writes that key
** into the index P1.  Data for the entry is nil.
**
** P3 is a flag that provides a hint to the b-tree layer that this
** insert is likely to be an append.
**
** This instruction only works for indices.  The equivalent instruction
** for tables is OP_Insert.
*/
case OP_SorterInsert: {      /* in2 */

  VdbeCursor *pC;

  assert( pOp->p1>=0 && pOp->p1<p->nCursor );
  pC = p->apCsr[pOp->p1];
  assert( pC!=0 );
  assert( pC->isSorter==(pOp->opcode==OP_SorterInsert) );
  pIn2 = &aMem[pOp->p2];
  assert( pIn2->flags & MEM_Blob );
  assert( pC->isTable==0 );
  rc = ExpandBlob(pIn2);
  if( rc==SQLITE_OK ){
    rc = sqlite4VdbeSorterWrite(db, pC, pIn2);
  }
  break;

}



/* Opcode: IdxInsert P1 P2 P3 * *
**
** Register P2 holds the data and register P3 holds the key for an
** index record.  Write this record into the index specified by the
** cursor P1.
*/
case OP_IdxInsert: {
  VdbeCursor *pC;
  Mem *pKey;
  Mem *pData;








>







 







|


|
|
|
>
>




|
|
<




|


<
<









>

|





<
<
<
<
<
<
<







 







>



<
|
<
<
|
|


<







 







|







 







|
<
<
<
<
<
<
<
<
<
<


>

<












>
|
>
|



|
|







2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
....
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185

2186
2187
2188
2189
2190
2191
2192


2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209







2210
2211
2212
2213
2214
2215
2216
....
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663

2664


2665
2666
2667
2668

2669
2670
2671
2672
2673
2674
2675
....
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
....
3674
3675
3676
3677
3678
3679
3680
3681










3682
3683
3684
3685

3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
      sqlite4VdbeDestroyDecoder(pCodec);
    }
  }else{
    sqlite4VdbeMemSetNull(pDest);
  }
  UPDATE_MAX_BLOBSIZE(pDest);
  REGISTER_TRACE(pOp->p3, pDest);
      assert( rc<100 );
  break;
}

/* Opcode: Affinity P1 P2 * P4 *
**
** Apply affinities to a range of P2 registers starting with P1.
**
................................................................................
    assert( memIsValid(pIn1) );
    applyAffinity(pIn1, cAff, encoding);
    pIn1++;
  }
  break;
}

/* Opcode: MakeIdxKey P1 P2 P3 * P5
**
** P1 is an open cursor. P2 is the first register in a contiguous array
** of N registers containing values to encode into a database key. Normally,
** N is equal to the number of columns indexed by P1, plus the number of 
** trailing primary key columns (if any). 
**
** Or, if P5 is non-zero, then any trailing primary key columns are omitted.
**
** This instruction encodes the N values into a database key and writes
** the result to register P3.
**
** No affinity transformations are applied to the input values before 
** they are encoded. 

*/
case OP_MakeIdxKey: {
  VdbeCursor *pC;
  KeyInfo *pKeyInfo;
  Mem *pData0;                    /* First in array of input registers */
  u8 *aRec;                       /* The constructed database key */
  int nRec;                       /* Size of aRec[] in bytes */


  
  pC = p->apCsr[pOp->p1];
  pKeyInfo = pC->pKeyInfo;
  pData0 = &aMem[pOp->p2];
  pOut = &aMem[pOp->p3];
  aRec = 0;

  memAboutToChange(p, pOut);

  assert( pOp->p5==0 );
  rc = sqlite4VdbeEncodeKey(
    db, pData0, pKeyInfo->nField, pC->iRoot, pKeyInfo, &aRec, &nRec, 0
  );

  if( rc ){
    sqlite4DbFree(db, aRec);
  }else{







    rc = sqlite4VdbeMemSetStr(pOut, aRec, nRec, 0, SQLITE_DYNAMIC);
    REGISTER_TRACE(pOp->p3, pOut);
    UPDATE_MAX_BLOBSIZE(pOut);
  }

  break;
}
................................................................................
case OP_OpenEphemeral: {
  VdbeCursor *pCx;

  assert( pOp->p1>=0 );
  pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
  if( pCx==0 ) goto no_mem;
  pCx->nullRow = 1;

  rc = sqlite4KVStoreOpen(db, "ephm", ":memory:", &pCx->pTmpKV,
          SQLITE_KVOPEN_TEMPORARY | SQLITE_KVOPEN_NO_TRANSACTIONS
  );

  if( rc==SQLITE_OK ) rc = sqlite4KVStoreOpenCursor(pCx->pTmpKV, &pCx->pKVCur);


  if( rc==SQLITE_OK ) rc = sqlite4KVStoreBegin(pCx->pTmpKV, 2);

  pCx->pKeyInfo = pOp->p4.pKeyInfo;
  if( pCx->pKeyInfo ) pCx->pKeyInfo->enc = ENC(p->db);

  pCx->isIndex = !pCx->isTable;
  break;
}

/* Opcode: OpenSorter P1 P2 * P4 *
**
** This opcode works like OP_OpenEphemeral except that it opens
................................................................................
  pOut = &aMem[pOp->p2];
  pC = p->apCsr[pOp->p1];
  assert( pC!=0 );
#ifndef SQLITE_OMIT_MERGE_SORT
  assert( pC->isSorter );
  rc = sqlite4VdbeSorterRowkey(pC, pOut);
#else
  pOp->opcode = OP_RowData;
  pc--;
#endif
  break;
}

/* Opcode: RowData P1 P2 * * *
**
................................................................................
    pC->nullRow = 1;
    rc = SQLITE_OK;
  }
  pC->rowidIsValid = 0;
  break;
}

/* Opcode: SorterInsert P1 P2 P3










*/
case OP_SorterInsert: {      /* in2 */
#ifndef SQLITE_OMIT_MERGE_SORT
  VdbeCursor *pC;

  assert( pOp->p1>=0 && pOp->p1<p->nCursor );
  pC = p->apCsr[pOp->p1];
  assert( pC!=0 );
  assert( pC->isSorter==(pOp->opcode==OP_SorterInsert) );
  pIn2 = &aMem[pOp->p2];
  assert( pIn2->flags & MEM_Blob );
  assert( pC->isTable==0 );
  rc = ExpandBlob(pIn2);
  if( rc==SQLITE_OK ){
    rc = sqlite4VdbeSorterWrite(db, pC, pIn2);
  }
  break;
#endif

  /* If OMIT_MERGE_SORT is defined, fall through to IdxInsert. */
}

/* Opcode: IdxInsert P1 P2 P3 * *
**
** Register P3 holds the key and register P2 holds the data for an
** index entry.  Write this record into the index specified by the
** cursor P1.
*/
case OP_IdxInsert: {
  VdbeCursor *pC;
  Mem *pKey;
  Mem *pData;

Changes to src/vdbeaux.c.

1801
1802
1803
1804
1805
1806
1807
1808








1809









1810

1811
1812
1813
1814
1815

1816
1817
1818

1819
1820
1821
1822
1823
1824
1825
1826
1827
1828

1829
1830
1831



1832
1833
1834

1835
1836
1837
1838
1839
1840
1841







1842
1843



1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
....
1859
1860
1861
1862
1863
1864
1865

1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
    return SQLITE_OK;
  }
  checkActiveVdbeCnt(db);

  if( p->pc<0 ){
    /* No commit or rollback needed if the program never started */
  }else{
    /* Check for immediate foreign key violations. */








    if( p->rc==SQLITE_OK ){









      sqlite4VdbeCheckFk(p, 0);

    }
  
    /* If the auto-commit flag is set and this is the only active writer 
    ** VM, then we do either a commit or rollback of the current transaction. 
    */

    if( !sqlite4VtabInSync(db) 
     && db->autoCommit 
     && db->writeVdbeCnt==(p->readOnly==0) 

    ){
      rc = sqlite4VdbeCheckFk(p, 1);
      if( rc!=SQLITE_OK ){
        rc = SQLITE_CONSTRAINT;
      }else{ 
        /* The auto-commit flag is true, the vdbe program was successful 
        ** or hit an 'OR FAIL' constraint and there are no deferred foreign
        ** key constraints to hold up the transaction. This means a commit 
        ** is required. */
        rc = vdbeCommit(db, p);

      }
      if( rc==SQLITE_BUSY && p->readOnly ){
        /* will return the error */



      }else if( rc!=SQLITE_OK ){
        p->rc = rc;
        sqlite4RollbackAll(db);

      }else{
        db->nDeferredCons = 0;
        sqlite4CommitInternalChanges(db);
      }
    }else{
      /* Not in auto-commit mode.  If the statement failed, rollback
      ** the effects of just this one statement */







      if( p->rc!=SQLITE_OK && p->stmtTransMask!=0 ){
        int i;



        for(i=0; i<db->nDb; i++){
          if( p->stmtTransMask & ((yDbMask)1)<<i ){
            KVStore *pKV = db->aDb[i].pKV;
            rc = sqlite4KVStoreRollback(pKV, pKV->iTransLevel-1);
            if( rc ){
              sqlite4RollbackAll(db);
              break;
            }
          }
        }
      }
    }
  }

  /* We have successfully halted and closed the VM.  Record this fact. */
................................................................................
  if( p->pc>=0 ){
    db->activeVdbeCnt--;
    if( !p->readOnly ){
      db->writeVdbeCnt--;
    }
    assert( db->activeVdbeCnt>=db->writeVdbeCnt );
  }

  p->magic = VDBE_MAGIC_HALT;
  checkActiveVdbeCnt(db);
  if( p->db->mallocFailed ){
    p->rc = SQLITE_NOMEM;
  }

  return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK);
}


/*
** Each VDBE holds the result of the most recent sqlite4_step() call
** in p->rc.  This routine sets that result back to SQLITE_OK.







|
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
|
>

|
<
<
<
>
|
<
|
>
|
|
<
<
<
<
<
<
<
<
>

<
<
>
>
>
|
|
<
>
|
|
|
|
<
<
<
>
>
>
>
>
>
>
|
|
>
>
>
|
|
|
|
|
|
|
<







 







>





<







1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830



1831
1832

1833
1834
1835
1836








1837
1838


1839
1840
1841
1842
1843

1844
1845
1846
1847
1848



1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867

1868
1869
1870
1871
1872
1873
1874
....
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887

1888
1889
1890
1891
1892
1893
1894
    return SQLITE_OK;
  }
  checkActiveVdbeCnt(db);

  if( p->pc<0 ){
    /* No commit or rollback needed if the program never started */
  }else{
    /* eAction:
    **
    **    0 - Do commit, either statement or transaction.
    **    1 - Do rollback, either statement or transaction.
    **    2 - Do transaction rollback.
    */
    int eAction;

    /* Check if an error has already occurred */
    if( p->rc==SQLITE_CONSTRAINT ){
      if( p->errorAction==OE_Rollback ){
        eAction = 2;
      }else if( p->errorAction==OE_Fail ){
        eAction = 0;
      }
    }else if( p->rc ){
      eAction = 1;
    }

    if( eAction==0 && sqlite4VdbeCheckFk(p, 0) ){
      eAction = 1;
    }




    if( eAction==2 || ( 
          !sqlite4VtabInSync(db)==0 

       && db->writeVdbeCnt==(p->readOnly==0) 
       && db->autoCommit 
    )){
      if( eAction==0 && sqlite4VdbeCheckFk(p, 1) ){








        eAction = 1;
      }



      if( eAction==0 ){
        int rc = vdbeCommit(db, p);
        if( rc!=SQLITE_OK ){
          p->rc = rc;

          eAction = 1;
        }else{
          assert( db->nDeferredCons<=0 );
          sqlite4CommitInternalChanges(db);
        }



      }

      if( eAction ){
        sqlite4RollbackAll(db);
      }

      db->nDeferredCons = 0;
    }else if( p->stmtTransMask ){
      int i;
      int (*xFunc)(KVStore *,int);
      xFunc = (eAction ? sqlite4KVStoreRollback : sqlite4KVStoreCommit);

      for(i=0; i<db->nDb; i++){
        if( p->stmtTransMask & ((yDbMask)1)<<i ){
          KVStore *pKV = db->aDb[i].pKV;
          rc = xFunc(pKV, pKV->iTransLevel-1);
          if( rc ){
            sqlite4RollbackAll(db);
            break;

          }
        }
      }
    }
  }

  /* We have successfully halted and closed the VM.  Record this fact. */
................................................................................
  if( p->pc>=0 ){
    db->activeVdbeCnt--;
    if( !p->readOnly ){
      db->writeVdbeCnt--;
    }
    assert( db->activeVdbeCnt>=db->writeVdbeCnt );
  }

  p->magic = VDBE_MAGIC_HALT;
  checkActiveVdbeCnt(db);
  if( p->db->mallocFailed ){
    p->rc = SQLITE_NOMEM;
  }

  return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK);
}


/*
** Each VDBE holds the result of the most recent sqlite4_step() call
** in p->rc.  This routine sets that result back to SQLITE_OK.

Changes to src/vdbecodec.c.

591
592
593
594
595
596
597

598

599
600
601
602
603
604
605
  u64 dummy;
  int i;

  p = aKey;
  p += sqlite4GetVarint64(p, pEnd-p, &dummy);

  for(i=0; i<nField; i++){

    switch( *(p++) ){

      case 0x05:                  /* NULL */
      case 0x06:                  /* NaN */
      case 0x07:                  /* -ve infinity */
      case 0x15:                  /* zero */
      case 0x23:                  /* +ve infinity */
        break;








>
|
>







591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
  u64 dummy;
  int i;

  p = aKey;
  p += sqlite4GetVarint64(p, pEnd-p, &dummy);

  for(i=0; i<nField; i++){
    u8 c = *(p++);
    switch( c ){

      case 0x05:                  /* NULL */
      case 0x06:                  /* NaN */
      case 0x07:                  /* -ve infinity */
      case 0x15:                  /* zero */
      case 0x23:                  /* +ve infinity */
        break;

Changes to test/conflict.test.

313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  if {$t0} {set t1 {column a is not unique}}
  if {[info exists TEMP_STORE] && $TEMP_STORE==3} {
    set t3 0
  } else {
    set t3 [expr {$t3+$t4}]
  }
  do_test conflict-6.$i {
    db close
    sqlite4 db test.db 
    if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
    execsql {pragma temp_store=file}
    set ::sqlite_opentemp_count 0
    set r0 [catch {execsql [subst {
      DROP TABLE t1;
      CREATE TABLE t1(a,b,c, UNIQUE(a) $conf1);
      INSERT INTO t1 SELECT * FROM t2;







|
|







313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  if {$t0} {set t1 {column a is not unique}}
  if {[info exists TEMP_STORE] && $TEMP_STORE==3} {
    set t3 0
  } else {
    set t3 [expr {$t3+$t4}]
  }
  do_test conflict-6.$i {
    #db close
    #sqlite4 db test.db 
    if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
    execsql {pragma temp_store=file}
    set ::sqlite_opentemp_count 0
    set r0 [catch {execsql [subst {
      DROP TABLE t1;
      CREATE TABLE t1(a,b,c, UNIQUE(a) $conf1);
      INSERT INTO t1 SELECT * FROM t2;

Changes to test/permutations.test.

120
121
122
123
124
125
126

127
128
129
130
131





132
133
134
135
136
137
138
#############################################################################
# Start of tests
#

#-------------------------------------------------------------------------
# Define the generic test suites:
#

#   veryquick
#   quick
#   full
#
lappend ::testsuitelist xxx






test_suite "veryquick" -prefix "" -description {
  "Very" quick test suite. Runs in less than 5 minutes on a workstation. 
  This test suite is the same as the "quick" tests, except that some files
  that test malloc and IO errors are omitted.
} -files [
  test_set $allquicktests -exclude *malloc* *ioerr* *fault*







>





>
>
>
>
>







120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#############################################################################
# Start of tests
#

#-------------------------------------------------------------------------
# Define the generic test suites:
#
#   src4
#   veryquick
#   quick
#   full
#
lappend ::testsuitelist xxx

test_suite "src4" -prefix "" -description {
} -files [
  test_set simple.test fkey1.test
]

test_suite "veryquick" -prefix "" -description {
  "Very" quick test suite. Runs in less than 5 minutes on a workstation. 
  This test suite is the same as the "quick" tests, except that some files
  that test malloc and IO errors are omitted.
} -files [
  test_set $allquicktests -exclude *malloc* *ioerr* *fault*

Changes to test/simple.test.

198
199
200
201
202
203
204



205


206
207
208
209
210
211
212
...
257
258
259
260
261
262
263









































































































264
265
266
267
268
269
270

do_execsql_test 8.1 {
  CREATE TABLE t1(a PRIMARY KEY, b);
  INSERT INTO t1 VALUES('a', 'b');
}

do_execsql_test 8.2 { DELETE FROM t1 WHERE b = 'b' }



do_execsql_test 8.3 { SELECT * FROM t1 } {}


do_execsql_test 8.4 {
  INSERT INTO t1 VALUES('a', 'A');
  INSERT INTO t1 VALUES('b', 'B');
  INSERT INTO t1 VALUES('c', 'A');
  INSERT INTO t1 VALUES('d', 'B');
  INSERT INTO t1 VALUES('e', 'A');
  INSERT INTO t1 VALUES('f', 'B');
................................................................................
  CREATE TABLE t1(a, b, c, UNIQUE(a));
  INSERT INTO t1 VALUES(1,2,3);
}
do_catchsql_test 11.2 { 
  INSERT INTO t1 VALUES(1,2,4)
} {1 {column a is not unique}}











































































































finish_test

#proc populate_t1 {} {
#  db eval {
#    INSERT INTO t1(a, b) VALUES(4, 'four');
#    INSERT INTO t1(a, b) VALUES(9, 'nine');







>
>
>

>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
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

do_execsql_test 8.1 {
  CREATE TABLE t1(a PRIMARY KEY, b);
  INSERT INTO t1 VALUES('a', 'b');
}

do_execsql_test 8.2 { DELETE FROM t1 WHERE b = 'b' }

execsql {PRAGMA vdbe_trace = 1}
breakpoint
do_execsql_test 8.3 { SELECT * FROM t1 } {}
finish_test

do_execsql_test 8.4 {
  INSERT INTO t1 VALUES('a', 'A');
  INSERT INTO t1 VALUES('b', 'B');
  INSERT INTO t1 VALUES('c', 'A');
  INSERT INTO t1 VALUES('d', 'B');
  INSERT INTO t1 VALUES('e', 'A');
  INSERT INTO t1 VALUES('f', 'B');
................................................................................
  CREATE TABLE t1(a, b, c, UNIQUE(a));
  INSERT INTO t1 VALUES(1,2,3);
}
do_catchsql_test 11.2 { 
  INSERT INTO t1 VALUES(1,2,4)
} {1 {column a is not unique}}

#-------------------------------------------------------------------------
reset_db

do_execsql_test 12.1 {
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(3, 'three');
  INSERT INTO t1 VALUES(1, 'one');
  INSERT INTO t1 VALUES(2, 'two');
}
do_execsql_test 12.2 { SELECT * FROM t1 ORDER BY a } {1 one 2 two 3 three}
do_execsql_test 12.3 { SELECT * FROM t1 ORDER BY b } {1 one 3 three 2 two}

#-------------------------------------------------------------------------
reset_db

do_execsql_test 13.1 {
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(3, 'three');
  INSERT INTO t1 VALUES(1, 'one');
  INSERT INTO t1 VALUES(2, 'two');
}
do_execsql_test 13.2  { SELECT a FROM t1 } {3 1 2}
do_execsql_test 13.3  { CREATE TABLE t2(x, y) }
do_execsql_test 13.4  { SELECT a FROM t1 } {3 1 2}
do_execsql_test 13.5  { DROP TABLE t2 }
do_execsql_test 13.6  { SELECT a FROM t1 } {3 1 2}
do_execsql_test 13.7  { CREATE TABLE t2 AS SELECT * FROM t1 }
do_execsql_test 13.8  { SELECT a FROM t2 } {3 1 2}
do_execsql_test 13.9  { DROP TABLE t1 }
do_execsql_test 13.10 { SELECT a FROM t2 } {3 1 2}

#-------------------------------------------------------------------------
reset_db

do_execsql_test 14.1 {
  CREATE TABLE t1(a,b,c NOT NULL  DEFAULT 5);
  CREATE TABLE t2(a,b,c); 
  CREATE TABLE t3(x);

  INSERT INTO t2 VALUES(1,2,1);
  INSERT INTO t2 VALUES(2,3,2);
  INSERT INTO t2 VALUES(3,4,1);
  INSERT INTO t2 VALUES(4,5,4);

  INSERT INTO t3 VALUES(1);
}

do_execsql_test 14.2 { DROP TABLE t1 }
do_execsql_test 14.3 { SELECT * FROM t3 } 1

#-------------------------------------------------------------------------
reset_db

do_execsql_test 15.1.1 {
  CREATE TABLE t1(x PRIMARY KEY);
  BEGIN;
    INSERT INTO t1 VALUES('rollback is not implemented yet');
}

do_execsql_test 15.1.2 { ROLLBACK }
do_execsql_test 15.1.3 { SELECT * FROM t1 } {}

#-------------------------------------------------------------------------
reset_db

do_execsql_test 16.1.1 {
  PRAGMA foreign_keys = ON;
  CREATE TABLE p1(x PRIMARY KEY);
  CREATE TABLE c1(y REFERENCES p1);

  INSERT INTO p1 VALUES(2);
  INSERT INTO p1 VALUES(4);
  INSERT INTO p1 VALUES(6);
}

do_execsql_test  16.1.2 { INSERT INTO c1 VALUES(2) }
do_catchsql_test 16.1.3 { 
  INSERT INTO c1 VALUES(3) 
} {1 {foreign key constraint failed}}
do_execsql_test 16.1.4 { SELECT * FROM c1 } {2}

#-------------------------------------------------------------------------
reset_db

do_execsql_test 17.1 {

  PRAGMA foreign_keys = ON;
  CREATE TABLE t1(x PRIMARY KEY);
  CREATE TABLE t2(a PRIMARY KEY, b);

  INSERT INTO t1 VALUES('X');

  INSERT INTO t2 VALUES(1, 'A');
  INSERT INTO t2 VALUES(2, 'B');
  INSERT INTO t2 VALUES(3, 'C');
  INSERT INTO t2 VALUES(4, 'D');
  INSERT INTO t2 VALUES(5, 'A');
}

breakpoint
do_catchsql_test 17.2 { 
  INSERT INTO t1 SELECT b FROM t2;
} {1 {column x is not unique}}

do_execsql_test 17.3 { SELECT * FROM t1 } {X}

finish_test

#proc populate_t1 {} {
#  db eval {
#    INSERT INTO t1(a, b) VALUES(4, 'four');
#    INSERT INTO t1(a, b) VALUES(9, 'nine');

Changes to test/storage1.test.

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  lappend res [storage_prev $c1]
  lappend res [storage_key $c1]
  lappend res [storage_data $c1]
  lappend res [storage_prev $c1]
  storage_close_cursor $c1
  storage_close $x
  set res
} {SQLITE_INEXACT 014557 EF01 SQLITE_OK 012345 ABCD SQLITE_DONE}
do_test storage1-1.6 {
  set x [storage_open :memory:]
  storage_begin $x 2
  storage_replace $x 012345 abcd
  storage_replace $x 014567 ef01
  storage_replace $x 013456 deaf
  storage_replace $x 012345 eeaa







|







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  lappend res [storage_prev $c1]
  lappend res [storage_key $c1]
  lappend res [storage_data $c1]
  lappend res [storage_prev $c1]
  storage_close_cursor $c1
  storage_close $x
  set res
} {SQLITE_INEXACT 014557 EF01 SQLITE_OK 012345 ABCD SQLITE_NOTFOUND}
do_test storage1-1.6 {
  set x [storage_open :memory:]
  storage_begin $x 2
  storage_replace $x 012345 abcd
  storage_replace $x 014567 ef01
  storage_replace $x 013456 deaf
  storage_replace $x 012345 eeaa