/ Check-in [b3bb660a]
Login

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

Overview
Comment:Prevent a virtual table from being destroyed while it is in use. Also: replace Vdbe.inVtabMethod with sqlite3.nVDestroy. Simplify the EXPLAIN output for P4.pVtab to only show the sqlite3_vtab pointer. Cherrypick of [cbeb9a1aed8c].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | branch-3.8.6
Files: files | file ages | folders
SHA1: b3bb660af9472e2c511d1fe87b5193256f74c0db
User & Date: dan 2015-05-21 17:24:32
Context
2017-07-21
03:23
Add new interfaces sqlite3_result_pointer(), and sqlite3_value_pointer() and use them to transfer the eponymous FTS3 column pointer to the snippet() and offsets() routines. This is a cherry-pick of [f0f492245e95], which is in turn inspired by check-in [72de49f2]. Leaf check-in: a66a5b39 user: drh tags: branch-3.8.6
2015-05-21
17:24
Prevent a virtual table from being destroyed while it is in use. Also: replace Vdbe.inVtabMethod with sqlite3.nVDestroy. Simplify the EXPLAIN output for P4.pVtab to only show the sqlite3_vtab pointer. Cherrypick of [cbeb9a1aed8c]. check-in: b3bb660a user: dan tags: branch-3.8.6
17:21
Avoid ever writing before the start of an allocated buffer in the DIRECT_OVERFLOW_READ code. Fix for [e3a290961a6]. Cherrypick of [c3c15d20c691]. check-in: 31b13eb5 user: dan tags: branch-3.8.6
2015-03-24
16:43
Prevent a virtual table from being destroyed while it is in use. Also: replace Vdbe.inVtabMethod with sqlite3.nVDestroy. Simplify the EXPLAIN output for P4.pVtab to only show the sqlite3_vtab pointer. check-in: cbeb9a1a user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/sqlite.h.in.

  5494   5494   ** take care that any prior string is freed by a call to [sqlite3_free()]
  5495   5495   ** prior to assigning a new string to zErrMsg.  ^After the error message
  5496   5496   ** is delivered up to the client application, the string will be automatically
  5497   5497   ** freed by sqlite3_free() and the zErrMsg field will be zeroed.
  5498   5498   */
  5499   5499   struct sqlite3_vtab {
  5500   5500     const sqlite3_module *pModule;  /* The module for this virtual table */
  5501         -  int nRef;                       /* NO LONGER USED */
         5501  +  int nRef;                       /* Number of open cursors */
  5502   5502     char *zErrMsg;                  /* Error message from sqlite3_mprintf() */
  5503   5503     /* Virtual table implementations will typically add additional fields */
  5504   5504   };
  5505   5505   
  5506   5506   /*
  5507   5507   ** CAPI3REF: Virtual Table Cursor Object
  5508   5508   ** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor}

Changes to src/sqliteInt.h.

   990    990       u8 busy;                    /* TRUE if currently initializing */
   991    991       u8 orphanTrigger;           /* Last statement is orphaned TEMP trigger */
   992    992     } init;
   993    993     int nVdbeActive;              /* Number of VDBEs currently running */
   994    994     int nVdbeRead;                /* Number of active VDBEs that read or write */
   995    995     int nVdbeWrite;               /* Number of active VDBEs that read and write */
   996    996     int nVdbeExec;                /* Number of nested calls to VdbeExec() */
          997  +  int nVDestroy;                /* Number of active OP_VDestroy operations */
   997    998     int nExtension;               /* Number of loaded extensions */
   998    999     void **aExtension;            /* Array of shared library handles */
   999   1000     void (*xTrace)(void*,const char*);        /* Trace function */
  1000   1001     void *pTraceArg;                          /* Argument to the trace function */
  1001   1002     void (*xProfile)(void*,const char*,u64);  /* Profiling function */
  1002   1003     void *pProfileArg;                        /* Argument to profile function */
  1003   1004     void *pCommitArg;                 /* Argument to xCommitCallback() */   

