/ Check-in [5fdb6b0aaf]
Login

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

Overview
Comment:Edit the WHEN and UPDATE OF clauses of trigger programs as part of ALTER TABLE RENAME COLUMN.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | edit-trigger-wrapper
Files: files | file ages | folders
SHA3-256: 5fdb6b0aafba727139e1937ef5950e4434a77f95a10fc46f8010ca2de3922326
User & Date: dan 2018-08-13 17:14:26
Context
2018-08-14
20:18
Have ALTER TABLE RENAME edit column references in CREATE VIEW statements. check-in: db829dc1a2 user: dan tags: edit-trigger-wrapper
2018-08-13
17:14
Edit the WHEN and UPDATE OF clauses of trigger programs as part of ALTER TABLE RENAME COLUMN. check-in: 5fdb6b0aaf user: dan tags: edit-trigger-wrapper
15:09
Fix legacy comments on Token. Begin commenting the new ALTER TABLE RENAME COLUMN code. Fix a memory leak in the sqlite_rename_column() SQL function. check-in: 32edc89203 user: drh tags: alter-table-rename-column
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/alter.c.

   842    842     if( !zNew ) goto exit_rename_column;
   843    843     assert( pNew->n>0 );
   844    844     bQuote = sqlite3Isquote(pNew->z[0]);
   845    845     sqlite3NestedParse(pParse, 
   846    846         "UPDATE \"%w\".%s SET "
   847    847         "sql = sqlite_rename_column(sql, %d, %d, %Q, %Q, %Q) "
   848    848         "WHERE name NOT LIKE 'sqlite_%%' AND ("
   849         -      "   type = 'table' OR (type='index' AND tbl_name = %Q)"
          849  +      "   type = 'table' OR (type IN ('index', 'trigger') AND tbl_name = %Q)"
   850    850         ")",
   851    851         zDb, MASTER_NAME, iCol, bQuote, zNew, pTab->zName, zOld, pTab->zName
   852    852     );
   853    853   
   854    854     /* Drop and reload the database schema. */
   855    855     if( pParse->pVdbe ){
   856    856       sqlite3ChangeCookie(pParse, iSchema);
................................................................................
   887    887     RenameToken *pNext;    /* Next is a list of all RenameToken objects */
   888    888   };
   889    889   
   890    890   /*
   891    891   ** The context of an ALTER TABLE RENAME COLUMN operation that gets passed
   892    892   ** down into the Walker.
   893    893   */
          894  +typedef struct RenameCtx RenameCtx;
   894    895   struct RenameCtx {
   895    896     RenameToken *pList;             /* List of tokens to overwrite */
   896    897     int nList;                      /* Number of tokens in pList */
   897    898     int iCol;                       /* Index of column being renamed */
          899  +  const char *zOld;               /* Old column name */
   898    900   };
   899    901   
   900    902   /*
   901    903   ** Add a new RenameToken object mapping parse tree element pPtr into
   902    904   ** token *pToken to the Parse object currently under construction.
   903    905   */
   904    906   void sqlite3RenameToken(Parse *pParse, void *pPtr, Token *pToken){
................................................................................
   936    938     RenameToken *p;
   937    939     for(p=pToken; p; p=pNext){
   938    940       pNext = p->pNext;
   939    941       sqlite3DbFree(db, p);
   940    942     }
   941    943   }
   942    944   
   943         -static RenameToken *renameTokenFind(Parse *pParse, void *pPtr){
          945  +static void renameTokenFind(Parse *pParse, RenameCtx *pCtx, void *pPtr){
   944    946     RenameToken **pp;
   945    947     for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){
   946    948       if( (*pp)->p==pPtr ){
   947    949         RenameToken *pToken = *pp;
   948    950         *pp = pToken->pNext;
   949         -      pToken->pNext = 0;
   950         -      return pToken;
          951  +      pToken->pNext = pCtx->pList;
          952  +      pCtx->pList = pToken;
          953  +      pCtx->nList++;
          954  +      break;
   951    955       }
   952    956     }
   953         -  return 0;
   954    957   }
   955    958   
   956    959   static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){
   957         -  struct RenameCtx *p = pWalker->u.pRename;
   958         -  if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol ){
   959         -    RenameToken *pTok = renameTokenFind(pWalker->pParse, (void*)pExpr);
   960         -    if( pTok ){
   961         -      pTok->pNext = p->pList;
   962         -      p->pList = pTok;
   963         -      p->nList++;
          960  +  RenameCtx *p = pWalker->u.pRename;
          961  +  if( p->zOld && pExpr->op==TK_DOT ){
          962  +    Expr *pLeft = pExpr->pLeft;
          963  +    Expr *pRight = pExpr->pRight;
          964  +    assert( pLeft->op==TK_ID && pRight->op==TK_ID );
          965  +    if( 0==sqlite3_stricmp(pLeft->u.zToken, "old")
          966  +     || 0==sqlite3_stricmp(pLeft->u.zToken, "new")
          967  +    ){
          968  +      if( 0==sqlite3_stricmp(pRight->u.zToken, p->zOld) ){
          969  +        renameTokenFind(pWalker->pParse, p, (void*)pRight);
          970  +      }
   964    971       }
          972  +  }else if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol ){
          973  +    renameTokenFind(pWalker->pParse, p, (void*)pExpr);
   965    974     }
   966    975     return WRC_Continue;
   967    976   }
   968    977   
   969         -static RenameToken *renameColumnTokenNext(struct RenameCtx *pCtx){
          978  +static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
   970    979     RenameToken *pBest = pCtx->pList;
   971    980     RenameToken *pToken;
   972    981     RenameToken **pp;
   973    982   
   974    983     for(pToken=pBest->pNext; pToken; pToken=pToken->pNext){
   975    984       if( pToken->t.z>pBest->t.z ) pBest = pToken;
   976    985     }
................................................................................
   985    994   */
   986    995   static void renameColumnFunc(
   987    996     sqlite3_context *context,
   988    997     int NotUsed,
   989    998     sqlite3_value **argv
   990    999   ){
   991   1000     sqlite3 *db = sqlite3_context_db_handle(context);
   992         -  struct RenameCtx sCtx;
         1001  +  RenameCtx sCtx;
   993   1002     const char *zSql = (const char*)sqlite3_value_text(argv[0]);
   994   1003     int nSql = sqlite3_value_bytes(argv[0]);
   995   1004     int bQuote = sqlite3_value_int(argv[2]);
   996   1005     const char *zNew = (const char*)sqlite3_value_text(argv[3]);
   997   1006     int nNew = sqlite3_value_bytes(argv[3]);
   998   1007     const char *zTable = (const char*)sqlite3_value_text(argv[4]);
   999   1008     const char *zOld = (const char*)sqlite3_value_text(argv[5]);
................................................................................
  1015   1024     memset(&sParse, 0, sizeof(sParse));
  1016   1025     sParse.eParseMode = PARSE_MODE_RENAME_COLUMN;
  1017   1026     sParse.db = db;
  1018   1027     sParse.nQueryLoop = 1;
  1019   1028     rc = sqlite3RunParser(&sParse, zSql, &zErr);
  1020   1029     assert( sParse.pNewTable==0 || sParse.pNewIndex==0 );
  1021   1030     if( db->mallocFailed ) rc = SQLITE_NOMEM;
  1022         -  if( rc==SQLITE_OK && sParse.pNewTable==0 && sParse.pNewIndex==0 ){
         1031  +  if( rc==SQLITE_OK 
         1032  +   && sParse.pNewTable==0 && sParse.pNewIndex==0 && sParse.pNewTrigger==0 
         1033  +  ){
  1023   1034       rc = SQLITE_CORRUPT_BKPT;
  1024   1035     }
  1025   1036   
  1026   1037     if( rc==SQLITE_OK ){
  1027   1038       zQuot = sqlite3_mprintf("\"%w\"", zNew);
  1028   1039       if( zQuot==0 ){
  1029   1040         rc = SQLITE_NOMEM;
................................................................................
  1063   1074     sWalker.xExprCallback = renameColumnExprCb;
  1064   1075     sWalker.u.pRename = &sCtx;
  1065   1076   
  1066   1077     if( sParse.pNewTable ){
  1067   1078       int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName);
  1068   1079       FKey *pFKey;
  1069   1080       if( bFKOnly==0 ){
  1070         -      sCtx.pList = renameTokenFind(
  1071         -          &sParse, (void*)sParse.pNewTable->aCol[sCtx.iCol].zName
         1081  +      renameTokenFind(
         1082  +          &sParse, &sCtx, (void*)sParse.pNewTable->aCol[sCtx.iCol].zName
  1072   1083         );
  1073         -      sCtx.nList = 1;
  1074   1084         assert( sCtx.iCol>=0 );
  1075   1085         if( sParse.pNewTable->iPKey==sCtx.iCol ){
  1076   1086           sCtx.iCol = -1;
  1077   1087         }
  1078   1088         sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck);
  1079   1089         for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){
  1080   1090           sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
  1081   1091         }
  1082   1092       }
  1083   1093   
  1084   1094       for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
  1085   1095         for(i=0; i<pFKey->nCol; i++){
  1086         -        RenameToken *pTok = 0;
  1087   1096           if( bFKOnly==0 && pFKey->aCol[i].iFrom==sCtx.iCol ){
  1088         -          pTok = renameTokenFind(&sParse, (void*)&pFKey->aCol[i]);
  1089         -          if( pTok ){
  1090         -            pTok->pNext = sCtx.pList;
  1091         -            sCtx.pList = pTok;
  1092         -            sCtx.nList++;
  1093         -          }
         1097  +          renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]);
  1094   1098           }
  1095   1099           if( 0==sqlite3_stricmp(pFKey->zTo, zTable)
  1096   1100            && 0==sqlite3_stricmp(pFKey->aCol[i].zCol, zOld)
  1097   1101           ){
  1098         -          pTok = renameTokenFind(&sParse, (void*)pFKey->aCol[i].zCol);
  1099         -          pTok->pNext = sCtx.pList;
  1100         -          sCtx.pList = pTok;
  1101         -          sCtx.nList++;
         1102  +          renameTokenFind(&sParse, &sCtx, (void*)pFKey->aCol[i].zCol);
  1102   1103           }
  1103   1104         }
  1104   1105       }
  1105         -  }else{
         1106  +  }else if( sParse.pNewIndex ){
  1106   1107       sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr);
  1107   1108       sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
         1109  +  }else{
         1110  +    sCtx.zOld = zOld;
         1111  +    sqlite3WalkExpr(&sWalker, sParse.pNewTrigger->pWhen);
         1112  +    if( sParse.pNewTrigger->pColumns ){
         1113  +      for(i=0; i<sParse.pNewTrigger->pColumns->nId; i++){
         1114  +        char *zName = sParse.pNewTrigger->pColumns->a[i].zName;
         1115  +        if( 0==sqlite3_stricmp(zName, zOld) ){
         1116  +          renameTokenFind(&sParse, &sCtx, (void*)zName);
         1117  +        }
         1118  +      }
         1119  +    }
  1108   1120     }
  1109   1121   
  1110   1122     assert( nQuot>=nNew );
  1111   1123     zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1);
  1112   1124     if( zOut ){
  1113   1125       int nOut = nSql;
  1114   1126       memcpy(zOut, zSql, nSql);
................................................................................
  1144   1156   
  1145   1157   renameColumnFunc_done:
  1146   1158     if( sParse.pVdbe ){
  1147   1159       sqlite3VdbeFinalize(sParse.pVdbe);
  1148   1160     }
  1149   1161     sqlite3DeleteTable(db, sParse.pNewTable);
  1150   1162     if( sParse.pNewIndex ) sqlite3FreeIndex(db, sParse.pNewIndex);
         1163  +  sqlite3DeleteTrigger(db, sParse.pNewTrigger);
  1151   1164     renameTokenFree(db, sParse.pRename);
  1152   1165     renameTokenFree(db, sCtx.pList);
  1153   1166     sqlite3ParserReset(&sParse);
  1154   1167     sqlite3_free(zQuot);
  1155   1168   }
  1156   1169   
  1157   1170   /*

Changes to src/build.c.

  3682   3682   
  3683   3683   /*
  3684   3684   ** Append a new element to the given IdList.  Create a new IdList if
  3685   3685   ** need be.
  3686   3686   **
  3687   3687   ** A new IdList is returned, or NULL if malloc() fails.
  3688   3688   */
  3689         -IdList *sqlite3IdListAppend(sqlite3 *db, IdList *pList, Token *pToken){
         3689  +IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
         3690  +  sqlite3 *db = pParse->db;
  3690   3691     int i;
  3691   3692     if( pList==0 ){
  3692   3693       pList = sqlite3DbMallocZero(db, sizeof(IdList) );
  3693   3694       if( pList==0 ) return 0;
  3694   3695     }
  3695   3696     pList->a = sqlite3ArrayAllocate(
  3696   3697         db,
................................................................................
  3700   3701         &i
  3701   3702     );
  3702   3703     if( i<0 ){
  3703   3704       sqlite3IdListDelete(db, pList);
  3704   3705       return 0;
  3705   3706     }
  3706   3707     pList->a[i].zName = sqlite3NameFromToken(db, pToken);
         3708  +  if( IN_RENAME_COLUMN && pList->a[i].zName ){
         3709  +    sqlite3RenameToken(pParse, (void*)pList->a[i].zName, pToken);
         3710  +  }
  3707   3711     return pList;
  3708   3712   }
  3709   3713   
  3710   3714   /*
  3711   3715   ** Delete an IdList.
  3712   3716   */
  3713   3717   void sqlite3IdListDelete(sqlite3 *db, IdList *pList){

Changes to src/parse.y.

   904    904   %destructor idlist_opt {sqlite3IdListDelete(pParse->db, $$);}
   905    905   %type idlist {IdList*}
   906    906   %destructor idlist {sqlite3IdListDelete(pParse->db, $$);}
   907    907   
   908    908   idlist_opt(A) ::= .                       {A = 0;}
   909    909   idlist_opt(A) ::= LP idlist(X) RP.    {A = X;}
   910    910   idlist(A) ::= idlist(A) COMMA nm(Y).
   911         -    {A = sqlite3IdListAppend(pParse->db,A,&Y);}
          911  +    {A = sqlite3IdListAppend(pParse,A,&Y);}
   912    912   idlist(A) ::= nm(Y).
   913         -    {A = sqlite3IdListAppend(pParse->db,0,&Y); /*A-overwrites-Y*/}
          913  +    {A = sqlite3IdListAppend(pParse,0,&Y); /*A-overwrites-Y*/}
   914    914   
   915    915   /////////////////////////// Expression Processing /////////////////////////////
   916    916   //
   917    917   
   918    918   %type expr {Expr*}
   919    919   %destructor expr {sqlite3ExprDelete(pParse->db, $$);}
   920    920   %type term {Expr*}

Changes to src/sqliteInt.h.

  3877   3877     void sqlite3AutoincrementEnd(Parse *pParse);
  3878   3878   #else
  3879   3879   # define sqlite3AutoincrementBegin(X)
  3880   3880   # define sqlite3AutoincrementEnd(X)
  3881   3881   #endif
  3882   3882   void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int, Upsert*);
  3883   3883   void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
  3884         -IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
         3884  +IdList *sqlite3IdListAppend(Parse*, IdList*, Token*);
  3885   3885   int sqlite3IdListIndex(IdList*,const char*);
  3886   3886   SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int);
  3887   3887   SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*);
  3888   3888   SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
  3889   3889                                         Token*, Select*, Expr*, IdList*);
  3890   3890   void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *);
  3891   3891   void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*);

