/ Check-in [abcb65af]
Login

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

Overview
Comment:Add the sqlite3_quota_fflush() interface. Enhance sqlite3_quota_remove() so that it can remove entire directories.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | quota-stdio
Files: files | file ages | folders
SHA1:abcb65af4cdd192beaccdbc2109ad45b9e7f9d00
User & Date: drh 2011-12-03 00:13:06
Context
2011-12-12
19:47
Make sure the quota logic is usable as C++. check-in: f4534bd3 user: drh tags: quota-stdio
2011-12-03
00:13
Add the sqlite3_quota_fflush() interface. Enhance sqlite3_quota_remove() so that it can remove entire directories. check-in: abcb65af user: drh tags: quota-stdio
2011-12-02
15:31
One minor documentation enhancement. check-in: 8cfd3575 user: drh tags: quota-stdio
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/test_quota.c.

   548    548     sqlite3_file *pSubOpen = quotaSubOpen(pConn);
   549    549     int rc;
   550    550     rc = pSubOpen->pMethods->xClose(pSubOpen);
   551    551     quotaEnter();
   552    552     pFile->nRef--;
   553    553     if( pFile->nRef==0 ){
   554    554       quotaGroup *pGroup = pFile->pGroup;
   555         -    if( pFile->deleteOnClose ) quotaRemoveFile(pFile);
          555  +    if( pFile->deleteOnClose ){
          556  +      gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
          557  +      quotaRemoveFile(pFile);
          558  +    }
   556    559       quotaGroupDeref(pGroup);
   557    560     }
   558    561     quotaLeave();
   559    562     return rc;
   560    563   }
   561    564   
   562    565   /* Pass xRead requests directory thru to the original VFS without
................................................................................
  1037   1040     rc = fclose(p->f);
  1038   1041     pFile = p->pFile;
  1039   1042     if( pFile ){
  1040   1043       quotaEnter();
  1041   1044       pFile->nRef--;
  1042   1045       if( pFile->nRef==0 ){
  1043   1046         quotaGroup *pGroup = pFile->pGroup;
  1044         -      if( pFile->deleteOnClose ) quotaRemoveFile(pFile);
         1047  +      if( pFile->deleteOnClose ){
         1048  +        gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
         1049  +        quotaRemoveFile(pFile);
         1050  +      }
  1045   1051         quotaGroupDeref(pGroup);
  1046   1052       }
  1047   1053       quotaLeave();
  1048   1054     }
  1049   1055     sqlite3_free(p);
  1050   1056     return rc;
  1051   1057   }
         1058  +
         1059  +/*
         1060  +** Flush memory buffers for a quota_FILE to disk.
         1061  +*/
         1062  +int sqlite3_quota_fflush(quota_FILE *p){
         1063  +  return fflush(p->f);
         1064  +}
  1052   1065   
  1053   1066   /*
  1054   1067   ** Seek on a quota_FILE stream.
  1055   1068   */
  1056   1069   int sqlite3_quota_fseek(quota_FILE *p, long offset, int whence){
  1057   1070     return fseek(p->f, offset, whence);
  1058   1071   }
