/ Check-in [01398fb7]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Fix problems with TEMP indices that lead to corrupt databases. These problems were discovered while working on ticket #317. No sure yet if that ticket is fixed. (CVS 981)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 01398fb78bab7e5c6f439f2b743f26e82118468a
User & Date: drh 2003-05-17 17:35:11
Context
2003-05-17
17:38
Bump the version number and update the change log. We are rushing out release 2.8.2 because of the database corruption problem fixed by the previous check-in. (CVS 982) check-in: e134459d user: drh tags: trunk
17:35
Fix problems with TEMP indices that lead to corrupt databases. These problems were discovered while working on ticket #317. No sure yet if that ticket is fixed. (CVS 981) check-in: 01398fb7 user: drh tags: trunk
02:44
Version 2.8.1 (CVS 980) check-in: 590f963b user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.92 2003/04/25 15:37:58 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
      checkAppendMsg(pCheck, zContext, zMsg);
      break;
    }
    if( isFreeList ){
      FreelistInfo *pInfo = (FreelistInfo*)pOvfl->aPayload;
      int n = SWAB32(pCheck->pBt, pInfo->nFree);
      for(i=0; i<n; i++){
        checkRef(pCheck, SWAB32(pCheck->pBt, pInfo->aFree[i]), zMsg);
      }
      N -= n;
    }
    iPage = SWAB32(pCheck->pBt, pOvfl->iNext);
    sqlitepager_unref(pOvfl);
  }
}







|







 







|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.93 2003/05/17 17:35:11 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
      checkAppendMsg(pCheck, zContext, zMsg);
      break;
    }
    if( isFreeList ){
      FreelistInfo *pInfo = (FreelistInfo*)pOvfl->aPayload;
      int n = SWAB32(pCheck->pBt, pInfo->nFree);
      for(i=0; i<n; i++){
        checkRef(pCheck, SWAB32(pCheck->pBt, pInfo->aFree[i]), zContext);
      }
      N -= n;
    }
    iPage = SWAB32(pCheck->pBt, pOvfl->iNext);
    sqlitepager_unref(pOvfl);
  }
}

Changes to src/btree_rb.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
27
28
29
30
31
32
33


34
35
36
37
38
39
40
41
42
43
44
..
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
...
122
123
124
125
126
127
128
129
130






131
132
133
134
135
136
137
138
139
140
...
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
...
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571

572
573




574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
...
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
...
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
...
647
648
649
650
651
652
653
654
655
656
657
658
659
660





661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679


680

681
682


683
684
685
686

687
688
689
690
691
692
693
694
...
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
...
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
...
782
783
784
785
786
787
788
789
790





791
792
793
794
795
796
797
...
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839

840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
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
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
...
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
...
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
...
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
....
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
....
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
....
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
....
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree_rb.c,v 1.9 2003/04/25 13:22:53 drh Exp $
**
** This file implements an in-core database using Red-Black balanced
** binary trees.
**
** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC.
*/
#include "btree.h"
................................................................................
*/
#ifndef SQLITE_OMIT_INMEMORYDB


typedef struct BtRbTree BtRbTree;
typedef struct BtRbNode BtRbNode;
typedef struct BtRollbackOp BtRollbackOp;



/* Forward declarations */
static BtOps sqliteBtreeOps;
static BtCursorOps sqliteBtreeCursorOps;

/*
 * During each transaction (or checkpoint), a linked-list of
 * "rollback-operations" is accumulated. If the transaction is rolled back,
 * then the list of operations must be executed (to restore the database to
 * it's state before the transaction started). If the transaction is to be
 * committed, just delete the list.
................................................................................
** Legal values for BtRollbackOp.eOp:
*/
#define ROLLBACK_INSERT 1 /* Insert a record */
#define ROLLBACK_DELETE 2 /* Delete a record */
#define ROLLBACK_CREATE 3 /* Create a table */
#define ROLLBACK_DROP   4 /* Drop a table */

struct Btree {
  BtOps *pOps;    /* Function table */
  int aMetaData[SQLITE_N_BTREE_META];

  int next_idx;   /* next available table index */
  Hash tblHash;   /* All created tables, by index */
  u8 isAnonymous; /* True if this Btree is to be deleted when closed */
  u8 eTransState; /* State of this Btree wrt transactions */

  BtRollbackOp *pTransRollback; 
  BtRollbackOp *pCheckRollback;
  BtRollbackOp *pCheckRollbackTail;
};

/*
** Legal values for Btree.eTransState.
*/
#define TRANS_NONE           0  /* No transaction is in progress */
#define TRANS_INTRANSACTION  1  /* A transaction is in progress */
#define TRANS_INCHECKPOINT   2  /* A checkpoint is in progress  */
#define TRANS_ROLLBACK       3  /* We are currently rolling back a checkpoint or
                                 * transaction. */

struct BtCursor {
  BtCursorOps *pOps;        /* Function table */
  Btree    *pBtree;
  BtRbTree *pTree;
  int       iTree;          /* Index of pTree in pBtree */
  BtRbNode *pNode;
  u8 eSkip;                 /* Determines if next step operation is a no-op */
};

/*
** Legal values for BtCursor.eSkip.
*/
#define SKIP_NONE     0   /* Always step the cursor */
#define SKIP_NEXT     1   /* The next sqliteBtreeNext() is a no-op */
#define SKIP_PREV     2   /* The next sqliteBtreePrevious() is a no-op */
#define SKIP_INVALID  3   /* Calls to Next() and Previous() are invalid */

struct BtRbTree {
  BtRbNode *pHead;   /* Head of the tree, or NULL */
};

struct BtRbNode {
................................................................................
  BtRbNode *pLeft;   /* Nodes left child, or NULL */
  BtRbNode *pRight;  /* Nodes right child, or NULL */

  int nBlackHeight;  /* Only used during the red-black integrity check */
};

/* Forward declarations */
static int memBtreeMoveto(BtCursor* pCur, const void *pKey, int nKey,int *pRes);
static int memBtreeClearTable(Btree* tree, int n);






static int memBtreeNext(BtCursor* pCur, int *pRes);
static int memBtreeLast(BtCursor* pCur, int *pRes);
static int memBtreePrevious(BtCursor* pCur, int *pRes);

/*
 * The key-compare function for the red-black trees. Returns as follows:
 *
 * (key1 < key2)             -1
 * (key1 == key2)             0 
 * (key1 > key2)              1
................................................................................
        break;
      default: assert(0);
    }
  }
} 

/*
 * Node pX has just been inserted into pTree (by code in sqliteBtreeInsert()).
 * It is possible that pX is a red node with a red parent, which is a violation
 * of the red-black tree properties. This function performs rotations and 
 * color changes to rebalance the tree
 */
static void do_insert_balancing(BtRbTree *pTree, BtRbNode *pX)
{
  /* In the first iteration of this loop, pX points to the red node just
................................................................................
    }
    pParent = pX->pParent;
  }
  if( pX ) pX->isBlack = 1;
}

/*
 * Create table n in tree pBtree. Table n must not exist.
 */
static void btreeCreateTable(Btree* pBtree, int n)
{
  BtRbTree *pNewTbl = sqliteMalloc(sizeof(BtRbTree));
  sqliteHashInsert(&pBtree->tblHash, 0, n, pNewTbl);
}

/*
 * Log a single "rollback-op" for the given Btree. See comments for struct
 * BtRollbackOp.
 */
static void btreeLogRollbackOp(Btree* pBtree, BtRollbackOp *pRollbackOp)
{
  assert( pBtree->eTransState == TRANS_INCHECKPOINT ||
      pBtree->eTransState == TRANS_INTRANSACTION );
  if( pBtree->eTransState == TRANS_INTRANSACTION ){
    pRollbackOp->pNext = pBtree->pTransRollback;
    pBtree->pTransRollback = pRollbackOp;
  }
  if( pBtree->eTransState == TRANS_INCHECKPOINT ){
    if( !pBtree->pCheckRollback ){
      pBtree->pCheckRollbackTail = pRollbackOp;
    }
    pRollbackOp->pNext = pBtree->pCheckRollback;
    pBtree->pCheckRollback = pRollbackOp;
  }
}


int sqliteRBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree)
{




  *ppBtree = (Btree *)sqliteMalloc(sizeof(Btree));
  sqliteHashInit(&(*ppBtree)->tblHash, SQLITE_HASH_INT, 0);

  /* Create a binary tree for the SQLITE_MASTER table at location 2 */
  btreeCreateTable(*ppBtree, 2);
  (*ppBtree)->next_idx = 3;
  (*ppBtree)->pOps = &sqliteBtreeOps;
  /* Set file type to 4; this is so that "attach ':memory:' as ...."  does not
  ** think that the database in uninitialised and refuse to attach
  */
  (*ppBtree)->aMetaData[2] = 4;
  
  return SQLITE_OK;
}

/*
 * Create a new table in the supplied Btree. Set *n to the new table number.
 * Return SQLITE_OK if the operation is a success.
 */
static int memBtreeCreateTable(Btree* tree, int* n)
{
  assert( tree->eTransState != TRANS_NONE );

  *n = tree->next_idx++;
  btreeCreateTable(tree, *n);

  /* Set up the rollback structure (if we are not doing this as part of a
................................................................................
    btreeLogRollbackOp(tree, pRollbackOp);
  }

  return SQLITE_OK;
}

/*
 * Delete table n from the supplied Btree. 
 */
static int memBtreeDropTable(Btree* tree, int n)
{
  BtRbTree *pTree;
  assert( tree->eTransState != TRANS_NONE );

  memBtreeClearTable(tree, n);
  pTree = sqliteHashInsert(&tree->tblHash, 0, n, 0);
  assert(pTree);
  sqliteFree(pTree);

  if( tree->eTransState != TRANS_ROLLBACK ){
    BtRollbackOp *pRollbackOp = sqliteMalloc(sizeof(BtRollbackOp));
    pRollbackOp->eOp = ROLLBACK_CREATE;
................................................................................
    pRollbackOp->iTab = n;
    btreeLogRollbackOp(tree, pRollbackOp);
  }

  return SQLITE_OK;
}

static int memBtreeKeyCompare(BtCursor* pCur, const void *pKey, int nKey,
                                 int nIgnore, int *pRes)
{
  assert(pCur);

  if( !pCur->pNode ) {
    *pRes = -1;
  } else {
................................................................................
          pKey, nKey);
    }
  }
  return SQLITE_OK;
}

/*
 * Get a new cursor for table iTable of the supplied Btree. The wrFlag
 * parameter is ignored, all cursors are capable of write-operations. 
 *
 * Note that BtCursor.eSkip and BtCursor.pNode both initialize to 0.
 */
static int memBtreeCursor(Btree* tree, int iTable, int wrFlag, BtCursor **ppCur)
{





  assert(tree);
  *ppCur = sqliteMalloc(sizeof(BtCursor));
  (*ppCur)->pTree  = sqliteHashFind(&tree->tblHash, 0, iTable);
  (*ppCur)->pBtree = tree;
  (*ppCur)->iTree  = iTable;
  (*ppCur)->pOps = &sqliteBtreeCursorOps;

  assert( (*ppCur)->pTree );
  return SQLITE_OK;
}

/*
 * Insert a new record into the Btree.  The key is given by (pKey,nKey)
 * and the data is given by (pData,nData).  The cursor is used only to
 * define what database the record should be inserted into.  The cursor
 * is left pointing at the new record.
 *
 * If the key exists already in the tree, just replace the data. 
 */


static int memBtreeInsert(BtCursor* pCur, const void *pKey, int nKey,

                             const void *pDataInput, int nData)
{


  void * pData;
  int match;

  /* It is illegal to call sqliteBtreeInsert() if we are not in a transaction */

  assert( pCur->pBtree->eTransState != TRANS_NONE );

  /* Take a copy of the input data now, in case we need it for the 
   * replace case */
  pData = sqliteMalloc(nData);
  memcpy(pData, pDataInput, nData);

  /* Move the cursor to a node near the key to be inserted. If the key already
................................................................................
   * If there is no exact match, then the cursor points at what would be either
   * the predecessor (match == -1) or successor (match == 1) of the
   * searched-for key, were it to be inserted. The new node becomes a child of
   * this node.
   * 
   * The new node is initially red.
   */
  memBtreeMoveto( pCur, pKey, nKey, &match);
  if( match ){
    BtRbNode *pNode = sqliteMalloc(sizeof(BtRbNode));
    pNode->nKey = nKey;
    pNode->pKey = sqliteMalloc(nKey);
    memcpy(pNode->pKey, pKey, nKey);
    pNode->nData = nData;
    pNode->pData = pData; 
................................................................................
    /* Point the cursor at the node just inserted, as per SQLite requirements */
    pCur->pNode = pNode;

    /* A new node has just been inserted, so run the balancing code */
    do_insert_balancing(pCur->pTree, pNode);

    /* Set up a rollback-op in case we have to roll this operation back */
    if( pCur->pBtree->eTransState != TRANS_ROLLBACK ){
      BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
      pOp->eOp = ROLLBACK_DELETE;
      pOp->iTab = pCur->iTree;
      pOp->nKey = pNode->nKey;
      pOp->pKey = sqliteMalloc( pOp->nKey );
      memcpy( pOp->pKey, pNode->pKey, pOp->nKey );
      btreeLogRollbackOp(pCur->pBtree, pOp);
    }

  }else{ 
    /* No need to insert a new node in the tree, as the key already exists.
     * Just clobber the current nodes data. */

    /* Set up a rollback-op in case we have to roll this operation back */
    if( pCur->pBtree->eTransState != TRANS_ROLLBACK ){
      BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
      pOp->iTab = pCur->iTree;
      pOp->nKey = pCur->pNode->nKey;
      pOp->pKey = sqliteMalloc( pOp->nKey );
      memcpy( pOp->pKey, pCur->pNode->pKey, pOp->nKey );
      pOp->nData = pCur->pNode->nData;
      pOp->pData = pCur->pNode->pData;
      pOp->eOp = ROLLBACK_INSERT;
      btreeLogRollbackOp(pCur->pBtree, pOp);
    }else{
      sqliteFree( pCur->pNode->pData );
    }

    /* Actually clobber the nodes data */
    pCur->pNode->pData = pData;
    pCur->pNode->nData = nData;
................................................................................
**
**     *pRes==0     The cursor is left pointing at an entry that
**                  exactly matches pKey.
**
**     *pRes>0      The cursor is left pointing at an entry that
**                  is larger than pKey.
*/
static int memBtreeMoveto(BtCursor* pCur, const void *pKey, int nKey, int *pRes)
{





  BtRbNode *pTmp = 0;

  pCur->pNode = pCur->pTree->pHead;
  *pRes = -1;
  while( pCur->pNode && *pRes ) {
    *pRes = key_compare(pCur->pNode->pKey, pCur->pNode->nKey, pKey, nKey);
    pTmp = pCur->pNode;
................................................................................

/*
** Delete the entry that the cursor is pointing to.
**
** The cursor is left pointing at either the next or the previous
** entry.  If the cursor is left pointing to the next entry, then 
** the pCur->eSkip flag is set to SKIP_NEXT which forces the next call to 
** sqliteBtreeNext() to be a no-op.  That way, you can always call
** sqliteBtreeNext() after a delete and the cursor will be left
** pointing to the first entry after the deleted entry.  Similarly,
** pCur->eSkip is set to SKIP_PREV is the cursor is left pointing to
** the entry prior to the deleted entry so that a subsequent call to
** sqliteBtreePrevious() will always leave the cursor pointing at the
** entry immediately before the one that was deleted.
*/
static int memBtreeDelete(BtCursor* pCur)
{
  BtRbNode *pZ;      /* The one being deleted */
  BtRbNode *pChild;  /* The child of the spliced out node */

  /* It is illegal to call sqliteBtreeDelete() if we are not in a transaction */

  assert( pCur->pBtree->eTransState != TRANS_NONE );

  pZ = pCur->pNode;
  if( !pZ ){
    return SQLITE_OK;
  }

  /* If we are not currently doing a rollback, set up a rollback op for this 
   * deletion */
  if( pCur->pBtree->eTransState != TRANS_ROLLBACK ){
    BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
    pOp->iTab = pCur->iTree;
    pOp->nKey = pZ->nKey;
    pOp->pKey = pZ->pKey;
    pOp->nData = pZ->nData;
    pOp->pData = pZ->pData;
    pOp->eOp = ROLLBACK_INSERT;
    btreeLogRollbackOp(pCur->pBtree, pOp);
  }

  /* First do a standard binary-tree delete (node pZ is to be deleted). How
   * to do this depends on how many children pZ has:
   *
   * If pZ has no children or one child, then splice out pZ.  If pZ has two
   * children, splice out the successor of pZ and replace the key and data of
   * pZ with the key and data of the spliced out successor.  */
  if( pZ->pLeft && pZ->pRight ){
    BtRbNode *pTmp;
    int dummy;
    pCur->eSkip = SKIP_NONE;
    memBtreeNext(pCur, &dummy);
    assert( dummy == 0 );
    if( pCur->pBtree->eTransState == TRANS_ROLLBACK ){
      sqliteFree(pZ->pKey);
      sqliteFree(pZ->pData);
    }
    pZ->pData = pCur->pNode->pData;
    pZ->nData = pCur->pNode->nData;
    pZ->pKey = pCur->pNode->pKey;
    pZ->nKey = pCur->pNode->nKey;
................................................................................
    pTmp = pZ;
    pZ = pCur->pNode;
    pCur->pNode = pTmp;
    pCur->eSkip = SKIP_NEXT;
  }else{
    int res;
    pCur->eSkip = SKIP_NONE;
    memBtreeNext(pCur, &res);
    pCur->eSkip = SKIP_NEXT;
    if( res ){
      memBtreeLast(pCur, &res);
      memBtreePrevious(pCur, &res);
      pCur->eSkip = SKIP_PREV;
    }
    if( pCur->pBtree->eTransState == TRANS_ROLLBACK ){
        sqliteFree(pZ->pKey);
        sqliteFree(pZ->pData);
    }
  }

  /* pZ now points at the node to be spliced out. This block does the 
   * splicing. */
................................................................................
  }

  sqliteFree(pZ);
  return SQLITE_OK;
}

/*
 * Empty table n of the Btree.
 */
static int memBtreeClearTable(Btree* tree, int n)
{
  BtRbTree *pTree;
  BtRbNode *pNode;

  pTree = sqliteHashFind(&tree->tblHash, 0, n);
  assert(pTree);

................................................................................
    }
  }

  pTree->pHead = 0;
  return SQLITE_OK;
}

static int memBtreeFirst(BtCursor* pCur, int *pRes)
{
  if( pCur->pTree->pHead ){
    pCur->pNode = pCur->pTree->pHead;
    while( pCur->pNode->pLeft ){
      pCur->pNode = pCur->pNode->pLeft;
    }
  }
................................................................................
  }else{
    *pRes = 1;
  }
  pCur->eSkip = SKIP_NONE;
  return SQLITE_OK;
}

