SQLite

Check-in [00a398cf90]
Login

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

Overview
Comment:Fix the preupdate hook so that it works when the "old.*" row has a column with a non-NULL default value that was added by ALTER TABLE ADD COLUMN after the current record was created.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | preupdate-hook-fix
Files: files | file ages | folders
SHA3-256: 00a398cf900179aa5a8aab09fe4a671d99e7a31583282848ef39390f2ef246eb
User & Date: dan 2024-09-18 15:02:27.631
Context
2024-09-18
15:52
Fix the preupdate hook so that it works when the "old.*" row has a column with a non-NULL default value that was added by ALTER TABLE ADD COLUMN after the current record was created. (check-in: 6a8c687904 user: dan tags: trunk)
15:02
Fix the preupdate hook so that it works when the "old.*" row has a column with a non-NULL default value that was added by ALTER TABLE ADD COLUMN after the current record was created. (Closed-Leaf check-in: 00a398cf90 user: dan tags: preupdate-hook-fix)
2024-09-17
22:57
Fix harmless static analyzer warning in sqlite3-rsync. (check-in: a63e412b6b user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/session/sessionfault3.test.
51
52
53
54
55
56
57
























58
59
  G add $::C2
  G output
  set {} {}
} -test {
  catch { G delete }
  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}

























finish_test







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


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
  G add $::C2
  G output
  set {} {}
} -test {
  catch { G delete }
  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}

#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
  INSERT INTO t1 VALUES(1, 'one');
  INSERT INTO t1 VALUES(2, 'two');
  ALTER TABLE t1 ADD COLUMN c DEFAULT 'abcdefghijklmnopqrstuvwxyz';
}
faultsim_save_and_close

do_faultsim_test 2 -faults oom-t* -prep {
  faultsim_restore_and_reopen
  db eval {SELECT * FROM sqlite_schema}
} -body {
  sqlite3session S db main
  S attach *
  execsql {
    DELETE FROM t1 WHERE a = 1;
  }
} -test {
  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  catch { S delete }
}

finish_test
Changes to ext/session/sqlite3session.c.
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
struct sqlite3_session {
  sqlite3 *db;                    /* Database handle session is attached to */
  char *zDb;                      /* Name of database session is attached to */
  int bEnableSize;                /* True if changeset_size() enabled */
  int bEnable;                    /* True if currently recording */
  int bIndirect;                  /* True if all changes are indirect */
  int bAutoAttach;                /* True to auto-attach tables */
  int bImplicitPK;                /* True to handle tables with implicit PK */
  int rc;                         /* Non-zero if an error has occurred */
  void *pFilterCtx;               /* First argument to pass to xTableFilter */
  int (*xTableFilter)(void *pCtx, const char *zTab);
  i64 nMalloc;                    /* Number of bytes of data allocated */
  i64 nMaxChangesetSize;
  sqlite3_value *pZeroBlob;       /* Value containing X'' */
  sqlite3_session *pNext;         /* Next session object on same db. */
  SessionTable *pTable;           /* List of attached tables */







|
<







44
45
46
47
48
49
50
51

52
53
54
55
56
57
58
struct sqlite3_session {
  sqlite3 *db;                    /* Database handle session is attached to */
  char *zDb;                      /* Name of database session is attached to */
  int bEnableSize;                /* True if changeset_size() enabled */
  int bEnable;                    /* True if currently recording */
  int bIndirect;                  /* True if all changes are indirect */
  int bAutoAttach;                /* True to auto-attach tables */
  int bImplicitPK;                /* True to handle tables with implicit PK */ int rc;                         /* Non-zero if an error has occurred */

  void *pFilterCtx;               /* First argument to pass to xTableFilter */
  int (*xTableFilter)(void *pCtx, const char *zTab);
  i64 nMalloc;                    /* Number of bytes of data allocated */
  i64 nMaxChangesetSize;
  sqlite3_value *pZeroBlob;       /* Value containing X'' */
  sqlite3_session *pNext;         /* Next session object on same db. */
  SessionTable *pTable;           /* List of attached tables */
1753
1754
1755
1756
1757
1758
1759


1760
1761
1762
1763
1764
1765
1766

1767
1768
1769

1770
1771
1772
1773
1774
1775
1776
      pTab->nEntry++;
  
      /* Figure out how large an allocation is required */
      nByte = sizeof(SessionChange);
      for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
        sqlite3_value *p = 0;
        if( op!=SQLITE_INSERT ){


          TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p);
          assert( trc==SQLITE_OK );
        }else if( pTab->abPK[i] ){
          TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p);
          assert( trc==SQLITE_OK );
        }


        /* This may fail if SQLite value p contains a utf-16 string that must
        ** be converted to utf-8 and an OOM error occurs while doing so. */
        rc = sessionSerializeValue(0, p, &nByte);

        if( rc!=SQLITE_OK ) goto error_out;
      }
      if( pTab->bRowid ){
        nByte += 9;               /* Size of rowid field - an integer */
      }
  
      /* Allocate the change object */