................................................................................
  1068   1081   ** Tell the current location of a quota_FILE stream.
  1069   1082   */
  1070   1083   long sqlite3_quota_ftell(quota_FILE *p){
  1071   1084     return ftell(p->f);
  1072   1085   }
  1073   1086   
  1074   1087   /*
  1075         -** Remove a file.  Update quotas accordingly.
         1088  +** Remove a managed file.  Update quotas accordingly.
  1076   1089   */
  1077   1090   int sqlite3_quota_remove(const char *zFilename){
  1078         -  int rc = remove(zFilename);
  1079         -  sqlite3_quota_file(zFilename);
         1091  +  char *zFull;            /* Full pathname for zFilename */
         1092  +  int nFull;              /* Number of bytes in zFilename */
         1093  +  int rc;                 /* Result code */
         1094  +  quotaGroup *pGroup;     /* Group containing zFilename */
         1095  +  quotaFile *pFile;       /* A file in the group */
         1096  +  quotaFile *pNextFile;   /* next file in the group */
         1097  +  int diff;               /* Difference between filenames */
         1098  +  char c;                 /* First character past end of pattern */
         1099  +
         1100  +  zFull = sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
         1101  +  if( zFull==0 ) return SQLITE_NOMEM;
         1102  +  rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
         1103  +                                      gQuota.sThisVfs.mxPathname+1, zFull);
         1104  +  if( rc ){
         1105  +    sqlite3_free(zFull);
         1106  +    return rc;
         1107  +  }
         1108  +
         1109  +  /* Figure out the length of the full pathname.  If the name ends with
         1110  +  ** / (or \ on windows) then remove the trailing /.
         1111  +  */
         1112  +  nFull = strlen(zFull);
         1113  +  if( nFull>0 && (zFull[nFull-1]=='/' || zFull[nFull-1]=='\\') ){
         1114  +    nFull--;
         1115  +    zFull[nFull] = 0;
         1116  +  }
         1117  +
         1118  +  quotaEnter();
         1119  +  pGroup = quotaGroupFind(zFull);
         1120  +  if( pGroup ){
         1121  +    for(pFile=pGroup->pFiles; pFile && rc==SQLITE_OK; pFile=pNextFile){
         1122  +      pNextFile = pFile->pNext;
         1123  +      diff = memcmp(zFull, pFile->zFilename, nFull);
         1124  +      if( diff==0 && ((c = pFile->zFilename[nFull])==0 || c=='/' || c=='\\') ){
         1125  +        if( pFile->nRef ){
         1126  +          pFile->deleteOnClose = 1;
         1127  +        }else{
         1128  +          rc = gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
         1129  +          quotaRemoveFile(pFile);
         1130  +          quotaGroupDeref(pGroup);
         1131  +        }
         1132  +      }
         1133  +    }
         1134  +  }
         1135  +  quotaLeave();
         1136  +  sqlite3_free(zFull);
  1080   1137     return rc;
  1081   1138   }
  1082         -
  1083   1139     
  1084   1140   /***************************** Test Code ***********************************/
  1085   1141   #ifdef SQLITE_TEST
  1086   1142   #include <tcl.h>
  1087   1143   
  1088   1144   /*
  1089   1145   ** Argument passed to a TCL quota-over-limit callback.
................................................................................
  1440   1496       return TCL_ERROR;
  1441   1497     }
  1442   1498     p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
  1443   1499     rc = sqlite3_quota_fclose(p);
  1444   1500     Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
  1445   1501     return TCL_OK;
  1446   1502   }
         1503  +
         1504  +/*
         1505  +** tclcmd: sqlite3_quota_fflush HANDLE
         1506  +*/
         1507  +static int test_quota_fflush(
         1508  +  void * clientData,
         1509  +  Tcl_Interp *interp,
         1510  +  int objc,
         1511  +  Tcl_Obj *CONST objv[]
         1512  +){
         1513  +  quota_FILE *p;
         1514  +  int rc;
         1515  +
         1516  +  if( objc!=2 ){
         1517  +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
         1518  +    return TCL_ERROR;
         1519  +  }
         1520  +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
         1521  +  rc = sqlite3_quota_fflush(p);
         1522  +  Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
         1523  +  return TCL_OK;
         1524  +}
  1447   1525   
  1448   1526   /*
  1449   1527   ** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE
  1450   1528   */
  1451   1529   static int test_quota_fseek(
  1452   1530     void * clientData,
  1453   1531     Tcl_Interp *interp,
................................................................................
  1585   1663       { "sqlite3_quota_set",        test_quota_set },
  1586   1664       { "sqlite3_quota_file",       test_quota_file },
  1587   1665       { "sqlite3_quota_dump",       test_quota_dump },
  1588   1666       { "sqlite3_quota_fopen",      test_quota_fopen },
  1589   1667       { "sqlite3_quota_fread",      test_quota_fread },
  1590   1668       { "sqlite3_quota_fwrite",     test_quota_fwrite },
  1591   1669       { "sqlite3_quota_fclose",     test_quota_fclose },
         1670  +    { "sqlite3_quota_fflush",     test_quota_fflush },
  1592   1671       { "sqlite3_quota_fseek",      test_quota_fseek },
  1593   1672       { "sqlite3_quota_rewind",     test_quota_rewind },
  1594   1673       { "sqlite3_quota_ftell",      test_quota_ftell },
  1595   1674       { "sqlite3_quota_remove",     test_quota_remove },
  1596   1675       { "sqlite3_quota_glob",       test_quota_glob },
  1597   1676     };
  1598   1677     int i;