static int memBtreeLast(BtCursor* pCur, int *pRes)
{
  if( pCur->pTree->pHead ){
    pCur->pNode = pCur->pTree->pHead;
    while( pCur->pNode->pRight ){
      pCur->pNode = pCur->pNode->pRight;
    }
  }
................................................................................

/*
** Advance the cursor to the next entry in the database.  If
** successful then set *pRes=0.  If the cursor
** was already pointing to the last entry in the database before
** this routine was called, then set *pRes=1.
*/
static int memBtreeNext(BtCursor* pCur, int *pRes)
{
  if( pCur->pNode && pCur->eSkip != SKIP_NEXT ){
    if( pCur->pNode->pRight ){
      pCur->pNode = pCur->pNode->pRight;
      while( pCur->pNode->pLeft )
        pCur->pNode = pCur->pNode->pLeft;
    }else{
................................................................................
  }else{
    *pRes = 0;
  }

  return SQLITE_OK;
}

static int memBtreePrevious(BtCursor* pCur, int *pRes)
{
  if( pCur->pNode && pCur->eSkip != SKIP_PREV ){
    if( pCur->pNode->pLeft ){
      pCur->pNode = pCur->pNode->pLeft;
      while( pCur->pNode->pRight )
        pCur->pNode = pCur->pNode->pRight;
    }else{
................................................................................
  }else{
    *pRes = 0;
  }

  return SQLITE_OK;
}

static int memBtreeKeySize(BtCursor* pCur, int *pSize)
{
  if( pCur->pNode ){
    *pSize = pCur->pNode->nKey;
  }else{
    *pSize = 0;
  }
  return SQLITE_OK;
}

static int memBtreeKey(BtCursor* pCur, int offset, int amt, char *zBuf)
{
  if( !pCur->pNode ) return 0;
  if( !pCur->pNode->pKey || ((amt + offset) <= pCur->pNode->nKey) ){
    memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, amt);
    return amt;
  }else{
    memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, pCur->pNode->nKey-offset);
    return pCur->pNode->nKey-offset;
  }
  assert(0);
}

static int memBtreeDataSize(BtCursor* pCur, int *pSize)
{
  if( pCur->pNode ){
    *pSize = pCur->pNode->nData;
  }else{
    *pSize = 0;
  }
  return SQLITE_OK;
}

static int memBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf)
{
  if( !pCur->pNode ) return 0;
  if( (amt + offset) <= pCur->pNode->nData ){
    memcpy(zBuf, ((char*)pCur->pNode->pData)+offset, amt);
    return amt;
  }else{
    memcpy(zBuf, ((char*)pCur->pNode->pData)+offset ,pCur->pNode->nData-offset);
    return pCur->pNode->nData-offset;
  }
  assert(0);
}

static int memBtreeCloseCursor(BtCursor* pCur)
{
  sqliteFree(pCur);
  return SQLITE_OK;
}

static int memBtreeGetMeta(Btree* tree, int* aMeta)
{
  memcpy( aMeta, tree->aMetaData, sizeof(int) * SQLITE_N_BTREE_META );
  return SQLITE_OK;
}

static int memBtreeUpdateMeta(Btree* tree, int* aMeta)
{
  memcpy( tree->aMetaData, aMeta, sizeof(int) * SQLITE_N_BTREE_META );
  return SQLITE_OK;
}

/*
 * Check that each table in the Btree meets the requirements for a red-black
 * binary tree. If an error is found, return an explanation of the problem in 
 * memory obtained from sqliteMalloc(). Parameters aRoot and nRoot are ignored. 
 */
static char *memBtreeIntegrityCheck(Btree* tree, int* aRoot, int nRoot)
{
  char * msg = 0;
  HashElem *p;

  for(p=sqliteHashFirst(&tree->tblHash); p; p=sqliteHashNext(p)){
    BtRbTree *pTree = sqliteHashData(p);
    check_redblack_tree(pTree, &msg);
  }

  return msg;
}

/*
 * Close the supplied Btree. Delete everything associated with it.
 */
static int memBtreeClose(Btree* tree)
{
  HashElem *p;
  while( (p=sqliteHashFirst(&tree->tblHash))!=0 ){
    tree->eTransState = TRANS_ROLLBACK;
    memBtreeDropTable(tree, sqliteHashKeysize(p));
  }
  sqliteHashClear(&tree->tblHash);
  sqliteFree(tree);
  return SQLITE_OK;
}

static int memBtreeSetCacheSize(Btree* tree, int sz)
{
  return SQLITE_OK;
}

static int memBtreeSetSafetyLevel(Btree *pBt, int level){
  return SQLITE_OK;
}

static int memBtreeBeginTrans(Btree* tree)
{
  if( tree->eTransState != TRANS_NONE ) 
    return SQLITE_ERROR;

  assert( tree->pTransRollback == 0 );
  tree->eTransState = TRANS_INTRANSACTION;
  return SQLITE_OK;
................................................................................
    sqliteFree(pOp->pData);
    sqliteFree(pOp->pKey);
    sqliteFree(pOp);
    pOp = pTmp;
  }
}

static int memBtreeCommit(Btree* tree){
  /* Just delete pTransRollback and pCheckRollback */
  deleteRollbackList(tree->pCheckRollback);
  deleteRollbackList(tree->pTransRollback);
  tree->pTransRollback = 0;
  tree->pCheckRollback = 0;
  tree->pCheckRollbackTail = 0;
  tree->eTransState = TRANS_NONE;
  return SQLITE_OK;
}

/*
 * Execute and delete the supplied rollback-list on pBtree.
 */
static void execute_rollback_list(Btree *pBtree, BtRollbackOp *pList)
{
  BtRollbackOp *pTmp;
  BtCursor cur;
  int res;

  cur.pBtree = pBtree;
  while( pList ){
    switch( pList->eOp ){
      case ROLLBACK_INSERT:
        cur.pTree  = sqliteHashFind( &pBtree->tblHash, 0, pList->iTab );
        assert(cur.pTree);
        cur.iTree  = pList->iTab;
        cur.eSkip  = SKIP_NONE;
        memBtreeInsert( &cur, pList->pKey,
            pList->nKey, pList->pData, pList->nData );
        break;
      case ROLLBACK_DELETE:
        cur.pTree  = sqliteHashFind( &pBtree->tblHash, 0, pList->iTab );
        assert(cur.pTree);
        cur.iTree  = pList->iTab;
        cur.eSkip  = SKIP_NONE;
        memBtreeMoveto(&cur, pList->pKey, pList->nKey, &res);
        assert(res == 0);
        memBtreeDelete( &cur );
        break;
      case ROLLBACK_CREATE:
        btreeCreateTable(pBtree, pList->iTab);
        break;
      case ROLLBACK_DROP:
        memBtreeDropTable(pBtree, pList->iTab);
        break;
      default:
        assert(0);
    }
    sqliteFree(pList->pKey);
    sqliteFree(pList->pData);
    pTmp = pList->pNext;
    sqliteFree(pList);
    pList = pTmp;
  }
}

static int memBtreeRollback(Btree* tree)
{
  tree->eTransState = TRANS_ROLLBACK;
  execute_rollback_list(tree, tree->pCheckRollback);
  execute_rollback_list(tree, tree->pTransRollback);
  tree->pTransRollback = 0;
  tree->pCheckRollback = 0;
  tree->pCheckRollbackTail = 0;
  tree->eTransState = TRANS_NONE;
  return SQLITE_OK;
}

static int memBtreeBeginCkpt(Btree* tree)
{
  if( tree->eTransState != TRANS_INTRANSACTION ) 
    return SQLITE_ERROR;

  assert( tree->pCheckRollback == 0 );
  assert( tree->pCheckRollbackTail == 0 );
  tree->eTransState = TRANS_INCHECKPOINT;
  return SQLITE_OK;
}

static int memBtreeCommitCkpt(Btree* tree)
{
  if( tree->eTransState == TRANS_INCHECKPOINT ){ 
    if( tree->pCheckRollback ){
      tree->pCheckRollbackTail->pNext = tree->pTransRollback;
      tree->pTransRollback = tree->pCheckRollback;
      tree->pCheckRollback = 0;
      tree->pCheckRollbackTail = 0;
    }
    tree->eTransState = TRANS_INTRANSACTION;
  }
  return SQLITE_OK;
}

static int memBtreeRollbackCkpt(Btree* tree)
{
  if( tree->eTransState != TRANS_INCHECKPOINT ) return SQLITE_OK;
  tree->eTransState = TRANS_ROLLBACK;
  execute_rollback_list(tree, tree->pCheckRollback);
  tree->pCheckRollback = 0;
  tree->pCheckRollbackTail = 0;
  tree->eTransState = TRANS_INTRANSACTION;
  return SQLITE_OK;
  return SQLITE_OK;
}

#ifdef SQLITE_TEST
static int memBtreePageDump(Btree* tree, int pgno, int rec)
{
  assert(!"Cannot call sqliteBtreePageDump");
  return SQLITE_OK;
}

static int memBtreeCursorDump(BtCursor* pCur, int* aRes)
{
  assert(!"Cannot call sqliteBtreeCursorDump");
  return SQLITE_OK;
}

static struct Pager *memBtreePager(Btree* tree)
{
  assert(!"Cannot call sqliteBtreePager");
  return SQLITE_OK;
}
#endif

/*
** Return the full pathname of the underlying database file.
*/
static const char *memBtreeGetFilename(Btree *pBt){
  return 0;  /* A NULL return indicates there is no underlying file */
}

/*
** The copy file function is not implemented for the in-memory database
*/
static int memBtreeCopyFile(Btree *pBt, Btree *pBt2){
  return SQLITE_INTERNAL;  /* Not implemented */
}

static BtOps sqliteBtreeOps = {
    memBtreeClose,
    memBtreeSetCacheSize,
    memBtreeSetSafetyLevel,
    memBtreeBeginTrans,
    memBtreeCommit,
    memBtreeRollback,
    memBtreeBeginCkpt,
    memBtreeCommitCkpt,
    memBtreeRollbackCkpt,
    memBtreeCreateTable,
    memBtreeCreateTable,
    memBtreeDropTable,
    memBtreeClearTable,
    memBtreeCursor,
    memBtreeGetMeta,
    memBtreeUpdateMeta,
    memBtreeIntegrityCheck,
    memBtreeGetFilename,
    memBtreeCopyFile,

#ifdef SQLITE_TEST
    memBtreePageDump,
    memBtreePager
#endif
};

static BtCursorOps sqliteBtreeCursorOps = {
    memBtreeMoveto,
    memBtreeDelete,
    memBtreeInsert,
    memBtreeFirst,
    memBtreeLast,
    memBtreeNext,
    memBtreePrevious,
    memBtreeKeySize,
    memBtreeKey,
    memBtreeKeyCompare,
    memBtreeDataSize,
    memBtreeData,
    memBtreeCloseCursor,
#ifdef SQLITE_TEST
    memBtreeCursorDump,
#endif

};

#endif /* SQLITE_OMIT_INMEMORYDB */







|







 







>
>


|
|







 







|





|
|







|







|

|

|





|


|
|







 







<
|
>
>
>
>
>
>
|
|
|







 







|







 







|

|


|



|


|

|
|
|
|
|

|
|
|

|
|



>
|
<
>
>
>
>
|
|


|
|
|



|





|


|







 







|

|




|







 







|







 







|


|

|
<
>
>
>
>
>

|

|

|






|






>
>
|
>
|
<
>
>



|
>
|







 







|







 







|






|







|








|







 







|
<
>
>
>
>
>







 







|
|



|


|




|
>
|








|







|












|

|







 







|


|
|


|







 







|

|







 







|







 







|







 







|







 







|







 







|









|












|









|












|





|





|






|



|













|

|




|






|




|



|







 







|











|

|


|


|



|



|



|



|

|


|


|












|











|










|













|












|

|



|

|



|

|







|






|



|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|


|
|



|
|
|
|
|
|
|
|
|
|
|
|
|
|

|





5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
..
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
...
124
125
126
127
128
129
130

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
...
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580

581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
...
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
...
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
...
658
659
660
661
662
663
664
665
666
667
668
669
670

671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699

700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
...
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
...
752
753
754
755
756
757
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
788
789
790
...
802
803
804
805
806
807
808
809

810
811
812
813
814
815
816
817
818
819
820
821
...
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
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
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
...
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
...
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
...
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
....
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
....
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
....
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
....
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
....
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree_rb.c,v 1.10 2003/05/17 17:35:11 drh Exp $
**
** This file implements an in-core database using Red-Black balanced
** binary trees.
**
** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC.
*/
#include "btree.h"
................................................................................
*/
#ifndef SQLITE_OMIT_INMEMORYDB


typedef struct BtRbTree BtRbTree;
typedef struct BtRbNode BtRbNode;
typedef struct BtRollbackOp BtRollbackOp;
typedef struct Rbtree Rbtree;
typedef struct RbtCursor RbtCursor;

/* Forward declarations */
static BtOps sqliteRbtreeOps;
static BtCursorOps sqliteRbtreeCursorOps;