Changes to src/vdbe.c.

  4942   4942   ** the last one in the database) then a zero is stored in register P2.
  4943   4943   ** If AUTOVACUUM is disabled then a zero is stored in register P2.
  4944   4944   **
  4945   4945   ** See also: Clear
  4946   4946   */
  4947   4947   case OP_Destroy: {     /* out2-prerelease */
  4948   4948     int iMoved;
  4949         -  int iCnt;
  4950         -  Vdbe *pVdbe;
  4951   4949     int iDb;
  4952   4950   
  4953   4951     assert( p->readOnly==0 );
  4954         -#ifndef SQLITE_OMIT_VIRTUALTABLE
  4955         -  iCnt = 0;
  4956         -  for(pVdbe=db->pVdbe; pVdbe; pVdbe = pVdbe->pNext){
  4957         -    if( pVdbe->magic==VDBE_MAGIC_RUN && pVdbe->bIsReader 
  4958         -     && pVdbe->inVtabMethod<2 && pVdbe->pc>=0 
  4959         -    ){
  4960         -      iCnt++;
  4961         -    }
  4962         -  }
  4963         -#else
  4964         -  iCnt = db->nVdbeRead;
  4965         -#endif
  4966   4952     pOut->flags = MEM_Null;
  4967         -  if( iCnt>1 ){
         4953  +  if( db->nVdbeRead > db->nVDestroy+1 ){
  4968   4954       rc = SQLITE_LOCKED;
  4969   4955       p->errorAction = OE_Abort;
  4970   4956     }else{
  4971   4957       iDb = pOp->p3;
  4972         -    assert( iCnt==1 );
  4973   4958       assert( DbMaskTest(p->btreeMask, iDb) );
  4974   4959       iMoved = 0;  /* Not needed.  Only to silence a warning. */
  4975   4960       rc = sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved);
  4976   4961       pOut->flags = MEM_Int;
  4977   4962       pOut->u.i = iMoved;
  4978   4963   #ifndef SQLITE_OMIT_AUTOVACUUM
  4979   4964       if( rc==SQLITE_OK && iMoved!=0 ){
................................................................................
  6003   5988   #ifndef SQLITE_OMIT_VIRTUALTABLE
  6004   5989   /* Opcode: VDestroy P1 * * P4 *
  6005   5990   **
  6006   5991   ** P4 is the name of a virtual table in database P1.  Call the xDestroy method
  6007   5992   ** of that table.
  6008   5993   */
  6009   5994   case OP_VDestroy: {
  6010         -  p->inVtabMethod = 2;
         5995  +  db->nVDestroy++;
  6011   5996     rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z);
  6012         -  p->inVtabMethod = 0;
         5997  +  db->nVDestroy--;
  6013   5998     break;
  6014   5999   }
  6015   6000   #endif /* SQLITE_OMIT_VIRTUALTABLE */
  6016   6001   
  6017   6002   #ifndef SQLITE_OMIT_VIRTUALTABLE
  6018   6003   /* Opcode: VOpen P1 * * P4 *
  6019   6004   **
................................................................................
  6021   6006   ** P1 is a cursor number.  This opcode opens a cursor to the virtual
  6022   6007   ** table and stores that cursor in P1.
  6023   6008   */
  6024   6009   case OP_VOpen: {
  6025   6010     VdbeCursor *pCur;
  6026   6011     sqlite3_vtab_cursor *pVtabCursor;
  6027   6012     sqlite3_vtab *pVtab;
  6028         -  sqlite3_module *pModule;
         6013  +  const sqlite3_module *pModule;
  6029   6014   
  6030   6015     assert( p->bIsReader );
  6031   6016     pCur = 0;
  6032   6017     pVtabCursor = 0;
  6033   6018     pVtab = pOp->p4.pVtab->pVtab;
  6034         -  pModule = (sqlite3_module *)pVtab->pModule;
  6035         -  assert(pVtab && pModule);
         6019  +  if( pVtab==0 || NEVER(pVtab->pModule==0) ){
         6020  +    rc = SQLITE_LOCKED;
         6021  +    break;
         6022  +  }
         6023  +  pModule = pVtab->pModule;
  6036   6024     rc = pModule->xOpen(pVtab, &pVtabCursor);
  6037   6025     sqlite3VtabImportErrmsg(p, pVtab);
  6038   6026     if( SQLITE_OK==rc ){
  6039   6027       /* Initialize sqlite3_vtab_cursor base class */
  6040   6028       pVtabCursor->pVtab = pVtab;
  6041   6029   
  6042   6030       /* Initialize vdbe cursor object */
  6043   6031       pCur = allocateCursor(p, pOp->p1, 0, -1, 0);
  6044   6032       if( pCur ){
  6045   6033         pCur->pVtabCursor = pVtabCursor;
         6034  +      pVtab->nRef++;
  6046   6035       }else{
  6047   6036         db->mallocFailed = 1;
  6048   6037         pModule->xClose(pVtabCursor);
  6049   6038       }
  6050   6039     }
  6051   6040     break;
  6052   6041   }
................................................................................
  6104   6093     {
  6105   6094       res = 0;
  6106   6095       apArg = p->apArg;
  6107   6096       for(i = 0; i<nArg; i++){
  6108   6097         apArg[i] = &pArgc[i+1];
  6109   6098       }
  6110   6099   
  6111         -    p->inVtabMethod = 1;
  6112   6100       rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg);
  6113         -    p->inVtabMethod = 0;
  6114   6101       sqlite3VtabImportErrmsg(p, pVtab);
  6115   6102       if( rc==SQLITE_OK ){
  6116   6103         res = pModule->xEof(pVtabCursor);
  6117   6104       }
  6118   6105       VdbeBranchTaken(res!=0,2);
  6119   6106       if( res ){
  6120   6107         pc = pOp->p2 - 1;
................................................................................
  6209   6196   
  6210   6197     /* Invoke the xNext() method of the module. There is no way for the
  6211   6198     ** underlying implementation to return an error if one occurs during
  6212   6199     ** xNext(). Instead, if an error occurs, true is returned (indicating that 
  6213   6200     ** data is available) and the error code returned when xColumn or
  6214   6201     ** some other method is next invoked on the save virtual table cursor.
  6215   6202     */
  6216         -  p->inVtabMethod = 1;
  6217   6203     rc = pModule->xNext(pCur->pVtabCursor);
  6218         -  p->inVtabMethod = 0;
  6219   6204     sqlite3VtabImportErrmsg(p, pVtab);
  6220   6205     if( rc==SQLITE_OK ){
  6221   6206       res = pModule->xEof(pCur->pVtabCursor);
  6222   6207     }
  6223   6208     VdbeBranchTaken(!res,2);
  6224   6209     if( !res ){
  6225   6210       /* If there is data, jump to P2 */
................................................................................
  6286   6271   ** is set to the value of the rowid for the row just inserted.
  6287   6272   **
  6288   6273   ** P5 is the error actions (OE_Replace, OE_Fail, OE_Ignore, etc) to
  6289   6274   ** apply in the case of a constraint failure on an insert or update.
  6290   6275   */
  6291   6276   case OP_VUpdate: {
  6292   6277     sqlite3_vtab *pVtab;
  6293         -  sqlite3_module *pModule;
         6278  +  const sqlite3_module *pModule;
  6294   6279     int nArg;
  6295   6280     int i;
  6296   6281     sqlite_int64 rowid;
  6297   6282     Mem **apArg;
  6298   6283     Mem *pX;
  6299   6284   
  6300   6285     assert( pOp->p2==1        || pOp->p5==OE_Fail   || pOp->p5==OE_Rollback 
  6301   6286          || pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
  6302   6287     );
  6303   6288     assert( p->readOnly==0 );
  6304   6289     pVtab = pOp->p4.pVtab->pVtab;
  6305         -  pModule = (sqlite3_module *)pVtab->pModule;
         6290  +  if( pVtab==0 || NEVER(pVtab->pModule==0) ){
         6291  +    rc = SQLITE_LOCKED;
         6292  +    break;
         6293  +  }
         6294  +  pModule = pVtab->pModule;
  6306   6295     nArg = pOp->p2;
  6307   6296     assert( pOp->p4type==P4_VTAB );
  6308   6297     if( ALWAYS(pModule->xUpdate) ){
  6309   6298       u8 vtabOnConflict = db->vtabOnConflict;
  6310   6299       apArg = p->apArg;
  6311   6300       pX = &aMem[pOp->p3];
  6312   6301       for(i=0; i<nArg; i++){

Changes to src/vdbeInt.h.

   296    296   
   297    297   /*
   298    298   ** An instance of the virtual machine.  This structure contains the complete
   299    299   ** state of the virtual machine.
   300    300   **
   301    301   ** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare()
   302    302   ** is really a pointer to an instance of this structure.
   303         -**
   304         -** The Vdbe.inVtabMethod variable is set to non-zero for the duration of
   305         -** any virtual table method invocations made by the vdbe program. It is
   306         -** set to 2 for xDestroy method calls and 1 for all other methods. This
   307         -** variable is used for two purposes: to allow xDestroy methods to execute
   308         -** "DROP TABLE" statements and to prevent some nasty side effects of
   309         -** malloc failure when SQLite is invoked recursively by a virtual table 
   310         -** method function.
   311    303   */
   312    304   struct Vdbe {
   313    305     sqlite3 *db;            /* The database connection that owns this statement */
   314    306     Op *aOp;                /* Space to hold the virtual machine's program */
   315    307     Mem *aMem;              /* The memory locations */
   316    308     Mem **apArg;            /* Arguments to currently executing user function */
   317    309     Mem *aColName;          /* Column names to return */
................................................................................
   331    323     u32 cacheCtr;           /* VdbeCursor row cache generation counter */
   332    324     int pc;                 /* The program counter */
   333    325     int rc;                 /* Value to return */
   334    326     u16 nResColumn;         /* Number of columns in one row of the result set */
   335    327     u8 errorAction;         /* Recovery action to do in case of an error */
   336    328     u8 minWriteFileFormat;  /* Minimum file format for writable database files */
   337    329     bft explain:2;          /* True if EXPLAIN present on SQL command */
   338         -  bft inVtabMethod:2;     /* See comments above */
   339    330     bft changeCntOn:1;      /* True to update the change-counter */
   340    331     bft expired:1;          /* True if the VM needs to be recompiled */
   341    332     bft runOnlyOnce:1;      /* Automatically expire on reset */
   342    333     bft usesStmtJournal:1;  /* True if uses a statement journal */
   343    334     bft readOnly:1;         /* True for statements that do not write */
   344    335     bft bIsReader:1;        /* True for statements that read */
   345    336     bft isPrepareV2:1;      /* True if prepared with prepare_v2() */

Changes to src/vdbeaux.c.

  1084   1084           zP4 = "(blob)";
  1085   1085         }
  1086   1086         break;
  1087   1087       }
  1088   1088   #ifndef SQLITE_OMIT_VIRTUALTABLE
  1089   1089       case P4_VTAB: {
  1090   1090         sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab;
  1091         -      sqlite3_snprintf(nTemp, zTemp, "vtab:%p:%p", pVtab, pVtab->pModule);
         1091  +      sqlite3_snprintf(nTemp, zTemp, "vtab:%p", pVtab);
  1092   1092         break;
  1093   1093       }
  1094   1094   #endif
  1095   1095       case P4_INTARRAY: {
  1096   1096         sqlite3_snprintf(nTemp, zTemp, "intarray");
  1097   1097         break;
  1098   1098       }
................................................................................
  1745   1745     }else if( pCx->pCursor ){
  1746   1746       sqlite3BtreeCloseCursor(pCx->pCursor);
  1747   1747     }
  1748   1748   #ifndef SQLITE_OMIT_VIRTUALTABLE
  1749   1749     if( pCx->pVtabCursor ){
  1750   1750       sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
  1751   1751       const sqlite3_module *pModule = pVtabCursor->pVtab->pModule;
  1752         -    p->inVtabMethod = 1;
         1752  +    assert( pVtabCursor->pVtab->nRef>0 );
         1753  +    pVtabCursor->pVtab->nRef--;
  1753   1754       pModule->xClose(pVtabCursor);
  1754         -    p->inVtabMethod = 0;
  1755   1755     }
  1756   1756   #endif
  1757   1757   }
  1758   1758   
  1759   1759   /*
  1760   1760   ** Copy the values stored in the VdbeFrame structure to its Vdbe. This
  1761   1761   ** is used, for example, when a trigger sub-program is halted to restore

Changes to src/vtab.c.

   774    774   */
   775    775   int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){
   776    776     int rc = SQLITE_OK;
   777    777     Table *pTab;
   778    778   
   779    779     pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
   780    780     if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){
   781         -    VTable *p = vtabDisconnectAll(db, pTab);
   782         -
   783         -    assert( rc==SQLITE_OK );
          781  +    VTable *p;
          782  +    for(p=pTab->pVTable; p; p=p->pNext){
          783  +      assert( p->pVtab );
          784  +      if( p->pVtab->nRef>0 ){
          785  +        return SQLITE_LOCKED;
          786  +      }
          787  +    }
          788  +    p = vtabDisconnectAll(db, pTab);
   784    789       rc = p->pMod->pModule->xDestroy(p->pVtab);
   785         -
   786    790       /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */
   787    791       if( rc==SQLITE_OK ){
   788    792         assert( pTab->pVTable==p && p->pNext==0 );
   789    793         p->pVtab = 0;
   790    794         pTab->pVTable = 0;
   791    795         sqlite3VtabUnlock(p);
   792    796       }

Changes to test/vtab1.test.

  1391   1391     SELECT * FROM t9v WHERE a<b;
  1392   1392   } {1 2 3}
  1393   1393   
  1394   1394   do_execsql_test 21.3 {
  1395   1395     SELECT * FROM t9v WHERE a=b;
  1396   1396   } {2 2 2}
  1397   1397   
         1398  +
         1399  +#-------------------------------------------------------------------------
         1400  +# The following tests verify that a DROP TABLE command on a virtual
         1401  +# table does not cause other operations to crash.
         1402  +#
         1403  +#   23.1: Dropping a vtab while a SELECT is running on it.
         1404  +#
         1405  +#   23.2: Dropping a vtab while a SELECT that will, but has not yet,
         1406  +#         open a cursor on the vtab, is running. In this case the
         1407  +#         DROP TABLE succeeds and the SELECT hits an error.
         1408  +#   
         1409  +#   23.3: Dropping a vtab from within a user-defined-function callback
         1410  +#         in the middle of an "INSERT INTO vtab SELECT ..." statement.
         1411  +#
         1412  +reset_db
         1413  +load_static_extension db wholenumber
         1414  +#load_static_extension db eval
         1415  +db func execsql execsql
         1416  +register_echo_module db
         1417  +
         1418  +do_test 23.1 {
         1419  +  execsql { CREATE VIRTUAL TABLE t1 USING wholenumber }
         1420  +  set res ""
         1421  +  db eval { SELECT value FROM t1 WHERE value<10 } {
         1422  +    if {$value == 5} {
         1423  +      set res [catchsql { DROP TABLE t1 }]
         1424  +    }
         1425  +  }
         1426  +  set res
         1427  +} {1 {database table is locked}}
         1428  +
         1429  +do_test 23.2 {
         1430  +  execsql { 
         1431  +    CREATE TABLE t2(value);
         1432  +    INSERT INTO t2 VALUES(1), (2), (3);
         1433  +  }
         1434  +
         1435  +  set res2 [list [catch {
         1436  +    db eval {
         1437  +      SELECT value FROM t2 UNION ALL 
         1438  +      SELECT value FROM t1 WHERE value<10
         1439  +    } {
         1440  +      if {$value == 2} { set res1 [catchsql { DROP TABLE t1 }] }
         1441  +    }
         1442  +  } msg] $msg]
         1443  +  list $res1 $res2
         1444  +} {{0 {}} {1 {database table is locked}}}
         1445  +
         1446  +do_test 23.3.1 {
         1447  +  execsql { CREATE VIRTUAL TABLE t1e USING echo(t2) }
         1448  +  execsql { INSERT INTO t1e SELECT 4 }
         1449  +  catchsql { INSERT INTO t1e SELECT execsql('DROP TABLE t1e') }
         1450  +} {1 {database table is locked}}
         1451  +do_execsql_test 23.3.2 { SELECT * FROM t1e } {1 2 3 4}
         1452  +
  1398   1453   finish_test