Changes to src/tokenize.c.

   693    693     if( !IN_SPECIAL_PARSE ){
   694    694       /* If the pParse->declareVtab flag is set, do not delete any table 
   695    695       ** structure built up in pParse->pNewTable. The calling code (see vtab.c)
   696    696       ** will take responsibility for freeing the Table structure.
   697    697       */
   698    698       sqlite3DeleteTable(db, pParse->pNewTable);
   699    699     }
          700  +  if( !IN_RENAME_COLUMN ){
          701  +    sqlite3DeleteTrigger(db, pParse->pNewTrigger);
          702  +  }
   700    703   
   701    704     if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
   702         -  sqlite3DeleteTrigger(db, pParse->pNewTrigger);
   703    705     sqlite3DbFree(db, pParse->pVList);
   704    706     while( pParse->pAinc ){
   705    707       AutoincInfo *p = pParse->pAinc;
   706    708       pParse->pAinc = p->pNext;
   707    709       sqlite3DbFreeNN(db, p);
   708    710     }
   709    711     while( pParse->pZombieTab ){

Changes to src/trigger.c.

   177    177     /* Check that the trigger name is not reserved and that no trigger of the
   178    178     ** specified name exists */
   179    179     zName = sqlite3NameFromToken(db, pName);
   180    180     if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
   181    181       goto trigger_cleanup;
   182    182     }
   183    183     assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
   184         -  if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
   185         -    if( !noErr ){
   186         -      sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
   187         -    }else{
   188         -      assert( !db->init.busy );
   189         -      sqlite3CodeVerifySchema(pParse, iDb);
          184  +  if( !IN_RENAME_COLUMN ){
          185  +    if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
          186  +      if( !noErr ){
          187  +        sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
          188  +      }else{
          189  +        assert( !db->init.busy );
          190  +        sqlite3CodeVerifySchema(pParse, iDb);
          191  +      }
          192  +      goto trigger_cleanup;
   190    193       }
   191         -    goto trigger_cleanup;
   192    194     }
   193    195   
   194    196     /* Do not create a trigger on a system table */
   195    197     if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
   196    198       sqlite3ErrorMsg(pParse, "cannot create trigger on system table");
   197    199       goto trigger_cleanup;
   198    200     }