/*
 * During each transaction (or checkpoint), a linked-list of
 * "rollback-operations" is accumulated. If the transaction is rolled back,
 * then the list of operations must be executed (to restore the database to
 * it's state before the transaction started). If the transaction is to be
 * committed, just delete the list.
................................................................................
** Legal values for BtRollbackOp.eOp:
*/
#define ROLLBACK_INSERT 1 /* Insert a record */
#define ROLLBACK_DELETE 2 /* Delete a record */
#define ROLLBACK_CREATE 3 /* Create a table */
#define ROLLBACK_DROP   4 /* Drop a table */

struct Rbtree {
  BtOps *pOps;    /* Function table */
  int aMetaData[SQLITE_N_BTREE_META];

  int next_idx;   /* next available table index */
  Hash tblHash;   /* All created tables, by index */
  u8 isAnonymous; /* True if this Rbtree is to be deleted when closed */
  u8 eTransState; /* State of this Rbtree wrt transactions */

  BtRollbackOp *pTransRollback; 
  BtRollbackOp *pCheckRollback;
  BtRollbackOp *pCheckRollbackTail;
};

/*
** Legal values for Rbtree.eTransState.
*/
#define TRANS_NONE           0  /* No transaction is in progress */
#define TRANS_INTRANSACTION  1  /* A transaction is in progress */
#define TRANS_INCHECKPOINT   2  /* A checkpoint is in progress  */
#define TRANS_ROLLBACK       3  /* We are currently rolling back a checkpoint or
                                 * transaction. */

struct RbtCursor {
  BtCursorOps *pOps;        /* Function table */
  Rbtree    *pRbtree;
  BtRbTree *pTree;
  int       iTree;          /* Index of pTree in pRbtree */
  BtRbNode *pNode;
  u8 eSkip;                 /* Determines if next step operation is a no-op */
};

/*
** Legal values for RbtCursor.eSkip.
*/
#define SKIP_NONE     0   /* Always step the cursor */
#define SKIP_NEXT     1   /* The next sqliteRbtreeNext() is a no-op */
#define SKIP_PREV     2   /* The next sqliteRbtreePrevious() is a no-op */
#define SKIP_INVALID  3   /* Calls to Next() and Previous() are invalid */

struct BtRbTree {
  BtRbNode *pHead;   /* Head of the tree, or NULL */
};

struct BtRbNode {
................................................................................
  BtRbNode *pLeft;   /* Nodes left child, or NULL */
  BtRbNode *pRight;  /* Nodes right child, or NULL */

  int nBlackHeight;  /* Only used during the red-black integrity check */
};

/* Forward declarations */

static int memRbtreeMoveto(
  RbtCursor* pCur,
  const void *pKey,
  int nKey,
  int *pRes
);
static int memRbtreeClearTable(Rbtree* tree, int n);
static int memRbtreeNext(RbtCursor* pCur, int *pRes);
static int memRbtreeLast(RbtCursor* pCur, int *pRes);
static int memRbtreePrevious(RbtCursor* pCur, int *pRes);

/*
 * The key-compare function for the red-black trees. Returns as follows:
 *
 * (key1 < key2)             -1
 * (key1 == key2)             0 
 * (key1 > key2)              1
................................................................................
        break;
      default: assert(0);
    }
  }
} 

/*
 * Node pX has just been inserted into pTree (by code in sqliteRbtreeInsert()).
 * It is possible that pX is a red node with a red parent, which is a violation
 * of the red-black tree properties. This function performs rotations and 
 * color changes to rebalance the tree
 */
static void do_insert_balancing(BtRbTree *pTree, BtRbNode *pX)
{
  /* In the first iteration of this loop, pX points to the red node just
................................................................................
    }
    pParent = pX->pParent;
  }
  if( pX ) pX->isBlack = 1;
}

/*
 * Create table n in tree pRbtree. Table n must not exist.
 */
static void btreeCreateTable(Rbtree* pRbtree, int n)
{
  BtRbTree *pNewTbl = sqliteMalloc(sizeof(BtRbTree));
  sqliteHashInsert(&pRbtree->tblHash, 0, n, pNewTbl);
}

/*
 * Log a single "rollback-op" for the given Rbtree. See comments for struct
 * BtRollbackOp.
 */
static void btreeLogRollbackOp(Rbtree* pRbtree, BtRollbackOp *pRollbackOp)
{
  assert( pRbtree->eTransState == TRANS_INCHECKPOINT ||
      pRbtree->eTransState == TRANS_INTRANSACTION );
  if( pRbtree->eTransState == TRANS_INTRANSACTION ){
    pRollbackOp->pNext = pRbtree->pTransRollback;
    pRbtree->pTransRollback = pRollbackOp;
  }
  if( pRbtree->eTransState == TRANS_INCHECKPOINT ){
    if( !pRbtree->pCheckRollback ){
      pRbtree->pCheckRollbackTail = pRollbackOp;
    }
    pRollbackOp->pNext = pRbtree->pCheckRollback;
    pRbtree->pCheckRollback = pRollbackOp;
  }
}

int sqliteRbtreeOpen(
  const char *zFilename,

  int mode,
  int nPg,
  Rbtree **ppRbtree
){
  *ppRbtree = (Rbtree *)sqliteMalloc(sizeof(Rbtree));
  sqliteHashInit(&(*ppRbtree)->tblHash, SQLITE_HASH_INT, 0);

  /* Create a binary tree for the SQLITE_MASTER table at location 2 */
  btreeCreateTable(*ppRbtree, 2);
  (*ppRbtree)->next_idx = 3;
  (*ppRbtree)->pOps = &sqliteRbtreeOps;
  /* Set file type to 4; this is so that "attach ':memory:' as ...."  does not
  ** think that the database in uninitialised and refuse to attach
  */
  (*ppRbtree)->aMetaData[2] = 4;
  
  return SQLITE_OK;
}

/*
 * Create a new table in the supplied Rbtree. Set *n to the new table number.
 * Return SQLITE_OK if the operation is a success.
 */
static int memRbtreeCreateTable(Rbtree* tree, int* n)
{
  assert( tree->eTransState != TRANS_NONE );

  *n = tree->next_idx++;
  btreeCreateTable(tree, *n);

  /* Set up the rollback structure (if we are not doing this as part of a
................................................................................
    btreeLogRollbackOp(tree, pRollbackOp);
  }

  return SQLITE_OK;
}

/*
 * Delete table n from the supplied Rbtree. 
 */
static int memRbtreeDropTable(Rbtree* tree, int n)
{
  BtRbTree *pTree;
  assert( tree->eTransState != TRANS_NONE );

  memRbtreeClearTable(tree, n);
  pTree = sqliteHashInsert(&tree->tblHash, 0, n, 0);
  assert(pTree);
  sqliteFree(pTree);

  if( tree->eTransState != TRANS_ROLLBACK ){
    BtRollbackOp *pRollbackOp = sqliteMalloc(sizeof(BtRollbackOp));
    pRollbackOp->eOp = ROLLBACK_CREATE;
................................................................................
    pRollbackOp->iTab = n;
    btreeLogRollbackOp(tree, pRollbackOp);
  }

  return SQLITE_OK;
}

static int memRbtreeKeyCompare(RbtCursor* pCur, const void *pKey, int nKey,
                                 int nIgnore, int *pRes)
{
  assert(pCur);

  if( !pCur->pNode ) {
    *pRes = -1;
  } else {
................................................................................
          pKey, nKey);
    }
  }
  return SQLITE_OK;
}

/*
 * Get a new cursor for table iTable of the supplied Rbtree. The wrFlag
 * parameter is ignored, all cursors are capable of write-operations. 
 *
 * Note that RbtCursor.eSkip and RbtCursor.pNode both initialize to 0.
 */
static int memRbtreeCursor(

  Rbtree* tree,
  int iTable,
  int wrFlag,
  RbtCursor **ppCur
){
  assert(tree);
  *ppCur = sqliteMalloc(sizeof(RbtCursor));
  (*ppCur)->pTree  = sqliteHashFind(&tree->tblHash, 0, iTable);
  (*ppCur)->pRbtree = tree;
  (*ppCur)->iTree  = iTable;
  (*ppCur)->pOps = &sqliteRbtreeCursorOps;

  assert( (*ppCur)->pTree );
  return SQLITE_OK;
}

/*
 * Insert a new record into the Rbtree.  The key is given by (pKey,nKey)
 * and the data is given by (pData,nData).  The cursor is used only to
 * define what database the record should be inserted into.  The cursor
 * is left pointing at the new record.
 *
 * If the key exists already in the tree, just replace the data. 
 */
static int memRbtreeInsert(
  RbtCursor* pCur,
  const void *pKey,
  int nKey,
  const void *pDataInput,

  int nData
){
  void * pData;
  int match;

  /* It is illegal to call sqliteRbtreeInsert() if we are
  ** not in a transaction */
  assert( pCur->pRbtree->eTransState != TRANS_NONE );

  /* Take a copy of the input data now, in case we need it for the 
   * replace case */
  pData = sqliteMalloc(nData);
  memcpy(pData, pDataInput, nData);

  /* Move the cursor to a node near the key to be inserted. If the key already
................................................................................
   * If there is no exact match, then the cursor points at what would be either
   * the predecessor (match == -1) or successor (match == 1) of the
   * searched-for key, were it to be inserted. The new node becomes a child of
   * this node.
   * 
   * The new node is initially red.
   */
  memRbtreeMoveto( pCur, pKey, nKey, &match);
  if( match ){
    BtRbNode *pNode = sqliteMalloc(sizeof(BtRbNode));
    pNode->nKey = nKey;
    pNode->pKey = sqliteMalloc(nKey);
    memcpy(pNode->pKey, pKey, nKey);
    pNode->nData = nData;
    pNode->pData = pData; 
................................................................................
    /* Point the cursor at the node just inserted, as per SQLite requirements */
    pCur->pNode = pNode;

    /* A new node has just been inserted, so run the balancing code */
    do_insert_balancing(pCur->pTree, pNode);

    /* Set up a rollback-op in case we have to roll this operation back */
    if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
      BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
      pOp->eOp = ROLLBACK_DELETE;
      pOp->iTab = pCur->iTree;
      pOp->nKey = pNode->nKey;
      pOp->pKey = sqliteMalloc( pOp->nKey );
      memcpy( pOp->pKey, pNode->pKey, pOp->nKey );
      btreeLogRollbackOp(pCur->pRbtree, pOp);
    }

  }else{ 
    /* No need to insert a new node in the tree, as the key already exists.
     * Just clobber the current nodes data. */

    /* Set up a rollback-op in case we have to roll this operation back */
    if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
      BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
      pOp->iTab = pCur->iTree;
      pOp->nKey = pCur->pNode->nKey;
      pOp->pKey = sqliteMalloc( pOp->nKey );
      memcpy( pOp->pKey, pCur->pNode->pKey, pOp->nKey );
      pOp->nData = pCur->pNode->nData;
      pOp->pData = pCur->pNode->pData;
      pOp->eOp = ROLLBACK_INSERT;
      btreeLogRollbackOp(pCur->pRbtree, pOp);
    }else{
      sqliteFree( pCur->pNode->pData );
    }

    /* Actually clobber the nodes data */
    pCur->pNode->pData = pData;
    pCur->pNode->nData = nData;
................................................................................
**
**     *pRes==0     The cursor is left pointing at an entry that
**                  exactly matches pKey.
**
**     *pRes>0      The cursor is left pointing at an entry that
**                  is larger than pKey.
*/
static int memRbtreeMoveto(

  RbtCursor* pCur,
  const void *pKey,
  int nKey,
  int *pRes
){
  BtRbNode *pTmp = 0;

  pCur->pNode = pCur->pTree->pHead;
  *pRes = -1;
  while( pCur->pNode && *pRes ) {
    *pRes = key_compare(pCur->pNode->pKey, pCur->pNode->nKey, pKey, nKey);
    pTmp = pCur->pNode;
................................................................................

/*
** Delete the entry that the cursor is pointing to.
**
** The cursor is left pointing at either the next or the previous
** entry.  If the cursor is left pointing to the next entry, then 
** the pCur->eSkip flag is set to SKIP_NEXT which forces the next call to 
** sqliteRbtreeNext() to be a no-op.  That way, you can always call
** sqliteRbtreeNext() after a delete and the cursor will be left
** pointing to the first entry after the deleted entry.  Similarly,
** pCur->eSkip is set to SKIP_PREV is the cursor is left pointing to
** the entry prior to the deleted entry so that a subsequent call to
** sqliteRbtreePrevious() will always leave the cursor pointing at the
** entry immediately before the one that was deleted.
*/
static int memRbtreeDelete(RbtCursor* pCur)
{
  BtRbNode *pZ;      /* The one being deleted */
  BtRbNode *pChild;  /* The child of the spliced out node */

  /* It is illegal to call sqliteRbtreeDelete() if we are
  ** not in a transaction */
  assert( pCur->pRbtree->eTransState != TRANS_NONE );

  pZ = pCur->pNode;
  if( !pZ ){
    return SQLITE_OK;
  }

  /* If we are not currently doing a rollback, set up a rollback op for this 
   * deletion */
  if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
    BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
    pOp->iTab = pCur->iTree;
    pOp->nKey = pZ->nKey;
    pOp->pKey = pZ->pKey;
    pOp->nData = pZ->nData;
    pOp->pData = pZ->pData;
    pOp->eOp = ROLLBACK_INSERT;
    btreeLogRollbackOp(pCur->pRbtree, pOp);
  }

  /* First do a standard binary-tree delete (node pZ is to be deleted). How
   * to do this depends on how many children pZ has:
   *
   * If pZ has no children or one child, then splice out pZ.  If pZ has two
   * children, splice out the successor of pZ and replace the key and data of
   * pZ with the key and data of the spliced out successor.  */
  if( pZ->pLeft && pZ->pRight ){
    BtRbNode *pTmp;
    int dummy;
    pCur->eSkip = SKIP_NONE;
    memRbtreeNext(pCur, &dummy);
    assert( dummy == 0 );
    if( pCur->pRbtree->eTransState == TRANS_ROLLBACK ){
      sqliteFree(pZ->pKey);
      sqliteFree(pZ->pData);
    }
    pZ->pData = pCur->pNode->pData;
    pZ->nData = pCur->pNode->nData;
    pZ->pKey = pCur->pNode->pKey;
    pZ->nKey = pCur->pNode->nKey;
................................................................................
    pTmp = pZ;
    pZ = pCur->pNode;
    pCur->pNode = pTmp;
    pCur->eSkip = SKIP_NEXT;
  }else{
    int res;
    pCur->eSkip = SKIP_NONE;
    memRbtreeNext(pCur, &res);
    pCur->eSkip = SKIP_NEXT;
    if( res ){
      memRbtreeLast(pCur, &res);
      memRbtreePrevious(pCur, &res);
      pCur->eSkip = SKIP_PREV;
    }
    if( pCur->pRbtree->eTransState == TRANS_ROLLBACK ){
        sqliteFree(pZ->pKey);
        sqliteFree(pZ->pData);
    }
  }

  /* pZ now points at the node to be spliced out. This block does the 
   * splicing. */
................................................................................
  }

  sqliteFree(pZ);
  return SQLITE_OK;
}

/*
 * Empty table n of the Rbtree.
 */
static int memRbtreeClearTable(Rbtree* tree, int n)
{
  BtRbTree *pTree;
  BtRbNode *pNode;

  pTree = sqliteHashFind(&tree->tblHash, 0, n);
  assert(pTree);

................................................................................
    }
  }

  pTree->pHead = 0;
  return SQLITE_OK;
}

static int memRbtreeFirst(RbtCursor* pCur, int *pRes)
{
  if( pCur->pTree->pHead ){
    pCur->pNode = pCur->pTree->pHead;
    while( pCur->pNode->pLeft ){
      pCur->pNode = pCur->pNode->pLeft;
    }
  }
................................................................................
  }else{
    *pRes = 1;
  }
  pCur->eSkip = SKIP_NONE;
  return SQLITE_OK;
}

