SQLite

Check-in [4cc12c18]
Login

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

Overview
Comment:Recompute the values for all generated columns after NOT NULL ON CONFLICT REPLACE constraints fire. Tickets [37823501c68a09f9] and [5fbc159eeb092130].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 4cc12c18860bc4801a407cf45e88e23d3d40391f01a461fbac2cac5f102100e1
User & Date: drh 2019-12-28 00:36:51
Context
2019-12-28
01:52
When an INSERT is receiving content from a SELECT, run an OP_ReleaseReg opcode at the top of each iteration of the loop in order to prevent spurious OP_SCopy misuse complaints. Ticket [de4b04149b9fdeae] (check-in: 6afadd3b user: drh tags: trunk)
00:36
Recompute the values for all generated columns after NOT NULL ON CONFLICT REPLACE constraints fire. Tickets [37823501c68a09f9] and [5fbc159eeb092130]. (check-in: 4cc12c18 user: drh tags: trunk)
2019-12-27
20:06
Remove a NEVER() that is no longer true. Fix for [36ffedcb9]. (check-in: 597896ed user: dan tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/insert.c.

1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
  Index *pIdx;         /* Pointer to one of the indices */
  Index *pPk = 0;      /* The PRIMARY KEY index */
  sqlite3 *db;         /* Database connection */
  int i;               /* loop counter */
  int ix;              /* Index loop counter */
  int nCol;            /* Number of columns */
  int onError;         /* Conflict resolution strategy */
  int addr1;           /* Address of jump instruction */
  int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
  int nPkField;        /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
  Index *pUpIdx = 0;   /* Index to which to apply the upsert */
  u8 isUpdate;         /* True if this is an UPDATE operation */
  u8 bAffinityDone = 0;  /* True if the OP_Affinity operation has been run */
  int upsertBypass = 0;  /* Address of Goto to bypass upsert subroutine */
  int upsertJump = 0;    /* Address of Goto that jumps into upsert subroutine */







<







1498
1499
1500
1501
1502
1503
1504

1505
1506
1507
1508
1509
1510
1511
  Index *pIdx;         /* Pointer to one of the indices */
  Index *pPk = 0;      /* The PRIMARY KEY index */
  sqlite3 *db;         /* Database connection */
  int i;               /* loop counter */
  int ix;              /* Index loop counter */
  int nCol;            /* Number of columns */
  int onError;         /* Conflict resolution strategy */

  int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
  int nPkField;        /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
  Index *pUpIdx = 0;   /* Index to which to apply the upsert */
  u8 isUpdate;         /* True if this is an UPDATE operation */
  u8 bAffinityDone = 0;  /* True if the OP_Affinity operation has been run */
  int upsertBypass = 0;  /* Address of Goto to bypass upsert subroutine */
  int upsertJump = 0;    /* Address of Goto that jumps into upsert subroutine */
1542
1543
1544
1545
1546
1547
1548




1549
1550


1551
1552
1553
1554
1555





1556
1557
1558
1559
1560
1561
1562
1563
1564
1565






1566


1567



1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581

1582
1583
1584
1585
1586
1587
1588

1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611






1612








1613


1614
1615
1616
1617
1618
1619
1620
  /* Record that this module has started */
  VdbeModuleComment((v, "BEGIN: GenCnstCks(%d,%d,%d,%d,%d)",
                     iDataCur, iIdxCur, regNewData, regOldData, pkChng));

  /* Test all NOT NULL constraints.
  */
  if( pTab->tabFlags & TF_HasNotNull ){




    for(i=0; i<nCol; i++){
      int iReg;


      onError = pTab->aCol[i].notNull;
      if( onError==OE_None ) continue; /* No NOT NULL on this column */
      if( i==pTab->iPKey ){
        continue;        /* ROWID is never NULL */
      }





      if( aiChng && aiChng[i]<0 ){
        /* Don't bother checking for NOT NULL on columns that do not change */
        continue;
      }
      if( overrideError!=OE_Default ){
        onError = overrideError;
      }else if( onError==OE_Default ){
        onError = OE_Abort;
      }
      if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){






        onError = OE_Abort;


      }



      assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
          || onError==OE_Ignore || onError==OE_Replace );
      addr1 = 0;
      testcase( i!=sqlite3TableColumnToStorage(pTab, i) );
      testcase( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL );
      testcase( pTab->aCol[i].colFlags & COLFLAG_STORED );
      iReg = sqlite3TableColumnToStorage(pTab, i) + regNewData + 1;
      switch( onError ){
        case OE_Replace: {
          assert( onError==OE_Replace );
          addr1 = sqlite3VdbeMakeLabel(pParse);
          sqlite3VdbeAddOp2(v, OP_NotNull, iReg, addr1);
            VdbeCoverage(v);
          if( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)==0 ){

            sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i);
            sqlite3VdbeAddOp2(v, OP_NotNull, iReg, addr1);
              VdbeCoverage(v);
          }
          onError = OE_Abort;
          /* Fall through into the OE_Abort case to generate code that runs
          ** if both the input and the default value are NULL */

        }
        case OE_Abort:
          sqlite3MayAbort(pParse);
          /* Fall through */
        case OE_Rollback:
        case OE_Fail: {
          char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
                                      pTab->aCol[i].zName);
          sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL,
                            onError, iReg);
          sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC);
          sqlite3VdbeChangeP5(v, P5_ConstraintNotNull);
          VdbeCoverage(v);
          if( addr1 ) sqlite3VdbeResolveLabel(v, addr1);
          break;
        }
        default: {
          assert( onError==OE_Ignore );
          sqlite3VdbeAddOp2(v, OP_IsNull, iReg, ignoreDest);
          VdbeCoverage(v);
          break;
        }
      }






    }








  }



  /* Test all CHECK constraints
  */
