/ Check-in [02b77a58]
Login

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

Overview
Comment:Fix a memory leak that occurs when an out-of-memory error occurs while preparing a statement that has multiple virtual table updates within triggers. Other virtual table changes to support full-coverage testing. (CVS 6661)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 02b77a582c65e0eb45eb1a9abbdeef11f52d7ce6
User & Date: drh 2009-05-20 20:10:47
Context
2009-05-21
04:42
Add conditional 'extern "C"' block to sqlite3async.h. Ticket #3866. (CVS 6662) check-in: e4d1b117 user: danielk1977 tags: trunk
2009-05-20
20:10
Fix a memory leak that occurs when an out-of-memory error occurs while preparing a statement that has multiple virtual table updates within triggers. Other virtual table changes to support full-coverage testing. (CVS 6661) check-in: 02b77a58 user: drh tags: trunk
16:22
Remove unused, undocumented, and untested error reporting logic from the xFindFunction interface in virtual tables. (CVS 6660) check-in: 55d6ced2 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/vtab.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
103
104
105
106
107
108
109

110
111
112
113
114


115
116
117
118
119
120
121
...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
...
374
375
376
377
378
379
380


381
382
383
384
385
386
387
388
...
449
450
451
452
453
454
455

456
457
458
459
460
461
462
463
...
525
526
527
528
529
530
531


532
533
534
535
536
537
538
539
...
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
...
635
636
637
638
639
640
641
642
643
644


645
646
647
648
649
650
651
...
664
665
666
667
668
669
670
671
672
673

674
675
676
677
678
679
680
...
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
...
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
...
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
...
828
829
830
831
832
833
834


835
836
837
838
839
840

841
842
843
844
845
846
847
848
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code used to help implement virtual tables.
**
** $Id: vtab.c,v 1.88 2009/05/20 16:22:02 drh Exp $
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
#include "sqliteInt.h"

/*
** The actual function that does the work of creating a new module.
** This function implements the sqlite3_create_module() and
................................................................................
*/
void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){
  assert( pVtab->nRef>0 );
  pVtab->nRef--;
  assert(db);
  assert( sqlite3SafetyCheckOk(db) );
  if( pVtab->nRef==0 ){

    if( db->magic==SQLITE_MAGIC_BUSY ){
      (void)sqlite3SafetyOff(db);
      pVtab->pModule->xDisconnect(pVtab);
      (void)sqlite3SafetyOn(db);
    } else {


      pVtab->pModule->xDisconnect(pVtab);
    }
  }
}

/*
** Clear any and all virtual-table information from the Table record.
................................................................................
  if( pParse->db->flags & SQLITE_SharedCache ){
    sqlite3ErrorMsg(pParse, "Cannot use virtual tables in shared-cache mode");
    return;
  }

  sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, 0);
  pTable = pParse->pNewTable;
  if( pTable==0 || pParse->nErr ) return;
  assert( 0==pTable->pIndex );

  db = pParse->db;
  iDb = sqlite3SchemaToIndex(db, pTable->pSchema);
  assert( iDb>=0 );

  pTable->tabFlags |= TF_Virtual;
................................................................................

/*
** This routine takes the module argument that has been accumulating
** in pParse->zArg[] and appends it to the list of arguments on the
** virtual table currently under construction in pParse->pTable.
*/
static void addArgumentToVtab(Parse *pParse){
  if( pParse->sArg.z && pParse->pNewTable ){
    const char *z = (const char*)pParse->sArg.z;
    int n = pParse->sArg.n;
    sqlite3 *db = pParse->db;
    addModuleArgument(db, pParse->pNewTable, sqlite3DbStrNDup(db, z, n));
  }
}