static int memRbtreeLast(RbtCursor* pCur, int *pRes)
{
  if( pCur->pTree->pHead ){
    pCur->pNode = pCur->pTree->pHead;
    while( pCur->pNode->pRight ){
      pCur->pNode = pCur->pNode->pRight;
    }
  }
................................................................................

/*
** Advance the cursor to the next entry in the database.  If
** successful then set *pRes=0.  If the cursor
** was already pointing to the last entry in the database before
** this routine was called, then set *pRes=1.
*/
static int memRbtreeNext(RbtCursor* pCur, int *pRes)
{
  if( pCur->pNode && pCur->eSkip != SKIP_NEXT ){
    if( pCur->pNode->pRight ){
      pCur->pNode = pCur->pNode->pRight;
      while( pCur->pNode->pLeft )
        pCur->pNode = pCur->pNode->pLeft;
    }else{
................................................................................
  }else{
    *pRes = 0;
  }

  return SQLITE_OK;
}

static int memRbtreePrevious(RbtCursor* pCur, int *pRes)
{
  if( pCur->pNode && pCur->eSkip != SKIP_PREV ){
    if( pCur->pNode->pLeft ){
      pCur->pNode = pCur->pNode->pLeft;
      while( pCur->pNode->pRight )
        pCur->pNode = pCur->pNode->pRight;
    }else{
................................................................................
  }else{
    *pRes = 0;
  }

  return SQLITE_OK;
}

static int memRbtreeKeySize(RbtCursor* pCur, int *pSize)
{
  if( pCur->pNode ){
    *pSize = pCur->pNode->nKey;
  }else{
    *pSize = 0;
  }
  return SQLITE_OK;
}

static int memRbtreeKey(RbtCursor* pCur, int offset, int amt, char *zBuf)
{
  if( !pCur->pNode ) return 0;
  if( !pCur->pNode->pKey || ((amt + offset) <= pCur->pNode->nKey) ){
    memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, amt);
    return amt;
  }else{
    memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, pCur->pNode->nKey-offset);
    return pCur->pNode->nKey-offset;
  }
  assert(0);
}

static int memRbtreeDataSize(RbtCursor* pCur, int *pSize)
{
  if( pCur->pNode ){
    *pSize = pCur->pNode->nData;
  }else{
    *pSize = 0;
  }
  return SQLITE_OK;
}

static int memRbtreeData(RbtCursor *pCur, int offset, int amt, char *zBuf)
{
  if( !pCur->pNode ) return 0;
  if( (amt + offset) <= pCur->pNode->nData ){
    memcpy(zBuf, ((char*)pCur->pNode->pData)+offset, amt);
    return amt;
  }else{
    memcpy(zBuf, ((char*)pCur->pNode->pData)+offset ,pCur->pNode->nData-offset);
    return pCur->pNode->nData-offset;
  }
  assert(0);
}

static int memRbtreeCloseCursor(RbtCursor* pCur)
{
  sqliteFree(pCur);
  return SQLITE_OK;
}

static int memRbtreeGetMeta(Rbtree* tree, int* aMeta)
{
  memcpy( aMeta, tree->aMetaData, sizeof(int) * SQLITE_N_BTREE_META );
  return SQLITE_OK;
}

static int memRbtreeUpdateMeta(Rbtree* tree, int* aMeta)
{
  memcpy( tree->aMetaData, aMeta, sizeof(int) * SQLITE_N_BTREE_META );
  return SQLITE_OK;
}

/*
 * Check that each table in the Rbtree meets the requirements for a red-black
 * binary tree. If an error is found, return an explanation of the problem in 
 * memory obtained from sqliteMalloc(). Parameters aRoot and nRoot are ignored. 
 */
static char *memRbtreeIntegrityCheck(Rbtree* tree, int* aRoot, int nRoot)
{
  char * msg = 0;
  HashElem *p;

  for(p=sqliteHashFirst(&tree->tblHash); p; p=sqliteHashNext(p)){
    BtRbTree *pTree = sqliteHashData(p);
    check_redblack_tree(pTree, &msg);
  }

  return msg;
}

/*
 * Close the supplied Rbtree. Delete everything associated with it.
 */
static int memRbtreeClose(Rbtree* tree)
{
  HashElem *p;
  while( (p=sqliteHashFirst(&tree->tblHash))!=0 ){
    tree->eTransState = TRANS_ROLLBACK;
    memRbtreeDropTable(tree, sqliteHashKeysize(p));
  }
  sqliteHashClear(&tree->tblHash);
  sqliteFree(tree);
  return SQLITE_OK;
}

static int memRbtreeSetCacheSize(Rbtree* tree, int sz)
{
  return SQLITE_OK;
}

static int memRbtreeSetSafetyLevel(Rbtree *pBt, int level){
  return SQLITE_OK;
}

static int memRbtreeBeginTrans(Rbtree* tree)
{
  if( tree->eTransState != TRANS_NONE ) 
    return SQLITE_ERROR;

  assert( tree->pTransRollback == 0 );
  tree->eTransState = TRANS_INTRANSACTION;
  return SQLITE_OK;
................................................................................
    sqliteFree(pOp->pData);
    sqliteFree(pOp->pKey);
    sqliteFree(pOp);
    pOp = pTmp;
  }
}

static int memRbtreeCommit(Rbtree* tree){
  /* Just delete pTransRollback and pCheckRollback */
  deleteRollbackList(tree->pCheckRollback);
  deleteRollbackList(tree->pTransRollback);
  tree->pTransRollback = 0;
  tree->pCheckRollback = 0;
  tree->pCheckRollbackTail = 0;
  tree->eTransState = TRANS_NONE;
  return SQLITE_OK;
}

/*
 * Execute and delete the supplied rollback-list on pRbtree.
 */
static void execute_rollback_list(Rbtree *pRbtree, BtRollbackOp *pList)
{
  BtRollbackOp *pTmp;
  RbtCursor cur;
  int res;

  cur.pRbtree = pRbtree;
  while( pList ){
    switch( pList->eOp ){
      case ROLLBACK_INSERT:
        cur.pTree  = sqliteHashFind( &pRbtree->tblHash, 0, pList->iTab );
        assert(cur.pTree);
        cur.iTree  = pList->iTab;
        cur.eSkip  = SKIP_NONE;
        memRbtreeInsert( &cur, pList->pKey,
            pList->nKey, pList->pData, pList->nData );
        break;
      case ROLLBACK_DELETE:
        cur.pTree  = sqliteHashFind( &pRbtree->tblHash, 0, pList->iTab );
        assert(cur.pTree);
        cur.iTree  = pList->iTab;
        cur.eSkip  = SKIP_NONE;
        memRbtreeMoveto(&cur, pList->pKey, pList->nKey, &res);
        assert(res == 0);
        memRbtreeDelete( &cur );
        break;
      case ROLLBACK_CREATE:
        btreeCreateTable(pRbtree, pList->iTab);
        break;
      case ROLLBACK_DROP:
        memRbtreeDropTable(pRbtree, pList->iTab);
        break;
      default:
        assert(0);
    }
    sqliteFree(pList->pKey);
    sqliteFree(pList->pData);
    pTmp = pList->pNext;
    sqliteFree(pList);
    pList = pTmp;
  }
}

static int memRbtreeRollback(Rbtree* tree)
{
  tree->eTransState = TRANS_ROLLBACK;
  execute_rollback_list(tree, tree->pCheckRollback);
  execute_rollback_list(tree, tree->pTransRollback);
  tree->pTransRollback = 0;
  tree->pCheckRollback = 0;
  tree->pCheckRollbackTail = 0;
  tree->eTransState = TRANS_NONE;
  return SQLITE_OK;
}

static int memRbtreeBeginCkpt(Rbtree* tree)
{
  if( tree->eTransState != TRANS_INTRANSACTION ) 
    return SQLITE_ERROR;

  assert( tree->pCheckRollback == 0 );
  assert( tree->pCheckRollbackTail == 0 );
  tree->eTransState = TRANS_INCHECKPOINT;
  return SQLITE_OK;
}

static int memRbtreeCommitCkpt(Rbtree* tree)
{
  if( tree->eTransState == TRANS_INCHECKPOINT ){ 
    if( tree->pCheckRollback ){
      tree->pCheckRollbackTail->pNext = tree->pTransRollback;
      tree->pTransRollback = tree->pCheckRollback;
      tree->pCheckRollback = 0;
      tree->pCheckRollbackTail = 0;
    }
    tree->eTransState = TRANS_INTRANSACTION;
  }
  return SQLITE_OK;
}

static int memRbtreeRollbackCkpt(Rbtree* tree)
{
  if( tree->eTransState != TRANS_INCHECKPOINT ) return SQLITE_OK;
  tree->eTransState = TRANS_ROLLBACK;
  execute_rollback_list(tree, tree->pCheckRollback);
  tree->pCheckRollback = 0;
  tree->pCheckRollbackTail = 0;
  tree->eTransState = TRANS_INTRANSACTION;
  return SQLITE_OK;
  return SQLITE_OK;
}

#ifdef SQLITE_TEST
static int memRbtreePageDump(Rbtree* tree, int pgno, int rec)
{
  assert(!"Cannot call sqliteRbtreePageDump");
  return SQLITE_OK;
}

static int memRbtreeCursorDump(RbtCursor* pCur, int* aRes)
{
  assert(!"Cannot call sqliteRbtreeCursorDump");
  return SQLITE_OK;
}

static struct Pager *memRbtreePager(Rbtree* tree)
{
  assert(!"Cannot call sqliteRbtreePager");
  return SQLITE_OK;
}
#endif

/*
** Return the full pathname of the underlying database file.
*/
static const char *memRbtreeGetFilename(Rbtree *pBt){
  return 0;  /* A NULL return indicates there is no underlying file */
}

/*
** The copy file function is not implemented for the in-memory database
*/
static int memRbtreeCopyFile(Rbtree *pBt, Rbtree *pBt2){
  return SQLITE_INTERNAL;  /* Not implemented */
}

static BtOps sqliteRbtreeOps = {
    (int(*)(Btree*)) memRbtreeClose,
    (int(*)(Btree*,int)) memRbtreeSetCacheSize,
    (int(*)(Btree*,int)) memRbtreeSetSafetyLevel,
    (int(*)(Btree*)) memRbtreeBeginTrans,
    (int(*)(Btree*)) memRbtreeCommit,
    (int(*)(Btree*)) memRbtreeRollback,
    (int(*)(Btree*)) memRbtreeBeginCkpt,
    (int(*)(Btree*)) memRbtreeCommitCkpt,
    (int(*)(Btree*)) memRbtreeRollbackCkpt,
    (int(*)(Btree*,int*)) memRbtreeCreateTable,
    (int(*)(Btree*,int*)) memRbtreeCreateTable,
    (int(*)(Btree*,int)) memRbtreeDropTable,
    (int(*)(Btree*,int)) memRbtreeClearTable,
    (int(*)(Btree*,int,int,BtCursor**)) memRbtreeCursor,
    (int(*)(Btree*,int*)) memRbtreeGetMeta,
    (int(*)(Btree*,int*)) memRbtreeUpdateMeta,
    (char*(*)(Btree*,int*,int)) memRbtreeIntegrityCheck,
    (const char*(*)(Btree*)) memRbtreeGetFilename,
    (int(*)(Btree*,Btree*)) memRbtreeCopyFile,

#ifdef SQLITE_TEST
    (int(*)(Btree*,int,int)) memRbtreePageDump,
    (struct Pager*(*)(Btree*)) memRbtreePager
#endif
};

static BtCursorOps sqliteRbtreeCursorOps = {
    (int(*)(BtCursor*,const void*,int,int*)) memRbtreeMoveto,
    (int(*)(BtCursor*)) memRbtreeDelete,
    (int(*)(BtCursor*,const void*,int,const void*,int)) memRbtreeInsert,
    (int(*)(BtCursor*,int*)) memRbtreeFirst,
    (int(*)(BtCursor*,int*)) memRbtreeLast,
    (int(*)(BtCursor*,int*)) memRbtreeNext,
    (int(*)(BtCursor*,int*)) memRbtreePrevious,
    (int(*)(BtCursor*,int*)) memRbtreeKeySize,
    (int(*)(BtCursor*,int,int,char*)) memRbtreeKey,
    (int(*)(BtCursor*,const void*,int,int,int*)) memRbtreeKeyCompare,
    (int(*)(BtCursor*,int*)) memRbtreeDataSize,
    (int(*)(BtCursor*,int,int,char*)) memRbtreeData,
    (int(*)(BtCursor*)) memRbtreeCloseCursor,
#ifdef SQLITE_TEST
    (int(*)(BtCursor*,int*)) memRbtreeCursorDump,
#endif

};

#endif /* SQLITE_OMIT_INMEMORYDB */

Changes to src/build.c.

19
20
21
22
23
24
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
..
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
...
278
279
280
281
282
283
284
285
286

287
288
289
290
291
292
293
....
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
....
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
....
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304



1305
1306


1307






1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
....
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123

2124
2125
2126

2127
2128
2129
2130
2131
2132
2133
2134
2135
....
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150


2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163

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
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.152 2003/05/02 14:32:13 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Check to see if the schema for the database needs
** to be read from the SQLITE_MASTER and SQLITE_TEMP_MASTER tables.
** If it does, then read it.
*/
void sqliteBeginParse(Parse *pParse, int explainFlag){
  sqlite *db = pParse->db;

  pParse->explain = explainFlag;
  if((db->flags & SQLITE_Initialized)==0 && pParse->initFlag==0 ){
    int rc = sqliteInit(db, &pParse->zErrMsg);
    if( rc!=SQLITE_OK ){
      pParse->rc = rc;
      pParse->nErr++;
    }






  }
}

/*
** This is a fake callback procedure used when sqlite_exec() is
** invoked with a NULL callback pointer.  If we pass a NULL callback
** pointer into sqliteVdbeExec() it will return at every OP_Callback,
................................................................................
      pParse->pVdbe = 0;
      pParse->rc = rc;
      if( rc ) pParse->nErr++;
    }else{
      pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE;
    }
    pParse->colNamesSet = 0;
    pParse->schemaVerified = 0;
  }else if( pParse->useCallback==0 ){
    pParse->rc = SQLITE_ERROR;
  }
  pParse->nTab = 0;
  pParse->nMem = 0;
  pParse->nSet = 0;
  pParse->nAgg = 0;
................................................................................
    sqliteHashClear(&temp2);
    sqliteHashInit(&pDb->tblHash, SQLITE_HASH_STRING, 0);
    for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
      Table *pTab = sqliteHashData(pElem);
      sqliteDeleteTable(db, pTab);
    }
    sqliteHashClear(&temp1);
    db->aDb[i].flags &= ~SQLITE_Initialized;
    if( iDb>0 ) return;
  }
  assert( iDb==0 );
  db->flags &= ~SQLITE_InternChanges;

  /* If one or more of the auxiliary database files has been closed,
  ** then remove then from the auxiliary database list.  We take the
................................................................................
  for(i=j=2; i<db->nDb; i++){
    if( db->aDb[i].pBt==0 ){
      sqliteFree(db->aDb[i].zName);
      db->aDb[i].zName = 0;
      continue;
    }
    if( j<i ){
      db->aDb[j++] = db->aDb[i];
    }

  }
  memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j]));
  db->nDb = j;
  if( db->nDb<=2 && db->aDb!=db->aDbStatic ){
    memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0]));
    sqliteFree(db->aDb);
    db->aDb = db->aDbStatic;
................................................................................
  if( pSelTab ){
    assert( pTable->aCol==0 );
    pTable->nCol = pSelTab->nCol;
    pTable->aCol = pSelTab->aCol;
    pSelTab->nCol = 0;
    pSelTab->aCol = 0;
    sqliteDeleteTable(0, pSelTab);
    pParse->db->aDb[pTable->iDb].flags |= SQLITE_UnresetViews;
  }else{
    pTable->nCol = 0;
    nErr++;
  }
  sqliteSelectUnbind(pSel);
  sqliteExprListDelete(pSel->pEList);
  pSel->pEList = pEList;
................................................................................
  }
  sqliteFree(pTable->aCol);
  pTable->aCol = 0;
  pTable->nCol = 0;
}

/*
** Clear the column names from every VIEW.
*/
static void sqliteViewResetAll(sqlite *db, int idx){
  HashElem *i;
  if( (db->aDb[idx].flags & SQLITE_UnresetViews)==0 ) return;
  for(i=sqliteHashFirst(&db->aDb[idx].tblHash); i; i=sqliteHashNext(i)){
    Table *pTab = sqliteHashData(i);
    if( pTab->pSelect ){
      sqliteViewResetColumnNames(pTab);
    }
  }
  db->aDb[idx].flags &= ~SQLITE_UnresetViews;
}

