/ Check-in [04111ce9]
Login

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

Overview
Comment:Changes to test_quota.c to make quota groups persistent even after files are closed. Files remain a part of the quota group until they are deleted.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 04111ce980df9692b7fe65a36105e7de9627a3bb
User & Date: drh 2011-08-24 01:25:55
Context
2011-08-24
15:12
Fix some harmless compiler warnings. check-in: 46f5a68b user: drh tags: trunk
01:25
Changes to test_quota.c to make quota groups persistent even after files are closed. Files remain a part of the quota group until they are deleted. check-in: 04111ce9 user: drh tags: trunk
2011-08-23
23:41
Simplifications to the SQLITE_PAGECACHE_BLOCKALLOC logic. Reduce the number of difficult-to-reach branches. check-in: d5d835fe user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Show Whitespace Changes Patch

Changes to src/test_quota.c.

    91     91   ** to the same file each point to a single instance of this object.
    92     92   */
    93     93   struct quotaFile {
    94     94     char *zFilename;                /* Name of this file */
    95     95     quotaGroup *pGroup;             /* Quota group to which this file belongs */
    96     96     sqlite3_int64 iSize;            /* Current size of this file */
    97     97     int nRef;                       /* Number of times this file is open */
           98  +  int deleteOnClose;              /* True to delete this file when it closes */
    98     99     quotaFile *pNext, **ppPrev;     /* Linked list of files in the same group */
    99    100   };
   100    101   
   101    102   /*
   102    103   ** An instance of the following object represents each open connection
   103    104   ** to a file that participates in quota tracking.  This object is a 
   104    105   ** subclass of sqlite3_file.  The sqlite3_file object for the underlying
................................................................................
   160    161   /*
   161    162   ** Acquire and release the mutex used to serialize access to the
   162    163   ** list of quotaGroups.
   163    164   */
   164    165   static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); }
   165    166   static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); }
   166    167   
          168  +/* Count the number of open files in a quotaGroup 
          169  +*/
          170  +static int quotaGroupOpenFileCount(quotaGroup *pGroup){
          171  +  int N = 0;
          172  +  quotaFile *pFile = pGroup->pFiles;
          173  +  while( pFile ){
          174  +    if( pFile->nRef ) N++;
          175  +    pFile = pFile->pNext;
          176  +  }
          177  +  return N;
          178  +}
          179  +
          180  +/* Remove a file from a quota group.
          181  +*/
          182  +static void quotaRemoveFile(quotaFile *pFile){
          183  +  quotaGroup *pGroup = pFile->pGroup;
          184  +  pGroup->iSize -= pFile->iSize;
          185  +  *pFile->ppPrev = pFile->pNext;
          186  +  if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev;
          187  +  sqlite3_free(pFile);
          188  +}
          189  +
          190  +/* Remove all files from a quota group.  It is always the case that
          191  +** all files will be closed when this routine is called.
          192  +*/
          193  +static void quotaRemoveAllFiles(quotaGroup *pGroup){
          194  +  while( pGroup->pFiles ){
          195  +    assert( pGroup->pFiles->nRef==0 );
          196  +    quotaRemoveFile(pGroup->pFiles);
          197  +  }
          198  +}
          199  +
   167    200   
   168    201   /* If the reference count and threshold for a quotaGroup are both
   169    202   ** zero, then destroy the quotaGroup.
   170    203   */
   171    204   static void quotaGroupDeref(quotaGroup *pGroup){
   172         -  if( pGroup->pFiles==0 && pGroup->iLimit==0 ){
          205  +  if( pGroup->iLimit==0 && quotaGroupOpenFileCount(pGroup)==0 ){
          206  +    quotaRemoveAllFiles(pGroup);
   173    207       *pGroup->ppPrev = pGroup->pNext;
   174    208       if( pGroup->pNext ) pGroup->pNext->ppPrev = pGroup->ppPrev;
   175    209       if( pGroup->xDestroy ) pGroup->xDestroy(pGroup->pArg);
   176    210       sqlite3_free(pGroup);
   177    211     }
   178    212   }
   179    213   
................................................................................
   271    305   /* Translate an sqlite3_file* that is really a quotaConn* into
   272    306   ** the sqlite3_file* for the underlying original VFS.
   273    307   */
   274    308   static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){
   275    309     quotaConn *p = (quotaConn*)pConn;
   276    310     return (sqlite3_file*)&p[1];
   277    311   }
          312  +
          313  +/* Find a file in a quota group and return a pointer to that file.
          314  +** Return NULL if the file is not in the group.
          315  +*/
          316  +static quotaFile *quotaFindFile(quotaGroup *pGroup, const char *zName){
          317  +  quotaFile *pFile = pGroup->pFiles;
          318  +  while( pFile && strcmp(pFile->zFilename, zName)!=0 ){
          319  +    pFile = pFile->pNext;
          320  +  }
          321  +  return pFile;
          322  +}
   278    323   
   279    324   /************************* VFS Method Wrappers *****************************/
   280    325   /*
   281    326   ** This is the xOpen method used for the "quota" VFS.
   282    327   **
   283    328   ** Most of the work is done by the underlying original VFS.  This method
   284    329   ** simply links the new file into the appropriate quota group if it is a
................................................................................
   315    360     }else{
   316    361       /* If we get to this point, it means the file needs to be quota tracked.
   317    362       */
   318    363       pQuotaOpen = (quotaConn*)pConn;
   319    364       pSubOpen = quotaSubOpen(pConn);
   320    365       rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
   321    366       if( rc==SQLITE_OK ){
   322         -      for(pFile=pGroup->pFiles; pFile && strcmp(pFile->zFilename, zName);
   323         -          pFile=pFile->pNext){}
          367  +      pFile = quotaFindFile(pGroup, zName);
   324    368         if( pFile==0 ){
   325    369           int nName = strlen(zName);
   326    370           pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
   327    371           if( pFile==0 ){
   328    372             quotaLeave();
   329    373             pSubOpen->pMethods->xClose(pSubOpen);
   330    374             return SQLITE_NOMEM;
................................................................................
   333    377           pFile->zFilename = (char*)&pFile[1];
   334    378           memcpy(pFile->zFilename, zName, nName+1);
   335    379           pFile->pNext = pGroup->pFiles;
   336    380           if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
   337    381           pFile->ppPrev = &pGroup->pFiles;
   338    382           pGroup->pFiles = pFile;
   339    383           pFile->pGroup = pGroup;
          384  +        pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
   340    385         }
   341    386         pFile->nRef++;
   342    387         pQuotaOpen->pFile = pFile;
   343    388         if( pSubOpen->pMethods->iVersion==1 ){
   344    389           pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV1;
   345    390         }else{
   346    391           pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV2;
   347    392         }
   348    393       }
   349    394     }
   350    395     quotaLeave();
   351    396     return rc;
   352    397   }
          398  +
          399  +/*
          400  +** This is the xDelete method used for the "quota" VFS.
          401  +**
          402  +** If the file being deleted is part of the quota group, then reduce
          403  +** the size of the quota group accordingly.  And remove the file from
          404  +** the set of files in the quota group.
          405  +*/
          406  +static int quotaDelete(
          407  +  sqlite3_vfs *pVfs,          /* The quota VFS */
          408  +  const char *zName,          /* Name of file to be deleted */
          409  +  int syncDir                 /* Do a directory sync after deleting */
          410  +){
          411  +  int rc;                                    /* Result code */         
          412  +  quotaFile *pFile;                          /* Files in the quota */
          413  +  quotaGroup *pGroup;                        /* The group file belongs to */
          414  +  sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs;   /* Real VFS */
          415  +
          416  +  /* Do the actual file delete */
          417  +  rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
          418  +
          419  +  /* If the file just deleted is a member of a quota group, then remove
          420  +  ** it from that quota group.
          421  +  */
          422  +  if( rc==SQLITE_OK ){
          423  +    quotaEnter();
          424  +    pGroup = quotaGroupFind(zName);
          425  +    if( pGroup ){
          426  +      pFile = quotaFindFile(pGroup, zName);
          427  +      if( pFile ){
          428  +        if( pFile->nRef ){
          429  +          pFile->deleteOnClose = 1;
          430  +        }else{
          431  +          quotaRemoveFile(pFile);
          432  +          quotaGroupDeref(pGroup);
          433  +        }
          434  +      }
          435  +    }
          436  +    quotaLeave();
          437  +  }
          438  +  return rc;
          439  +}
          440  +
   353    441   
   354    442   /************************ I/O Method Wrappers *******************************/
   355    443   
   356    444   /* xClose requests get passed through to the original VFS.  But we
   357    445   ** also have to unlink the quotaConn from the quotaFile and quotaGroup.
   358    446   ** The quotaFile and/or quotaGroup are freed if they are no longer in use.
   359    447   */