>
>
|
<





>
|
|
|
>







1752
1753
1754
1755
1756
1757
1758
1759
1760
1761

1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
      pTab->nEntry++;
  
      /* Figure out how large an allocation is required */
      nByte = sizeof(SessionChange);
      for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
        sqlite3_value *p = 0;
        if( op!=SQLITE_INSERT ){
          /* This may fail if the column has a non-NULL default and was added 
          ** using ALTER TABLE ADD COLUMN after this record was created. */
          rc = pSession->hook.xOld(pSession->hook.pCtx, i, &p);

        }else if( pTab->abPK[i] ){
          TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p);
          assert( trc==SQLITE_OK );
        }

        if( rc==SQLITE_OK ){
          /* This may fail if SQLite value p contains a utf-16 string that must
          ** be converted to utf-8 and an OOM error occurs while doing so. */
          rc = sessionSerializeValue(0, p, &nByte);
        }
        if( rc!=SQLITE_OK ) goto error_out;
      }
      if( pTab->bRowid ){
        nByte += 9;               /* Size of rowid field - an integer */
      }
  
      /* Allocate the change object */
Changes to src/vdbeInt.h.
539
540
541
542
543
544
545

546
547
548
549
550
551
552
  int iNewReg;                    /* Register for new.* values */
  int iBlobWrite;                 /* Value returned by preupdate_blobwrite() */
  i64 iKey1;                      /* First key value passed to hook */
  i64 iKey2;                      /* Second key value passed to hook */
  Mem *aNew;                      /* Array of new.* values */
  Table *pTab;                    /* Schema object being updated */
  Index *pPk;                     /* PK index if pTab is WITHOUT ROWID */

};

/*
** An instance of this object is used to pass an vector of values into
** OP_VFilter, the xFilter method of a virtual table.  The vector is the
** set of values on the right-hand side of an IN constraint.
**







>







539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
  int iNewReg;                    /* Register for new.* values */
  int iBlobWrite;                 /* Value returned by preupdate_blobwrite() */
  i64 iKey1;                      /* First key value passed to hook */
  i64 iKey2;                      /* Second key value passed to hook */
  Mem *aNew;                      /* Array of new.* values */
  Table *pTab;                    /* Schema object being updated */
  Index *pPk;                     /* PK index if pTab is WITHOUT ROWID */
  sqlite3_value **apDflt;         /* Array of default values, if required */
};

/*
** An instance of this object is used to pass an vector of values into
** OP_VFilter, the xFilter method of a virtual table.  The vector is the
** set of values on the right-hand side of an IN constraint.
**
Changes to src/vdbeapi.c.
2218
2219
2220
2221
2222
2223
2224




















2225

2226
2227
2228
2229
2230
2231
2232
    p->aRecord = aRec;
  }

  pMem = *ppValue = &p->pUnpacked->aMem[iIdx];
  if( iIdx==p->pTab->iPKey ){
    sqlite3VdbeMemSetInt64(pMem, p->iKey1);
  }else if( iIdx>=p->pUnpacked->nField ){




















    *ppValue = (sqlite3_value *)columnNullValue();

  }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
    if( pMem->flags & (MEM_Int|MEM_IntReal) ){
      testcase( pMem->flags & MEM_Int );
      testcase( pMem->flags & MEM_IntReal );
      sqlite3VdbeMemRealify(pMem);
    }
  }







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







2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
    p->aRecord = aRec;
  }

  pMem = *ppValue = &p->pUnpacked->aMem[iIdx];
  if( iIdx==p->pTab->iPKey ){
    sqlite3VdbeMemSetInt64(pMem, p->iKey1);
  }else if( iIdx>=p->pUnpacked->nField ){
    /* This occurs when the table has been extended using ALTER TABLE
    ** ADD COLUMN. The value to return is the default value of the column. */
    Column *pCol = &p->pTab->aCol[iIdx];
    if( pCol->iDflt>0 ){
      if( p->apDflt==0 ){
        int nByte = sizeof(sqlite3_value*)*p->pTab->nCol;
        p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte);
        if( p->apDflt==0 ) goto preupdate_old_out;
      }
      if( p->apDflt[iIdx]==0 ){
        Expr *pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
        sqlite3_value *pVal = 0;
        rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal);
        if( rc==SQLITE_OK && pVal==0 ){
          rc = SQLITE_CORRUPT_BKPT;
        }
        p->apDflt[iIdx] = pVal;
      }
      *ppValue = p->apDflt[iIdx];
    }else{
      *ppValue = (sqlite3_value *)columnNullValue();
    }
  }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
    if( pMem->flags & (MEM_Int|MEM_IntReal) ){
      testcase( pMem->flags & MEM_Int );
      testcase( pMem->flags & MEM_IntReal );
      sqlite3VdbeMemRealify(pMem);
    }
  }
