/ Check-in [635d6a77]
Login

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

Overview
Comment:Fix an OOM related crash in fkey.c.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 635d6a775a3f192d4292738905f5e01bc956a712
User & Date: dan 2009-09-22 15:53:48
Context
2009-09-22
16:08
Fix a problem with ON DELETE SET DEFAULT actions. check-in: 94069950 user: dan tags: trunk
15:53
Fix an OOM related crash in fkey.c. check-in: 635d6a77 user: dan tags: trunk
13:25
Allow specific exclusion of localtime_s() usage on Windows. check-in: 216bcda7 user: shane tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/fkey.c.

   618    618   static Trigger *fkActionTrigger(
   619    619     Parse *pParse,
   620    620     Table *pTab,                    /* Table being updated or deleted from */
   621    621     FKey *pFKey,                    /* Foreign key to get action for */
   622    622     ExprList *pChanges              /* Change-list for UPDATE, NULL for DELETE */
   623    623   ){
   624    624     sqlite3 *db = pParse->db;       /* Database handle */
   625         -  int action;
   626         -  Trigger *pTrigger;
          625  +  int action;                     /* One of OE_None, OE_Cascade etc. */
          626  +  Trigger *pTrigger;              /* Trigger definition to return */
   627    627   
   628    628     if( pChanges ){
   629    629       action = pFKey->updateConf;
   630    630       pTrigger = pFKey->pOnUpdate;
   631    631     }else{
   632    632       action = pFKey->deleteConf;
   633    633       pTrigger = pFKey->pOnDelete;
   634    634     }
   635    635   
   636    636     assert( OE_SetNull>OE_Restrict && OE_SetDflt>OE_Restrict );
   637    637     assert( OE_Cascade>OE_Restrict && OE_None<OE_Restrict );
   638    638   
   639    639     if( action>OE_Restrict && !pTrigger ){
          640  +    u8 enableLookaside;           /* Copy of db->lookaside.bEnabled */
   640    641       char const *zFrom;            /* Name of referencing table */
   641    642       int nFrom;                    /* Length in bytes of zFrom */
   642         -    Index *pIdx = 0;
   643         -    int *aiCol = 0;
   644         -    TriggerStep *pStep;
   645         -    sqlite3 *dbMem = pTab->dbMem;
   646         -    Expr *pWhere = 0;
   647         -    ExprList *pList = 0;
   648         -    int i;
          643  +    Index *pIdx = 0;              /* Parent key index for this FK */
          644  +    int *aiCol = 0;               /* child table cols -> parent key cols */
          645  +    TriggerStep *pStep;           /* First (only) step of trigger program */
          646  +    Expr *pWhere = 0;             /* WHERE clause of trigger step */
          647  +    ExprList *pList = 0;          /* Changes list if ON UPDATE CASCADE */
          648  +    int i;                        /* Iterator variable */
   649    649   
   650    650       if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0;
   651    651       assert( aiCol || pFKey->nCol==1 );
   652    652   
   653         -    assert( dbMem==0 || dbMem==pParse->db );
   654         -    zFrom = pFKey->pFrom->zName;
   655         -    nFrom = sqlite3Strlen30(zFrom);
   656         -    pTrigger = (Trigger *)sqlite3DbMallocZero(dbMem, 
   657         -        sizeof(Trigger) +         /* struct Trigger */
   658         -        sizeof(TriggerStep) +     /* Single step in trigger program */
   659         -        nFrom + 1                 /* Space for pStep->target.z */
   660         -    );
   661         -    if( !pTrigger ){
   662         -      pParse->db->mallocFailed = 1;
   663         -      return 0;
   664         -    }
   665         -    pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1];
   666         -    pStep->target.z = (char *)&pStep[1];
   667         -    pStep->target.n = nFrom;
   668         -    memcpy((char *)pStep->target.z, zFrom, nFrom);
   669         -
   670    653       for(i=0; i<pFKey->nCol; i++){
   671         -      Expr *pEq;
   672         -      int iFromCol;               /* Idx of column in referencing table */
          654  +      Token tOld = { "old", 3 };  /* Literal "old" token */
          655  +      Token tNew = { "new", 3 };  /* Literal "new" token */
   673    656         Token tFromCol;             /* Name of column in referencing table */
   674    657         Token tToCol;               /* Name of column in referenced table */
   675         -      Token tOld = { "old", 3 };  /* Literal "old" token */
   676         -      Token tNew = { "new", 3 };  /* Literal "new" token */
          658  +      int iFromCol;               /* Idx of column in referencing table */
          659  +      Expr *pEq;                  /* tFromCol = OLD.tToCol */
   677    660   
   678    661         iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
   679    662         tToCol.z = pIdx ? pTab->aCol[pIdx->aiColumn[i]].zName : "oid";
   680    663         tFromCol.z = iFromCol<0 ? "oid" : pFKey->pFrom->aCol[iFromCol].zName;
   681    664   
   682    665         tToCol.n = sqlite3Strlen30(tToCol.z);
   683    666         tFromCol.n = sqlite3Strlen30(tFromCol.z);
................................................................................
   686    669         pEq = sqlite3PExpr(pParse, TK_EQ,
   687    670             sqlite3PExpr(pParse, TK_ID, 0, 0, &tFromCol),
   688    671             sqlite3PExpr(pParse, TK_DOT, 
   689    672               sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld),
   690    673               sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol)
   691    674             , 0)
   692    675         , 0);
   693         -      pWhere = sqlite3ExprAnd(pParse->db, pWhere, pEq);
          676  +      pWhere = sqlite3ExprAnd(db, pWhere, pEq);
   694    677   
   695    678         if( action!=OE_Cascade || pChanges ){
   696    679           Expr *pNew;
   697    680           if( action==OE_Cascade ){
   698    681             pNew = sqlite3PExpr(pParse, TK_DOT, 
   699    682               sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew),
   700    683               sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol)
................................................................................
   709    692           }else{
   710    693             pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0);
   711    694           }
   712    695           pList = sqlite3ExprListAppend(pParse, pList, pNew);
   713    696           sqlite3ExprListSetName(pParse, pList, &tFromCol, 0);
   714    697         }
   715    698       }
   716         -    sqlite3DbFree(pParse->db, aiCol);
          699  +    sqlite3DbFree(db, aiCol);
          700  +
          701  +    /* If pTab->dbMem==0, then the table may be part of a shared-schema.
          702  +    ** Disable the lookaside buffer before allocating space for the
          703  +    ** trigger definition in this case.  */
          704  +    enableLookaside = db->lookaside.bEnabled;
          705  +    if( pTab->dbMem==0 ){
          706  +      db->lookaside.bEnabled = 0;
          707  +    }
          708  +
          709  +    zFrom = pFKey->pFrom->zName;
          710  +    nFrom = sqlite3Strlen30(zFrom);
          711  +    pTrigger = (Trigger *)sqlite3DbMallocZero(db, 
          712  +        sizeof(Trigger) +         /* struct Trigger */
          713  +        sizeof(TriggerStep) +     /* Single step in trigger program */
          714  +        nFrom + 1                 /* Space for pStep->target.z */
          715  +    );
          716  +    if( pTrigger ){
          717  +      pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1];
          718  +      pStep->target.z = (char *)&pStep[1];
          719  +      pStep->target.n = nFrom;
          720  +      memcpy((char *)pStep->target.z, zFrom, nFrom);
          721  +  
          722  +      pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
          723  +      pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE);
          724  +    }
   717    725   
   718         -    pStep->pWhere = sqlite3ExprDup(dbMem, pWhere, EXPRDUP_REDUCE);
   719         -    pStep->pExprList = sqlite3ExprListDup(dbMem, pList, EXPRDUP_REDUCE);
          726  +    /* Re-enable the lookaside buffer, if it was disabled earlier. */
          727  +    db->lookaside.bEnabled = enableLookaside;
          728  +
   720    729       sqlite3ExprDelete(pParse->db, pWhere);
   721    730       sqlite3ExprListDelete(pParse->db, pList);
          731  +    if( db->mallocFailed==1 ){
          732  +      fkTriggerDelete(db, pTrigger);
          733  +      return 0;
          734  +    }
   722    735   
   723    736       pStep->op = (action!=OE_Cascade || pChanges) ? TK_UPDATE : TK_DELETE;
   724    737       pStep->pTrig = pTrigger;
   725    738       pTrigger->pSchema = pTab->pSchema;
   726    739       pTrigger->pTabSchema = pTab->pSchema;
   727    740   
   728    741       if( pChanges ){

Changes to test/fkey2.test.

    39     39   # fkey2-5.*: Check that if foreign-keys are enabled, it is not possible
    40     40   #            to write to an FK column using the incremental blob API.
    41     41   #
    42     42   # fkey2-6.*: Test that FK processing is automatically disabled when 
    43     43   #            running VACUUM.
    44     44   #
    45     45   # fkey2-7.*: Test using an IPK as the key in the child (referencing) table.
           46  +#
           47  +# fkey2-8.*: Test that enabling/disabling foreign key support while a 
           48  +#            transaction is active is not possible.
    46     49   #
    47     50   # fkey2-genfkey.*: Tests that were used with the shell tool .genfkey
    48     51   #            command. Recycled to test the built-in implementation.
    49     52   #
    50     53   
    51     54   
    52     55   proc drop_all_tables {{db db}} {
................................................................................
   410    413       }
   411    414     } {}
   412    415   }
   413    416   
   414    417   #-------------------------------------------------------------------------
   415    418   # Test that it is possible to use an INTEGER PRIMARY KEY as the child key
   416    419   # of a foreign constraint.
          420  +# 
   417    421   drop_all_tables
   418    422   do_test fkey2-7.1 {
   419    423     execsql {
   420    424       CREATE TABLE t1(a PRIMARY KEY, b);
   421    425       CREATE TABLE t2(c INTEGER PRIMARY KEY REFERENCES t1, b);
   422    426     }
   423    427   } {}