Changes to src/test_quota.h.

    97     97   ** Calling this routine on a zPattern that does not exist and with a
    98     98   ** zero iLimit is a no-op.
    99     99   **
   100    100   ** A quota group must exist with a non-zero iLimit prior to opening
   101    101   ** database connections if those connections are to participate in the
   102    102   ** quota group.  Creating a quota group does not affect database connections
   103    103   ** that are already open.
          104  +**
          105  +** The patterns that define the various quota groups should be distinct.
          106  +** If the same filename matches more than one quota group pattern, then
          107  +** the behavior of this package is undefined.
   104    108   */
   105    109   int sqlite3_quota_set(
   106    110     const char *zPattern,           /* The filename pattern */
   107    111     sqlite3_int64 iLimit,           /* New quota to set for this quota group */
   108    112     void (*xCallback)(              /* Callback invoked when going over quota */
   109    113        const char *zFilename,         /* Name of file whose size increases */
   110    114        sqlite3_int64 *piLimit,        /* IN/OUT: The current limit */
................................................................................
   112    116        void *pArg                     /* Client data */
   113    117     ),
   114    118     void *pArg,                     /* client data passed thru to callback */
   115    119     void (*xDestroy)(void*)         /* Optional destructor for pArg */
   116    120   );
   117    121   
   118    122   /*
   119         -** Bring the named file under quota management.  Or if it is already under
   120         -** management, update its size.
          123  +** Bring the named file under quota management, assuming its name matches
          124  +** the glob pattern of some quota group.  Or if it is already under
          125  +** management, update its size.  If zFilename does not match the glob
          126  +** pattern of any quota group, this routine is a no-op.
   121    127   */
   122    128   int sqlite3_quota_file(const char *zFilename);
   123    129   
   124    130   /*
   125    131   ** The following object serves the same role as FILE in the standard C
   126    132   ** library.  It represents an open connection to a file on disk for I/O.
          133  +**
          134  +** A single quota_FILE should not be used by two or more threads at the
          135  +** same time.  Multiple threads can be using different quota_FILE objects
          136  +** simultaneously, but not the same quota_FILE object.
   127    137   */
   128    138   typedef struct quota_FILE quota_FILE;
   129    139   
   130    140   /*
   131    141   ** Create a new quota_FILE object used to read and/or write to the
   132    142   ** file zFilename.  The zMode parameter is as with standard library zMode.
   133    143   */