/*
** Given a token, look up a table with that name.  If not found, leave
** an error for the parser to find and return NULL.
*/
Table *sqliteTableFromToken(Parse *pParse, Token *pTok){
................................................................................
      { OP_Ne,         0, ADDR(7),  0},
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(3),  0}, /* 7 */
    };
    Index *pIdx;
    Trigger *pTrigger;
    sqliteBeginWriteOperation(pParse, 0, pTable->iDb);
    sqliteOpenMasterTable(v, pTable->iDb);
    /* Drop all triggers associated with the table being dropped */
    pTrigger = pTable->pTrigger;
    while( pTrigger ){
      SrcList *pNm;
      assert( pTrigger->iDb==pTable->iDb );
      pNm = sqliteSrcListAppend(0, 0, 0);
      pNm->a[0].zName = sqliteStrDup(pTrigger->name);
      pNm->a[0].zDatabase = sqliteStrDup(db->aDb[pTable->iDb].zName);
      sqliteDropTrigger(pParse, pNm, 1);
      if( pParse->explain ){
        pTrigger = pTrigger->pNext;
      }else{
        pTrigger = pTable->pTrigger;
      }
    }



    base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
    sqliteVdbeChangeP3(v, base+1, pTable->zName, 0);


    if( !pTable->iDb ){






      sqliteChangeCookie(db, v);
    }
    sqliteVdbeAddOp(v, OP_Close, 0, 0);
    if( !isView ){
      sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->iDb);
      for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){
        sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pTable->iDb);
      }
    }
    sqliteEndWriteOperation(pParse);
  }

  /* Delete the in-memory description of the table.
  **
................................................................................
  db->onError = OE_Default;
}

/*
** Generate VDBE code that will verify the schema cookie for all
** named database files.
*/
void sqliteCodeVerifySchema(Parse *pParse){
  int i;
  sqlite *db = pParse->db;
  Vdbe *v = sqliteGetVdbe(pParse);

  for(i=0; i<db->nDb; i++){
    if( i==1 || db->aDb[i].pBt==0 ) continue;
    sqliteVdbeAddOp(v, OP_VerifyCookie, i, db->aDb[i].schema_cookie);

  }
  pParse->schemaVerified = 1;
}

/*
** Generate VDBE code that prepares for doing an operation that
** might change the database.
**
** This routine starts a new transaction if we are not already within
................................................................................
** is set if the setCheckpoint parameter is true.  A checkpoint should
** be set for operations that might fail (due to a constraint) part of
** the way through and which will need to undo some writes without having to
** rollback the whole transaction.  For operations where all constraints
** can be checked before any changes are made to the database, it is never
** necessary to undo a write and the checkpoint should not be set.
**
** The tempOnly flag indicates that only temporary tables will be changed
** during this write operation.  The primary database table is not
** write-locked.  Only the temporary database file gets a write lock.
** Other processes can continue to read or write the primary database file.
*/
void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int tempOnly){
  Vdbe *v;


  v = sqliteGetVdbe(pParse);
  if( v==0 ) return;
  if( pParse->trigStack ) return; /* if this is in a trigger */
  if( (pParse->db->flags & SQLITE_InTrans)==0 ){
    sqliteVdbeAddOp(v, OP_Transaction, 1, 0);
    if( !tempOnly ){
      int i;
      sqlite *db = pParse->db;
      sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
      for(i=2; i<db->nDb; i++){
        if( db->aDb[i].pBt==0 ) continue;
        sqliteVdbeAddOp(v, OP_Transaction, i, 0);
      }

      sqliteCodeVerifySchema(pParse);


    }
  }else if( setCheckpoint ){
    sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0);
    sqliteVdbeAddOp(v, OP_Checkpoint, 1, 0);

  }
}

/*
** Generate code that concludes an operation that may have changed
** the database.  This is a companion function to BeginWriteOperation().
** If a transaction was started, then commit it.  If a checkpoint was

** started then commit that.




*/
void sqliteEndWriteOperation(Parse *pParse){
  Vdbe *v;

  if( pParse->trigStack ) return; /* if this is in a trigger */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) return;
  if( pParse->db->flags & SQLITE_InTrans ){
    /* Do Nothing */

  }else{
    sqliteVdbeAddOp(v, OP_Commit, 0, 0);
  }
}







|












>







>
>
>
>
>
>







 







<







 







|







 







|

>







 







|







 







|



|






|







 







|




|










>
>
>


>
>
|
>
>
>
>
>
>






|







 







|
<


>
|
|
|
>

<







 







|
|
|
|

|

>
>


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


|
<
>





<
|
>
|
>
>
>
>



>



|
|
>




19
20
21
22
23
24
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
..
99
100
101
102
103
104
105

106
107
108
109
110
111
112
...
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
...
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
....
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
....
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
....
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
....
2131
2132
2133
2134
2135
2136
2137
2138

2139
2140
2141
2142
2143
2144
2145
2146

2147
2148
2149
2150
2151
2152
2153
....
2155
2156
2157
2158
2159
2160
2161
2162
2163
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
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.153 2003/05/17 17:35:11 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Check to see if the schema for the database needs
** to be read from the SQLITE_MASTER and SQLITE_TEMP_MASTER tables.
** If it does, then read it.
*/
void sqliteBeginParse(Parse *pParse, int explainFlag){
  sqlite *db = pParse->db;
  int i;
  pParse->explain = explainFlag;
  if((db->flags & SQLITE_Initialized)==0 && pParse->initFlag==0 ){
    int rc = sqliteInit(db, &pParse->zErrMsg);
    if( rc!=SQLITE_OK ){
      pParse->rc = rc;
      pParse->nErr++;
    }
  }
  for(i=0; i<db->nDb; i++){
    DbClearProperty(db, i, DB_Locked);
    if( !db->aDb[i].inTrans ){
      DbClearProperty(db, i, DB_Cookie);
    }
  }
}

/*
** This is a fake callback procedure used when sqlite_exec() is
** invoked with a NULL callback pointer.  If we pass a NULL callback
** pointer into sqliteVdbeExec() it will return at every OP_Callback,
................................................................................
      pParse->pVdbe = 0;
      pParse->rc = rc;
      if( rc ) pParse->nErr++;
    }else{
      pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE;
    }
    pParse->colNamesSet = 0;

  }else if( pParse->useCallback==0 ){
    pParse->rc = SQLITE_ERROR;
  }
  pParse->nTab = 0;
  pParse->nMem = 0;
  pParse->nSet = 0;
  pParse->nAgg = 0;
................................................................................
    sqliteHashClear(&temp2);
    sqliteHashInit(&pDb->tblHash, SQLITE_HASH_STRING, 0);
    for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
      Table *pTab = sqliteHashData(pElem);
      sqliteDeleteTable(db, pTab);
    }
    sqliteHashClear(&temp1);
    DbClearProperty(db, i, DB_SchemaLoaded);
    if( iDb>0 ) return;
  }
  assert( iDb==0 );
  db->flags &= ~SQLITE_InternChanges;

  /* If one or more of the auxiliary database files has been closed,
  ** then remove then from the auxiliary database list.  We take the
................................................................................
  for(i=j=2; i<db->nDb; i++){
    if( db->aDb[i].pBt==0 ){
      sqliteFree(db->aDb[i].zName);
      db->aDb[i].zName = 0;
      continue;
    }
    if( j<i ){
      db->aDb[j] = db->aDb[i];
    }
    j++;
  }
  memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j]));
  db->nDb = j;
  if( db->nDb<=2 && db->aDb!=db->aDbStatic ){
    memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0]));
    sqliteFree(db->aDb);
    db->aDb = db->aDbStatic;
................................................................................
  if( pSelTab ){
    assert( pTable->aCol==0 );
    pTable->nCol = pSelTab->nCol;
    pTable->aCol = pSelTab->aCol;
    pSelTab->nCol = 0;
    pSelTab->aCol = 0;
    sqliteDeleteTable(0, pSelTab);
    DbSetProperty(pParse->db, pTable->iDb, DB_UnresetViews);
  }else{
    pTable->nCol = 0;
    nErr++;
  }
  sqliteSelectUnbind(pSel);
  sqliteExprListDelete(pSel->pEList);
  pSel->pEList = pEList;
................................................................................
  }
  sqliteFree(pTable->aCol);
  pTable->aCol = 0;
  pTable->nCol = 0;
}

/*
** Clear the column names from every VIEW in database idx.
*/
static void sqliteViewResetAll(sqlite *db, int idx){
  HashElem *i;
  if( !DbHasProperty(db, idx, DB_UnresetViews) ) return;
  for(i=sqliteHashFirst(&db->aDb[idx].tblHash); i; i=sqliteHashNext(i)){
    Table *pTab = sqliteHashData(i);
    if( pTab->pSelect ){
      sqliteViewResetColumnNames(pTab);
    }
  }
  DbClearProperty(db, idx, DB_UnresetViews);
}

/*
** Given a token, look up a table with that name.  If not found, leave
** an error for the parser to find and return NULL.
*/
Table *sqliteTableFromToken(Parse *pParse, Token *pTok){
................................................................................
      { OP_Ne,         0, ADDR(7),  0},
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(3),  0}, /* 7 */
    };
    Index *pIdx;
    Trigger *pTrigger;
    sqliteBeginWriteOperation(pParse, 0, pTable->iDb);

    /* Drop all triggers associated with the table being dropped */
    pTrigger = pTable->pTrigger;
    while( pTrigger ){
      SrcList *pNm;
      assert( pTrigger->iDb==pTable->iDb || pTrigger->iDb==1 );
      pNm = sqliteSrcListAppend(0, 0, 0);
      pNm->a[0].zName = sqliteStrDup(pTrigger->name);
      pNm->a[0].zDatabase = sqliteStrDup(db->aDb[pTable->iDb].zName);
      sqliteDropTrigger(pParse, pNm, 1);
      if( pParse->explain ){
        pTrigger = pTrigger->pNext;
      }else{
        pTrigger = pTable->pTrigger;
      }
    }

    /* Drop all SQLITE_MASTER entries that refer to the table */
    sqliteOpenMasterTable(v, pTable->iDb);
    base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
    sqliteVdbeChangeP3(v, base+1, pTable->zName, 0);

    /* Drop all SQLITE_TEMP_MASTER entries that refer to the table */
    if( pTable->iDb!=1 ){
      sqliteOpenMasterTable(v, 1);
      base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
      sqliteVdbeChangeP3(v, base+1, pTable->zName, 0);
    }

    if( pTable->iDb==0 ){
      sqliteChangeCookie(db, v);
    }
    sqliteVdbeAddOp(v, OP_Close, 0, 0);
    if( !isView ){
      sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->iDb);
      for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){
        sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pIdx->iDb);
      }
    }
    sqliteEndWriteOperation(pParse);
  }

  /* Delete the in-memory description of the table.
  **
................................................................................
  db->onError = OE_Default;
}

/*
** Generate VDBE code that will verify the schema cookie for all
** named database files.
*/
void sqliteCodeVerifySchema(Parse *pParse, int iDb){

  sqlite *db = pParse->db;
  Vdbe *v = sqliteGetVdbe(pParse);
  assert( iDb>=0 && iDb<db->nDb );
  assert( db->aDb[iDb].pBt!=0 );
  if( iDb!=1 && !DbHasProperty(db, iDb, DB_Cookie) ){
    sqliteVdbeAddOp(v, OP_VerifyCookie, iDb, db->aDb[iDb].schema_cookie);
    DbSetProperty(db, iDb, DB_Cookie);
  }

}

/*
** Generate VDBE code that prepares for doing an operation that
** might change the database.
**
** This routine starts a new transaction if we are not already within
................................................................................
** is set if the setCheckpoint parameter is true.  A checkpoint should
** be set for operations that might fail (due to a constraint) part of
** the way through and which will need to undo some writes without having to
** rollback the whole transaction.  For operations where all constraints
** can be checked before any changes are made to the database, it is never
** necessary to undo a write and the checkpoint should not be set.
**
** Only database iDb and the temp database are made writable by this call.
** If iDb==0, then the main and temp databases are made writable.   If
** iDb==1 then only the temp database is made writable.  If iDb>1 then the
** specified auxiliary database and the temp database are made writable.
*/
void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int iDb){
  Vdbe *v;
  sqlite *db = pParse->db;
  if( DbHasProperty(db, iDb, DB_Locked) ) return;
  v = sqliteGetVdbe(pParse);
  if( v==0 ) return;


  if( !db->aDb[iDb].inTrans ){






    sqliteVdbeAddOp(v, OP_Transaction, iDb, 0);

    DbSetProperty(db, iDb, DB_Locked);
    sqliteCodeVerifySchema(pParse, iDb);
    if( iDb!=1 ){
      sqliteBeginWriteOperation(pParse, setCheckpoint, 1);
    }
  }else if( setCheckpoint ){
    sqliteVdbeAddOp(v, OP_Checkpoint, iDb, 0);

    DbSetProperty(db, iDb, DB_Locked);
  }
}

/*
** Generate code that concludes an operation that may have changed

** the database.  If a statement transaction was started, then emit
** an OP_Commit that will cause the changes to be committed to disk.
**
** Note that checkpoints are automatically committed at the end of
** a statement.  Note also that there can be multiple calls to 
** sqliteBeginWriteOperation() but there should only be a single
** call to sqliteEndWriteOperation() at the conclusion of the statement.
*/
void sqliteEndWriteOperation(Parse *pParse){
  Vdbe *v;
  sqlite *db = pParse->db;
  if( pParse->trigStack ) return; /* if this is in a trigger */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) return;
  if( db->flags & SQLITE_InTrans ){
    /* A BEGIN has executed.  Do not commit until we see an explicit
    ** COMMIT statement. */
  }else{
    sqliteVdbeAddOp(v, OP_Commit, 0, 0);
  }
}

Changes to src/copy.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code used to implement the COPY command.
**
** $Id: copy.c,v 1.4 2003/04/24 01:45:04 drh Exp $
*/
#include "sqliteInt.h"

/*
** The COPY command is for compatibility with PostgreSQL and specificially
** for the ability to read the output of pg_dump.  The format is as
** follows:
................................................................................
  zDb = db->aDb[pTab->iDb].zName;
  if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb)
      || sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile, zDb) ){
    goto copy_cleanup;
  }
  v = sqliteGetVdbe(pParse);
  if( v ){
    sqliteBeginWriteOperation(pParse, 1, pTab->iDb==1);
    addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0);
    sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
    sqliteVdbeDequoteP3(v, addr);
    sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
    sqliteVdbeAddOp(v, OP_OpenWrite, 0, pTab->tnum);
    sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
    for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){







|







 







|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code used to implement the COPY command.
**
** $Id: copy.c,v 1.5 2003/05/17 17:35:11 drh Exp $
*/
#include "sqliteInt.h"

/*
** The COPY command is for compatibility with PostgreSQL and specificially
** for the ability to read the output of pg_dump.  The format is as
** follows:
................................................................................
  zDb = db->aDb[pTab->iDb].zName;
  if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb)
      || sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile, zDb) ){
    goto copy_cleanup;
  }
  v = sqliteGetVdbe(pParse);
  if( v ){
    sqliteBeginWriteOperation(pParse, 1, pTab->iDb);
    addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0);
    sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
    sqliteVdbeDequoteP3(v, addr);
    sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
    sqliteVdbeAddOp(v, OP_OpenWrite, 0, pTab->tnum);
    sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
    for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){

Changes to src/delete.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
** $Id: delete.c,v 1.56 2003/05/02 14:32:13 drh Exp $
*/
#include "sqliteInt.h"