................................................................................
   442    446   } {1 {foreign key constraint failed}}
   443    447   do_test fkey2-7.7 {
   444    448     execsql { DELETE FROM t1 WHERE a = 1 }
   445    449   } {}
   446    450   do_test fkey2-7.8 {
   447    451     catchsql { UPDATE t1 SET a = 3 }
   448    452   } {1 {foreign key constraint failed}}
          453  +
          454  +#-------------------------------------------------------------------------
          455  +# Test that it is not possible to enable/disable FK support while a
          456  +# transaction is open.
          457  +# 
          458  +drop_all_tables
          459  +proc fkey2-8-test {tn zSql value} {
          460  +  do_test fkey-2.8.$tn.1 [list execsql $zSql] {}
          461  +  do_test fkey-2.8.$tn.2 { execsql "PRAGMA foreign_keys" } $value
          462  +}
          463  +fkey2-8-test  1 { PRAGMA foreign_keys = 0     } 0
          464  +fkey2-8-test  2 { PRAGMA foreign_keys = 1     } 1
          465  +fkey2-8-test  3 { BEGIN                       } 1
          466  +fkey2-8-test  4 { PRAGMA foreign_keys = 0     } 1
          467  +fkey2-8-test  5 { COMMIT                      } 1
          468  +fkey2-8-test  6 { PRAGMA foreign_keys = 0     } 0
          469  +fkey2-8-test  7 { BEGIN                       } 0
          470  +fkey2-8-test  8 { PRAGMA foreign_keys = 1     } 0
          471  +fkey2-8-test  9 { COMMIT                      } 0
          472  +fkey2-8-test 10 { PRAGMA foreign_keys = 1     } 1
          473  +fkey2-8-test 11 { PRAGMA foreign_keys = off   } 0
          474  +fkey2-8-test 12 { PRAGMA foreign_keys = on    } 1
          475  +fkey2-8-test 13 { PRAGMA foreign_keys = no    } 0
          476  +fkey2-8-test 14 { PRAGMA foreign_keys = yes   } 1
          477  +fkey2-8-test 15 { PRAGMA foreign_keys = false } 0
          478  +fkey2-8-test 16 { PRAGMA foreign_keys = true  } 1
   449    479   
   450    480   #-------------------------------------------------------------------------
   451    481   # The following block of tests, those prefixed with "fkey2-genfkey.", are 
   452    482   # the same tests that were used to test the ".genfkey" command provided 
   453    483   # by the shell tool. So these tests show that the built-in foreign key 
   454    484   # implementation is more or less compatible with the triggers generated 
   455    485   # by genfkey.

Added test/fkey_malloc.test.

            1  +# 2009 September 22
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +#
           13  +
           14  +set testdir [file dirname $argv0]
           15  +source $testdir/tester.tcl
           16  +
           17  +ifcapable !foreignkey||!trigger {
           18  +  finish_test
           19  +  return
           20  +}
           21  +source $testdir/malloc_common.tcl
           22  +
           23  +do_malloc_test fkey_malloc-1 -sqlprep {
           24  +  PRAGMA foreign_keys = 1;
           25  +  CREATE TABLE t1(a PRIMARY KEY, b);
           26  +  CREATE TABLE t2(x REFERENCES t1 ON UPDATE CASCADE ON DELETE CASCADE);
           27  +} -sqlbody {
           28  +  INSERT INTO t1 VALUES('aaa', 1);
           29  +  INSERT INTO t2 VALUES('aaa');
           30  +  UPDATE t1 SET a = 'bbb';
           31  +  DELETE FROM t1;
           32  +}
           33  +
           34  +finish_test
           35  +
           36  +