................................................................................
   137    147   ** Perform I/O against a quota_FILE object.  When doing writes, the
   138    148   ** quota mechanism may result in a short write, in order to prevent
   139    149   ** the sum of sizes of all files from going over quota.
   140    150   */
   141    151   size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*);
   142    152   size_t sqlite3_quota_fwrite(void*, size_t, size_t, quota_FILE*);
   143    153   
          154  +/*
          155  +** Flush all written content held in memory buffers out to disk.
          156  +** This is the equivalent of fflush() in the standard library - not
          157  +** an fsync().
          158  +*/
          159  +int sqlite3_quota_fflush(quota_FILE*);
          160  +
   144    161   /*
   145    162   ** Close a quota_FILE object and free all associated resources.  The
   146    163   ** file remains under quota management.
   147    164   */
   148    165   int sqlite3_quota_fclose(quota_FILE*);
   149    166   
   150    167   /*
................................................................................
   152    169   ** current location of the read/write pointer.
   153    170   */
   154    171   int sqlite3_quota_fseek(quota_FILE*, long, int);
   155    172   void sqlite3_quota_rewind(quota_FILE*);
   156    173   long sqlite3_quota_ftell(quota_FILE*);
   157    174   
   158    175   /*
   159         -** Delete a file from the disk.  If that file is under quota management,
   160         -** then adjust quotas accordingly.
          176  +** Delete a file from the disk, if that file is under quota management.
          177  +** Adjust quotas accordingly.
          178  +**
          179  +** If zFilename is the name of a directory that matches one of the
          180  +** quota glob patterns, then all files under quota management that
          181  +** are contained within that directory are deleted.
          182  +**
          183  +** A standard SQLite result code is returned (SQLITE_OK, SQLITE_NOMEM, etc.)
          184  +** When deleting a directory of files, if the deletion of any one
          185  +** file fails (for example due to an I/O error), then this routine
          186  +** returns immediately, with the error code, and does not try to 
          187  +** delete any of the other files in the specified directory.
          188  +**
          189  +** All files are removed from quota management and deleted from disk.
          190  +** However, no attempt is made to remove empty directories.
   161    191   **
   162         -** The file being deleted must not be open for reading or writing or as
   163         -** a database when it is deleted.
          192  +** This routine is a no-op for files that are not under quota management.
   164    193   */
   165    194   int sqlite3_quota_remove(const char *zFilename);
   166    195   
   167    196   #endif /* _QUOTA_H_ */

