]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add stdio-like I/O interfaces to the test_quota VFS. This is a prototype
authordrh <drh@noemail.net>
Thu, 1 Dec 2011 18:44:21 +0000 (18:44 +0000)
committerdrh <drh@noemail.net>
Thu, 1 Dec 2011 18:44:21 +0000 (18:44 +0000)
change for discussion and is mostly untested.  This is an alternative to
adding stdio-like I/O interfaces in the core.  There is no guarantee that
this code will make it into the trunk.  If it does get to trunk, there
could be many changes to the interface first.

FossilOrigin-Name: bd3ce723f1b5be52be46ede8614ca316f56e7e6f

manifest
manifest.uuid
src/test_quota.c
src/test_quota.h [new file with mode: 0644]

index ef7ae4ea6019894361035bb774b706d525e67037..cade689e9a9419dcf04dbc5b94f807934ec05767 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Remove\sunused\sfields\sfrom\sthe\sParse\sobject.\s\sDocumentation\sand\sformatting\nimprovements\son\sdata\sstructure\sdefinitions.
-D 2011-11-29T15:40:32.491
+C Add\sstdio-like\sI/O\sinterfaces\sto\sthe\stest_quota\sVFS.\s\sThis\sis\sa\sprototype\nchange\sfor\sdiscussion\sand\sis\smostly\suntested.\s\sThis\sis\san\salternative\sto\nadding\sstdio-like\sI/O\sinterfaces\sin\sthe\score.\s\sThere\sis\sno\sguarantee\sthat\nthis\scode\swill\smake\sit\sinto\sthe\strunk.\s\sIf\sit\sdoes\sget\sto\strunk,\sthere\ncould\sbe\smany\schanges\sto\sthe\sinterface\sfirst.
+D 2011-12-01T18:44:21.630
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -220,7 +220,8 @@ F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
 F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec
 F src/test_osinst.c 62b0b8ef21ce754cc94e17bb42377ed8795dba32
 F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
-F src/test_quota.c a391c866217e92986c6f523f05b08aa6956c8419
+F src/test_quota.c f3ed8e130fff8e824a320a80668cfaffd6cb55ff
+F src/test_quota.h 118dba604ae5b6903acdd40d2b94a1f319047612
 F src/test_rtree.c 6d06306e29946dc36f528a3a2cdc3add794656f1
 F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
 F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
@@ -976,7 +977,10 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
 F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
-P b10d091ec02e94643e865743129e2a21147b3136
-R 7d0a9d11e01c28eaf1d831b1553005fb
+P 431556cac0b2c86d7f6a60412ff1023feeaafedf
+R e31672c1519f90c43111109b685a8ffb
+T *branch * quota-stdio
+T *sym-quota-stdio *
+T -sym-trunk *
 U drh
-Z fa710425b85d2a4b5e2c971fb970b087
+Z 0283eb6e7b14cfb0e1559cbc303ec8df
index b014af186d06fa853545cfb300b0067cece3f2dc..f43d772bdbe893156861494d2e0e35329a0e40df 100644 (file)
@@ -1 +1 @@
-431556cac0b2c86d7f6a60412ff1023feeaafedf
\ No newline at end of file
+bd3ce723f1b5be52be46ede8614ca316f56e7e6f
\ No newline at end of file
index 74d1a6d3ba2118b6623efa8a1862920aade9ac01..1866e7998689870cb023d2040c2260892b75c3c1 100644 (file)
@@ -27,7 +27,7 @@
 ** files within the group is less than the new quota, then the write
 ** continues as if nothing had happened.
 */
-#include "sqlite3.h"
+#include "test_quota.h"
 #include <string.h>
 #include <assert.h>
 
@@ -111,6 +111,18 @@ struct quotaConn {
   /* The underlying VFS sqlite3_file is appended to this object */
 };
 
+/*
+** An instance of the following object records the state of an
+** open file.  This object is opaque to all users - the internal
+** structure is only visible to the functions below.
+*/
+struct quota_FILE {
+  FILE *f;                /* Open stdio file pointer */
+  sqlite3_int64 iOfst;    /* Current offset into the file */
+  quotaFile *pFile;       /* The file record in the quota system */
+};
+
+
 /************************* Global Variables **********************************/
 /*
 ** All global variables used by this file are containing within the following
@@ -313,11 +325,29 @@ static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){
 /* Find a file in a quota group and return a pointer to that file.
 ** Return NULL if the file is not in the group.
 */