#ifndef SQLITE_OMIT_CHECK
  if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){
    ExprList *pCheck = pTab->pCheck;
    pParse->iSelfTab = -(regNewData+1);







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

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







1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590

1591


1592
1593
1594

1595

1596
1597
1598
1599
1600





1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614

1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
  /* Record that this module has started */
  VdbeModuleComment((v, "BEGIN: GenCnstCks(%d,%d,%d,%d,%d)",
                     iDataCur, iIdxCur, regNewData, regOldData, pkChng));

  /* Test all NOT NULL constraints.
  */
  if( pTab->tabFlags & TF_HasNotNull ){
    int b2ndPass = 0;         /* True if currently running 2nd pass */
    int nSeenReplace = 0;     /* Number of ON CONFLICT REPLACE operations */
    int nGenerated = 0;       /* Number of generated columns with NOT NULL */
    while(1){  /* Make 2 passes over columns. Exit loop via "break" */
      for(i=0; i<nCol; i++){
        int iReg;                        /* Register holding column value */
        Column *pCol = &pTab->aCol[i];   /* The column to check for NOT NULL */
        int isGenerated;                 /* non-zero if column is generated */
        onError = pCol->notNull;
        if( onError==OE_None ) continue; /* No NOT NULL on this column */
        if( i==pTab->iPKey ){
          continue;        /* ROWID is never NULL */
        }
        isGenerated = pCol->colFlags & COLFLAG_GENERATED;
        if( isGenerated && !b2ndPass ){
          nGenerated++;
          continue;        /* Generated columns processed on 2nd pass */
        }
        if( aiChng && aiChng[i]<0 && !isGenerated ){
          /* Do not check NOT NULL on columns that do not change */
          continue;
        }
        if( overrideError!=OE_Default ){
          onError = overrideError;
        }else if( onError==OE_Default ){
          onError = OE_Abort;
        }
        if( onError==OE_Replace ){
          if( b2ndPass        /* REPLACE becomes ABORT on the 2nd pass */
           || pCol->pDflt==0  /* REPLACE is ABORT if no DEFAULT value */
          ){
            testcase( pCol->colFlags & COLFLAG_VIRTUAL );
            testcase( pCol->colFlags & COLFLAG_STORED );
            testcase( pCol->colFlags & COLFLAG_GENERATED );
            onError = OE_Abort;
          }else{
            assert( !isGenerated );
          }
        }else if( b2ndPass && !isGenerated ){
          continue;
        }
        assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
            || onError==OE_Ignore || onError==OE_Replace );

        testcase( i!=sqlite3TableColumnToStorage(pTab, i) );


        iReg = sqlite3TableColumnToStorage(pTab, i) + regNewData + 1;
        switch( onError ){
          case OE_Replace: {

            int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, iReg);

            VdbeCoverage(v);
            assert( (pCol->colFlags & COLFLAG_GENERATED)==0 );
            nSeenReplace++;
            sqlite3ExprCode(pParse, pCol->pDflt, iReg);
            sqlite3VdbeJumpHere(v, addr1);





            break;
          }
          case OE_Abort:
            sqlite3MayAbort(pParse);
            /* Fall through */
          case OE_Rollback:
          case OE_Fail: {
            char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
                                        pCol->zName);
            sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL,
                              onError, iReg);
            sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC);
            sqlite3VdbeChangeP5(v, P5_ConstraintNotNull);
            VdbeCoverage(v);

            break;
          }
          default: {
            assert( onError==OE_Ignore );
            sqlite3VdbeAddOp2(v, OP_IsNull, iReg, ignoreDest);
            VdbeCoverage(v);
            break;
          }
        } /* end switch(onError) */
      } /* end loop i over columns */
      if( nGenerated==0 && nSeenReplace==0 ){
        /* If there are no generated columns with NOT NULL constraints
        ** and no NOT NULL ON CONFLICT REPLACE constraints, then a single
        ** pass is sufficient */
        break;
      }
      if( b2ndPass ) break;  /* Never need more than 2 passes */
      b2ndPass = 1;
      if( nSeenReplace>0 && (pTab->tabFlags & TF_HasGenerated)!=0 ){
        /* If any NOT NULL ON CONFLICT REPLACE constraints fired on the
        ** first pass, recomputed values for all generated columns, as
        ** those values might depend on columns affected by the REPLACE.
        */
        sqlite3ComputeGeneratedColumns(pParse, regNewData+1, pTab);
      }
    } /* end of 2-pass loop */
  } /* end if( has-not-null-constraints ) */

  /* Test all CHECK constraints
  */