................................................................................
   208    210     if( !pTab->pSelect && tr_tm==TK_INSTEAD ){
   209    211       sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF"
   210    212           " trigger on table: %S", pTableName, 0);
   211    213       goto trigger_cleanup;
   212    214     }
   213    215   
   214    216   #ifndef SQLITE_OMIT_AUTHORIZATION
   215         -  {
          217  +  if( !IN_RENAME_COLUMN ){
   216    218       int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
   217    219       int code = SQLITE_CREATE_TRIGGER;
   218    220       const char *zDb = db->aDb[iTabDb].zDbSName;
   219    221       const char *zDbTrig = isTemp ? db->aDb[1].zDbSName : zDb;
   220    222       if( iTabDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
   221    223       if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){
   222    224         goto trigger_cleanup;
................................................................................
   242    244     pTrigger->zName = zName;
   243    245     zName = 0;
   244    246     pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName);
   245    247     pTrigger->pSchema = db->aDb[iDb].pSchema;
   246    248     pTrigger->pTabSchema = pTab->pSchema;
   247    249     pTrigger->op = (u8)op;
   248    250     pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
   249         -  pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
   250         -  pTrigger->pColumns = sqlite3IdListDup(db, pColumns);
          251  +  if( IN_RENAME_COLUMN ){
          252  +    pTrigger->pWhen = pWhen;
          253  +    pWhen = 0;
          254  +  }else{
          255  +    pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
          256  +  }
          257  +  pTrigger->pColumns = pColumns;
          258  +  pColumns = 0;
   251    259     assert( pParse->pNewTrigger==0 );
   252    260     pParse->pNewTrigger = pTrigger;
   253    261   
   254    262   trigger_cleanup:
   255    263     sqlite3DbFree(db, zName);
   256    264     sqlite3SrcListDelete(db, pTableName);
   257    265     sqlite3IdListDelete(db, pColumns);
................................................................................
   291    299     sqlite3TokenInit(&nameToken, pTrig->zName);
   292    300     sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken);
   293    301     if( sqlite3FixTriggerStep(&sFix, pTrig->step_list) 
   294    302      || sqlite3FixExpr(&sFix, pTrig->pWhen) 
   295    303     ){
   296    304       goto triggerfinish_cleanup;
   297    305     }
          306  +
          307  +#ifndef SQLITE_OMIT_ALTERTABLE
          308  +  if( IN_RENAME_COLUMN ){
          309  +    assert( !db->init.busy );
          310  +    pParse->pNewTrigger = pTrig;
          311  +    pTrig = 0;
          312  +  }else
          313  +#endif
   298    314   
   299    315     /* if we are not initializing,
   300    316     ** build the sqlite_master entry
   301    317     */
   302    318     if( !db->init.busy ){
   303    319       Vdbe *v;
   304    320       char *z;
................................................................................
   333    349         pLink->pNext = pTab->pTrigger;
   334    350         pTab->pTrigger = pLink;
   335    351       }
   336    352     }
   337    353   
   338    354   triggerfinish_cleanup:
   339    355     sqlite3DeleteTrigger(db, pTrig);
   340         -  assert( !pParse->pNewTrigger );
          356  +  assert( IN_RENAME_COLUMN || !pParse->pNewTrigger );
   341    357     sqlite3DeleteTriggerStep(db, pStepList);
   342    358   }
   343    359   
   344    360   /*
   345    361   ** Duplicate a range of text from an SQL statement, then convert all
   346    362   ** whitespace characters into ordinary space characters.
   347    363   */

