Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Get the ON CONFLICT DO NOTHING form of upsert working by mapping it into INSERT OR IGNORE. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | upsert |
Files: | files | file ages | folders |
SHA3-256: |
d07f05e98bb9ce0f9b46db159d9df161 |
User & Date: | drh 2018-04-13 15:14:33.373 |
Context
2018-04-13
| ||
16:29 | Merge the preupdate hook change from trunk. (check-in: 7353caabb3 user: drh tags: upsert) | |
15:14 | Get the ON CONFLICT DO NOTHING form of upsert working by mapping it into INSERT OR IGNORE. (check-in: d07f05e98b user: drh tags: upsert) | |
14:27 | Get the conflict-target clause parsing working correctly, with test cases. This change involves an enhancement to sqlite3ExprCompare() which needs to be reviewed on trunk prior to merging. (check-in: 5bf7042562 user: drh tags: upsert) | |
Changes
Changes to src/insert.c.
︙ | ︙ | |||
1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 | /* pkChng!=0 does not mean that the rowid has changed, only that ** it might have changed. Skip the conflict logic below if the rowid ** is unchanged. */ sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData); sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); VdbeCoverage(v); } /* If the response to a rowid conflict is REPLACE but the response ** to some other UNIQUE constraint is FAIL or IGNORE, then we need ** to defer the running of the rowid conflict checking until after ** the UNIQUE constraints have run. */ if( onError==OE_Replace && overrideError!=OE_Replace ){ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->onError==OE_Ignore || pIdx->onError==OE_Fail ){ ipkTop = sqlite3VdbeAddOp0(v, OP_Goto); break; } } } /* Check to see if the new rowid already exists in the table. Skip ** the following conflict logic if it does not. */ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData); VdbeCoverage(v); | > > > > > > > > > < | 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 | /* pkChng!=0 does not mean that the rowid has changed, only that ** it might have changed. Skip the conflict logic below if the rowid ** is unchanged. */ sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData); sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); VdbeCoverage(v); } /* figure out whether or not upsert applies in this case */ if( pUpsert && (pUpsert->pUpsertTarget==0 || pUpsert->pUpsertIdx==0) ){ if( pUpsert->pUpsertSet==0 ){ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ }else{ onError = OE_Update; /* DO UPDATE */ } } /* If the response to a rowid conflict is REPLACE but the response ** to some other UNIQUE constraint is FAIL or IGNORE, then we need ** to defer the running of the rowid conflict checking until after ** the UNIQUE constraints have run. */ if( onError==OE_Replace && overrideError!=OE_Replace ){ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->onError==OE_Ignore || pIdx->onError==OE_Fail ){ ipkTop = sqlite3VdbeAddOp0(v, OP_Goto); break; } } } /* Check to see if the new rowid already exists in the table. Skip ** the following conflict logic if it does not. */ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData); VdbeCoverage(v); switch( onError ){ default: { onError = OE_Abort; /* Fall thru into the next case */ } case OE_Rollback: case OE_Abort: |
︙ | ︙ | |||
1474 1475 1476 1477 1478 1479 1480 | sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,-1); } } seenReplace = 1; break; } case OE_Ignore: { | < | 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 | sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,-1); } } seenReplace = 1; break; } case OE_Ignore: { sqlite3VdbeGoto(v, ignoreDest); break; } } sqlite3VdbeResolveLabel(v, addrRowidOk); if( ipkTop ){ ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto); |
︙ | ︙ | |||
1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 | continue; /* pIdx is not a UNIQUE index */ } if( overrideError!=OE_Default ){ onError = overrideError; }else if( onError==OE_Default ){ onError = OE_Abort; } /* Collision detection may be omitted if all of the following are true: ** (1) The conflict resolution algorithm is REPLACE ** (2) The table is a WITHOUT ROWID table ** (3) There are no secondary indexes on the table ** (4) No delete triggers need to be fired if there is a conflict ** (5) No FK constraint counters need to be updated if a conflict occurs. | > > > > > > > > > | 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 | continue; /* pIdx is not a UNIQUE index */ } if( overrideError!=OE_Default ){ onError = overrideError; }else if( onError==OE_Default ){ onError = OE_Abort; } /* Figure out if the upsert clause applies to this index */ if( pUpsert && (pUpsert->pUpsertTarget==0 || pUpsert->pUpsertIdx==pIdx) ){ if( pUpsert->pUpsertSet==0 ){ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ }else{ onError = OE_Update; /* DO UPDATE */ } } /* Collision detection may be omitted if all of the following are true: ** (1) The conflict resolution algorithm is REPLACE ** (2) The table is a WITHOUT ROWID table ** (3) There are no secondary indexes on the table ** (4) No delete triggers need to be fired if there is a conflict ** (5) No FK constraint counters need to be updated if a conflict occurs. |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
2043 2044 2045 2046 2047 2048 2049 | */ #define OE_None 0 /* There is no constraint to check */ #define OE_Rollback 1 /* Fail the operation and rollback the transaction */ #define OE_Abort 2 /* Back out changes but do no rollback transaction */ #define OE_Fail 3 /* Stop the operation but leave all prior changes */ #define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */ #define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */ | | | | | | < | | 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 | */ #define OE_None 0 /* There is no constraint to check */ #define OE_Rollback 1 /* Fail the operation and rollback the transaction */ #define OE_Abort 2 /* Back out changes but do no rollback transaction */ #define OE_Fail 3 /* Stop the operation but leave all prior changes */ #define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */ #define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */ #define OE_Update 6 /* Process as a DO UPDATE in an upsert */ #define OE_Restrict 7 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */ #define OE_SetNull 8 /* Set the foreign key value to NULL */ #define OE_SetDflt 9 /* Set the foreign key value to its default */ #define OE_Cascade 10 /* Cascade the changes */ #define OE_Default 11 /* Do whatever the default action is */ /* ** An instance of the following structure is passed as the first ** argument to sqlite3VdbeKeyCompare and is used to control the ** comparison of the two index keys. ** |
︙ | ︙ |
Changes to test/upsert1.test.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | source $testdir/tester.tcl set testprefix zipfile do_execsql_test upsert1-100 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c DEFAULT 0); CREATE UNIQUE INDEX t1x1 ON t1(b); INSERT INTO t1(a,b) VALUES(1,2) ON CONFLICT DO NOTHING; SELECT * FROM t1; } {1 2 0} do_execsql_test upsert1-101 { DELETE FROM t1; INSERT INTO t1(a,b) VALUES(2,3) ON CONFLICT(a) DO NOTHING; SELECT * FROM t1; } {2 3 0} do_execsql_test upsert1-102 { DELETE FROM t1; INSERT INTO t1(a,b) VALUES(3,4) ON CONFLICT(b) DO NOTHING; SELECT * FROM t1; } {3 4 0} do_catchsql_test upsert1-110 { INSERT INTO t1(a,b) VALUES(5,6) ON CONFLICT(x) DO NOTHING; SELECT * FROM t1; } {1 {no such column: x}} do_catchsql_test upsert1-120 { | > > > | 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 | source $testdir/tester.tcl set testprefix zipfile do_execsql_test upsert1-100 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c DEFAULT 0); CREATE UNIQUE INDEX t1x1 ON t1(b); INSERT INTO t1(a,b) VALUES(1,2) ON CONFLICT DO NOTHING; INSERT INTO t1(a,b) VALUES(1,99),(99,2) ON CONFLICT DO NOTHING; SELECT * FROM t1; } {1 2 0} do_execsql_test upsert1-101 { DELETE FROM t1; INSERT INTO t1(a,b) VALUES(2,3) ON CONFLICT(a) DO NOTHING; INSERT INTO t1(a,b) VALUES(2,99) ON CONFLICT(a) DO NOTHING; SELECT * FROM t1; } {2 3 0} do_execsql_test upsert1-102 { DELETE FROM t1; INSERT INTO t1(a,b) VALUES(3,4) ON CONFLICT(b) DO NOTHING; INSERT INTO t1(a,b) VALUES(99,4) ON CONFLICT(b) DO NOTHING; SELECT * FROM t1; } {3 4 0} do_catchsql_test upsert1-110 { INSERT INTO t1(a,b) VALUES(5,6) ON CONFLICT(x) DO NOTHING; SELECT * FROM t1; } {1 {no such column: x}} do_catchsql_test upsert1-120 { |
︙ | ︙ | |||
47 48 49 50 51 52 53 | do_execsql_test upsert1-140 { DELETE FROM t1; INSERT INTO t1(a,b) VALUES(5,6) ON CONFLICT(b COLLATE binary) DO NOTHING; SELECT * FROM t1; } {5 6 0} do_catchsql_test upsert1-200 { | | | | | > | > > > | | | > | | | | 50 51 52 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 80 81 82 83 84 85 86 87 88 89 90 91 92 | do_execsql_test upsert1-140 { DELETE FROM t1; INSERT INTO t1(a,b) VALUES(5,6) ON CONFLICT(b COLLATE binary) DO NOTHING; SELECT * FROM t1; } {5 6 0} do_catchsql_test upsert1-200 { DROP TABLE t1; CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT, c DEFAULT 0); CREATE UNIQUE INDEX t1x1 ON t1(a+b); INSERT INTO t1(a,b) VALUES(7,8) ON CONFLICT(a+b) DO NOTHING; INSERT INTO t1(a,b) VALUES(8,7),(9,6) ON CONFLICT(a+b) DO NOTHING; SELECT * FROM t1; } {0 {7 8 0}} do_catchsql_test upsert1-201 { INSERT INTO t1(a,b) VALUES(8,7),(9,6) ON CONFLICT(a) DO NOTHING; } {1 {UNIQUE constraint failed: index 't1x1'}} do_catchsql_test upsert1-210 { DELETE FROM t1; INSERT INTO t1(a,b) VALUES(9,10) ON CONFLICT(a+(+b)) DO NOTHING; SELECT * FROM t1; } {1 {ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint}} do_catchsql_test upsert1-300 { DROP INDEX t1x1; DELETE FROM t1; CREATE UNIQUE INDEX t1x1 ON t1(b) WHERE b>10; INSERT INTO t1(a,b) VALUES(1,2),(3,2) ON CONFLICT(b) DO NOTHING; SELECT * FROM t1; } {1 {ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint}} do_catchsql_test upsert1-310 { DELETE FROM t1; INSERT INTO t1(a,b) VALUES(1,2),(3,2) ON CONFLICT(b) WHERE b!=10 DO NOTHING; SELECT * FROM t1; } {1 {ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint}} do_execsql_test upsert1-320 { DELETE FROM t1; INSERT INTO t1(a,b) VALUES(1,2),(3,2),(4,20),(5,20) ON CONFLICT(b) WHERE b>10 DO NOTHING; SELECT *, 'x' FROM t1 ORDER BY b, a; } {1 2 0 x 3 2 0 x 4 20 0 x} finish_test |