Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | The new ON CONFLICT logic is in and passes the legacy tests. But the new capabilities have not been tested and are likely broken. (CVS 356) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
ac8a4189e2a0c41161ee359db25de944 |
User & Date: | drh 2002-01-29 23:07:02.000 |
Context
2002-01-30
| ||
00:54 | More bug fixes in the ON CONFLICT enhancement. (CVS 357) (check-in: 8229b5f6a3 user: drh tags: trunk) | |
2002-01-29
| ||
23:07 | The new ON CONFLICT logic is in and passes the legacy tests. But the new capabilities have not been tested and are likely broken. (CVS 356) (check-in: ac8a4189e2 user: drh tags: trunk) | |
18:41 | Beginning to insert the infrastructure for ON CONFLICT clauses. (CVS 355) (check-in: e00a9ff8f9 user: drh tags: trunk) | |
Changes
Changes to src/build.c.
︙ | ︙ | |||
21 22 23 24 25 26 27 | ** COPY ** VACUUM ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK ** PRAGMA ** | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ** COPY ** VACUUM ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK ** PRAGMA ** ** $Id: build.c,v 1.68 2002/01/29 23:07:02 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> /* ** This routine is called after a single SQL statement has been ** parsed and we want to execute the VDBE code to implement |
︙ | ︙ | |||
862 863 864 865 866 867 868 869 870 871 872 873 874 875 | char *zName = 0; int i, j; Token nullId; /* Fake token for an empty ID list */ sqlite *db = pParse->db; int hideName = 0; /* Do not put table name in the hash table */ if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index; /* ** Find the table that is to be indexed. Return early if not found. */ if( pTable!=0 ){ assert( pName!=0 ); pTab = sqliteTableFromToken(pParse, pTable); | > | 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 | char *zName = 0; int i, j; Token nullId; /* Fake token for an empty ID list */ sqlite *db = pParse->db; int hideName = 0; /* Do not put table name in the hash table */ if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index; if( onError==OE_Default ) onError = OE_Abort; /* ** Find the table that is to be indexed. Return early if not found. */ if( pTable!=0 ){ assert( pName!=0 ); pTab = sqliteTableFromToken(pParse, pTable); |
︙ | ︙ |
Changes to src/delete.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** 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. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** 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.25 2002/01/29 23:07:02 drh Exp $ */ #include "sqliteInt.h" /* ** Process a DELETE FROM statement. */ void sqliteDeleteFrom( |
︙ | ︙ | |||
197 198 199 200 201 202 203 204 205 206 | ** entries that point to that record. */ void sqliteGenerateRowDelete( Vdbe *v, /* Generate code into this VDBE */ Table *pTab, /* Table containing the row to be deleted */ int base /* Cursor number for the table */ ){ int i; Index *pIdx; | > > > > > > > > > > > > > > > > > > > > > > > > > > > < < | | > | | | | | | | | | | | | | < < | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | ** entries that point to that record. */ void sqliteGenerateRowDelete( Vdbe *v, /* Generate code into this VDBE */ Table *pTab, /* Table containing the row to be deleted */ int base /* Cursor number for the table */ ){ sqliteVdbeAddOp(v, OP_MoveTo, base, 0); sqliteGenerateRowIndexDelete(v, pTab, base, 0); sqliteVdbeAddOp(v, OP_Delete, base, 0); } /* ** This routine generates VDBE code that causes the deletion of all ** index entries associated with a single row of a single table. ** ** The VDBE must be in a particular state when this routine is called. ** These are the requirements: ** ** 1. A read/write cursor pointing to pTab, the table containing the row ** to be deleted, must be opened as cursor number "base". ** ** 2. Read/write cursors for all indices of pTab must be open as ** cursor number base+i for the i-th index. ** ** 3. The "base" cursor must be pointing to the row that is to be ** deleted. */ void sqliteGenerateRowIndexDelete( Vdbe *v, /* Generate code into this VDBE */ Table *pTab, /* Table containing the row to be deleted */ int base, /* Cursor number for the table */ char *aIdxUsed /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */ ){ int i; Index *pIdx; for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ int j; if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue; sqliteVdbeAddOp(v, OP_Recno, base, 0); for(j=0; j<pIdx->nColumn; j++){ int idx = pIdx->aiColumn[j]; if( idx==pTab->iPKey ){ sqliteVdbeAddOp(v, OP_Dup, j, 0); }else{ sqliteVdbeAddOp(v, OP_Column, base, idx); } } sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0); sqliteVdbeAddOp(v, OP_IdxDelete, base+i, 0); } } |
Changes to src/insert.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** 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. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** 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.35 2002/01/29 23:07:02 drh Exp $ */ #include "sqliteInt.h" /* ** This routine is call to handle SQL of the following forms: ** ** insert into TABLE (IDLIST) values(EXPRLIST) |
︙ | ︙ | |||
47 48 49 50 51 52 53 54 55 56 57 58 59 60 | int srcTab; /* Date comes from this temporary cursor if >=0 */ int nColumn; /* Number of columns in the data */ int base; /* First available cursor */ int iCont, iBreak; /* Beginning and end of the loop over srcTab */ sqlite *db; /* The main database structure */ int openOp; /* Opcode used to open cursors */ int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup; db = pParse->db; /* Locate the table into which we will be inserting new information. */ zTab = sqliteTableNameFromToken(pTableName); | > | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | int srcTab; /* Date comes from this temporary cursor if >=0 */ int nColumn; /* Number of columns in the data */ int base; /* First available cursor */ int iCont, iBreak; /* Beginning and end of the loop over srcTab */ sqlite *db; /* The main database structure */ int openOp; /* Opcode used to open cursors */ int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ int endOfLoop; /* Label for the end of the insertion loop */ if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup; db = pParse->db; /* Locate the table into which we will be inserting new information. */ zTab = sqliteTableNameFromToken(pTableName); |
︙ | ︙ | |||
197 198 199 200 201 202 203 | sqliteVdbeAddOp(v, OP_Rewind, srcTab, iBreak); iCont = sqliteVdbeCurrentAddr(v); } /* Push the record number for the new entry onto the stack. The ** record number is a randomly generate integer created by NewRecno ** except when the table has an INTEGER PRIMARY KEY column, in which | | > > < < < < < | < | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | sqliteVdbeAddOp(v, OP_Rewind, srcTab, iBreak); iCont = sqliteVdbeCurrentAddr(v); } /* Push the record number for the new entry onto the stack. The ** record number is a randomly generate integer created by NewRecno ** except when the table has an INTEGER PRIMARY KEY column, in which ** case the record number is the same as that column. May a copy ** because sqliteGenerateConstraintChecks() requires two copies of ** the record number. */ if( keyColumn>=0 ){ if( srcTab>=0 ){ sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn); }else{ sqliteExprCode(pParse, pList->a[keyColumn].pExpr); } sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0); }else{ sqliteVdbeAddOp(v, OP_NewRecno, base, 0); } sqliteVdbeAddOp(v, OP_Dup, 0, 0); /* Push onto the stack, data for all columns of the new entry, beginning ** with the first column. */ for(i=0; i<pTab->nCol; i++){ if( i==pTab->iPKey ){ /* The value of the INTEGER PRIMARY KEY column is always a NULL. |
︙ | ︙ | |||
246 247 248 249 250 251 252 | }else if( srcTab>=0 ){ sqliteVdbeAddOp(v, OP_Column, srcTab, i); }else{ sqliteExprCode(pParse, pList->a[j].pExpr); } } | < < < < | < | < < | < < < < < < < < < < < < < < < < < < < < < | < < < < < | > | 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 | }else if( srcTab>=0 ){ sqliteVdbeAddOp(v, OP_Column, srcTab, i); }else{ sqliteExprCode(pParse, pList->a[j].pExpr); } } /* Generate code to check constraints and generate index keys and ** do the insertion. */ endOfLoop = sqliteVdbeMakeLabel(v); sqliteGenerateConstraintChecks(pParse, pTab, base, 0,1,onError,endOfLoop,0); sqliteCompleteInsertion(pParse, pTab, base, 0, 1); /* If inserting from a SELECT, keep a count of the number of ** rows inserted. */ if( srcTab>=0 && (db->flags & SQLITE_CountRows)!=0 ){ sqliteVdbeAddOp(v, OP_AddImm, 1, 0); } /* The bottom of the loop, if the data source is a SELECT statement */ sqliteVdbeResolveLabel(v, endOfLoop); if( srcTab>=0 ){ sqliteVdbeAddOp(v, OP_Next, srcTab, iCont); sqliteVdbeResolveLabel(v, iBreak); sqliteVdbeAddOp(v, OP_Close, srcTab, 0); } sqliteVdbeAddOp(v, OP_Close, base, 0); for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ |
︙ | ︙ | |||
327 328 329 330 331 332 333 | insert_cleanup: if( pList ) sqliteExprListDelete(pList); if( pSelect ) sqliteSelectDelete(pSelect); sqliteIdListDelete(pColumn); } | < > > | > > > > > > > > > > > > > > > | | | | | | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | insert_cleanup: if( pList ) sqliteExprListDelete(pList); if( pSelect ) sqliteSelectDelete(pSelect); sqliteIdListDelete(pColumn); } /* ** Generate code to do a constraint check prior to an INSERT or an UPDATE. ** ** When this routine is called, the stack contains (from bottom to top) ** the following values: ** ** 1. The recno of the row to be updated before it is updated. ** ** 2. The recno of the row after the update. (This is usually the ** same as (1) but can be different if an UPDATE changes an ** INTEGER PRIMARY KEY column.) ** ** 3. The data in the first column of the entry after the update. ** ** i. Data from middle columns... ** ** N. The data in the last column of the entry after the update. ** ** The old recno shown as entry (1) above is omitted if the recnoChng ** parameter is 0. recnoChange is true if the record number is changing ** and false if not. ** ** The code generated by this routine pushes additional entries onto ** the stack which are the keys for new index entries for the new record. ** The order of index keys is the same as the order of the indices on ** the pTable->pIndex list. A key is only created for index i if ** aIdxUsed!=0 and aIdxUsed[i]!=0. ** ** This routine also generates code to check constraints. NOT NULL, ** CHECK, and UNIQUE constraints are all checked. If a constraint fails, ** then the appropriate action is performed. The default action is to ** execute OP_Halt to abort the transaction and cause sqlite_exec() to ** return SQLITE_CONSTRAINT. This is the so-called "ABORT" action. ** Other actions are REPLACE and IGNORE. The following table summarizes |
︙ | ︙ | |||
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 | ** Without the isUpdate flag, the "base" cursor might be moved. */ void sqliteGenerateConstraintChecks( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ int base, /* Index of a read/write cursor pointing at pTab */ char *aIdxUsed, /* Which indices are used. NULL means all are used */ int overrideError, /* Override onError to this if not OE_Default */ int ignoreDest, /* Jump to this label on an OE_Ignore resolution */ int isUpdate /* True for UPDATE, False for INSERT */ ){ int i; Vdbe *v; int nCol; int onError; int addr; int extra; | > > | | > > > > > > > | | | | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < | | > | > | < | | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | ** Without the isUpdate flag, the "base" cursor might be moved. */ void sqliteGenerateConstraintChecks( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ int base, /* Index of a read/write cursor pointing at pTab */ char *aIdxUsed, /* Which indices are used. NULL means all are used */ int recnoChng, /* True if the record number will change */ int overrideError, /* Override onError to this if not OE_Default */ int ignoreDest, /* Jump to this label on an OE_Ignore resolution */ int isUpdate /* True for UPDATE, False for INSERT */ ){ int i; Vdbe *v; int nCol; int onError; int addr; int extra; int iCur; Index *pIdx; int seenReplace = 0; int jumpInst; int contAddr; v = sqliteGetVdbe(pParse); assert( v!=0 ); nCol = pTab->nCol; recnoChng = (recnoChng!=0); /* Must be either 1 or 0 */ /* Test all NOT NULL constraints. */ for(i=0; i<nCol; i++){ if( i==pTab->iPKey ){ /* Fix me: Make sure the INTEGER PRIMARY KEY is not NULL. */ continue; } onError = pTab->aCol[i].notNull; if( onError==OE_None ) continue; if( overrideError!=OE_Default ){ onError = overrideError; } if( onError==OE_Replace && pTab->aCol[i].zDflt==0 ){ onError = OE_Abort; } addr = sqliteVdbeAddOp(v, OP_Dup, nCol-i, 1); sqliteVdbeAddOp(v, OP_NotNull, 0, addr+1+(onError!=OE_Abort)); switch( onError ){ case OE_Abort: { sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, 0); break; } case OE_Ignore: { sqliteVdbeAddOp(v, OP_Pop, nCol+1+recnoChng, 0); sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest); break; } case OE_Replace: { sqliteVdbeAddOp(v, OP_String, 0, 0); sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC); sqliteVdbeAddOp(v, OP_Push, nCol-i, 0); break; } default: assert(0); } } /* Test all CHECK constraints */ /* Test all UNIQUE constraints. Add index records as we go. */ if( recnoChng && pTab->iPKey>=0 && pTab->keyConf!=OE_Replace && overrideError!=OE_Replace ){ sqliteVdbeAddOp(v, OP_Dup, nCol, 1); jumpInst = sqliteVdbeAddOp(v, OP_NotExists, base, 0); onError = pTab->keyConf; if( overrideError!=OE_Default ){ onError = overrideError; } switch( onError ){ case OE_Abort: { sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, 0); break; } case OE_Ignore: { sqliteVdbeAddOp(v, OP_Pop, nCol+2, 0); sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest); break; } default: assert(0); } contAddr = sqliteVdbeCurrentAddr(v); sqliteVdbeChangeP2(v, jumpInst, contAddr); if( isUpdate ){ sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); } } extra = 0; for(extra=(-1), iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ if( aIdxUsed && aIdxUsed[iCur]==0 ) continue; extra++; sqliteVdbeAddOp(v, OP_Dup, nCol+extra, 1); for(i=0; i<pIdx->nColumn; i++){ int idx = pIdx->aiColumn[i]; if( idx==pTab->iPKey ){ sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol+1, 1); }else{ sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 1); } } sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0); onError = pIdx->onError; if( onError==OE_None ) continue; if( overrideError!=OE_Default ){ onError = overrideError; } sqliteVdbeAddOp(v, OP_Dup, extra+nCol+2, 1); jumpInst = sqliteVdbeAddOp(v, OP_IsUnique, base+iCur+1, 0); switch( onError ){ case OE_Abort: { sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, 0); break; } case OE_Ignore: { assert( seenReplace==0 ); sqliteVdbeAddOp(v, OP_Pop, nCol+extra+2+recnoChng, 0); sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest); break; } case OE_Replace: { sqliteVdbeAddOp(v, OP_MoveTo, base, 0); sqliteGenerateRowDelete(v, pTab, base); if( isUpdate ){ sqliteVdbeAddOp(v, OP_Dup, nCol+extra+recnoChng, 1); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); } seenReplace = 1; break; } default: assert(0); } contAddr = sqliteVdbeCurrentAddr(v); sqliteVdbeChangeP2(v, jumpInst, contAddr); } } /* ** This routine generates code to finish the INSERT or UPDATE operation ** that was started by a prior call to sqliteGenerateConstraintChecks. ** The stack must contain keys for all active indices followed by data ** and the recno for the new entry. This routine creates the new ** entries in all indices and in the main table. ** ** The arguments to this routine should be the same as the first five ** arguments to sqliteGenerateConstraintChecks. */ void sqliteCompleteInsertion( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ int base, /* Index of a read/write cursor pointing at pTab */ char *aIdxUsed, /* Which indices are used. NULL means all are used */ int recnoChng /* True if the record number changed */ ){ int i; Vdbe *v; int nIdx; Index *pIdx; v = sqliteGetVdbe(pParse); assert( v!=0 ); for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} for(i=nIdx-1; i>=0; i--){ if( aIdxUsed && aIdxUsed[i]==0 ) continue; sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, 0); } sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); sqliteVdbeAddOp(v, OP_PutIntKey, base, 0); if( recnoChng ){ sqliteVdbeAddOp(v, OP_Pop, 1, 0); } } |
Changes to src/sqliteInt.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** 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. ** ************************************************************************* ** Internal interface definitions for SQLite. ** | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** 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. ** ************************************************************************* ** Internal interface definitions for SQLite. ** ** @(#) $Id: sqliteInt.h,v 1.81 2002/01/29 23:07:02 drh Exp $ */ #include "sqlite.h" #include "hash.h" #include "vdbe.h" #include "parse.h" #include "btree.h" #include <stdio.h> |
︙ | ︙ | |||
543 544 545 546 547 548 549 | int sqliteRandomByte(void); int sqliteRandomInteger(void); void sqliteBeginTransaction(Parse*); void sqliteCommitTransaction(Parse*); void sqliteRollbackTransaction(Parse*); char *sqlite_mprintf(const char *, ...); int sqliteExprIsConstant(Expr*); | > > > > | 543 544 545 546 547 548 549 550 551 552 553 | int sqliteRandomByte(void); int sqliteRandomInteger(void); void sqliteBeginTransaction(Parse*); void sqliteCommitTransaction(Parse*); void sqliteRollbackTransaction(Parse*); char *sqlite_mprintf(const char *, ...); int sqliteExprIsConstant(Expr*); void sqliteGenerateRowDelete(Vdbe*, Table*, int); void sqliteGenerateRowIndexDelete(Vdbe*, Table*, int, char*); void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int); void sqliteCompleteInsertion(Parse*, Table*, int, char*, int); |
Changes to src/update.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** 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. ** | | | > > > | 8 9 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 46 47 48 49 50 51 52 53 54 55 | ** 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.30 2002/01/29 23:07:02 drh Exp $ */ #include "sqliteInt.h" /* ** Process an UPDATE statement. */ void sqliteUpdate( Parse *pParse, /* The parser context */ Token *pTableName, /* The table in which we should change things */ ExprList *pChanges, /* Things to be changed */ Expr *pWhere, /* The WHERE clause. May be null */ int onError /* How to handle constraint errors */ ){ int i, j; /* Loop counters */ Table *pTab; /* The table to be updated */ IdList *pTabList = 0; /* List containing only pTab */ int addr; /* VDBE instruction address of the start of the loop */ WhereInfo *pWInfo; /* Information about the WHERE clause */ Vdbe *v; /* The virtual database engine */ Index *pIdx; /* For looping over indices */ int nIdx; /* Number of indices that need updating */ int nIdxTotal; /* Total number of indices */ int base; /* Index of first available table cursor */ sqlite *db; /* The database structure */ Index **apIdx = 0; /* An array of indices that need updating too */ char *aIdxUsed = 0; /* aIdxUsed[i] if the i-th index is used */ int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ int openOp; /* Opcode used to open tables */ int chngRecno; /* True if the record number is being changed */ Expr *pRecnoExpr; /* Expression defining the new record number */ int openAll; /* True if all indices need to be opened */ if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup; db = pParse->db; /* Locate the table which we want to update. This table has to be ** put in an IdList structure because some of the subroutines we ** will be calling are designed to work with multiple tables and expect |
︙ | ︙ | |||
119 120 121 122 123 124 125 | } /* Allocate memory for the array apIdx[] and fill it with pointers to every ** index that needs to be updated. Indices only need updating if their ** key includes one of the columns named in pChanges or if the record ** number of the original table entry is changing. */ | | | | > | | > > > > > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | } /* Allocate memory for the array apIdx[] and fill it with pointers to every ** index that needs to be updated. Indices only need updating if their ** key includes one of the columns named in pChanges or if the record ** number of the original table entry is changing. */ for(nIdx=nIdxTotal=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdxTotal++){ if( chngRecno ){ i = 0; }else { for(i=0; i<pIdx->nColumn; i++){ if( aXRef[pIdx->aiColumn[i]]>=0 ) break; } } if( i<pIdx->nColumn ) nIdx++; } if( nIdxTotal>0 ){ apIdx = sqliteMalloc( sizeof(Index*) * nIdx + nIdxTotal ); if( apIdx==0 ) goto update_cleanup; aIdxUsed = (char*)&apIdx[nIdx]; } for(nIdx=j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ if( chngRecno ){ i = 0; }else{ for(i=0; i<pIdx->nColumn; i++){ if( aXRef[pIdx->aiColumn[i]]>=0 ) break; } } if( i<pIdx->nColumn ){ apIdx[nIdx++] = pIdx; aIdxUsed[j] = 1; }else{ aIdxUsed[j] = 0; } } /* Begin generating code. */ v = sqliteGetVdbe(pParse); if( v==0 ) goto update_cleanup; if( (db->flags & SQLITE_InTrans)==0 ){ |
︙ | ︙ | |||
174 175 176 177 178 179 180 | /* Initialize the count of updated rows */ if( db->flags & SQLITE_CountRows ){ sqliteVdbeAddOp(v, OP_Integer, 0, 0); } /* Rewind the list of records that need to be updated and | | > > > > > > > > > > > > > > | > | > < | < < < < < < < < < < < < < < < < | | | > | > | | > | > > > > > > > > > | < < < < < < < < < < < < < < | < < < < | | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 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 | /* Initialize the count of updated rows */ if( db->flags & SQLITE_CountRows ){ sqliteVdbeAddOp(v, OP_Integer, 0, 0); } /* Rewind the list of records that need to be updated and ** open every index that needs updating. Note that if any ** index could potentially invoke a REPLACE conflict resolution ** action, then we need to open all indices because we might need ** to be deleting some records. */ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); base = pParse->nTab; openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; sqliteVdbeAddOp(v, openOp, base, pTab->tnum); if( onError==OE_Replace ){ openAll = 1; }else{ openAll = 0; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->onError==OE_Replace ){ openAll = 1; break; } } } for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ if( openAll || aIdxUsed[i] ){ sqliteVdbeAddOp(v, openOp, base+i+1, pIdx->tnum); } } /* Loop over every record that needs updating. We have to load ** the old data for each record to be updated because some columns ** might not change and we will need to copy the old value. ** Also, the old data is needed to delete the old index entires. ** So make the cursor point at the old record. */ addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0); sqliteVdbeAddOp(v, OP_Dup, 0, 0); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); /* If the record number will change, push the record number as it ** will be after the update. (The old record number is currently ** on top of the stack.) */ if( chngRecno ){ if( pTab->iPKey<0 || (j = aXRef[pTab->iPKey])<0 ){ sqliteVdbeAddOp(v, OP_Dup, 0, 0); }else{ sqliteExprCode(pParse, pChanges->a[j].pExpr); sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0); } } /* Compute new data for this record. */ for(i=0; i<pTab->nCol; i++){ if( i==pTab->iPKey ){ sqliteVdbeAddOp(v, OP_String, 0, 0); continue; } j = aXRef[i]; if( j<0 ){ sqliteVdbeAddOp(v, OP_Column, base, i); }else{ sqliteExprCode(pParse, pChanges->a[j].pExpr); } } /* Do constraint checks */ sqliteGenerateConstraintChecks(pParse, pTab, base, aIdxUsed, chngRecno, onError, addr,1); /* Delete the old indices for the current record. */ sqliteGenerateRowIndexDelete(v, pTab, base, aIdxUsed); /* If changing the record number, delete the old record. */ if( chngRecno ){ sqliteVdbeAddOp(v, OP_Delete, 0, 0); } /* Create the new index entries and the new record. */ sqliteCompleteInsertion(pParse, pTab, base, aIdxUsed, chngRecno); /* Increment the row counter */ if( db->flags & SQLITE_CountRows ){ sqliteVdbeAddOp(v, OP_AddImm, 1, 0); } /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. */ sqliteVdbeAddOp(v, OP_Goto, 0, addr); sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v)); sqliteVdbeAddOp(v, OP_ListReset, 0, 0); if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Commit, 0, 0); } /* ** Return the number of rows that were changed. |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 | ** type to the other occurs as necessary. ** ** Most of the code in this file is taken up by the sqliteVdbeExec() ** function which does the work of interpreting a VDBE program. ** But other routines are also provided to help in building up ** a program instruction by instruction. ** | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ** type to the other occurs as necessary. ** ** Most of the code in this file is taken up by the sqliteVdbeExec() ** function which does the work of interpreting a VDBE program. ** But other routines are also provided to help in building up ** a program instruction by instruction. ** ** $Id: vdbe.c,v 1.110 2002/01/29 23:07:02 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> /* ** The following global variable is incremented every time a cursor ** moves, either by the OP_MoveTo or the OP_Next opcode. The test |
︙ | ︙ | |||
1295 1296 1297 1298 1299 1300 1301 | ** Overwrite the value of the P1-th element down on the ** stack (P1==0 is the top of the stack) with the value ** of the top of the stack. The pop the top of the stack. */ case OP_Push: { int from = p->tos; int to = p->tos - pOp->p1; | | < < | 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 | ** Overwrite the value of the P1-th element down on the ** stack (P1==0 is the top of the stack) with the value ** of the top of the stack. The pop the top of the stack. */ case OP_Push: { int from = p->tos; int to = p->tos - pOp->p1; VERIFY( if( to<0 ) goto not_enough_stack; ) if( aStack[to].flags & STK_Dyn ){ sqliteFree(zStack[to]); } aStack[to] = aStack[from]; if( aStack[to].flags & (STK_Dyn|STK_Static) ){ zStack[to] = zStack[from]; |
︙ | ︙ | |||
2752 2753 2754 2755 2756 2757 2758 | POPSTACK; } break; } /* Opcode: IsUnique P1 P2 * ** | | | > | > | | < | > | < > | < > | > > > > | > > > > > > | > > > | | < | > > > > > > > > > > | < > | > > > > > > | | | 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 | POPSTACK; } break; } /* Opcode: IsUnique P1 P2 * ** ** The top of the stack is an integer record number. Call this ** record number R. The next on the stack is an index key created ** using MakeIdxKey. Call it K. This instruction pops R from the ** stack but it leaves K unchanged. ** ** P1 is an index. So all but the last four bytes of K are an ** index string. The last four bytes of K are a record number. ** ** This instruction asks if there is an entry in P1 where the ** index string matches K but the record number is different ** from R. If there is no such entry, then there is an immediate ** jump to P2. If any entry does exist where the index string ** matches K but the record number is not R, then the record ** number for that entry is pushed onto the stack and control ** falls through to the next instruction. ** ** See also: Distinct, NotFound, NotExists */ case OP_IsUnique: { int i = pOp->p1; int tos = p->tos; int nos = tos-1; BtCursor *pCrsr; int R; /* Pop the value R off the top of the stack */ VERIFY( if( nos<0 ) goto not_enough_stack; ) Integerify(p, tos); R = aStack[tos].i; POPSTACK; if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ int res, rc; int v; /* The record number on the P1 entry that matches K */ char *zKey; /* The value of K */ int nKey; /* Number of bytes in K */ /* Make sure K is a string and make zKey point to K */ if( Stringify(p, nos) ) goto no_mem; zKey = zStack[nos]; nKey = aStack[nos].n; assert( nKey >= 4 ); /* Search for an entry in P1 where all but the last four bytes match K. ** If there is no such entry, jump immediately to P2. */ rc = sqliteBtreeMoveto(pCrsr, zKey, nKey-4, &res); if( rc!=SQLITE_OK ) goto abort_due_to_error; if( res<0 ){ rc = sqliteBtreeNext(pCrsr, &res); if( res ){ pc = pOp->p2 - 1; break; } } rc = sqliteBtreeKeyCompare(pCrsr, zKey, nKey-4, 4, &res); if( rc!=SQLITE_OK ) goto abort_due_to_error; if( res>0 ){ pc = pOp->p2 - 1; break; } /* At this point, pCrsr is pointing to an entry in P1 where all but ** the last for bytes of the key match K. Check to see if the last ** four bytes of the key are different from R. If the last four ** bytes equal R then jump immediately to P2. */ sqliteBtreeKey(pCrsr, nKey - 4, 4, (char*)&v); v = keyToInt(v); if( v==R ){ pc = pOp->p2 - 1; break; } /* The last four bytes of the key are different from R. Convert the ** last four bytes of the key into an integer and push it onto the ** stack. (These bytes are the record number of an entry that ** violates a UNIQUE constraint.) */ p->tos++; VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) aStack[tos].i = v; aStack[tos].flags = STK_Int; } break; } /* Opcode: NotExists P1 P2 * ** |
︙ | ︙ | |||
2826 2827 2828 2829 2830 2831 2832 | ** is a string. ** ** See also: Distinct, Found, MoveTo, NotExists */ case OP_NotExists: { int i = pOp->p1; int tos = p->tos; | < | | | | 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 | ** is a string. ** ** See also: Distinct, Found, MoveTo, NotExists */ case OP_NotExists: { int i = pOp->p1; int tos = p->tos; BtCursor *pCrsr; VERIFY( if( tos<0 ) goto not_enough_stack; ) if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ int res, rx, iKey; assert( aStack[tos].flags & STK_Int ); iKey = intToKey(aStack[tos].i); rx = sqliteBtreeMoveto(pCrsr, (char*)&iKey, sizeof(int), &res); if( rx!=SQLITE_OK || res!=0 ){ pc = pOp->p2 - 1; } } POPSTACK; break; } |
︙ | ︙ | |||
2906 2907 2908 2909 2910 2911 2912 | VERIFY( NeedStack(p, p->tos+1); ) p->tos++; aStack[p->tos].i = v; aStack[p->tos].flags = STK_Int; break; } | | | | 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 | VERIFY( NeedStack(p, p->tos+1); ) p->tos++; aStack[p->tos].i = v; aStack[p->tos].flags = STK_Int; break; } /* Opcode: PutIntKey P1 P2 * ** ** Write an entry into the database file P1. A new entry is ** created if it doesn't already exist or the data for an existing ** entry is overwritten. The data is the value on the top of the ** stack. The key is the next value down on the stack. The key must ** be an integer. The stack is popped twice by this instruction. ** ** If P2==1 then overwriting is prohibited. If a prior entry with ** the same key exists, an SQLITE_CONSTRAINT exception is raised. */ /* Opcode: PutStrKey P1 P2 * ** ** Write an entry into the database file P1. A new entry is ** created if it doesn't already exist or the data for an existing ** entry is overwritten. The data is the value on the top of the ** stack. The key is the next value down on the stack. The key must ** be a string. The stack is popped twice by this instruction. ** |
︙ | ︙ |
Changes to test/intpkey.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for the special processing associated # with INTEGER PRIMARY KEY columns. # | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for the special processing associated # with INTEGER PRIMARY KEY columns. # # $Id: intpkey.test,v 1.7 2002/01/29 23:07:02 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Create a table with a primary key and a datatype other than # integer # |
︙ | ︙ | |||
175 176 177 178 179 180 181 182 183 184 185 186 187 188 | } } {-3 y z} do_test intpkey-2.1.4 { execsql { SELECT * FROM t1 WHERE b>='y' AND rowid<10 } } {-3 y z} do_test intpkey-2.2 { execsql { UPDATE t1 SET a=8 WHERE b=='y'; SELECT * FROM t1 WHERE b=='y'; } } {8 y z} do_test intpkey-2.3 { | > | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | } } {-3 y z} do_test intpkey-2.1.4 { execsql { SELECT * FROM t1 WHERE b>='y' AND rowid<10 } } {-3 y z} do_test intpkey-2.2 { execsql { UPDATE t1 SET a=8 WHERE b=='y'; SELECT * FROM t1 WHERE b=='y'; } } {8 y z} do_test intpkey-2.3 { |
︙ | ︙ |