................................................................................

  db->pVTab = pTab;
  rc = sqlite3SafetyOff(db);
  assert( rc==SQLITE_OK );
  rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVtab, &zErr);
  rc2 = sqlite3SafetyOn(db);
  if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;


  if( rc==SQLITE_OK && pVtab ){
    pVtab->pModule = pMod->pModule;
    pVtab->nRef = 1;
    pTab->pVtab = pVtab;
  }

  if( SQLITE_OK!=rc ){
    if( zErr==0 ){
................................................................................
**
** This call is a no-op if table pTab is not a virtual table.
*/
int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
  Module *pMod;
  int rc = SQLITE_OK;


  if( !pTab || (pTab->tabFlags & TF_Virtual)==0 || pTab->pVtab ){
    return SQLITE_OK;
  }

  pMod = pTab->pMod;
  if( !pMod ){
    const char *zModule = pTab->azModuleArg[0];
    sqlite3ErrorMsg(pParse, "no such module: %s", zModule);
................................................................................
  if( !pMod ){
    *pzErr = sqlite3MPrintf(db, "no such module: %s", zModule);
    rc = SQLITE_ERROR;
  }else{
    rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr);
  }



  if( rc==SQLITE_OK && pTab->pVtab ){
      rc = addToVTrans(db, pTab->pVtab);
  }

  return rc;
}

/*
................................................................................
/*
** This function is invoked by the vdbe to call the xDestroy method
** of the virtual table named zTab in database iDb. This occurs
** when a DROP TABLE is mentioned.
**
** This call is a no-op if zTab is not a virtual table.
*/
int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab)
{
  int rc = SQLITE_OK;
  Table *pTab;

  pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
  assert(pTab);
  if( pTab->pVtab ){
    int (*xDestroy)(sqlite3_vtab *pVTab) = pTab->pMod->pModule->xDestroy;
    rc = sqlite3SafetyOff(db);
    assert( rc==SQLITE_OK );
    if( xDestroy ){
      rc = xDestroy(pTab->pVtab);
    }
    (void)sqlite3SafetyOn(db);
    if( rc==SQLITE_OK ){
      int i;
      for(i=0; i<db->nVTrans; i++){
        if( db->aVTrans[i]==pTab->pVtab ){
          db->aVTrans[i] = db->aVTrans[--db->nVTrans];
          break;
................................................................................
** the offset of the method to call in the sqlite3_module structure.
**
** The array is cleared after invoking the callbacks. 
*/
static void callFinaliser(sqlite3 *db, int offset){
  int i;
  if( db->aVTrans ){
    for(i=0; i<db->nVTrans && db->aVTrans[i]; i++){
      sqlite3_vtab *pVtab = db->aVTrans[i];
      int (*x)(sqlite3_vtab *);


      x = *(int (**)(sqlite3_vtab *))((char *)pVtab->pModule + offset);
      if( x ) x(pVtab);
      sqlite3VtabUnlock(db, pVtab);
    }
    sqlite3DbFree(db, db->aVTrans);
    db->nVTrans = 0;
    db->aVTrans = 0;
................................................................................
  int i;
  int rc = SQLITE_OK;
  int rcsafety;
  sqlite3_vtab **aVTrans = db->aVTrans;

  rc = sqlite3SafetyOff(db);
  db->aVTrans = 0;
  for(i=0; rc==SQLITE_OK && i<db->nVTrans && aVTrans[i]; i++){
    sqlite3_vtab *pVtab = aVTrans[i];
    int (*x)(sqlite3_vtab *);

    x = pVtab->pModule->xSync;
    if( x ){
      rc = x(pVtab);
      sqlite3DbFree(db, *pzErrmsg);
      *pzErrmsg = pVtab->zErrMsg;
      pVtab->zErrMsg = 0;
    }
................................................................................
int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){
  int rc = SQLITE_OK;
  const sqlite3_module *pModule;

  /* Special case: If db->aVTrans is NULL and db->nVTrans is greater
  ** than zero, then this function is being called from within a
  ** virtual module xSync() callback. It is illegal to write to 
  ** virtual module tables in this case, so return SQLITE_MISUSE.
  */
  if( sqlite3VtabInSync(db) ){
    return SQLITE_LOCKED;
  }
  if( !pVtab ){
    return SQLITE_OK;
  } 
................................................................................
  pModule = pVtab->pModule;

  if( pModule->xBegin ){
    int i;


    /* If pVtab is already in the aVTrans array, return early */
    for(i=0; (i<db->nVTrans) && 0!=db->aVTrans[i]; i++){
      if( db->aVTrans[i]==pVtab ){
        return SQLITE_OK;
      }
    }

    /* Invoke the xBegin method */
    rc = pModule->xBegin(pVtab);
................................................................................
  FuncDef *pNew;
  int rc = 0;
  char *zLowerName;
  unsigned char *z;


  /* Check to see the left operand is a column in a virtual table */
  if( pExpr==0 ) return pDef;
  if( pExpr->op!=TK_COLUMN ) return pDef;
  pTab = pExpr->pTab;
  if( pTab==0 ) return pDef;
  if( (pTab->tabFlags & TF_Virtual)==0 ) return pDef;
  pVtab = pTab->pVtab;
  assert( pVtab!=0 );
  assert( pVtab->pModule!=0 );
  pMod = (sqlite3_module *)pVtab->pModule;
  if( pMod->xFindFunction==0 ) return pDef;
 
................................................................................
** Make sure virtual table pTab is contained in the pParse->apVirtualLock[]
** array so that an OP_VBegin will get generated for it.  Add pTab to the
** array if it is missing.  If pTab is already in the array, this routine
** is a no-op.
*/
void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
  int i, n;


  assert( IsVirtual(pTab) );
  for(i=0; i<pParse->nVtabLock; i++){
    if( pTab==pParse->apVtabLock[i] ) return;
  }
  n = (pParse->nVtabLock+1)*sizeof(pParse->apVtabLock[0]);
  pParse->apVtabLock = sqlite3_realloc(pParse->apVtabLock, n);

  if( pParse->apVtabLock ){
    pParse->apVtabLock[pParse->nVtabLock++] = pTab;
  }else{
    pParse->db->mallocFailed = 1;
  }
}

#endif /* SQLITE_OMIT_VIRTUALTABLE */







|







 







>




|
>
>







 







|







 







|







 







>
>
|







 







>
|







 







>
>
|







 







|
<




<
|



<
|
<







 







|


>
>







 







|


>







 







|







 







|







 







|


|







 







>
>





|
>
|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
...
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
...
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
...
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
...
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
...
602
603
604
605
606
607
608
609

610
611
612
613

614
615
616
617

618

619
620
621
622
623
624
625
...
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
...
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
...
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
...
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
...
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
...
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code used to help implement virtual tables.
**
** $Id: vtab.c,v 1.89 2009/05/20 20:10:47 drh Exp $
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
#include "sqliteInt.h"

/*
** The actual function that does the work of creating a new module.
** This function implements the sqlite3_create_module() and
................................................................................
*/
void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){
  assert( pVtab->nRef>0 );
  pVtab->nRef--;
  assert(db);
  assert( sqlite3SafetyCheckOk(db) );
  if( pVtab->nRef==0 ){
#ifdef SQLITE_DEBUG
    if( db->magic==SQLITE_MAGIC_BUSY ){
      (void)sqlite3SafetyOff(db);
      pVtab->pModule->xDisconnect(pVtab);
      (void)sqlite3SafetyOn(db);
    } else 
#endif
    {
      pVtab->pModule->xDisconnect(pVtab);
    }
  }
}

/*
** Clear any and all virtual-table information from the Table record.
................................................................................
  if( pParse->db->flags & SQLITE_SharedCache ){
    sqlite3ErrorMsg(pParse, "Cannot use virtual tables in shared-cache mode");
    return;
  }

  sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, 0);
  pTable = pParse->pNewTable;
  if( pTable==0 ) return;
  assert( 0==pTable->pIndex );

  db = pParse->db;
  iDb = sqlite3SchemaToIndex(db, pTable->pSchema);
  assert( iDb>=0 );

  pTable->tabFlags |= TF_Virtual;
................................................................................

/*
** This routine takes the module argument that has been accumulating
** in pParse->zArg[] and appends it to the list of arguments on the
** virtual table currently under construction in pParse->pTable.
*/
static void addArgumentToVtab(Parse *pParse){
  if( pParse->sArg.z && ALWAYS(pParse->pNewTable) ){
    const char *z = (const char*)pParse->sArg.z;
    int n = pParse->sArg.n;
    sqlite3 *db = pParse->db;
    addModuleArgument(db, pParse->pNewTable, sqlite3DbStrNDup(db, z, n));
  }
}

................................................................................

  db->pVTab = pTab;
  rc = sqlite3SafetyOff(db);
  assert( rc==SQLITE_OK );
  rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVtab, &zErr);
  rc2 = sqlite3SafetyOn(db);
  if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
  /* Justification of ALWAYS():  A correct vtab constructor must allocate
  ** the sqlite3_vtab object if successful. */
  if( rc==SQLITE_OK && ALWAYS(pVtab) ){
    pVtab->pModule = pMod->pModule;
    pVtab->nRef = 1;
    pTab->pVtab = pVtab;
  }

  if( SQLITE_OK!=rc ){
    if( zErr==0 ){
................................................................................
**
** This call is a no-op if table pTab is not a virtual table.
*/
int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
  Module *pMod;
  int rc = SQLITE_OK;

  assert( pTab );
  if( (pTab->tabFlags & TF_Virtual)==0 || pTab->pVtab ){
    return SQLITE_OK;
  }

  pMod = pTab->pMod;
  if( !pMod ){
    const char *zModule = pTab->azModuleArg[0];
    sqlite3ErrorMsg(pParse, "no such module: %s", zModule);
................................................................................
  if( !pMod ){
    *pzErr = sqlite3MPrintf(db, "no such module: %s", zModule);
    rc = SQLITE_ERROR;
  }else{
    rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr);
  }

  /* Justification of ALWAYS():  The xConstructor method is required to
  ** create a valid sqlite3_vtab if it returns SQLITE_OK. */
  if( rc==SQLITE_OK && ALWAYS(pTab->pVtab) ){
      rc = addToVTrans(db, pTab->pVtab);
  }

  return rc;
}

/*
................................................................................
/*
** This function is invoked by the vdbe to call the xDestroy method
** of the virtual table named zTab in database iDb. This occurs
** when a DROP TABLE is mentioned.
**
** This call is a no-op if zTab is not a virtual table.
*/
int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){

  int rc = SQLITE_OK;
  Table *pTab;

  pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);

  if( ALWAYS(pTab!=0 && pTab->pVtab!=0) ){
    int (*xDestroy)(sqlite3_vtab *pVTab) = pTab->pMod->pModule->xDestroy;
    rc = sqlite3SafetyOff(db);
    assert( rc==SQLITE_OK );

    rc = xDestroy(pTab->pVtab);

    (void)sqlite3SafetyOn(db);
    if( rc==SQLITE_OK ){
      int i;
      for(i=0; i<db->nVTrans; i++){
        if( db->aVTrans[i]==pTab->pVtab ){
          db->aVTrans[i] = db->aVTrans[--db->nVTrans];
          break;
................................................................................
** the offset of the method to call in the sqlite3_module structure.
**
** The array is cleared after invoking the callbacks. 
*/
static void callFinaliser(sqlite3 *db, int offset){
  int i;
  if( db->aVTrans ){
    for(i=0; i<db->nVTrans; i++){
      sqlite3_vtab *pVtab = db->aVTrans[i];
      int (*x)(sqlite3_vtab *);

      assert( pVtab!=0 );
      x = *(int (**)(sqlite3_vtab *))((char *)pVtab->pModule + offset);
      if( x ) x(pVtab);
      sqlite3VtabUnlock(db, pVtab);
    }
    sqlite3DbFree(db, db->aVTrans);
    db->nVTrans = 0;
    db->aVTrans = 0;
................................................................................
  int i;
  int rc = SQLITE_OK;
  int rcsafety;
  sqlite3_vtab **aVTrans = db->aVTrans;

  rc = sqlite3SafetyOff(db);
  db->aVTrans = 0;
  for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
    sqlite3_vtab *pVtab = aVTrans[i];
    int (*x)(sqlite3_vtab *);
    assert( pVtab!=0 );
    x = pVtab->pModule->xSync;
    if( x ){
      rc = x(pVtab);
      sqlite3DbFree(db, *pzErrmsg);
      *pzErrmsg = pVtab->zErrMsg;
      pVtab->zErrMsg = 0;
    }
................................................................................
int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){
  int rc = SQLITE_OK;
  const sqlite3_module *pModule;

  /* Special case: If db->aVTrans is NULL and db->nVTrans is greater
  ** than zero, then this function is being called from within a
  ** virtual module xSync() callback. It is illegal to write to 
  ** virtual module tables in this case, so return SQLITE_LOCKED.
  */
  if( sqlite3VtabInSync(db) ){
    return SQLITE_LOCKED;
  }
  if( !pVtab ){
    return SQLITE_OK;
  } 
................................................................................
  pModule = pVtab->pModule;

  if( pModule->xBegin ){
    int i;


    /* If pVtab is already in the aVTrans array, return early */
    for(i=0; i<db->nVTrans; i++){
      if( db->aVTrans[i]==pVtab ){
        return SQLITE_OK;
      }
    }

    /* Invoke the xBegin method */
    rc = pModule->xBegin(pVtab);
................................................................................
  FuncDef *pNew;
  int rc = 0;
  char *zLowerName;
  unsigned char *z;


  /* Check to see the left operand is a column in a virtual table */
  if( NEVER(pExpr==0) ) return pDef;
  if( pExpr->op!=TK_COLUMN ) return pDef;
  pTab = pExpr->pTab;
  if( NEVER(pTab==0) ) return pDef;
  if( (pTab->tabFlags & TF_Virtual)==0 ) return pDef;
  pVtab = pTab->pVtab;
  assert( pVtab!=0 );
  assert( pVtab->pModule!=0 );
  pMod = (sqlite3_module *)pVtab->pModule;
  if( pMod->xFindFunction==0 ) return pDef;
 
................................................................................
** Make sure virtual table pTab is contained in the pParse->apVirtualLock[]
** array so that an OP_VBegin will get generated for it.  Add pTab to the
** array if it is missing.  If pTab is already in the array, this routine
** is a no-op.
*/
void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
  int i, n;
  Table **apVtabLock;

  assert( IsVirtual(pTab) );
  for(i=0; i<pParse->nVtabLock; i++){
    if( pTab==pParse->apVtabLock[i] ) return;
  }
  n = (pParse->nVtabLock+1)*sizeof(pParse->apVtabLock[0]);
  apVtabLock = sqlite3_realloc(pParse->apVtabLock, n);
  if( apVtabLock ){
    pParse->apVtabLock = apVtabLock;
    pParse->apVtabLock[pParse->nVtabLock++] = pTab;
  }else{
    pParse->db->mallocFailed = 1;
  }
}

#endif /* SQLITE_OMIT_VIRTUALTABLE */