#ifndef SQLITE_OMIT_CHECK
  if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){
    ExprList *pCheck = pTab->pCheck;
    pParse->iSelfTab = -(regNewData+1);

Changes to test/gencol1.test.

207
208
209
210
211
212
213
214







215
216
217
218
219
220












221





222































223











224
225
226
227
228
229
230

do_catchsql_test gencol1-6.10 {
  DROP TABLE IF EXISTS t0;
  CREATE TABLE t0(c0 NOT NULL AS(c1), c1);
  REPLACE INTO t0(c1) VALUES(NULL);
} {1 {NOT NULL constraint failed: t0.c0}}

# 2019-11-06 ticket b13b7dce76e9352b34e7







do_execsql_test gencol1-7.10 {
  DROP TABLE IF EXISTS t0;
  CREATE TABLE t0 (c0 GENERATED ALWAYS AS (1), c1 UNIQUE, c2 UNIQUE);
  INSERT INTO t0(c1) VALUES (1);
  SELECT quote(0 = t0.c2 OR t0.c1 BETWEEN t0.c2 AND 1) FROM t0;
} {NULL}












do_execsql_test gencol1-7.20 {





  SELECT 99 FROM t0 WHERE 0 = t0.c2 OR t0.c1 BETWEEN t0.c2 AND 1;  































} {}












# 2019-11-06 ticket 4fc08501f4e56692
do_execsql_test gencol1-8.10 {
  DROP TABLE IF EXISTS t0;
  CREATE TABLE t0(
    c0 AS (('a', 9) < ('b', c1)),
    c1 AS (1),







|
>
>
>
>
>
>
>

<




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

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







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
291
292
293
294
295

do_catchsql_test gencol1-6.10 {
  DROP TABLE IF EXISTS t0;
  CREATE TABLE t0(c0 NOT NULL AS(c1), c1);
  REPLACE INTO t0(c1) VALUES(NULL);
} {1 {NOT NULL constraint failed: t0.c0}}

# 2019-11-06 ticket https://www.sqlite.org/src/info/2399f5986134f79c
# 2019-12-27 ticket https://www.sqlite.org/src/info/5fbc159eeb092130
# 2019-12-27 ticket https://www.sqlite.org/src/info/37823501c68a09f9
#
# All of the above tickets deal with NOT NULL ON CONFLICT REPLACE
# constraints on tables that have generated columns.
#
reset_db
do_execsql_test gencol1-7.10 {

  CREATE TABLE t0 (c0 GENERATED ALWAYS AS (1), c1 UNIQUE, c2 UNIQUE);
  INSERT INTO t0(c1) VALUES (1);
  SELECT quote(0 = t0.c2 OR t0.c1 BETWEEN t0.c2 AND 1) FROM t0;
} {NULL}
do_execsql_test gencol1-7.11 {
  DROP TABLE t0;
  CREATE TABLE t0(c0 NOT NULL DEFAULT 'xyz', c1 AS(c0) NOT NULL);
  REPLACE INTO t0(c0) VALUES(NULL);
  SELECT * FROM t0;
} {xyz xyz}
do_execsql_test gencol1-7.12 {
  DROP TABLE t0;
  CREATE TABLE t0(c0 NOT NULL DEFAULT 'xyz', c1 AS(c0) STORED NOT NULL);
  REPLACE INTO t0(c0) VALUES(NULL);
  SELECT * FROM t0;
} {xyz xyz}
do_execsql_test gencol1-7.20 {
  CREATE TABLE t1(
   a NOT NULL DEFAULT 'aaa',
   b AS(c) NOT NULL,
   c NOT NULL DEFAULT 'ccc');
  REPLACE INTO t1(a,c) VALUES(NULL,NULL);
  SELECT * FROM t1;
} {aaa ccc ccc}
do_execsql_test gencol1-7.21 {
  DROP TABLE t1;
  CREATE TABLE t1(
   a NOT NULL DEFAULT 'aaa',
   b AS(c) STORED NOT NULL,
   c NOT NULL DEFAULT 'ccc');
  REPLACE INTO t1(a,c) VALUES(NULL,NULL);
  SELECT * FROM t1;
} {aaa ccc ccc}
do_execsql_test gencol1-7.30 {
  CREATE TABLE t2(
   a NOT NULL DEFAULT 'aaa',
   b AS(a) NOT NULL,
   c NOT NULL DEFAULT 'ccc');
  REPLACE INTO t2(a,c) VALUES(NULL,NULL);
  SELECT * FROM t2;
} {aaa aaa ccc}
do_execsql_test gencol1-7.31 {
  DROP TABLE t2;
  CREATE TABLE t2(
   a NOT NULL DEFAULT 'aaa',
   b AS(a) STORED NOT NULL,
   c NOT NULL DEFAULT 'ccc');
  REPLACE INTO t2(a,c) VALUES(NULL,NULL);
  SELECT * FROM t2;
} {aaa aaa ccc}
do_execsql_test gencol1-7.40 {
  CREATE TABLE t3(a NOT NULL DEFAULT 123, b AS(a) UNIQUE);
  REPLACE INTO t3 VALUES(NULL);
  SELECT * FROM t3;
} {123 123}
do_execsql_test gencol1-7.41 {
  SELECT * FROM t3 WHERE b=123;
} {123 123}
do_execsql_test gencol1-7.50 {
  CREATE TABLE t4(a NOT NULL DEFAULT 123, b AS(a*10+4) STORED UNIQUE);
  REPLACE INTO t4 VALUES(NULL);
  SELECT * FROM t4;
} {123 1234}
do_execsql_test gencol1-7.51 {
  SELECT * FROM t4 WHERE b=1234;
} {123 1234}

# 2019-11-06 ticket 4fc08501f4e56692
do_execsql_test gencol1-8.10 {
  DROP TABLE IF EXISTS t0;
  CREATE TABLE t0(
    c0 AS (('a', 9) < ('b', c1)),
    c1 AS (1),
241
242
243
244
245
246
247

248
249
250
251
252
253
254
255
256
257
    c2 AS(c1) CHECK(c2)
  );
  UPDATE t0 SET c0 = NULL;
} {1 {generated column loop on "c2"}}

# 2019-11-21 Problems in the new generated column logic
# reported by Yongheng Chen and Rui Zhong

do_execsql_test gencol1-9.10 {
  PRAGMA foreign_keys=OFF;
  DROP TABLE t1;
  CREATE TABLE t1(aa , bb AS (17) UNIQUE);
  INSERT INTO t1 VALUES(17);
  CREATE TABLE t2(cc);
  INSERT INTO t2 VALUES(41);
  SELECT * FROM t2 JOIN t1 WHERE t1.bb=t1.aa AND t1.bb=17;
} {41 17 17}
do_execsql_test gencol1-9.20 {







>


<







306
307
308
309
310
311
312
313
314
315

316
317
318
319
320
321
322
    c2 AS(c1) CHECK(c2)
  );
  UPDATE t0 SET c0 = NULL;
} {1 {generated column loop on "c2"}}

# 2019-11-21 Problems in the new generated column logic
# reported by Yongheng Chen and Rui Zhong
reset_db
do_execsql_test gencol1-9.10 {
  PRAGMA foreign_keys=OFF;

  CREATE TABLE t1(aa , bb AS (17) UNIQUE);
  INSERT INTO t1 VALUES(17);
  CREATE TABLE t2(cc);
  INSERT INTO t2 VALUES(41);
  SELECT * FROM t2 JOIN t1 WHERE t1.bb=t1.aa AND t1.bb=17;
} {41 17 17}
do_execsql_test gencol1-9.20 {