-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
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
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
-431556cac0b2c86d7f6a60412ff1023feeaafedf
\ No newline at end of file
+bd3ce723f1b5be52be46ede8614ca316f56e7e6f
\ No newline at end of file
** 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>
/* 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
/* 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;
}
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 ){
quotaEnter();
pGroup = quotaGroupFind(zName);
if( pGroup ){
- pFile = quotaFindFile(pGroup, zName);
+ pFile = quotaFindFile(pGroup, zName, 0);
if( pFile ){
if( pFile->nRef ){
pFile->deleteOnClose = 1;
quotaEnter();
pGroup = quotaGroupFind(zFull);
if( pGroup ){
- pFile = quotaFindFile(pGroup, zFull);
+ pFile = quotaFindFile(pGroup, zFull, 0);
if( pFile ) quotaRemoveFile(pFile);
}
quotaLeave();
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
--- /dev/null
+/*
+** 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_ */