................................................................................
   363    451     sqlite3_file *pSubOpen = quotaSubOpen(pConn);
   364    452     int rc;
   365    453     rc = pSubOpen->pMethods->xClose(pSubOpen);
   366    454     quotaEnter();
   367    455     pFile->nRef--;
   368    456     if( pFile->nRef==0 ){
   369    457       quotaGroup *pGroup = pFile->pGroup;
   370         -    pGroup->iSize -= pFile->iSize;
   371         -    if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev;
   372         -    *pFile->ppPrev = pFile->pNext;
          458  +    if( pFile->deleteOnClose ) quotaRemoveFile(pFile);
   373    459       quotaGroupDeref(pGroup);
   374         -    sqlite3_free(pFile);
   375    460     }
   376    461     quotaLeave();
   377    462     return rc;
   378    463   }
   379    464   
   380    465   /* Pass xRead requests directory thru to the original VFS without
   381    466   ** further processing.
................................................................................
   582    667     if( !gQuota.pMutex ){
   583    668       return SQLITE_NOMEM;
   584    669     }
   585    670     gQuota.isInitialized = 1;
   586    671     gQuota.pOrigVfs = pOrigVfs;
   587    672     gQuota.sThisVfs = *pOrigVfs;
   588    673     gQuota.sThisVfs.xOpen = quotaOpen;
          674  +  gQuota.sThisVfs.xDelete = quotaDelete;
   589    675     gQuota.sThisVfs.szOsFile += sizeof(quotaConn);
   590    676     gQuota.sThisVfs.zName = "quota";
   591    677     gQuota.sIoMethodsV1.iVersion = 1;
   592    678     gQuota.sIoMethodsV1.xClose = quotaClose;
   593    679     gQuota.sIoMethodsV1.xRead = quotaRead;
   594    680     gQuota.sIoMethodsV1.xWrite = quotaWrite;
   595    681     gQuota.sIoMethodsV1.xTruncate = quotaTruncate;
................................................................................
   613    699   
   614    700   /*
   615    701   ** Shutdown the quota system.
   616    702   **
   617    703   ** All SQLite database connections must be closed before calling this
   618    704   ** routine.
   619    705   **
   620         -** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly one while
          706  +** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once while
   621    707   ** shutting down in order to free all remaining quota groups.
   622    708   */
   623    709   int sqlite3_quota_shutdown(void){
   624    710     quotaGroup *pGroup;
   625    711     if( gQuota.isInitialized==0 ) return SQLITE_MISUSE;
   626    712     for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){
   627         -    if( pGroup->pFiles ) return SQLITE_MISUSE;
          713  +    if( quotaGroupOpenFileCount(pGroup)>0 ) return SQLITE_MISUSE;
   628    714     }
   629    715     while( gQuota.pGroup ){
   630    716       pGroup = gQuota.pGroup;
   631    717       gQuota.pGroup = pGroup->pNext;
   632    718       pGroup->iLimit = 0;
          719  +    assert( quotaGroupOpenFileCount(pGroup)==0 );
   633    720       quotaGroupDeref(pGroup);
   634    721     }
   635    722     gQuota.isInitialized = 0;
   636    723     sqlite3_mutex_free(gQuota.pMutex);
   637    724     sqlite3_vfs_unregister(&gQuota.sThisVfs);
   638    725     memset(&gQuota, 0, sizeof(gQuota));
   639    726     return SQLITE_OK;