Changes to test/altercol.test.

   126    126   db close
   127    127   sqlite3 db test.db
   128    128   
   129    129   do_execsql_test 3.2 {
   130    130     SELECT * FROM t4;
   131    131   } {3 2 1}
   132    132   
   133         -# do_execsql_test 3.3 { INSERT INTO t4 VALUES(6, 5, 4); } {}
          133  +do_execsql_test 3.3 { INSERT INTO t4 VALUES(6, 5, 4); } {}
   134    134   
   135    135   #-------------------------------------------------------------------------
   136    136   #
   137    137   do_execsql_test 4.0 {
   138    138     CREATE TABLE c1(a, b, FOREIGN KEY (a, b) REFERENCES p1(c, d));
   139    139     CREATE TABLE p1(c, d, PRIMARY KEY(c, d));
   140    140     PRAGMA foreign_keys = 1;
................................................................................
   203    203   do_catchsql_test 6.2 {
   204    204     ALTER TABLE "blob" RENAME COLUMN "a1" TO [where];
   205    205   } {0 {}}
   206    206   
   207    207   do_execsql_test 6.3 {
   208    208     SELECT "where" FROM blob;
   209    209   } {}
          210  +
          211  +#-------------------------------------------------------------------------
          212  +#
          213  +reset_db
          214  +do_execsql_test 7.0 {
          215  +  CREATE TABLE c(x);
          216  +  INSERT INTO c VALUES(0);
          217  +  CREATE TABLE t6("col a", "col b", "col c");
          218  +  CREATE TRIGGER zzz AFTER UPDATE OF "col a", "col c" ON t6 BEGIN
          219  +    UPDATE c SET x=x+1;
          220  +  END;
          221  +}
          222  +
          223  +do_execsql_test 7.1 {
          224  +  INSERT INTO t6 VALUES(0, 0, 0);
          225  +  UPDATE t6 SET "col c" = 1;
          226  +  SELECT * FROM c;
          227  +} {1}
          228  +
          229  +do_execsql_test 7.2 {
          230  +  ALTER TABLE t6 RENAME "col c" TO "col 3";
          231  +}
          232  +
          233  +do_execsql_test 7.3 {
          234  +  UPDATE t6 SET "col 3" = 0;
          235  +  SELECT * FROM c;
          236  +} {2}
   210    237   
   211    238   finish_test
   212    239