Changes to test/quota2.test.

    13     13   set testdir [file dirname $argv0]
    14     14   source $testdir/tester.tcl
    15     15   source $testdir/malloc_common.tcl
    16     16   
    17     17   db close
    18     18   sqlite3_quota_initialize "" 1
    19     19   
    20         -foreach dir {quota2a quota2b quota2c} {
           20  +foreach dir {quota2a/x1 quota2a/x2 quota2a quota2b quota2c} {
    21     21     file delete -force $dir
           22  +}
           23  +foreach dir {quota2a quota2a/x1 quota2a/x2 quota2b quota2c} {
    22     24     file mkdir $dir
    23     25   }
    24     26   
    25     27   # The standard_path procedure converts a pathname into a standard format
    26     28   # that is the same across platforms.
    27     29   #
    28     30   unset -nocomplain ::quota_pwd ::quota_mapping
................................................................................
   105    107   do_test quota2-1.10 {
   106    108     sqlite3_quota_rewind $::h1
   107    109     sqlite3_quota_ftell $::h1
   108    110   } {0}
   109    111   do_test quota2-1.11 {
   110    112     standard_path [sqlite3_quota_dump]
   111    113   } {{*/quota2b/* 5000 0} {*/quota2a/* 4000 4000 {PWD/quota2a/xyz.txt 4000 1 0}}}
   112         -do_test quota1-1.12 {
          114  +do_test quota2-1.12 {
   113    115     sqlite3_quota_fclose $::h1
   114    116     standard_path [sqlite3_quota_dump]
   115    117   } {{*/quota2b/* 5000 0} {*/quota2a/* 4000 4000 {PWD/quota2a/xyz.txt 4000 0 0}}}
   116         -do_test quota1-1.13 {
          118  +do_test quota2-1.13 {
   117    119     sqlite3_quota_remove quota2a/xyz.txt
   118    120     standard_path [sqlite3_quota_dump]
   119    121   } {{*/quota2b/* 5000 0} {*/quota2a/* 4000 0}}
   120    122   
   121    123   
   122    124   set quota {}
   123    125   do_test quota2-2.1 {
................................................................................
   158    160   do_test quota2-2.10 {
   159    161     sqlite3_quota_rewind $::h1
   160    162     sqlite3_quota_ftell $::h1
   161    163   } {0}
   162    164   do_test quota2-2.11 {
   163    165     standard_path [sqlite3_quota_dump]
   164    166   } {{*/quota2b/* 5000 0} {*/quota2a/* 4000 0}}
   165         -do_test quota1-2.12 {
          167  +do_test quota2-2.12 {
   166    168     sqlite3_quota_fclose $::h1
   167    169     standard_path [sqlite3_quota_dump]
   168    170   } {{*/quota2b/* 5000 0} {*/quota2a/* 4000 0}}
          171  +
          172  +do_test quota2-3.1 {
          173  +  sqlite3_quota_set */quota2b/* 0 quota_check
          174  +  set ::h1 [sqlite3_quota_fopen quota2a/x1/a.txt a]
          175  +  sqlite3_quota_fwrite $::h1 10 10 $bigtext
          176  +} {10}
          177  +do_test quota2-3.2 {
          178  +  standard_path [sqlite3_quota_dump]
          179  +} {{*/quota2a/* 4000 100 {PWD/quota2a/x1/a.txt 100 1 0}}}
          180  +do_test quota2-3.3 {
          181  +  sqlite3_quota_fflush $::h1
          182  +  standard_path [sqlite3_quota_dump]
          183  +} {{*/quota2a/* 4000 100 {PWD/quota2a/x1/a.txt 100 1 0}}}
          184  +do_test quota2-3.4 {
          185  +  sqlite3_quota_fclose $::h1
          186  +  standard_path [sqlite3_quota_dump]
          187  +} {{*/quota2a/* 4000 100 {PWD/quota2a/x1/a.txt 100 0 0}}}
          188  +do_test quota2-3.5 {
          189  +  set ::h2 [sqlite3_quota_fopen quota2a/x2/b.txt a]
          190  +  sqlite3_quota_fwrite $::h2 10 20 $bigtext
          191  +  standard_path [sqlite3_quota_dump]
          192  +} {{*/quota2a/* 4000 300 {PWD/quota2a/x2/b.txt 200 1 0} {PWD/quota2a/x1/a.txt 100 0 0}}}
          193  +do_test quota2-3.6 {
          194  +  set ::h3 [sqlite3_quota_fopen quota2a/x1/c.txt a]
          195  +  sqlite3_quota_fwrite $::h3 10 50 $bigtext
          196  +  standard_path [sqlite3_quota_dump]
          197  +} {{*/quota2a/* 4000 800 {PWD/quota2a/x1/c.txt 500 1 0} {PWD/quota2a/x2/b.txt 200 1 0} {PWD/quota2a/x1/a.txt 100 0 0}}}
          198  +do_test quota2-3.7 {
          199  +  file exists quota2a/x1/a.txt
          200  +} {1}
          201  +do_test quota2-3.8 {
          202  +  file exists quota2a/x2/b.txt
          203  +} {1}
          204  +do_test quota2-3.9 {
          205  +  file exists quota2a/x1/c.txt
          206  +} {1}
          207  +do_test quota2-3.10 {
          208  +  sqlite3_quota_remove quota2a/x1
          209  +  standard_path [sqlite3_quota_dump]
          210  +} {{*/quota2a/* 4000 700 {PWD/quota2a/x1/c.txt 500 1 1} {PWD/quota2a/x2/b.txt 200 1 0}}}
          211  +do_test quota2-3.11 {
          212  +  sqlite3_quota_fclose $::h2
          213  +  sqlite3_quota_fclose $::h3
          214  +  standard_path [sqlite3_quota_dump]
          215  +} {{*/quota2a/* 4000 200 {PWD/quota2a/x2/b.txt 200 0 0}}}
          216  +do_test quota2-3.12 {
          217  +  file exists quota2a/x1/a.txt
          218  +} {0}
          219  +do_test quota2-3.13 {
          220  +  file exists quota2a/x2/b.txt
          221  +} {1}
          222  +do_test quota2-3.14 {
          223  +  file exists quota2a/x1/c.txt
          224  +} {0}
   169    225   
   170    226   catch { sqlite3_quota_shutdown }
   171    227   catch { unset quota_request_ok }
   172    228   finish_test