................................................................................
   913   1000         pFileTerm = Tcl_NewObj();
   914   1001         Tcl_ListObjAppendElement(interp, pFileTerm,
   915   1002               Tcl_NewStringObj(pFile->zFilename, -1));
   916   1003         Tcl_ListObjAppendElement(interp, pFileTerm,
   917   1004               Tcl_NewWideIntObj(pFile->iSize));
   918   1005         Tcl_ListObjAppendElement(interp, pFileTerm,
   919   1006               Tcl_NewWideIntObj(pFile->nRef));
         1007  +      Tcl_ListObjAppendElement(interp, pFileTerm,
         1008  +            Tcl_NewWideIntObj(pFile->deleteOnClose));
   920   1009         Tcl_ListObjAppendElement(interp, pGroupTerm, pFileTerm);
   921   1010       }
   922   1011       Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
   923   1012     }
   924   1013     quotaLeave();
   925   1014     Tcl_SetObjResult(interp, pResult);
   926   1015     return TCL_OK;

Changes to test/quota.test.

   219    219   
   220    220   do_test quota-3.2.X {
   221    221     foreach db {db1a db2a db2b db1b} { catch { $db close } }
   222    222     sqlite3_quota_set * 0 {}
   223    223   } {SQLITE_OK}
   224    224   
   225    225   #-------------------------------------------------------------------------
   226         -# Quotas are deleted when unused and when there limit is set to zero
          226  +# Quotas are deleted when unused and when their limit is set to zero
   227    227   #
   228    228   
   229    229   # Return a list of all currently defined quotas.  Each quota is identified
   230    230   # by its pattern.
   231    231   proc quota_list {} {
   232    232     set allq {}
   233    233     foreach q [sqlite3_quota_dump] {
................................................................................
   326    326     sqlite3 db A
   327    327     sqlite3_quota_set A 0 quota_callback
   328    328     db close
   329    329     quota_list
   330    330   } {}
   331    331   
   332    332   do_test quota-4.4.1 {
          333  +  set ::quota {}
          334  +  sqlite3_quota_set */quota-test-A?.db 10000 quota_callback
          335  +  file delete -force ./quota-test-A1.db ./quota-test-A2.db
          336  +  sqlite3 db ./quota-test-A1.db
          337  +  db eval {
          338  +     CREATE TABLE t1(x);
          339  +     INSERT INTO t1 VALUES(randomblob(5000));
          340  +  }
          341  +  quota_list
          342  +} {*/quota-test-A?.db}
          343  +do_test quota-4.4.2 {
          344  +  expr {$::quota==""}
          345  +} {1}
          346  +do_test quota-4.4.3 {
          347  +  db close
          348  +  sqlite3 db ./quota-test-A2.db
          349  +  db eval {
          350  +     CREATE TABLE t1(x);
          351  +     INSERT INTO t1 VALUES(randomblob(5000));
          352  +  }
          353  +  quota_list
          354  +} {*/quota-test-A?.db}
          355  +do_test quota-4.4.4 {
          356  +  expr {$::quota!=""}
          357  +} {1}
          358  +
          359  +
          360  +do_test quota-4.9.1 {
          361  +  db close
   333    362     sqlite3_quota_set A 1000 quota_callback
   334    363     sqlite3_quota_shutdown
   335    364   } {SQLITE_OK}
   336         -do_test quota-4.4.2 {
          365  +do_test quota-4.9.2 {
   337    366     quota_list
   338    367   } {}
   339    368   
   340    369   #-------------------------------------------------------------------------
   341    370   # The following tests test that the quota VFS handles malloc and IO 
   342    371   # errors.
   343    372   #