/*
** Look up every table that is named in pSrc.  If any table is not found,
** add an error message to pParse->zErrMsg and return NULL.  If all tables
** are found, return a pointer to the last table.
................................................................................

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ){
    goto delete_from_cleanup;
  }
  sqliteBeginWriteOperation(pParse, row_triggers_exist,
       !row_triggers_exist && pTab->iDb==1);

  /* If we are trying to delete from a view, construct that view into
  ** a temporary table.
  */
  if( isView ){
    Select *pView = sqliteSelectDup(pTab->pSelect);
    sqliteSelect(pParse, pView, SRT_TempTable, iCur, 0, 0, 0);







|







 







|
<







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
** $Id: delete.c,v 1.57 2003/05/17 17:35:11 drh Exp $
*/
#include "sqliteInt.h"

/*
** Look up every table that is named in pSrc.  If any table is not found,
** add an error message to pParse->zErrMsg and return NULL.  If all tables
** are found, return a pointer to the last table.
................................................................................

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ){
    goto delete_from_cleanup;
  }
  sqliteBeginWriteOperation(pParse, row_triggers_exist, pTab->iDb);


  /* If we are trying to delete from a view, construct that view into
  ** a temporary table.
  */
  if( isView ){
    Select *pView = sqliteSelectDup(pTab->pSelect);
    sqliteSelect(pParse, pView, SRT_TempTable, iCur, 0, 0, 0);

Changes to src/insert.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
**
** $Id: insert.c,v 1.84 2003/05/16 02:30:27 drh Exp $
*/
#include "sqliteInt.h"

/*
** This routine is call to handle SQL of the following forms:
**
**    insert into TABLE (IDLIST) values(EXPRLIST)
................................................................................
    goto insert_cleanup;
  }

  /* Allocate a VDBE
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto insert_cleanup;
  sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist,
         !row_triggers_exist && pTab->iDb==1);

  /* if there are row triggers, allocate a temp table for new.* references. */
  if( row_triggers_exist ){
    newIdx = pParse->nTab++;
  }

  /* Figure out how many columns of data are supplied.  If the data







|







 







|
<







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
155
156
157
158
159
160
161
162

163
164
165
166
167
168
169
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
**
** $Id: insert.c,v 1.85 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"

/*
** This routine is call to handle SQL of the following forms:
**
**    insert into TABLE (IDLIST) values(EXPRLIST)
................................................................................
    goto insert_cleanup;
  }

  /* Allocate a VDBE
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto insert_cleanup;
  sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist, pTab->iDb);


  /* if there are row triggers, allocate a temp table for new.* references. */
  if( row_triggers_exist ){
    newIdx = pParse->nTab++;
  }

  /* Figure out how many columns of data are supplied.  If the data

Changes to src/main.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
25
26
27
28
29
30
31







32
33
34
35
36
37
38
..
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61




62
63
64
65
66
67
68
...
338
339
340
341
342
343
344
345



346
347
348
349
350
351
352
...
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
...
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.130 2003/05/04 17:58:26 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** A pointer to this structure is used to communicate information
................................................................................
** from sqliteInit into the sqliteInitCallback.
*/
typedef struct {
  sqlite *db;         /* The database being initialized */
  char **pzErrMsg;    /* Error message stored here */
} InitData;









/*
** This is the callback routine for the code that initializes the
** database.  See sqliteInit() below for additional information.
**
** Each callback contains the following information:
**
................................................................................
*/
static
int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
  InitData *pData = (InitData*)pInit;
  Parse sParse;
  int nErr = 0;

  /* TODO: Do some validity checks on all fields.  In particular,
  ** make sure fields do not contain NULLs. Otherwise we might core
  ** when attempting to initialize from a corrupt database file. */


  assert( argc==5 );
  switch( argv[0][0] ){
    case 'v':
    case 'i':
    case 't': {  /* CREATE TABLE, CREATE INDEX, or CREATE VIEW statements */




      if( argv[3] && argv[3][0] ){
        /* Call the parser to process a CREATE TABLE, INDEX or VIEW.
        ** But because sParse.initFlag is set to 1, no VDBE code is generated
        ** or executed.  All the parser does is build the internal data
        ** structures that describe the table, index, or view.
        */
        memset(&sParse, 0, sizeof(sParse));
................................................................................
  sqliteBtreeCloseCursor(curMain);
  if( sqlite_malloc_failed ){
    sqliteSetString(pzErrMsg, "out of memory", 0);
    sParse.rc = SQLITE_NOMEM;
    sqliteResetInternalSchema(db, 0);
  }
  if( sParse.rc==SQLITE_OK ){
    db->aDb[iDb].flags |= SQLITE_Initialized;



  }else{
    sqliteResetInternalSchema(db, iDb);
  }
  return sParse.rc;
}

/*
................................................................................
*/
int sqliteInit(sqlite *db, char **pzErrMsg){
  int i, rc;
  
  assert( (db->flags & SQLITE_Initialized)==0 );
  rc = SQLITE_OK;
  for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
    if( db->aDb[i].flags & SQLITE_Initialized ) continue;
    if( i==1 ) continue;  /* Skip the temp database - initialized with 0 */
    rc = sqliteInitOne(db, i, pzErrMsg);
  }
  if( rc==SQLITE_OK ){
    db->flags |= SQLITE_Initialized;
    sqliteCommitInternalChanges(db);
  }else{
    db->flags &= ~SQLITE_Initialized;
................................................................................
    } else if (TEMP_STORE == 1 || TEMP_STORE == 2) {
      /* Switch depending on compile-time and/or runtime settings. */
      int location = db->temp_store==0 ? TEMP_STORE : db->temp_store;

      if (location == 1) {
        return sqliteBtreeOpen(zFilename, omitJournal, nCache, ppBtree);
      } else {
        return sqliteRBtreeOpen(0, 0, 0, ppBtree);
      }
    } else {
      /* Always use in-core DB */
      return sqliteRBtreeOpen(0, 0, 0, ppBtree);
    }
  }else if( zFilename[0]==':' && strcmp(zFilename,":memory:")==0 ){
    return sqliteRBtreeOpen(0, 0, 0, ppBtree);
  }else
#endif
  {
    return sqliteBtreeOpen(zFilename, omitJournal, nCache, ppBtree);
  }
}







|







 







>
>
>
>
>
>
>







 







|
|
|
>
|
<




>
>
>
>







 







|
>
>
>







 







|
|







 







|



|


|






10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
..
53
54
55
56
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
...
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
...
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
...
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.131 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** A pointer to this structure is used to communicate information
................................................................................
** from sqliteInit into the sqliteInitCallback.
*/
typedef struct {
  sqlite *db;         /* The database being initialized */
  char **pzErrMsg;    /* Error message stored here */
} InitData;

/*
** Fill the InitData structure with an error message that indicates
** that the database is corrupt.
*/
static void corruptSchema(InitData *pData){
  sqliteSetString(pData->pzErrMsg, "malformed database schema", 0);
}

/*
** This is the callback routine for the code that initializes the
** database.  See sqliteInit() below for additional information.
**
** Each callback contains the following information:
**
................................................................................
*/
static
int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
  InitData *pData = (InitData*)pInit;
  Parse sParse;
  int nErr = 0;

  assert( argc==5 );
  if( argv[0]==0 ){
    corruptSchema(pData);
    return 1;
  }

  switch( argv[0][0] ){
    case 'v':
    case 'i':
    case 't': {  /* CREATE TABLE, CREATE INDEX, or CREATE VIEW statements */
      if( argv[2]==0 || argv[4]==0 ){
        corruptSchema(pData);
        return 1;
      }
      if( argv[3] && argv[3][0] ){
        /* Call the parser to process a CREATE TABLE, INDEX or VIEW.
        ** But because sParse.initFlag is set to 1, no VDBE code is generated
        ** or executed.  All the parser does is build the internal data
        ** structures that describe the table, index, or view.
        */
        memset(&sParse, 0, sizeof(sParse));
................................................................................
  sqliteBtreeCloseCursor(curMain);
  if( sqlite_malloc_failed ){
    sqliteSetString(pzErrMsg, "out of memory", 0);
    sParse.rc = SQLITE_NOMEM;
    sqliteResetInternalSchema(db, 0);
  }
  if( sParse.rc==SQLITE_OK ){
    DbSetProperty(db, iDb, DB_SchemaLoaded);
    if( iDb==0 ){
      DbSetProperty(db, 1, DB_SchemaLoaded);
    }
  }else{
    sqliteResetInternalSchema(db, iDb);
  }
  return sParse.rc;
}

/*
................................................................................
*/
int sqliteInit(sqlite *db, char **pzErrMsg){
  int i, rc;
  
  assert( (db->flags & SQLITE_Initialized)==0 );
  rc = SQLITE_OK;
  for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
    if( DbHasProperty(db, i, DB_SchemaLoaded) ) continue;
    assert( i!=1 );  /* Should have been initialized together with 0 */
    rc = sqliteInitOne(db, i, pzErrMsg);
  }
  if( rc==SQLITE_OK ){
    db->flags |= SQLITE_Initialized;
    sqliteCommitInternalChanges(db);
  }else{
    db->flags &= ~SQLITE_Initialized;
................................................................................
    } else if (TEMP_STORE == 1 || TEMP_STORE == 2) {
      /* Switch depending on compile-time and/or runtime settings. */
      int location = db->temp_store==0 ? TEMP_STORE : db->temp_store;

      if (location == 1) {
        return sqliteBtreeOpen(zFilename, omitJournal, nCache, ppBtree);
      } else {
        return sqliteRbtreeOpen(0, 0, 0, ppBtree);
      }
    } else {
      /* Always use in-core DB */
      return sqliteRbtreeOpen(0, 0, 0, ppBtree);
    }
  }else if( zFilename[0]==':' && strcmp(zFilename,":memory:")==0 ){
    return sqliteRbtreeOpen(0, 0, 0, ppBtree);
  }else
#endif
  {
    return sqliteBtreeOpen(zFilename, omitJournal, nCache, ppBtree);
  }
}

Changes to src/select.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
** $Id: select.c,v 1.138 2003/05/06 20:35:16 drh Exp $
*/
#include "sqliteInt.h"


/*
** Allocate a new Select structure and return a pointer to that
** structure.
................................................................................
  }

  /* Generating code to find the min or the max.  Basically all we have
  ** to do is find the first or the last entry in the chosen index.  If
  ** the min() or max() is on the INTEGER PRIMARY KEY, then find the first
  ** or last entry in the main table.
  */
  if( !pParse->schemaVerified && (pParse->db->flags & SQLITE_InTrans)==0 ){
    sqliteCodeVerifySchema(pParse);
  }
  base = p->pSrc->a[0].iCursor;
  sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
  sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
  sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
  cont = sqliteVdbeMakeLabel(v);
  if( pIdx==0 ){
    sqliteVdbeAddOp(v, seekOp, base, 0);







|







 







<
|
<







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1841
1842
1843
1844
1845
1846
1847

1848

1849
1850
1851
1852
1853
1854
1855
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
** $Id: select.c,v 1.139 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"


/*
** Allocate a new Select structure and return a pointer to that
** structure.
................................................................................
  }

  /* Generating code to find the min or the max.  Basically all we have
  ** to do is find the first or the last entry in the chosen index.  If
  ** the min() or max() is on the INTEGER PRIMARY KEY, then find the first
  ** or last entry in the main table.
  */

  sqliteCodeVerifySchema(pParse, pTab->iDb);

  base = p->pSrc->a[0].iCursor;
  sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
  sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
  sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
  cont = sqliteVdbeMakeLabel(v);
  if( pIdx==0 ){
    sqliteVdbeAddOp(v, seekOp, base, 0);

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
235
236
237
238
239
240
241
































242
243
244
245
246
247
248
...
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
...
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
....
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.184 2003/05/10 03:04:34 jplyon Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
................................................................................
  Hash tblHash;        /* All tables indexed by name */
  Hash idxHash;        /* All (named) indices indexed by name */
  Hash trigHash;       /* All triggers indexed by name */
  Hash aFKey;          /* Foreign keys indexed by to-table */
  u8 inTrans;          /* True if a transaction is underway for this backend */
  u16 flags;           /* Flags associated with this database */
};

































/*
** Each database is an instance of the following structure.
**
** The sqlite.file_format is initialized by the database file
** and helps determines how the data in the database file is
** represented.  This field allows newer versions of the library
................................................................................
  int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
                                /* Access authorization function */
  void *pAuthArg;               /* 1st argument to the access auth function */
#endif
};

/*
** Possible values for the sqlite.flags.




*/
#define SQLITE_VdbeTrace      0x00000001  /* True to trace VDBE execution */
#define SQLITE_Initialized    0x00000002  /* True after initialization */
#define SQLITE_Interrupt      0x00000004  /* Cancel current operation */
#define SQLITE_InTrans        0x00000008  /* True if in a transaction */
#define SQLITE_InternChanges  0x00000010  /* Uncommitted Hash table changes */
#define SQLITE_FullColNames   0x00000020  /* Show full column names on SELECT */
#define SQLITE_CountRows      0x00000040  /* Count rows changed by INSERT, */
                                          /*   DELETE, or UPDATE and return */
                                          /*   the count using a callback. */
#define SQLITE_NullCallback   0x00000080  /* Invoke the callback once if the */
                                          /*   result set is empty */
/*#define SQLITE_ResultDetails  0x00000100 * (UNUSED -- flag free for reuse) */
#define SQLITE_UnresetViews   0x00000200  /* True if one or more views have */
                                          /*   defined column names */
#define SQLITE_ReportTypes    0x00000400  /* Include information on datatypes */
                                          /*   in 4th argument of callback */

/*
** Possible values for the sqlite.magic field.
** The numbers are obtained at random and have no special meaning, other
** than being distinct from one another.
*/
................................................................................
  Vdbe *pVdbe;         /* An engine for executing database bytecode */
  u8 colNamesSet;      /* TRUE after OP_ColumnName has been issued to pVdbe */
  u8 explain;          /* True if the EXPLAIN flag is found on the query */
  u8 initFlag;         /* True if reparsing CREATE TABLEs */
  u8 nameClash;        /* A permanent table name clashes with temp table name */
  u8 useAgg;           /* If true, extract field values from the aggregator
                       ** while generating expressions.  Normally false */
  u8 schemaVerified;   /* True if an OP_VerifySchema has been coded someplace
                       ** other than after an OP_Transaction */
  u8 iDb;              /* Index of database whose schema is being parsed */
  u8 useCallback;      /* True if callbacks should be used to report results */
  int useDb;           /* Restrict references to tables in this database */
  int newTnum;         /* Table number to use when reparsing CREATE TABLEs */
  int nErr;            /* Number of errors seen */
  int nTab;            /* Number of previously allocated VDBE cursors */
  int nMem;            /* Number of memory cells used so far */
................................................................................
int sqliteFuncId(Token*);
int sqliteExprResolveIds(Parse*, SrcList*, ExprList*, Expr*);
int sqliteExprAnalyzeAggregates(Parse*, Expr*);
Vdbe *sqliteGetVdbe(Parse*);
int sqliteRandomByte(void);
int sqliteRandomInteger(void);
void sqliteRollbackAll(sqlite*);
void sqliteCodeVerifySchema(Parse*);
void sqliteBeginTransaction(Parse*, int);
void sqliteCommitTransaction(Parse*);
void sqliteRollbackTransaction(Parse*);
int sqliteExprIsConstant(Expr*);
int sqliteExprIsInteger(Expr*, int*);
int sqliteIsRowid(const char*);
void sqliteGenerateRowDelete(sqlite*, Vdbe*, Table*, int, int);







|







 







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







 







|
>
>
>
>












<
<
<
|







 







<
<







 







|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
...
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
...
846
847
848
849
850
851
852


853
854
855
856
857
858
859
....
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.185 2003/05/17 17:35:12 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
................................................................................
  Hash tblHash;        /* All tables indexed by name */
  Hash idxHash;        /* All (named) indices indexed by name */
  Hash trigHash;       /* All triggers indexed by name */
  Hash aFKey;          /* Foreign keys indexed by to-table */
  u8 inTrans;          /* True if a transaction is underway for this backend */
  u16 flags;           /* Flags associated with this database */
};

/*
** These macros can be used to test, set, or clear bits in the 
** Db.flags field.
*/
#define DbHasProperty(D,I,P)     (((D)->aDb[I].flags&(P))==(P))
#define DbHasAnyProperty(D,I,P)  (((D)->aDb[I].flags&(P))!=0)
#define DbSetProperty(D,I,P)     (D)->aDb[I].flags|=(P)
#define DbClearProperty(D,I,P)   (D)->aDb[I].flags&=~(P)

/*
** Allowed values for the DB.flags field.
**
** The DB_Locked flag is set when the first OP_Transaction or OP_Checkpoint
** opcode is emitted for a database.  This prevents multiple occurances
** of those opcodes for the same database in the same program.  Similarly,
** the DB_Cookie flag is set when the OP_VerifyCookie opcode is emitted,
** and prevents duplicate OP_VerifyCookies from taking up space and slowing
** down execution.
**
** The DB_SchemaLoaded flag is set after the database schema has been
** read into internal hash tables.
**
** DB_UnresetViews means that one or more views have column names that
** have been filled out.  If the schema changes, these column names might
** changes and so the view will need to be reset.
*/
#define DB_Locked          0x0001  /* OP_Transaction opcode has been emitted */
#define DB_Cookie          0x0002  /* OP_VerifyCookie opcode has been emiited */
#define DB_SchemaLoaded    0x0004  /* The schema has been loaded */
#define DB_UnresetViews    0x0008  /* Some views have defined column names */


/*
** Each database is an instance of the following structure.
**
** The sqlite.file_format is initialized by the database file
** and helps determines how the data in the database file is
** represented.  This field allows newer versions of the library
................................................................................
  int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
                                /* Access authorization function */
  void *pAuthArg;               /* 1st argument to the access auth function */
#endif
};