Changes to src/vdbeaux.c.
5539
5540
5541
5542
5543
5544
5545







5546
5547
  if( preupdate.aNew ){
    int i;
    for(i=0; i<pCsr->nField; i++){
      sqlite3VdbeMemRelease(&preupdate.aNew[i]);
    }
    sqlite3DbNNFreeNN(db, preupdate.aNew);
  }







}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */







>
>
>
>
>
>
>


5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
  if( preupdate.aNew ){
    int i;
    for(i=0; i<pCsr->nField; i++){
      sqlite3VdbeMemRelease(&preupdate.aNew[i]);
    }
    sqlite3DbNNFreeNN(db, preupdate.aNew);
  }
  if( preupdate.apDflt ){
    int i;
    for(i=0; i<pTab->nCol; i++){
      sqlite3ValueFree(preupdate.apDflt[i]);
    }
    sqlite3DbFree(db, preupdate.apDflt);
  }
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
Changes to test/hook.test.
702
703
704
705
706
707
708
709
710
711
712


713
714
715
716
717
718
719
    CREATE TABLE t8(a, b);
    INSERT INTO t8 VALUES('one', 'two');
    INSERT INTO t8 VALUES('three', 'four');
    ALTER TABLE t8 ADD COLUMN c DEFAULT 'xxx';
  }
}

if 0 {
  # At time of writing, these two are broken. They demonstrate that the
  # sqlite3_preupdate_old() method does not handle the case where ALTER TABLE
  # has been used to add a column with a default value other than NULL.


  #
  do_preupdate_test 7.5.2.1 {
    DELETE FROM t8 WHERE a = 'one'
  } {
    DELETE main t8 1 1   one two xxx
  }
  do_preupdate_test 7.5.2.2 {







|



>
>







702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
    CREATE TABLE t8(a, b);
    INSERT INTO t8 VALUES('one', 'two');
    INSERT INTO t8 VALUES('three', 'four');
    ALTER TABLE t8 ADD COLUMN c DEFAULT 'xxx';
  }
}

if 1 {
  # At time of writing, these two are broken. They demonstrate that the
  # sqlite3_preupdate_old() method does not handle the case where ALTER TABLE
  # has been used to add a column with a default value other than NULL.
  #
  # 2024-09-18: These are now fixed.
  #
  do_preupdate_test 7.5.2.1 {
    DELETE FROM t8 WHERE a = 'one'
  } {
    DELETE main t8 1 1   one two xxx
  }
  do_preupdate_test 7.5.2.2 {
1017
1018
1019
1020
1021
1022
1023
1024

































1025
  execsql VACUUM
  set ::res
} {}

do_catchsql_test 12.6 {
  INSERT INTO t4 VALUES('def', 3);
} {1 {UNIQUE constraint failed: t4.a}}


































finish_test








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

1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
  execsql VACUUM
  set ::res
} {}

do_catchsql_test 12.6 {
  INSERT INTO t4 VALUES('def', 3);
} {1 {UNIQUE constraint failed: t4.a}}

#-------------------------------------------------------------------------
# Test adding non-NULL default values using ALTER TABLE.
#
reset_db
db preupdate hook preupdate_hook
do_execsql_test 13.0 {
  CREATE TABLE t1(a INTEGER PRIMARY KEY);
  INSERT INTO t1 VALUES(100), (200), (300), (400);
}

do_execsql_test 13.1 {
  ALTER TABLE t1 ADD COLUMN b DEFAULT 1234;
  ALTER TABLE t1 ADD COLUMN c DEFAULT 'abcdef';
  ALTER TABLE t1 ADD COLUMN d DEFAULT NULL;
}

do_preupdate_test 13.2 {
  DELETE FROM t1 WHERE a=300
} {DELETE main t1 300 300 0 300 1234 abcdef {}}

do_preupdate_test 13.3 {
  UPDATE t1 SET d='hello world' WHERE a=200
} {
  UPDATE main t1 200 200 0 200 1234 abcdef {} 
                           200 1234 abcdef {hello world}
}

do_preupdate_test 13.4 {
  INSERT INTO t1 DEFAULT VALUES;
} {
  INSERT main t1 401 401 0 401 1234 abcdef {}
}

finish_test