-static quotaFile *quotaFindFile(quotaGroup *pGroup, const char *zName){
+static quotaFile *quotaFindFile(
+  quotaGroup *pGroup,     /* Group in which to look for the file */
+  const char *zName,      /* Full pathname of the file */
+  int createFlag          /* Try to create the file if not found */
+){
   quotaFile *pFile = pGroup->pFiles;
   while( pFile && strcmp(pFile->zFilename, zName)!=0 ){
     pFile = pFile->pNext;
   }
+  if( pFile==0 && createFlag ){
+    int nName = strlen(zName);
+    pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
+    if( pFile ){
+      memset(pFile, 0, sizeof(*pFile));
+      pFile->zFilename = (char*)&pFile[1];
+      memcpy(pFile->zFilename, zName, nName+1);
+      pFile->pNext = pGroup->pFiles;
+      if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
+      pFile->ppPrev = &pGroup->pFiles;
+      pGroup->pFiles = pFile;
+      pFile->pGroup = pGroup;
+    }
+  }
   return pFile;
 }
 
@@ -364,25 +394,13 @@ static int quotaOpen(
     pSubOpen = quotaSubOpen(pConn);
     rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
     if( rc==SQLITE_OK ){
-      pFile = quotaFindFile(pGroup, zName);
+      pFile = quotaFindFile(pGroup, zName, 1);
       if( pFile==0 ){
-        int nName = strlen(zName);
-        pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
-        if( pFile==0 ){
-          quotaLeave();
-          pSubOpen->pMethods->xClose(pSubOpen);
-          return SQLITE_NOMEM;
-        }
-        memset(pFile, 0, sizeof(*pFile));
-        pFile->zFilename = (char*)&pFile[1];
-        memcpy(pFile->zFilename, zName, nName+1);
-        pFile->pNext = pGroup->pFiles;
-        if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
-        pFile->ppPrev = &pGroup->pFiles;
-        pGroup->pFiles = pFile;
-        pFile->pGroup = pGroup;
-        pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
+        quotaLeave();
+        pSubOpen->pMethods->xClose(pSubOpen);
+        return SQLITE_NOMEM;
       }
+      pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
       pFile->nRef++;
       pQuotaOpen->pFile = pFile;
       if( pSubOpen->pMethods->iVersion==1 ){
@@ -423,7 +441,7 @@ static int quotaDelete(
     quotaEnter();
     pGroup = quotaGroupFind(zName);
     if( pGroup ){
-      pFile = quotaFindFile(pGroup, zName);
+      pFile = quotaFindFile(pGroup, zName, 0);
       if( pFile ){
         if( pFile->nRef ){
           pFile->deleteOnClose = 1;
@@ -823,7 +841,7 @@ int sqlite3_quota_file(const char *zFilename){
     quotaEnter();
     pGroup = quotaGroupFind(zFull);
     if( pGroup ){
-      pFile = quotaFindFile(pGroup, zFull);
+      pFile = quotaFindFile(pGroup, zFull, 0);
       if( pFile ) quotaRemoveFile(pFile);
     }
     quotaLeave();
@@ -832,6 +850,152 @@ int sqlite3_quota_file(const char *zFilename){
   return rc;
 }
 
+/*
+** Open a potentially quotaed file for I/O.
+*/
+quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
+  quota_FILE *p = 0;
+  char *zFull = 0;
+  int rc;
+  quotaGroup *pGroup;
+  quotaFile *pFile;
+
+  p = sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
+  if( p==0 ) return 0;
+  zFull = (char*)&p[1];
+  rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
+                                      gQuota.sThisVfs.mxPathname+1, zFull);
+  if( rc ) goto quota_fopen_error;
+  p = sqlite3_malloc(sizeof(*p));
+  if( p==0 ) goto quota_fopen_error;
+  memset(p, 0, sizeof(*p));
+  p->f = fopen(zFull, zMode);
+  if( p->f==0 ) goto quota_fopen_error;
+  quotaEnter();
+  pGroup = quotaGroupFind(zFull);
+  if( pGroup ){
+    pFile = quotaFindFile(pGroup, zFull, 1);
+    if( pFile==0 ){
+      quotaLeave();
+      goto quota_fopen_error;
+    }
+    pFile->nRef++;
+    p->pFile = pFile;
+  }
+  quotaLeave();
+  sqlite3_free(zFull);
+  return p;
+
+quota_fopen_error:
+  sqlite3_free(zFull);
+  if( p && p->f ) fclose(p->f);
+  sqlite3_free(p);
+  return 0;
+}
+
+/*
+** Read content from a quota_FILE
+*/
+size_t sqlite3_quota_fread(
+  void *pBuf,            /* Store the content here */
+  size_t size,           /* Size of each element */
+  size_t nmemb,          /* Number of elements to read */
+  quota_FILE *p          /* Read from this quota_FILE object */
+){
+  return fread(pBuf, size, nmemb, p->f);
+}
+
+/*
+** Write content into a quota_FILE.  Invoke the quota callback and block
+** the write if we exceed quota.
+*/
+size_t sqlite3_quota_fwrite(
+  void *pBuf,            /* Take content to write from here */
+  size_t size,           /* Size of each element */
+  size_t nmemb,          /* Number of elements */
+  quota_FILE *p          /* Write to this quota_FILE objecct */
+){
+  sqlite3_int64 iOfst;
+  sqlite3_int64 iEnd;
+  sqlite3_int64 szNew;
+  quotaFile *pFile;
+  
+  iOfst = ftell(p->f);
+  iEnd = iOfst + size*nmemb;
+  pFile = p->pFile;
+  if( pFile->iSize<iEnd ){
+    quotaGroup *pGroup = pFile->pGroup;
+    quotaEnter();
+    szNew = pGroup->iSize - pFile->iSize + iEnd;
+    if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
+      if( pGroup->xCallback ){
+        pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew, 
+                          pGroup->pArg);
+      }
+      if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
+        iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize;
+        nmemb = (iEnd - iOfst)/size;
+        iEnd = iOfst + size*nmemb;
+        szNew = pGroup->iSize - pFile->iSize + iEnd;
+      }
+    }
+    pGroup->iSize = szNew;
+    pFile->iSize = iEnd;
+    quotaLeave();
+  }
+  return fwrite(pBuf, size, nmemb, p->f);
+}
+
+/*
+** Close an open quota_FILE stream.
+*/
+int sqlite3_quota_fclose(quota_FILE *p){
+  int rc;
+  quotaFile *pFile;
+  rc = fclose(p->f);
+  pFile = p->pFile;
+  quotaEnter();
+  pFile->nRef--;
+  if( pFile->nRef==0 ){
+    quotaGroup *pGroup = pFile->pGroup;
+    if( pFile->deleteOnClose ) quotaRemoveFile(pFile);
+    quotaGroupDeref(pGroup);
+  }
+  quotaLeave();
+  sqlite3_free(p);
+  return rc;
+}
+
+/*
+** Seek on a quota_FILE stream.
+*/
+int sqlite3_quota_fseek(quota_FILE *p, long offset, int whence){
+  return fseek(p->f, offset, whence);
+}
+
+/*
+** rewind a quota_FILE stream.
+*/
+void sqlite3_quota_rewind(quota_FILE *p){
+  rewind(p->f);
+}
+
+/*
+** Tell the current location of a quota_FILE stream.
+*/
+long sqlite3_quota_ftell(quota_FILE *p){
+  return ftell(p->f);
+}
+
+/*
+** Remove a file.  Update quotas accordingly.
+*/
+int sqlite3_quota_remove(const char *zFilename){
+  int rc = remove(zFilename);
+  sqlite3_quota_file(zFilename);
+  return rc;
+}
+
   
 /***************************** Test Code ***********************************/
 #ifdef SQLITE_TEST
diff --git a/src/test_quota.h b/src/test_quota.h
new file mode 100644 (file)
index 0000000..df78e7e
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+** 2011 December 1
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    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 the interface definition for the quota a VFS shim.
+**
+** This particular shim enforces a quota system on files.  One or more
+** database files are in a "quota group" that is defined by a GLOB
+** pattern.  A quota is set for the combined size of all files in the
+** the group.  A quota of zero means "no limit".  If the total size
+** of all files in the quota group is greater than the limit, then
+** write requests that attempt to enlarge a file fail with SQLITE_FULL.
+**
+** However, before returning SQLITE_FULL, the write requests invoke
+** a callback function that is configurable for each quota group.
+** This callback has the opportunity to enlarge the quota.  If the
+** callback does enlarge the quota such that the total size of all
+** files within the group is less than the new quota, then the write
+** continues as if nothing had happened.
+*/
+#ifndef _QUOTA_H_
+#include "sqlite3.h"
+#include <stdio.h>
+
+/*
+** Initialize the quota VFS shim.  Use the VFS named zOrigVfsName
+** as the VFS that does the actual work.  Use the default if
+** zOrigVfsName==NULL.  
+**
+** The quota VFS shim is named "quota".  It will become the default
+** VFS if makeDefault is non-zero.
+**
+** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once
+** during start-up.
+*/
+int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault);
+
+/*
+** Shutdown the quota system.
+**
+** All SQLite database connections must be closed before calling this
+** routine.
+**
+** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once while
+** shutting down in order to free all remaining quota groups.
+*/
+int sqlite3_quota_shutdown(void);
+
+/*
+** Create or destroy a quota group.
+**
+** The quota group is defined by the zPattern.  When calling this routine
+** with a zPattern for a quota group that already exists, this routine
+** merely updates the iLimit, xCallback, and pArg values for that quota
+** group.  If zPattern is new, then a new quota group is created.
+**
+** The zPattern is always compared against the full pathname of the file.
+** Even if APIs are called with relative pathnames, SQLite converts the
+** name to a full pathname before comparing it against zPattern.  zPattern
+** is a standard glob pattern with the following matching rules:
+**
+**      '*'       Matches any sequence of zero or more characters.
+**
+**      '?'       Matches exactly one character.
+**
+**     [...]      Matches one character from the enclosed list of
+**                characters.
+**
+**     [^...]     Matches one character not in the enclosed list.
+**
+** Note that, unlike unix shell globbing, the directory separator "/"
+** can match a wildcard.  So, for example, the pattern "/abc/xyz/" "*"
+** matches any files anywhere in the directory hierarchy beneath
+** /abc/xyz
+**
+** If the iLimit for a quota group is set to zero, then the quota group
+** is disabled and will be deleted when the last database connection using
+** the quota group is closed.
+**
+** Calling this routine on a zPattern that does not exist and with a
+** zero iLimit is a no-op.
+**
+** A quota group must exist with a non-zero iLimit prior to opening
+** database connections if those connections are to participate in the
+** quota group.  Creating a quota group does not affect database connections
+** that are already open.
+*/
+int sqlite3_quota_set(
+  const char *zPattern,           /* The filename pattern */
+  sqlite3_int64 iLimit,           /* New quota to set for this quota group */
+  void (*xCallback)(              /* Callback invoked when going over quota */
+     const char *zFilename,         /* Name of file whose size increases */
+     sqlite3_int64 *piLimit,        /* IN/OUT: The current limit */
+     sqlite3_int64 iSize,           /* Total size of all files in the group */
+     void *pArg                     /* Client data */
+  ),
+  void *pArg,                     /* client data passed thru to callback */
+  void (*xDestroy)(void*)         /* Optional destructor for pArg */
+);
+
+/*
+** Bring the named file under quota management.  Or if it is already under
+** management, update its size.
+*/
+int sqlite3_quota_file(const char *zFilename);
+
+/*
+** The following object serves the same role as FILE in the standard C
+** library.  It represents an open connection to a file on disk for I/O.
+*/
+typedef struct quota_FILE quota_FILE;
+
+/*
+** Create a new quota_FILE object used to read and/or write to the
+** file zFilename.  The zMode parameter is as with standard library zMode.
+*/
+quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode);
+
+/*
+** Perform I/O against a quota_FILE object.  When doing writes, the
+** quota mechanism may result in a short write, in order to prevent
+** the sum of sizes of all files from going over quota.
+*/
+size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*);
+size_t sqlite3_quota_fwrite(void*, size_t, size_t, quota_FILE*);
+
+/*
+** Close a quota_FILE object and free all associated resources.  The
+** file remains under quota management.
+*/
+int sqlite3_quota_fclose(quota_FILE*);
+
+/*
+** Move the read/write pointer for a quota_FILE object.  Or tell the
+** current location of the read/write pointer.
+*/
+int sqlite3_quota_fseek(quota_FILE*, long, int);
+void sqlite3_quota_rewind(quota_FILE*);
+long sqlite3_quota_ftell(quota_FILE*);
+
+/*
+** Delete a file from the disk.  If that file is under quota management,
+** then adjust quotas accordingly.
+**
+** The file being deleted must not be open for reading or writing or as
+** a database when it is deleted.
+*/
+int sqlite3_quota_remove(const char *zFilename);
+
+#endif /* _QUOTA_H_ */