/*
** Possible values for the sqlite.flags and or Db.flags fields.
**
** On sqlite.flags, the SQLITE_InTrans value means that we have
** executed a BEGIN.  On Db.flags, SQLITE_InTrans means a statement
** transaction is active on that particular database file.
*/
#define SQLITE_VdbeTrace      0x00000001  /* True to trace VDBE execution */
#define SQLITE_Initialized    0x00000002  /* True after initialization */
#define SQLITE_Interrupt      0x00000004  /* Cancel current operation */
#define SQLITE_InTrans        0x00000008  /* True if in a transaction */
#define SQLITE_InternChanges  0x00000010  /* Uncommitted Hash table changes */
#define SQLITE_FullColNames   0x00000020  /* Show full column names on SELECT */
#define SQLITE_CountRows      0x00000040  /* Count rows changed by INSERT, */
                                          /*   DELETE, or UPDATE and return */
                                          /*   the count using a callback. */
#define SQLITE_NullCallback   0x00000080  /* Invoke the callback once if the */
                                          /*   result set is empty */



#define SQLITE_ReportTypes    0x00000200  /* Include information on datatypes */
                                          /*   in 4th argument of callback */

/*
** Possible values for the sqlite.magic field.
** The numbers are obtained at random and have no special meaning, other
** than being distinct from one another.
*/
................................................................................
  Vdbe *pVdbe;         /* An engine for executing database bytecode */
  u8 colNamesSet;      /* TRUE after OP_ColumnName has been issued to pVdbe */
  u8 explain;          /* True if the EXPLAIN flag is found on the query */
  u8 initFlag;         /* True if reparsing CREATE TABLEs */
  u8 nameClash;        /* A permanent table name clashes with temp table name */
  u8 useAgg;           /* If true, extract field values from the aggregator
                       ** while generating expressions.  Normally false */


  u8 iDb;              /* Index of database whose schema is being parsed */
  u8 useCallback;      /* True if callbacks should be used to report results */
  int useDb;           /* Restrict references to tables in this database */
  int newTnum;         /* Table number to use when reparsing CREATE TABLEs */
  int nErr;            /* Number of errors seen */
  int nTab;            /* Number of previously allocated VDBE cursors */
  int nMem;            /* Number of memory cells used so far */
................................................................................
int sqliteFuncId(Token*);
int sqliteExprResolveIds(Parse*, SrcList*, ExprList*, Expr*);
int sqliteExprAnalyzeAggregates(Parse*, Expr*);
Vdbe *sqliteGetVdbe(Parse*);
int sqliteRandomByte(void);
int sqliteRandomInteger(void);
void sqliteRollbackAll(sqlite*);
void sqliteCodeVerifySchema(Parse*, int);
void sqliteBeginTransaction(Parse*, int);
void sqliteCommitTransaction(Parse*);
void sqliteRollbackTransaction(Parse*);
int sqliteExprIsConstant(Expr*);
int sqliteExprIsInteger(Expr*, int*);
int sqliteIsRowid(const char*);
void sqliteGenerateRowDelete(sqlite*, Vdbe*, Table*, int, int);

Changes to src/update.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.65 2003/05/02 14:32:14 drh Exp $
*/
#include "sqliteInt.h"

/*
** Process an UPDATE statement.
**
**   UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
................................................................................
    sqliteAuthContextPush(pParse, &sContext, pTab->zName);
  }

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto update_cleanup;
  sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->iDb==1);

  /* If we are trying to update a view, construct that view into
  ** a temporary table.
  */
  if( isView ){
    Select *pView;
    pView = sqliteSelectDup(pTab->pSelect);







|







 







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.66 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"

/*
** Process an UPDATE statement.
**
**   UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
................................................................................
    sqliteAuthContextPush(pParse, &sContext, pTab->zName);
  }

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto update_cleanup;
  sqliteBeginWriteOperation(pParse, 1, pTab->iDb);

  /* If we are trying to update a view, construct that view into
  ** a temporary table.
  */
  if( isView ){
    Select *pView;
    pView = sqliteSelectDup(pTab->pSelect);

Changes to src/vdbe.c.

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
....
3189
3190
3191
3192
3193
3194
3195


3196
3197
3198
3199
3200
3201
3202
3203
....
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
....
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.222 2003/05/10 03:36:54 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** The makefile scans this source file and creates the following
** array of string constants which are the names of all VDBE opcodes.
................................................................................
** transaction is underway.  Starting a transaction also creates a
** rollback journal.  A transaction must be started before any changes
** can be made to the database.
*/
case OP_Transaction: {
  int busy = 1;
  int i = pOp->p1;


  while( i>=0 && i<db->nDb && db->aDb[i].pBt!=0 && busy ){
    rc = sqliteBtreeBeginTrans(db->aDb[i].pBt);
    switch( rc ){
      case SQLITE_BUSY: {
        if( db->xBusyCallback==0 ){
          p->pc = pc;
          p->undoTransOnError = 1;
          p->rc = SQLITE_BUSY;
................................................................................
** The table being destroyed is in the main database file if P2==0.  If
** P2==1 then the table to be clear is in the auxiliary database file
** that is used to store tables create using CREATE TEMPORARY TABLE.
**
** See also: Clear
*/
case OP_Destroy: {
  sqliteBtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1);
  break;
}

/* Opcode: Clear P1 P2 *
**
** Delete all contents of the database table or index whose root page
** in the database file is given by P1.  But, unlike Destroy, do not
................................................................................
** The table being clear is in the main database file if P2==0.  If
** P2==1 then the table to be clear is in the auxiliary database file
** that is used to store tables create using CREATE TEMPORARY TABLE.
**
** See also: Destroy
*/
case OP_Clear: {
  sqliteBtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1);
  break;
}

/* Opcode: CreateTable * P2 P3
**
** Allocate a new table in the main database file if P2==0 or in the
** auxiliary database file if P2==1.  Push the page number







|







 







>
>
|







 







|







 







|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
....
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
....
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
....
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.223 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** The makefile scans this source file and creates the following
** array of string constants which are the names of all VDBE opcodes.
................................................................................
** transaction is underway.  Starting a transaction also creates a
** rollback journal.  A transaction must be started before any changes
** can be made to the database.
*/
case OP_Transaction: {
  int busy = 1;
  int i = pOp->p1;
  assert( i>=0 && i<db->nDb );
  if( db->aDb[i].inTrans ) break;
  while( db->aDb[i].pBt!=0 && busy ){
    rc = sqliteBtreeBeginTrans(db->aDb[i].pBt);
    switch( rc ){
      case SQLITE_BUSY: {
        if( db->xBusyCallback==0 ){
          p->pc = pc;
          p->undoTransOnError = 1;
          p->rc = SQLITE_BUSY;
................................................................................
** The table being destroyed is in the main database file if P2==0.  If
** P2==1 then the table to be clear is in the auxiliary database file
** that is used to store tables create using CREATE TEMPORARY TABLE.
**
** See also: Clear
*/
case OP_Destroy: {
  rc = sqliteBtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1);
  break;
}

/* Opcode: Clear P1 P2 *
**
** Delete all contents of the database table or index whose root page
** in the database file is given by P1.  But, unlike Destroy, do not
................................................................................
** The table being clear is in the main database file if P2==0.  If
** P2==1 then the table to be clear is in the auxiliary database file
** that is used to store tables create using CREATE TEMPORARY TABLE.
**
** See also: Destroy
*/
case OP_Clear: {
  rc = sqliteBtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1);
  break;
}

/* Opcode: CreateTable * P2 P3
**
** Allocate a new table in the main database file if P2==0 or in the
** auxiliary database file if P2==1.  Push the page number

Changes to src/where.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This module contains C code that generates VDBE code used to process
** the WHERE clause of SQL statements.
**
** $Id: where.c,v 1.78 2003/05/02 14:32:14 drh Exp $
*/
#include "sqliteInt.h"

/*
** The query generator uses an array of instances of this structure to
** help it analyze the subexpressions of the WHERE clause.  Each WHERE
** clause subexpression is separated from the others by an AND operator.
................................................................................
    Table *pTab;

    pTab = pTabList->a[i].pTab;
    if( pTab->isTransient || pTab->pSelect ) continue;
    sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
    sqliteVdbeAddOp(v, OP_OpenRead, pTabList->a[i].iCursor, pTab->tnum);
    sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
    if( i==0 && !pParse->schemaVerified &&
          (pParse->db->flags & SQLITE_InTrans)==0 ){
      sqliteCodeVerifySchema(pParse);
    }
    if( pWInfo->a[i].pIdx!=0 ){
      sqliteVdbeAddOp(v, OP_Integer, pWInfo->a[i].pIdx->iDb, 0);
      sqliteVdbeAddOp(v, OP_OpenRead,
                      pWInfo->a[i].iCur, pWInfo->a[i].pIdx->tnum);
      sqliteVdbeChangeP3(v, -1, pWInfo->a[i].pIdx->zName, P3_STATIC);
    }
  }







|







 







<
<
|
<







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
676
677
678
679
680
681
682


683

684
685
686
687
688
689
690
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This module contains C code that generates VDBE code used to process
** the WHERE clause of SQL statements.
**
** $Id: where.c,v 1.79 2003/05/17 17:35:13 drh Exp $
*/
#include "sqliteInt.h"

/*
** The query generator uses an array of instances of this structure to
** help it analyze the subexpressions of the WHERE clause.  Each WHERE
** clause subexpression is separated from the others by an AND operator.
................................................................................
    Table *pTab;

    pTab = pTabList->a[i].pTab;
    if( pTab->isTransient || pTab->pSelect ) continue;
    sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
    sqliteVdbeAddOp(v, OP_OpenRead, pTabList->a[i].iCursor, pTab->tnum);
    sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);


    sqliteCodeVerifySchema(pParse, pTab->iDb);

    if( pWInfo->a[i].pIdx!=0 ){
      sqliteVdbeAddOp(v, OP_Integer, pWInfo->a[i].pIdx->iDb, 0);
      sqliteVdbeAddOp(v, OP_OpenRead,
                      pWInfo->a[i].iCur, pWInfo->a[i].pIdx->tnum);
      sqliteVdbeChangeP3(v, -1, pWInfo->a[i].pIdx->zName, P3_STATIC);
    }
  }

Changes to test/attach.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
233
234
235
236
237
238
239






















































240
241
242
243
244
245
246
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: attach.test,v 1.3 2003/04/17 22:57:55 drh Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

for {set i 2} {$i<=15} {incr i} {
  file delete -force test$i.db
................................................................................
  }
} {1 11 x x 2 12 y y 11 21 x x 12 22 y y}
do_test attach-2.6 {
  execsql {
    SELECT * FROM main.tx;
  }
} {}























































for {set i 2} {$i<=15} {incr i} {
  catch {db$i close}
}


finish_test







|







 







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







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
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
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: attach.test,v 1.4 2003/05/17 17:35:13 drh Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

for {set i 2} {$i<=15} {incr i} {
  file delete -force test$i.db
................................................................................
  }
} {1 11 x x 2 12 y y 11 21 x x 12 22 y y}
do_test attach-2.6 {
  execsql {
    SELECT * FROM main.tx;
  }
} {}
do_test attach-2.7 {
  execsql {
    SELECT type, name, tbl_name FROM db2.sqlite_master;
  }
} {table t2 t2 table tx tx trigger r1 t2}
do_test attach-2.8 {
  execsql {
    PRAGMA database_list
  }
} {0 main 1 temp 2 db2}
do_test attach-2.9 {
  execsql {
    CREATE INDEX i2 ON t2(x);
    SELECT * FROM t2 WHERE x>5;
  } db2
} {21 x 22 y}
do_test attach-2.10 {
  execsql {
    SELECT type, name, tbl_name FROM sqlite_master;
  } db2
} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}
do_test attach-2.11 {
  catchsql { pragma vdbe_trace=on;
    SELECT * FROM t2 WHERE x>5;
  }
} {1 {database schema has changed}}
do_test attach-2.12 {
  execsql {
    PRAGMA database_list
  }
} {0 main 1 temp 2 db2}
do_test attach-2.13 {
  catchsql {
    SELECT * FROM t2 WHERE x>5;
  }
} {0 {21 x 22 y}}
do_test attach-2.14 {
  execsql {
    SELECT type, name, tbl_name FROM sqlite_master;
  }
} {table t1 t1 table tx tx}
do_test attach-2.15 {
  execsql {
    SELECT type, name, tbl_name FROM db2.sqlite_master;
  }
} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}
do_test attach-2.16 {
  db close
  sqlite db test.db
  execsql {
    ATTACH 'test2.db' AS db2;
    SELECT type, name, tbl_name FROM db2.sqlite_master;
  }
} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}

for {set i 2} {$i<=15} {incr i} {
  catch {db$i close}
}


finish_test

Changes to test/temptable.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177















178
179
180
181
182
183
184
185
186
...
213
214
215
216
217
218
219
220
221
222
223





224





225
226
227
228
229
230
231
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file implements tests for temporary tables and indices.
#
# $Id: temptable.test,v 1.9 2003/03/30 00:19:50 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Create an alternative connection to the database
#
do_test temptable-1.0 {
................................................................................
    SELECT * FROM t2;
  }
} {9 8 7}
do_test temptable-4.3 {
  catchsql {
    SELECT * FROM t2;
  } db2
} {1 {database schema has changed}}
do_test temptable-4.4.1 {
  catchsql {
    SELECT * FROM temp.t2;
  } db2
} {0 {10 20}}
do_test temptable-4.4.2 {
  catchsql {
    SELECT * FROM main.t2;
  } db2















} {0 {9 8 7}}
do_test temptable-4.4.3 {
  # TEMP takes precedence over MAIN
  catchsql {
    SELECT * FROM t2;
  } db2
} {0 {10 20}}
do_test temptable-4.5 {
  catchsql {
................................................................................
do_test temptable-4.9 {
  execsql {
    CREATE TABLE t2(x unique, y);
    INSERT INTO t2 VALUES(3,4);
    SELECT * FROM t2;
  }
} {3 4}
do_test temptable-4.10 {
  catchsql {
    SELECT * FROM t2;
  } db2





} {1 {database schema has changed}}





do_test temptable-4.11 {
  execsql {
    SELECT * FROM t2;
  } db2
} {1 2}
do_test temptable-4.12 {
  execsql {







|







 







|









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

|







 







|



>
>
>
>
>

>
>
>
>
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
...
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
254
255
256
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file implements tests for temporary tables and indices.
#
# $Id: temptable.test,v 1.10 2003/05/17 17:35:13 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Create an alternative connection to the database
#
do_test temptable-1.0 {
................................................................................
    SELECT * FROM t2;
  }
} {9 8 7}
do_test temptable-4.3 {
  catchsql {
    SELECT * FROM t2;
  } db2
} {0 {10 20}}
do_test temptable-4.4.1 {
  catchsql {
    SELECT * FROM temp.t2;
  } db2
} {0 {10 20}}
do_test temptable-4.4.2 {
  catchsql {
    SELECT * FROM main.t2;
  } db2
} {1 {no such table: main.t2}}
do_test temptable-4.4.3 {
  catchsql {
    SELECT name FROM main.sqlite_master WHERE type='table';
  } db2
} {1 {database schema has changed}}
do_test temptable-4.4.4 {
  catchsql {
    SELECT name FROM main.sqlite_master WHERE type='table';
  } db2
} {0 {t1 t2}}
do_test temptable-4.4.5 {
  catchsql {
    SELECT * FROM main.t2;
  } db2
} {0 {9 8 7}}
do_test temptable-4.4.6 {
  # TEMP takes precedence over MAIN
  catchsql {
    SELECT * FROM t2;
  } db2
} {0 {10 20}}
do_test temptable-4.5 {
  catchsql {
................................................................................
do_test temptable-4.9 {
  execsql {
    CREATE TABLE t2(x unique, y);
    INSERT INTO t2 VALUES(3,4);
    SELECT * FROM t2;
  }
} {3 4}
do_test temptable-4.10.1 {
  catchsql {
    SELECT * FROM t2;
  } db2
} {0 {1 2}}
do_test temptable-4.10.2 {
  catchsql {
    SELECT name FROM sqlite_master WHERE type='table'
  } db2
} {1 {database schema has changed}}
do_test temptable-4.10.3 {
  catchsql {
    SELECT name FROM sqlite_master WHERE type='table'
  } db2
} {0 {t1 t2}}
do_test temptable-4.11 {
  execsql {
    SELECT * FROM t2;
  } db2
} {1 2}
do_test temptable-4.12 {
  execsql {

Changes to test/trigger2.test.

48
49
50
51
52
53
54
55
56
57
58

59
60



61
62
63
64
65
66
67
..
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179






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
...
258
259
260
261
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
...
357
358
359
360
361
362
363





364
365
366
367
368
369
370
...
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
...
685
686
687
688
689
690
691



692
693
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# 1.
set ii 0
foreach tbl_defn [ list \
	{CREATE TABLE tbl (a, b);} \
	{CREATE TEMP TABLE tbl (a, b);} \
	{CREATE TABLE tbl (a INTEGER PRIMARY KEY, b);} \

        {CREATE TABLE tbl (a, b PRIMARY KEY);} \
	{CREATE TABLE tbl (a, b); CREATE INDEX tbl_idx ON tbl(b);} ] {



  incr ii
  catchsql { DROP INDEX tbl_idx; }
  catchsql {
    DROP TABLE rlog;
    DROP TABLE clog;
    DROP TABLE tbl;
    DROP TABLE other_tbl;
................................................................................
      INSERT INTO clog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM clog), 
	  old.a, old.b, 
	  (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
	  new.a, new.b);
    END;
  }

  do_test trigger2-1.1.$ii {
    execsql {
      UPDATE tbl SET a = a * 10, b = b * 10;
      SELECT * FROM rlog ORDER BY idx;
      SELECT * FROM clog ORDER BY idx;
    }
  } [list 1 1 2  4  6 10 20 \
          2 1 2 13 24 10 20 \
................................................................................
      BEGIN
      INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
	  old.a, old.b, 
	  (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
	  0, 0);
    END;
  }
  do_test trigger2-1.2.$ii {
    execsql {
      DELETE FROM tbl;
      SELECT * FROM rlog;
    }
  } [list 1 100 100 400 300 0 0 \
          2 100 100 300 200 0 0 \
          3 300 200 300 200 0 0 \
................................................................................
      BEGIN
      INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
	  0, 0,
	  (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
	  new.a, new.b);
    END;
  }
  do_test trigger2-1.3.$ii {
    execsql {

      CREATE TABLE other_tbl(a, b);
      INSERT INTO other_tbl VALUES(1, 2);
      INSERT INTO other_tbl VALUES(3, 4);
      -- INSERT INTO tbl SELECT * FROM other_tbl;
      INSERT INTO tbl VALUES(5, 6);
      DROP TABLE other_tbl;

      SELECT * FROM rlog;
    }
  } [list 1 0 0 0 0 5 6 \
          2 0 0 5 6 5 6 ]






}
catchsql {
  DROP TABLE rlog;
  DROP TABLE clog;
  DROP TABLE tbl;
  DROP TABLE other_tbl;
}

# 2.
set ii 0
foreach tr_program [ list \
   {UPDATE tbl SET b = old.b;} \
  {INSERT INTO log VALUES(new.c, 2, 3);} \
  {DELETE FROM log WHERE a = 1;} \
  {INSERT INTO tbl VALUES(500, new.b * 10, 700); 
    UPDATE tbl SET c = old.c; 
    DELETE FROM log;} \
  {INSERT INTO log select * from tbl;} 
   ] \
{
  foreach test_varset [ list \
    {
      set statement {UPDATE tbl SET c = 10 WHERE a = 1;} 
      set prep      {INSERT INTO tbl VALUES(1, 2, 3);}
      set newC 10
      set newB 2
      set newA 1
................................................................................
    regsub -all old\.b $tr_program_cooked $oldB tr_program_cooked 
    regsub -all old\.c $tr_program_cooked $oldC tr_program_cooked 

    catchsql {
      DROP TABLE tbl;
      DROP TABLE log;
    }

    execsql {
      CREATE TABLE tbl(a PRIMARY KEY, b, c);
      CREATE TABLE log(a, b, c);
    }

    set query {SELECT * FROM tbl; SELECT * FROM log;}
    set prep "$prep; INSERT INTO log VALUES(1, 2, 3); INSERT INTO log VALUES(10, 20, 30);"


# Check execution of BEFORE programs:

    set before_data [ execsql "$prep $tr_program_cooked $statement $query" ]

    execsql "DELETE FROM tbl; DELETE FROM log; $prep";
    execsql "CREATE TRIGGER the_trigger BEFORE [string range $statement 0 6] ON tbl BEGIN $tr_program_fixed END;"


    do_test trigger2-2-$ii-before "execsql {$statement $query}" $before_data

    execsql "DROP TRIGGER the_trigger;"
    execsql "DELETE FROM tbl; DELETE FROM log;"

# Check execution of AFTER programs
    set after_data [ execsql "$prep $statement $tr_program_cooked $query" ]

    execsql "DELETE FROM tbl; DELETE FROM log; $prep";

    execsql "CREATE TRIGGER the_trigger AFTER [string range $statement 0 6] ON tbl BEGIN $tr_program_fixed END;"


    do_test trigger2-2-$ii-after "execsql {$statement $query}" $after_data
    execsql "DROP TRIGGER the_trigger;"







  }
}
catchsql {
  DROP TABLE tbl;
  DROP TABLE log;
}

................................................................................
    UPDATE log SET a = 0;
  }
} {1 0 1}
execsql {
  DROP TABLE tbl;
  DROP TABLE log;
}






# Simple cascaded trigger
execsql {
  CREATE TABLE tblA(a, b);
  CREATE TABLE tblB(a, b);
  CREATE TABLE tblC(a, b);

................................................................................
	0, 0, 0, 0, new.a, new.b, new.c, new.d);
  END;
   CREATE TRIGGER after_insert INSTEAD OF INSERT ON abcd BEGIN
    INSERT INTO tlog VALUES(NULL, 
	0, 0, 0, 0, new.a, new.b, new.c, new.d);
   END;
  }
} {}

#explain {delete from abcd where a=1;}
do_test trigger2-7.2 {
  execsql {
    UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
    DELETE FROM abcd WHERE a = 1;
    INSERT INTO abcd VALUES(10, 20, 30, 40);
    SELECT * FROM tlog;
  }
................................................................................
    END;
    DELETE FROM v1log;
    UPDATE v1 SET x=x+100, y=y+200, z=z+300;
    SELECT * FROM v1log;
  }
} {3 103 5 205 4 304 9 109 11 211 10 310}





finish_test







|
|
|
|
>
|
|
>
>
>







 







|







 







|







 







|













>
>
>
>
>
>










|
|
|
|


|

|
<







 







>






|
>






|
>

|








<
|
>

|

>
>
>
>
>
>
>







 







>
>
>
>
>







 







|

<







 







>
>
>


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
...
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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
...
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
...
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
...
605
606
607
608
609
610
611
612
613

614
615
616
617
618
619
620
...
708
709
710
711
712
713
714
715
716
717
718
719
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# 1.
set ii 0
foreach tbl_defn {
	{CREATE TEMP TABLE tbl (a, b);} 
	{CREATE TABLE tbl (a, b);} 
	{CREATE TABLE tbl (a INTEGER PRIMARY KEY, b);} 
	{CREATE TEMPORARY TABLE tbl (a INTEGER PRIMARY KEY, b);} 
        {CREATE TABLE tbl (a, b PRIMARY KEY);} 
	{CREATE TABLE tbl (a, b); CREATE INDEX tbl_idx ON tbl(b);} 
	{CREATE TEMP TABLE tbl (a, b); CREATE INDEX tbl_idx ON tbl(b);} 
	{CREATE TABLE tbl (a, b); CREATE TEMP INDEX tbl_idx ON tbl(b);} 
} {
  incr ii
  catchsql { DROP INDEX tbl_idx; }
  catchsql {
    DROP TABLE rlog;
    DROP TABLE clog;
    DROP TABLE tbl;
    DROP TABLE other_tbl;
................................................................................
      INSERT INTO clog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM clog), 
	  old.a, old.b, 
	  (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
	  new.a, new.b);
    END;
  }

  do_test trigger2-1.$ii.1 {
    execsql { 
      UPDATE tbl SET a = a * 10, b = b * 10;
      SELECT * FROM rlog ORDER BY idx;
      SELECT * FROM clog ORDER BY idx;
    }
  } [list 1 1 2  4  6 10 20 \
          2 1 2 13 24 10 20 \
................................................................................
      BEGIN
      INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
	  old.a, old.b, 
	  (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
	  0, 0);
    END;
  }
  do_test trigger2-1.$ii.2 {
    execsql {
      DELETE FROM tbl;
      SELECT * FROM rlog;
    }
  } [list 1 100 100 400 300 0 0 \
          2 100 100 300 200 0 0 \
          3 300 200 300 200 0 0 \
................................................................................
      BEGIN
      INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
	  0, 0,
	  (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
	  new.a, new.b);
    END;
  }
  do_test trigger2-1.$ii.3 {
    execsql {

      CREATE TABLE other_tbl(a, b);
      INSERT INTO other_tbl VALUES(1, 2);
      INSERT INTO other_tbl VALUES(3, 4);
      -- INSERT INTO tbl SELECT * FROM other_tbl;
      INSERT INTO tbl VALUES(5, 6);
      DROP TABLE other_tbl;

      SELECT * FROM rlog;
    }
  } [list 1 0 0 0 0 5 6 \
          2 0 0 5 6 5 6 ]

  do_test trigger2-1.$ii.4 {
    execsql {
      PRAGMA integrity_check;
    }
  } {ok ok}
}
catchsql {
  DROP TABLE rlog;
  DROP TABLE clog;
  DROP TABLE tbl;
  DROP TABLE other_tbl;
}

# 2.
set ii 0
foreach tr_program {
  {UPDATE tbl SET b = old.b;}
  {INSERT INTO log VALUES(new.c, 2, 3);}
  {DELETE FROM log WHERE a = 1;}
  {INSERT INTO tbl VALUES(500, new.b * 10, 700); 
    UPDATE tbl SET c = old.c; 
    DELETE FROM log;}
  {INSERT INTO log select * from tbl;} 
} {

  foreach test_varset [ list \
    {
      set statement {UPDATE tbl SET c = 10 WHERE a = 1;} 
      set prep      {INSERT INTO tbl VALUES(1, 2, 3);}
      set newC 10
      set newB 2
      set newA 1
................................................................................
    regsub -all old\.b $tr_program_cooked $oldB tr_program_cooked 
    regsub -all old\.c $tr_program_cooked $oldC tr_program_cooked 

    catchsql {
      DROP TABLE tbl;
      DROP TABLE log;
    }

    execsql {
      CREATE TABLE tbl(a PRIMARY KEY, b, c);
      CREATE TABLE log(a, b, c);
    }

    set query {SELECT * FROM tbl; SELECT * FROM log;}
    set prep "$prep; INSERT INTO log VALUES(1, 2, 3);\
             INSERT INTO log VALUES(10, 20, 30);"

# Check execution of BEFORE programs:

    set before_data [ execsql "$prep $tr_program_cooked $statement $query" ]

    execsql "DELETE FROM tbl; DELETE FROM log; $prep";
    execsql "CREATE TRIGGER the_trigger BEFORE [string range $statement 0 6]\
             ON tbl BEGIN $tr_program_fixed END;"

    do_test trigger2-2.$ii-before "execsql {$statement $query}" $before_data

    execsql "DROP TRIGGER the_trigger;"
    execsql "DELETE FROM tbl; DELETE FROM log;"

# Check execution of AFTER programs
    set after_data [ execsql "$prep $statement $tr_program_cooked $query" ]

    execsql "DELETE FROM tbl; DELETE FROM log; $prep";

    execsql "CREATE TRIGGER the_trigger AFTER [string range $statement 0 6]\
             ON tbl BEGIN $tr_program_fixed END;"

    do_test trigger2-2.$ii-after "execsql {$statement $query}" $after_data
    execsql "DROP TRIGGER the_trigger;"

    do_test trigger2-2.$ii-integrity {
      execsql {
        PRAGMA integrity_check;
      }
    } {ok ok}

  }
}
catchsql {
  DROP TABLE tbl;
  DROP TABLE log;
}

................................................................................
    UPDATE log SET a = 0;
  }
} {1 0 1}
execsql {
  DROP TABLE tbl;
  DROP TABLE log;
}
do_test trigger2-3.3 {
  execsql {
    PRAGMA integrity_check;
  }
} {ok ok}

# Simple cascaded trigger
execsql {
  CREATE TABLE tblA(a, b);
  CREATE TABLE tblB(a, b);
  CREATE TABLE tblC(a, b);

................................................................................
	0, 0, 0, 0, new.a, new.b, new.c, new.d);
  END;
   CREATE TRIGGER after_insert INSTEAD OF INSERT ON abcd BEGIN
    INSERT INTO tlog VALUES(NULL, 
	0, 0, 0, 0, new.a, new.b, new.c, new.d);
   END;
  }
} {};


do_test trigger2-7.2 {
  execsql {
    UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
    DELETE FROM abcd WHERE a = 1;
    INSERT INTO abcd VALUES(10, 20, 30, 40);
    SELECT * FROM tlog;
  }
................................................................................
    END;
    DELETE FROM v1log;
    UPDATE v1 SET x=x+100, y=y+200, z=z+300;
    SELECT * FROM v1log;
  }
} {3 103 5 205 4 304 9 109 11 211 10 310}

do_test trigger2-9.9 {
  execsql {PRAGMA integrity_check}
} {ok ok}

finish_test