mirror of
https://github.com/status-im/sqlcipher.git
synced 2026-08-30 22:11:14 +00:00
Merge branch 'sqlite-release' into prerelease-integration
This commit is contained in:
+2
-2
@@ -216,7 +216,7 @@ static void attachFunc(
|
||||
db->aDb[iDb].pBt = 0;
|
||||
db->aDb[iDb].pSchema = 0;
|
||||
}
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
db->nDb = iDb;
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
||||
db->mallocFailed = 1;
|
||||
@@ -288,7 +288,7 @@ static void detachFunc(
|
||||
sqlite3BtreeClose(pDb->pBt);
|
||||
pDb->pBt = 0;
|
||||
pDb->pSchema = 0;
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
return;
|
||||
|
||||
detach_error:
|
||||
|
||||
+1
-1
@@ -414,7 +414,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( p->pDestDb ){
|
||||
sqlite3ResetInternalSchema(p->pDestDb, -1);
|
||||
sqlite3ResetAllSchemasOfConnection(p->pDestDb);
|
||||
}
|
||||
if( destMode==PAGER_JOURNALMODE_WAL ){
|
||||
rc = sqlite3BtreeSetVersion(p->pDest, 2);
|
||||
|
||||
+17
-10
@@ -1721,7 +1721,8 @@ int sqlite3BtreeOpen(
|
||||
const int isMemdb = 0;
|
||||
#else
|
||||
const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0)
|
||||
|| (isTempDb && sqlite3TempInMemory(db));
|
||||
|| (isTempDb && sqlite3TempInMemory(db))
|
||||
|| (vfsFlags & SQLITE_OPEN_MEMORY)!=0;
|
||||
#endif
|
||||
|
||||
assert( db!=0 );
|
||||
@@ -1757,7 +1758,7 @@ int sqlite3BtreeOpen(
|
||||
** If this Btree is a candidate for shared cache, try to find an
|
||||
** existing BtShared object that we can share with
|
||||
*/
|
||||
if( isMemdb==0 && isTempDb==0 ){
|
||||
if( isTempDb==0 && (isMemdb==0 || (vfsFlags&SQLITE_OPEN_URI)!=0) ){
|
||||
if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){
|
||||
int nFullPathname = pVfs->mxPathname+1;
|
||||
char *zFullPathname = sqlite3Malloc(nFullPathname);
|
||||
@@ -1767,11 +1768,16 @@ int sqlite3BtreeOpen(
|
||||
sqlite3_free(p);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);
|
||||
if( rc ){
|
||||
sqlite3_free(zFullPathname);
|
||||
sqlite3_free(p);
|
||||
return rc;
|
||||
if( isMemdb ){
|
||||
memcpy(zFullPathname, zFilename, sqlite3Strlen30(zFilename)+1);
|
||||
}else{
|
||||
rc = sqlite3OsFullPathname(pVfs, zFilename,
|
||||
nFullPathname, zFullPathname);
|
||||
if( rc ){
|
||||
sqlite3_free(zFullPathname);
|
||||
sqlite3_free(p);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
#if SQLITE_THREADSAFE
|
||||
mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
|
||||
@@ -1781,7 +1787,7 @@ int sqlite3BtreeOpen(
|
||||
#endif
|
||||
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
|
||||
assert( pBt->nRef>0 );
|
||||
if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager))
|
||||
if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager, 0))
|
||||
&& sqlite3PagerVfs(pBt->pPager)==pVfs ){
|
||||
int iDb;
|
||||
for(iDb=db->nDb-1; iDb>=0; iDb--){
|
||||
@@ -8046,14 +8052,15 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
|
||||
|
||||
/*
|
||||
** Return the full pathname of the underlying database file.
|
||||
** Return the full pathname of the underlying database file. Return
|
||||
** an empty string if the database is in-memory or a TEMP database.
|
||||
**
|
||||
** The pager filename is invariant as long as the pager is
|
||||
** open so it is safe to access without the BtShared mutex.
|
||||
*/
|
||||
const char *sqlite3BtreeGetFilename(Btree *p){
|
||||
assert( p->pBt->pPager!=0 );
|
||||
return sqlite3PagerFilename(p->pBt->pPager);
|
||||
return sqlite3PagerFilename(p->pBt->pPager, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+52
-50
@@ -394,58 +394,15 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
|
||||
}
|
||||
|
||||
/*
|
||||
** Erase all schema information from the in-memory hash tables of
|
||||
** a single database. This routine is called to reclaim memory
|
||||
** before the database closes. It is also called during a rollback
|
||||
** if there were schema changes during the transaction or if a
|
||||
** schema-cookie mismatch occurs.
|
||||
** Look through the list of open database files in db->aDb[] and if
|
||||
** any have been closed, remove them from the list. Reallocate the
|
||||
** db->aDb[] structure to a smaller size, if possible.
|
||||
**
|
||||
** If iDb<0 then reset the internal schema tables for all database
|
||||
** files. If iDb>=0 then reset the internal schema for only the
|
||||
** single file indicated.
|
||||
** Entry 0 (the "main" database) and entry 1 (the "temp" database)
|
||||
** are never candidates for being collapsed.
|
||||
*/
|
||||
void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
|
||||
void sqlite3CollapseDatabaseArray(sqlite3 *db){
|
||||
int i, j;
|
||||
assert( iDb<db->nDb );
|
||||
|
||||
if( iDb>=0 ){
|
||||
/* Case 1: Reset the single schema identified by iDb */
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
assert( pDb->pSchema!=0 );
|
||||
sqlite3SchemaClear(pDb->pSchema);
|
||||
|
||||
/* If any database other than TEMP is reset, then also reset TEMP
|
||||
** since TEMP might be holding triggers that reference tables in the
|
||||
** other database.
|
||||
*/
|
||||
if( iDb!=1 ){
|
||||
pDb = &db->aDb[1];
|
||||
assert( pDb->pSchema!=0 );
|
||||
sqlite3SchemaClear(pDb->pSchema);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* Case 2 (from here to the end): Reset all schemas for all attached
|
||||
** databases. */
|
||||
assert( iDb<0 );
|
||||
sqlite3BtreeEnterAll(db);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Db *pDb = &db->aDb[i];
|
||||
if( pDb->pSchema ){
|
||||
sqlite3SchemaClear(pDb->pSchema);
|
||||
}
|
||||
}
|
||||
db->flags &= ~SQLITE_InternChanges;
|
||||
sqlite3VtabUnlockList(db);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
|
||||
/* If one or more of the auxiliary database files has been closed,
|
||||
** then remove them from the auxiliary database list. We take the
|
||||
** opportunity to do this here since we have just deleted all of the
|
||||
** schema hash tables and therefore do not have to make any changes
|
||||
** to any of those tables.
|
||||
*/
|
||||
for(i=j=2; i<db->nDb; i++){
|
||||
struct Db *pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 ){
|
||||
@@ -467,6 +424,51 @@ void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset the schema for the database at index iDb. Also reset the
|
||||
** TEMP schema.
|
||||
*/
|
||||
void sqlite3ResetOneSchema(sqlite3 *db, int iDb){
|
||||
Db *pDb;
|
||||
assert( iDb<db->nDb );
|
||||
|
||||
/* Case 1: Reset the single schema identified by iDb */
|
||||
pDb = &db->aDb[iDb];
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
assert( pDb->pSchema!=0 );
|
||||
sqlite3SchemaClear(pDb->pSchema);
|
||||
|
||||
/* If any database other than TEMP is reset, then also reset TEMP
|
||||
** since TEMP might be holding triggers that reference tables in the
|
||||
** other database.
|
||||
*/
|
||||
if( iDb!=1 ){
|
||||
pDb = &db->aDb[1];
|
||||
assert( pDb->pSchema!=0 );
|
||||
sqlite3SchemaClear(pDb->pSchema);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** Erase all schema information from all attached databases (including
|
||||
** "main" and "temp") for a single database connection.
|
||||
*/
|
||||
void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
|
||||
int i;
|
||||
sqlite3BtreeEnterAll(db);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Db *pDb = &db->aDb[i];
|
||||
if( pDb->pSchema ){
|
||||
sqlite3SchemaClear(pDb->pSchema);
|
||||
}
|
||||
}
|
||||
db->flags &= ~SQLITE_InternChanges;
|
||||
sqlite3VtabUnlockList(db);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
sqlite3CollapseDatabaseArray(db);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called when a commit occurs.
|
||||
*/
|
||||
@@ -2763,7 +2765,7 @@ Index *sqlite3CreateIndex(
|
||||
}else{
|
||||
zColl = pTab->aCol[j].zColl;
|
||||
if( !zColl ){
|
||||
zColl = db->pDfltColl->zName;
|
||||
zColl = "BINARY";
|
||||
}
|
||||
}
|
||||
if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl) ){
|
||||
|
||||
@@ -48,6 +48,9 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_COVERAGE_TEST
|
||||
"COVERAGE_TEST",
|
||||
#endif
|
||||
#ifdef SQLITE_CURDIR
|
||||
"CURDIR",
|
||||
#endif
|
||||
#ifdef SQLITE_DEBUG
|
||||
"DEBUG",
|
||||
#endif
|
||||
|
||||
+65
-9
@@ -74,6 +74,15 @@ void (*sqlite3IoTrace)(const char*, ...) = 0;
|
||||
*/
|
||||
char *sqlite3_temp_directory = 0;
|
||||
|
||||
/*
|
||||
** If the following global variable points to a string which is the
|
||||
** name of a directory, then that directory will be used to store
|
||||
** all database files specified with a relative pathname.
|
||||
**
|
||||
** See also the "PRAGMA data_store_directory" SQL command.
|
||||
*/
|
||||
char *sqlite3_data_directory = 0;
|
||||
|
||||
/*
|
||||
** Initialize SQLite.
|
||||
**
|
||||
@@ -272,6 +281,18 @@ int sqlite3_shutdown(void){
|
||||
if( sqlite3GlobalConfig.isMallocInit ){
|
||||
sqlite3MallocEnd();
|
||||
sqlite3GlobalConfig.isMallocInit = 0;
|
||||
|
||||
#ifndef SQLITE_OMIT_SHUTDOWN_DIRECTORIES
|
||||
/* The heap subsystem has now been shutdown and these values are supposed
|
||||
** to be NULL or point to memory that was obtained from sqlite3_malloc(),
|
||||
** which would rely on that heap subsystem; therefore, make sure these
|
||||
** values cannot refer to heap memory that was just invalidated when the
|
||||
** heap subsystem was shutdown. This is only done if the current call to
|
||||
** this function resulted in the heap subsystem actually being shutdown.
|
||||
*/
|
||||
sqlite3_data_directory = 0;
|
||||
sqlite3_temp_directory = 0;
|
||||
#endif
|
||||
}
|
||||
if( sqlite3GlobalConfig.isMutexInit ){
|
||||
sqlite3MutexEnd();
|
||||
@@ -720,6 +741,30 @@ static void functionDestroy(sqlite3 *db, FuncDef *p){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Disconnect all sqlite3_vtab objects that belong to database connection
|
||||
** db. This is called when db is being closed.
|
||||
*/
|
||||
static void disconnectAllVtab(sqlite3 *db){
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
int i;
|
||||
sqlite3BtreeEnterAll(db);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Schema *pSchema = db->aDb[i].pSchema;
|
||||
if( db->aDb[i].pSchema ){
|
||||
HashElem *p;
|
||||
for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
|
||||
Table *pTab = (Table *)sqliteHashData(p);
|
||||
if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
#else
|
||||
UNUSED_PARAMETER(db);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Close an existing SQLite database
|
||||
*/
|
||||
@@ -735,10 +780,10 @@ int sqlite3_close(sqlite3 *db){
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
/* Force xDestroy calls on all virtual tables */
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
/* Force xDisconnect calls on all virtual tables */
|
||||
disconnectAllVtab(db);
|
||||
|
||||
/* If a transaction is open, the ResetInternalSchema() call above
|
||||
/* If a transaction is open, the disconnectAllVtab() call above
|
||||
** will not have called the xDisconnect() method on any virtual
|
||||
** tables in the db->aVTrans[] array. The following sqlite3VtabRollback()
|
||||
** call will do so. We need to do this before the check for active
|
||||
@@ -769,6 +814,7 @@ int sqlite3_close(sqlite3 *db){
|
||||
/* Free any outstanding Savepoint structures. */
|
||||
sqlite3CloseSavepoints(db);
|
||||
|
||||
/* Close all database connections */
|
||||
for(j=0; j<db->nDb; j++){
|
||||
struct Db *pDb = &db->aDb[j];
|
||||
if( pDb->pBt ){
|
||||
@@ -779,15 +825,22 @@ int sqlite3_close(sqlite3 *db){
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
/* Clear the TEMP schema separately and last */
|
||||
if( db->aDb[1].pSchema ){
|
||||
sqlite3SchemaClear(db->aDb[1].pSchema);
|
||||
}
|
||||
sqlite3VtabUnlockList(db);
|
||||
|
||||
/* Free up the array of auxiliary databases */
|
||||
sqlite3CollapseDatabaseArray(db);
|
||||
assert( db->nDb<=2 );
|
||||
assert( db->aDb==db->aDbStatic );
|
||||
|
||||
/* Tell the code in notify.c that the connection no longer holds any
|
||||
** locks and does not require any further unlock-notify callbacks.
|
||||
*/
|
||||
sqlite3ConnectionClosed(db);
|
||||
|
||||
assert( db->nDb<=2 );
|
||||
assert( db->aDb==db->aDbStatic );
|
||||
for(j=0; j<ArraySize(db->aFunc.a); j++){
|
||||
FuncDef *pNext, *pHash, *p;
|
||||
for(p=db->aFunc.a[j]; p; p=pHash){
|
||||
@@ -874,7 +927,7 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
|
||||
|
||||
if( db->flags&SQLITE_InternChanges ){
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
}
|
||||
|
||||
/* Any deferred constraint violations have now been resolved. */
|
||||
@@ -2012,10 +2065,12 @@ int sqlite3ParseUri(
|
||||
{ "ro", SQLITE_OPEN_READONLY },
|
||||
{ "rw", SQLITE_OPEN_READWRITE },
|
||||
{ "rwc", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE },
|
||||
{ "memory", SQLITE_OPEN_MEMORY },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
mask = SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
|
||||
mask = SQLITE_OPEN_READONLY | SQLITE_OPEN_READWRITE
|
||||
| SQLITE_OPEN_CREATE | SQLITE_OPEN_MEMORY;
|
||||
aMode = aOpenMode;
|
||||
limit = mask & flags;
|
||||
zModeType = "access";
|
||||
@@ -2036,7 +2091,7 @@ int sqlite3ParseUri(
|
||||
rc = SQLITE_ERROR;
|
||||
goto parse_uri_out;
|
||||
}
|
||||
if( mode>limit ){
|
||||
if( (mode & ~SQLITE_OPEN_MEMORY)>limit ){
|
||||
*pzErrMsg = sqlite3_mprintf("%s mode not allowed: %s",
|
||||
zModeType, zVal);
|
||||
rc = SQLITE_PERM;
|
||||
@@ -2055,6 +2110,7 @@ int sqlite3ParseUri(
|
||||
memcpy(zFile, zUri, nUri);
|
||||
zFile[nUri] = '\0';
|
||||
zFile[nUri+1] = '\0';
|
||||
flags &= ~SQLITE_OPEN_URI;
|
||||
}
|
||||
|
||||
*ppVfs = sqlite3_vfs_find(zVfs);
|
||||
|
||||
+12
-2
@@ -56,7 +56,7 @@ struct sqlite3_mutex {
|
||||
** this out as well.
|
||||
*/
|
||||
#if 0
|
||||
#if SQLITE_OS_WINCE
|
||||
#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
|
||||
# define mutexIsNT() (1)
|
||||
#else
|
||||
static int mutexIsNT(void){
|
||||
@@ -109,18 +109,24 @@ static int winMutex_isInit = 0;
|
||||
*/
|
||||
static long winMutex_lock = 0;
|
||||
|
||||
extern void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
|
||||
|
||||
static int winMutexInit(void){
|
||||
/* The first to increment to 1 does actual initialization */
|
||||
if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
|
||||
int i;
|
||||
for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
|
||||
#if SQLITE_OS_WINRT
|
||||
InitializeCriticalSectionEx(&winMutex_staticMutexes[i].mutex, 0, 0);
|
||||
#else
|
||||
InitializeCriticalSection(&winMutex_staticMutexes[i].mutex);
|
||||
#endif
|
||||
}
|
||||
winMutex_isInit = 1;
|
||||
}else{
|
||||
/* Someone else is in the process of initing the static mutexes */
|
||||
while( !winMutex_isInit ){
|
||||
Sleep(1);
|
||||
sqlite3_win32_sleep(1);
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
@@ -194,7 +200,11 @@ static sqlite3_mutex *winMutexAlloc(int iType){
|
||||
#ifdef SQLITE_DEBUG
|
||||
p->id = iType;
|
||||
#endif
|
||||
#if SQLITE_OS_WINRT
|
||||
InitializeCriticalSectionEx(&p->mutex, 0, 0);
|
||||
#else
|
||||
InitializeCriticalSection(&p->mutex);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -65,13 +65,11 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Define the maximum size of a temporary filename
|
||||
*/
|
||||
#if SQLITE_OS_WIN
|
||||
# include <windows.h>
|
||||
# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50)
|
||||
#elif SQLITE_OS_OS2
|
||||
#endif
|
||||
|
||||
#if SQLITE_OS_OS2
|
||||
# if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY)
|
||||
# include <os2safe.h> /* has to be included before os2.h for linking to work */
|
||||
# endif
|
||||
@@ -84,9 +82,6 @@
|
||||
# define INCL_DOSSEMAPHORES
|
||||
# include <os2.h>
|
||||
# include <uconv.h>
|
||||
# define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP)
|
||||
#else
|
||||
# define SQLITE_TEMPNAME_SIZE 200
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -120,6 +115,22 @@
|
||||
# define SQLITE_OS_WINCE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with WindowsRT (Metro) as this has a different and
|
||||
** incompatible API from win32.
|
||||
*/
|
||||
#if !defined(SQLITE_OS_WINRT)
|
||||
# define SQLITE_OS_WINRT 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** When compiled for WinCE or WinRT, there is no concept of the current
|
||||
** directory.
|
||||
*/
|
||||
#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
||||
# define SQLITE_CURDIR 1
|
||||
#endif
|
||||
|
||||
/* If the SET_FULLSYNC macro is not defined above, then make it
|
||||
** a no-op
|
||||
*/
|
||||
|
||||
+21
-21
@@ -227,7 +227,7 @@ struct unixFile {
|
||||
#if OS_VXWORKS
|
||||
struct vxworksFileId *pId; /* Unique file ID */
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* The next group of variables are used to track whether or not the
|
||||
** transaction counter in bytes 24-27 of database files are updated
|
||||
** whenever any part of the database changes. An assertion fault will
|
||||
@@ -262,7 +262,6 @@ struct unixFile {
|
||||
#define UNIXFILE_DELETE 0x20 /* Delete on close */
|
||||
#define UNIXFILE_URI 0x40 /* Filename might have query parameters */
|
||||
#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */
|
||||
#define UNIXFILE_CHOWN 0x100 /* File ownership was changed */
|
||||
|
||||
/*
|
||||
** Include code that is common to all os_*.c files
|
||||
@@ -308,6 +307,15 @@ static int posixOpen(const char *zFile, int flags, int mode){
|
||||
return open(zFile, flags, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
** On some systems, calls to fchown() will trigger a message in a security
|
||||
** log if they come from non-root processes. So avoid calling fchown() if
|
||||
** we are not running as root.
|
||||
*/
|
||||
static int posixFchown(int fd, uid_t uid, gid_t gid){
|
||||
return geteuid() ? 0 : fchown(fd,uid,gid);
|
||||
}
|
||||
|
||||
/* Forward reference */
|
||||
static int openDirectory(const char*, int*);
|
||||
|
||||
@@ -419,7 +427,7 @@ static struct unix_syscall {
|
||||
{ "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
|
||||
#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
|
||||
|
||||
{ "fchown", (sqlite3_syscall_ptr)fchown, 0 },
|
||||
{ "fchown", (sqlite3_syscall_ptr)posixFchown, 0 },
|
||||
#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
|
||||
|
||||
{ "umask", (sqlite3_syscall_ptr)umask, 0 },
|
||||
@@ -1563,7 +1571,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
|
||||
}
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Set up the transaction-counter change checking flags when
|
||||
** transitioning from a SHARED to a RESERVED lock. The change
|
||||
** from SHARED to RESERVED marks the beginning of a normal
|
||||
@@ -1642,7 +1650,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
|
||||
if( pFile->eFileLock>SHARED_LOCK ){
|
||||
assert( pInode->eFileLock==pFile->eFileLock );
|
||||
|
||||
#ifndef NDEBUG
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* When reducing a lock such that other processes can start
|
||||
** reading the database file again, make sure that the
|
||||
** transaction counter was updated if any part of the database
|
||||
@@ -2841,7 +2849,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
|
||||
SimulateIOError( h=(-1) )
|
||||
SimulateIOErrorBenign(0);
|
||||
|
||||
#ifndef NDEBUG
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* When reducing a lock such that other processes can start
|
||||
** reading the database file again, make sure that the
|
||||
** transaction counter was updated if any part of the database
|
||||
@@ -3145,7 +3153,7 @@ static int unixWrite(
|
||||
);
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* If we are doing a normal write to a database file (as opposed to
|
||||
** doing a hot-journal rollback or a write to some file other than a
|
||||
** normal database file) then record the fact that the database
|
||||
@@ -3436,7 +3444,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
|
||||
pFile->lastErrno = errno;
|
||||
return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
|
||||
}else{
|
||||
#ifndef NDEBUG
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* If we are doing a normal write to a database file (as opposed to
|
||||
** doing a hot-journal rollback or a write to some file other than a
|
||||
** normal database file) and we truncate the file to zero length,
|
||||
@@ -3593,7 +3601,7 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
*(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* The pager calls this method to signal that it has done
|
||||
** a rollback and that the database is therefore unchanged and
|
||||
** it hence it is OK for the transaction change counter to be
|
||||
@@ -3944,14 +3952,9 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
||||
|
||||
/* If this process is running as root, make sure that the SHM file
|
||||
** is owned by the same user that owns the original database. Otherwise,
|
||||
** the original owner will not be able to connect. If this process is
|
||||
** not root, the following fchown() will fail, but we don't care. The
|
||||
** if(){..} and the UNIXFILE_CHOWN flag are purely to silence compiler
|
||||
** warnings.
|
||||
** the original owner will not be able to connect.
|
||||
*/
|
||||
if( osFchown(pShmNode->h, sStat.st_uid, sStat.st_gid)==0 ){
|
||||
pDbFd->ctrlFlags |= UNIXFILE_CHOWN;
|
||||
}
|
||||
osFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
|
||||
|
||||
/* Check to see if another process is holding the dead-man switch.
|
||||
** If not, truncate the file to zero length.
|
||||
@@ -5157,13 +5160,10 @@ static int unixOpen(
|
||||
|
||||
/* If this process is running as root and if creating a new rollback
|
||||
** journal or WAL file, set the ownership of the journal or WAL to be
|
||||
** the same as the original database. If we are not running as root,
|
||||
** then the fchown() call will fail, but that's ok. The "if(){}" and
|
||||
** the setting of the UNIXFILE_CHOWN flag are purely to silence compiler
|
||||
** warnings from gcc.
|
||||
** the same as the original database.
|
||||
*/
|
||||
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
|
||||
if( osFchown(fd, uid, gid)==0 ){ p->ctrlFlags |= UNIXFILE_CHOWN; }
|
||||
osFchown(fd, uid, gid);
|
||||
}
|
||||
}
|
||||
assert( fd>=0 );
|
||||
|
||||
+770
-262
File diff suppressed because it is too large
Load Diff
+20
-8
@@ -4360,7 +4360,12 @@ int sqlite3PagerOpen(
|
||||
#ifndef SQLITE_OMIT_MEMORYDB
|
||||
if( flags & PAGER_MEMORY ){
|
||||
memDb = 1;
|
||||
zFilename = 0;
|
||||
if( zFilename && zFilename[0] ){
|
||||
zPathname = sqlite3DbStrDup(0, zFilename);
|
||||
if( zPathname==0 ) return SQLITE_NOMEM;
|
||||
nPathname = sqlite3Strlen30(zPathname);
|
||||
zFilename = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4371,7 +4376,7 @@ int sqlite3PagerOpen(
|
||||
if( zFilename && zFilename[0] ){
|
||||
const char *z;
|
||||
nPathname = pVfs->mxPathname+1;
|
||||
zPathname = sqlite3Malloc(nPathname*2);
|
||||
zPathname = sqlite3DbMallocRaw(0, nPathname*2);
|
||||
if( zPathname==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
@@ -4395,7 +4400,7 @@ int sqlite3PagerOpen(
|
||||
rc = SQLITE_CANTOPEN_BKPT;
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(zPathname);
|
||||
sqlite3DbFree(0, zPathname);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
@@ -4425,7 +4430,7 @@ int sqlite3PagerOpen(
|
||||
);
|
||||
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
|
||||
if( !pPtr ){
|
||||
sqlite3_free(zPathname);
|
||||
sqlite3DbFree(0, zPathname);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pPager = (Pager*)(pPtr);
|
||||
@@ -4441,7 +4446,7 @@ int sqlite3PagerOpen(
|
||||
assert( nPathname>0 );
|
||||
pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri);
|
||||
memcpy(pPager->zFilename, zPathname, nPathname);
|
||||
memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
|
||||
if( nUri ) memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
|
||||
memcpy(pPager->zJournal, zPathname, nPathname);
|
||||
memcpy(&pPager->zJournal[nPathname], "-journal\000", 8+1);
|
||||
sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal);
|
||||
@@ -4451,7 +4456,7 @@ int sqlite3PagerOpen(
|
||||
memcpy(&pPager->zWal[nPathname], "-wal\000", 4+1);
|
||||
sqlite3FileSuffix3(pPager->zFilename, pPager->zWal);
|
||||
#endif
|
||||
sqlite3_free(zPathname);
|
||||
sqlite3DbFree(0, zPathname);
|
||||
}
|
||||
pPager->pVfs = pVfs;
|
||||
pPager->vfsFlags = vfsFlags;
|
||||
@@ -6296,9 +6301,16 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
|
||||
|
||||
/*
|
||||
** Return the full pathname of the database file.
|
||||
**
|
||||
** Except, if the pager is in-memory only, then return an empty string if
|
||||
** nullIfMemDb is true. This routine is called with nullIfMemDb==1 when
|
||||
** used to report the filename to the user, for compatibility with legacy
|
||||
** behavior. But when the Btree needs to know the filename for matching to
|
||||
** shared cache, it uses nullIfMemDb==0 so that in-memory databases can
|
||||
** participate in shared-cache.
|
||||
*/
|
||||
const char *sqlite3PagerFilename(Pager *pPager){
|
||||
return pPager->zFilename;
|
||||
const char *sqlite3PagerFilename(Pager *pPager, int nullIfMemDb){
|
||||
return (nullIfMemDb && pPager->memDb) ? "" : pPager->zFilename;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -151,7 +151,7 @@ int sqlite3PagerCloseWal(Pager *pPager);
|
||||
u8 sqlite3PagerIsreadonly(Pager*);
|
||||
int sqlite3PagerRefcount(Pager*);
|
||||
int sqlite3PagerMemUsed(Pager*);
|
||||
const char *sqlite3PagerFilename(Pager*);
|
||||
const char *sqlite3PagerFilename(Pager*, int);
|
||||
const sqlite3_vfs *sqlite3PagerVfs(Pager*);
|
||||
sqlite3_file *sqlite3PagerFile(Pager*);
|
||||
const char *sqlite3PagerJournalname(Pager*);
|
||||
|
||||
@@ -212,12 +212,14 @@ static void *pcache1Alloc(int nByte){
|
||||
** it from sqlite3Malloc instead.
|
||||
*/
|
||||
p = sqlite3Malloc(nByte);
|
||||
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
|
||||
if( p ){
|
||||
int sz = sqlite3MallocSize(p);
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
}
|
||||
#endif
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
|
||||
}
|
||||
return p;
|
||||
@@ -244,9 +246,11 @@ static int pcache1Free(void *p){
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
||||
nFreed = sqlite3MallocSize(p);
|
||||
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -nFreed);
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
#endif
|
||||
sqlite3_free(p);
|
||||
}
|
||||
return nFreed;
|
||||
|
||||
+45
-1
@@ -118,7 +118,7 @@ static int invalidateTempStorage(Parse *pParse){
|
||||
}
|
||||
sqlite3BtreeClose(db->aDb[1].pBt);
|
||||
db->aDb[1].pBt = 0;
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -817,6 +817,50 @@ void sqlite3Pragma(
|
||||
}
|
||||
}else
|
||||
|
||||
#if SQLITE_OS_WIN
|
||||
/*
|
||||
** PRAGMA data_store_directory
|
||||
** PRAGMA data_store_directory = ""|"directory_name"
|
||||
**
|
||||
** Return or set the local value of the data_store_directory flag. Changing
|
||||
** the value sets a specific directory to be used for database files that
|
||||
** were specified with a relative pathname. Setting to a null string reverts
|
||||
** to the default database directory, which for database files specified with
|
||||
** a relative path will probably be based on the current directory for the
|
||||
** process. Database file specified with an absolute path are not impacted
|
||||
** by this setting, regardless of its value.
|
||||
**
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft, "data_store_directory")==0 ){
|
||||
if( !zRight ){
|
||||
if( sqlite3_data_directory ){
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME,
|
||||
"data_store_directory", SQLITE_STATIC);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, sqlite3_data_directory, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
||||
}
|
||||
}else{
|
||||
#ifndef SQLITE_OMIT_WSD
|
||||
if( zRight[0] ){
|
||||
int res;
|
||||
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
|
||||
if( rc!=SQLITE_OK || res==0 ){
|
||||
sqlite3ErrorMsg(pParse, "not a writable directory");
|
||||
goto pragma_out;
|
||||
}
|
||||
}
|
||||
sqlite3_free(sqlite3_data_directory);
|
||||
if( zRight[0] ){
|
||||
sqlite3_data_directory = sqlite3_mprintf("%s", zRight);
|
||||
}else{
|
||||
sqlite3_data_directory = 0;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_WSD */
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
|
||||
# if defined(__APPLE__)
|
||||
# define SQLITE_ENABLE_LOCKING_STYLE 1
|
||||
|
||||
+4
-5
@@ -262,7 +262,6 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3;
|
||||
if( encoding==0 ) encoding = SQLITE_UTF8;
|
||||
ENC(db) = encoding;
|
||||
db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 0);
|
||||
}else{
|
||||
/* If opening an attached database, the encoding much match ENC(db) */
|
||||
if( meta[BTREE_TEXT_ENCODING-1]!=ENC(db) ){
|
||||
@@ -342,7 +341,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
rc = SQLITE_NOMEM;
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
}
|
||||
if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){
|
||||
/* Black magic: If the SQLITE_RecoveryMode flag is set, then consider
|
||||
@@ -395,7 +394,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){
|
||||
if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue;
|
||||
rc = sqlite3InitOne(db, i, pzErrMsg);
|
||||
if( rc ){
|
||||
sqlite3ResetInternalSchema(db, i);
|
||||
sqlite3ResetOneSchema(db, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,7 +407,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){
|
||||
&& !DbHasProperty(db, 1, DB_SchemaLoaded) ){
|
||||
rc = sqlite3InitOne(db, 1, pzErrMsg);
|
||||
if( rc ){
|
||||
sqlite3ResetInternalSchema(db, 1);
|
||||
sqlite3ResetOneSchema(db, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -476,7 +475,7 @@ static void schemaIsValid(Parse *pParse){
|
||||
sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie);
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){
|
||||
sqlite3ResetInternalSchema(db, iDb);
|
||||
sqlite3ResetOneSchema(db, iDb);
|
||||
pParse->rc = SQLITE_SCHEMA;
|
||||
}
|
||||
|
||||
|
||||
+49
-10
@@ -473,6 +473,7 @@ int sqlite3_exec(
|
||||
#define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */
|
||||
#define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */
|
||||
#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */
|
||||
#define SQLITE_OPEN_MEMORY 0x00000080 /* Ok for sqlite3_open_v2() */
|
||||
#define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */
|
||||
#define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */
|
||||
#define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */
|
||||
@@ -2164,12 +2165,12 @@ char *sqlite3_vsnprintf(int,char*,const char*, va_list);
|
||||
** implementation of these routines to be omitted. That capability
|
||||
** is no longer provided. Only built-in memory allocators can be used.
|
||||
**
|
||||
** The Windows OS interface layer calls
|
||||
** Prior to SQLite version 3.7.10, the Windows OS interface layer called
|
||||
** the system malloc() and free() directly when converting
|
||||
** filenames between the UTF-8 encoding used by SQLite
|
||||
** and whatever filename encoding is used by the particular Windows
|
||||
** installation. Memory allocation errors are detected, but
|
||||
** they are reported back as [SQLITE_CANTOPEN] or
|
||||
** installation. Memory allocation errors were detected, but
|
||||
** they were reported back as [SQLITE_CANTOPEN] or
|
||||
** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
|
||||
**
|
||||
** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
|
||||
@@ -2570,18 +2571,20 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||
** present, then the VFS specified by the option takes precedence over
|
||||
** the value passed as the fourth parameter to sqlite3_open_v2().
|
||||
**
|
||||
** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw" or
|
||||
** "rwc". Attempting to set it to any other value is an error)^.
|
||||
** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw",
|
||||
** "rwc", or "memory". Attempting to set it to any other value is
|
||||
** an error)^.
|
||||
** ^If "ro" is specified, then the database is opened for read-only
|
||||
** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the
|
||||
** third argument to sqlite3_prepare_v2(). ^If the mode option is set to
|
||||
** "rw", then the database is opened for read-write (but not create)
|
||||
** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had
|
||||
** been set. ^Value "rwc" is equivalent to setting both
|
||||
** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If sqlite3_open_v2() is
|
||||
** used, it is an error to specify a value for the mode parameter that is
|
||||
** less restrictive than that specified by the flags passed as the third
|
||||
** parameter.
|
||||
** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is
|
||||
** set to "memory" then a pure [in-memory database] that never reads
|
||||
** or writes from disk is used. ^It is an error to specify a value for
|
||||
** the mode parameter that is less restrictive than that specified by
|
||||
** the flags passed in the third parameter to sqlite3_open_v2().
|
||||
**
|
||||
** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or
|
||||
** "private". ^Setting it to "shared" is equivalent to setting the
|
||||
@@ -4444,6 +4447,43 @@ int sqlite3_sleep(int);
|
||||
*/
|
||||
SQLITE_EXTERN char *sqlite3_temp_directory;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Name Of The Folder Holding Database Files
|
||||
**
|
||||
** ^(If this global variable is made to point to a string which is
|
||||
** the name of a folder (a.k.a. directory), then all database files
|
||||
** specified with a relative pathname and created or accessed by
|
||||
** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed
|
||||
** to be relative to that directory.)^ ^If this variable is a NULL
|
||||
** pointer, then SQLite assumes that all database files specified
|
||||
** with a relative pathname are relative to the current directory
|
||||
** for the process. Only the windows VFS makes use of this global
|
||||
** variable; it is ignored by the unix VFS.
|
||||
**
|
||||
** Changing the value of this variable while a database connection is
|
||||
** open can result in a corrupt database.
|
||||
**
|
||||
** It is not safe to read or modify this variable in more than one
|
||||
** thread at a time. It is not safe to read or modify this variable
|
||||
** if a [database connection] is being used at the same time in a separate
|
||||
** thread.
|
||||
** It is intended that this variable be set once
|
||||
** as part of process initialization and before any SQLite interface
|
||||
** routines have been called and that this variable remain unchanged
|
||||
** thereafter.
|
||||
**
|
||||
** ^The [data_store_directory pragma] may modify this variable and cause
|
||||
** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore,
|
||||
** the [data_store_directory pragma] always assumes that any string
|
||||
** that this variable points to is held in memory obtained from
|
||||
** [sqlite3_malloc] and the pragma may attempt to free that memory
|
||||
** using [sqlite3_free].
|
||||
** Hence, if this variable is modified directly, either it should be
|
||||
** made NULL or made to point to memory obtained from [sqlite3_malloc]
|
||||
** or else the use of the [data_store_directory pragma] should be avoided.
|
||||
*/
|
||||
SQLITE_EXTERN char *sqlite3_data_directory;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Test For Auto-Commit Mode
|
||||
** KEYWORDS: {autocommit mode}
|
||||
@@ -4622,7 +4662,6 @@ void *sqlite3_update_hook(
|
||||
|
||||
/*
|
||||
** CAPI3REF: Enable Or Disable Shared Pager Cache
|
||||
** KEYWORDS: {shared cache}
|
||||
**
|
||||
** ^(This routine enables or disables the sharing of the database cache
|
||||
** and schema data structures between [database connection | connections]
|
||||
|
||||
+15
-5
@@ -203,15 +203,22 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Many people are failing to set -DNDEBUG=1 when compiling SQLite.
|
||||
** Setting NDEBUG makes the code smaller and run faster. So the following
|
||||
** lines are added to automatically set NDEBUG unless the -DSQLITE_DEBUG=1
|
||||
** option is set. Thus NDEBUG becomes an opt-in rather than an opt-out
|
||||
** NDEBUG and SQLITE_DEBUG are opposites. It should always be true that
|
||||
** defined(NDEBUG)==!defined(SQLITE_DEBUG). If this is not currently true,
|
||||
** make it true by defining or undefining NDEBUG.
|
||||
**
|
||||
** Setting NDEBUG makes the code smaller and run faster by disabling the
|
||||
** number assert() statements in the code. So we want the default action
|
||||
** to be for NDEBUG to be set and NDEBUG to be undefined only if SQLITE_DEBUG
|
||||
** is set. Thus NDEBUG becomes an opt-in rather than an opt-out
|
||||
** feature.
|
||||
*/
|
||||
#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
|
||||
# define NDEBUG 1
|
||||
#endif
|
||||
#if defined(NDEBUG) && defined(SQLITE_DEBUG)
|
||||
# undef NDEBUG
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The testcase() macro is used to aid in coverage testing. When
|
||||
@@ -2702,7 +2709,9 @@ void sqlite3ExprListDelete(sqlite3*, ExprList*);
|
||||
int sqlite3Init(sqlite3*, char**);
|
||||
int sqlite3InitCallback(void*, int, char**, char**);
|
||||
void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
|
||||
void sqlite3ResetInternalSchema(sqlite3*, int);
|
||||
void sqlite3ResetAllSchemasOfConnection(sqlite3*);
|
||||
void sqlite3ResetOneSchema(sqlite3*,int);
|
||||
void sqlite3CollapseDatabaseArray(sqlite3*);
|
||||
void sqlite3BeginParse(Parse*,int);
|
||||
void sqlite3CommitInternalChanges(sqlite3*);
|
||||
Table *sqlite3ResultSetOfSelect(Parse*,Select*);
|
||||
@@ -3107,6 +3116,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
|
||||
# define sqlite3GetVTable(X,Y) ((VTable*)0)
|
||||
#else
|
||||
void sqlite3VtabClear(sqlite3 *db, Table*);
|
||||
void sqlite3VtabDisconnect(sqlite3 *db, Table *p);
|
||||
int sqlite3VtabSync(sqlite3 *db, char **);
|
||||
int sqlite3VtabRollback(sqlite3 *db);
|
||||
int sqlite3VtabCommit(sqlite3 *db);
|
||||
|
||||
@@ -6287,6 +6287,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
(char*)&sqlite_static_bind_nbyte, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite_temp_directory",
|
||||
(char*)&sqlite3_temp_directory, TCL_LINK_STRING);
|
||||
Tcl_LinkVar(interp, "sqlite_data_directory",
|
||||
(char*)&sqlite3_data_directory, TCL_LINK_STRING);
|
||||
Tcl_LinkVar(interp, "bitmask_size",
|
||||
(char*)&bitmask_size, TCL_LINK_INT|TCL_LINK_READ_ONLY);
|
||||
Tcl_LinkVar(interp, "sqlite_sync_count",
|
||||
|
||||
+14
-6
@@ -1300,6 +1300,7 @@ static sqlite3_module echoModuleV2 = {
|
||||
** Decode a pointer to an sqlite3 object.
|
||||
*/
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
extern const char *sqlite3TestErrorName(int rc);
|
||||
|
||||
static void moduleDestroy(void *p){
|
||||
sqlite3_free(p);
|
||||
@@ -1314,6 +1315,7 @@ static int register_echo_module(
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
int rc;
|
||||
sqlite3 *db;
|
||||
EchoModule *pMod;
|
||||
if( objc!=2 ){
|
||||
@@ -1325,14 +1327,20 @@ static int register_echo_module(
|
||||
/* Virtual table module "echo" */
|
||||
pMod = sqlite3_malloc(sizeof(EchoModule));
|
||||
pMod->interp = interp;
|
||||
sqlite3_create_module_v2(db, "echo", &echoModule, (void*)pMod, moduleDestroy);
|
||||
rc = sqlite3_create_module_v2(
|
||||
db, "echo", &echoModule, (void*)pMod, moduleDestroy
|
||||
);
|
||||
|
||||
/* Virtual table module "echo_v2" */
|
||||
pMod = sqlite3_malloc(sizeof(EchoModule));
|
||||
pMod->interp = interp;
|
||||
sqlite3_create_module_v2(db, "echo_v2",
|
||||
&echoModuleV2, (void*)pMod, moduleDestroy
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
pMod = sqlite3_malloc(sizeof(EchoModule));
|
||||
pMod->interp = interp;
|
||||
rc = sqlite3_create_module_v2(db, "echo_v2",
|
||||
&echoModuleV2, (void*)pMod, moduleDestroy
|
||||
);
|
||||
}
|
||||
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ int sqlite3BtreeSharedCacheReport(
|
||||
BtShared *pBt;
|
||||
Tcl_Obj *pRet = Tcl_NewObj();
|
||||
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
|
||||
const char *zFile = sqlite3PagerFilename(pBt->pPager);
|
||||
const char *zFile = sqlite3PagerFilename(pBt->pPager, 1);
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(zFile, -1));
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(pBt->nRef));
|
||||
}
|
||||
|
||||
@@ -57,6 +57,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options","casesensitivelike","0",TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_CURDIR
|
||||
Tcl_SetVar2(interp, "sqlite_options", "curdir", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "curdir", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
Tcl_SetVar2(interp, "sqlite_options", "debug", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -307,6 +313,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fts3", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_ENABLE_FTS4_UNICODE61)
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fts3_unicode", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fts3_unicode", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_GET_TABLE
|
||||
Tcl_SetVar2(interp, "sqlite_options", "gettable", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
|
||||
+78
-3
@@ -1042,7 +1042,7 @@ size_t sqlite3_quota_fread(
|
||||
** the write if we exceed quota.
|
||||
*/
|
||||
size_t sqlite3_quota_fwrite(
|
||||
void *pBuf, /* Take content to write from here */
|
||||
const 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 */
|
||||
@@ -1052,7 +1052,7 @@ size_t sqlite3_quota_fwrite(
|
||||
sqlite3_int64 szNew;
|
||||
quotaFile *pFile;
|
||||
size_t rc;
|
||||
|
||||
|
||||
iOfst = ftell(p->f);
|
||||
iEnd = iOfst + size*nmemb;
|
||||
pFile = p->pFile;
|
||||
@@ -1091,7 +1091,7 @@ size_t sqlite3_quota_fwrite(
|
||||
pFile->iSize = iNewEnd;
|
||||
quotaLeave();
|
||||
}
|
||||
return rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1160,6 +1160,13 @@ long sqlite3_quota_ftell(quota_FILE *p){
|
||||
return ftell(p->f);
|
||||
}
|
||||
|
||||
/*
|
||||
** Test the error indicator for the given file.
|
||||
*/
|
||||
int sqlite3_quota_ferror(quota_FILE *p){
|
||||
return ferror(p->f);
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate a file to szNew bytes.
|
||||
*/
|
||||
@@ -1236,6 +1243,25 @@ sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE *p){
|
||||
sqlite3_int64 sqlite3_quota_file_size(quota_FILE *p){
|
||||
return p->pFile ? p->pFile->iSize : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Determine the amount of data in bytes available for reading
|
||||
** in the given file.
|
||||
*/
|
||||
long sqlite3_quota_file_available(quota_FILE *p){
|
||||
FILE* f = p->f;
|
||||
long pos1, pos2;
|
||||
int rc;
|
||||
pos1 = ftell(f);
|
||||
if ( pos1 < 0 ) return -1;
|
||||
rc = fseek(f, 0, SEEK_END);
|
||||
if ( rc != 0 ) return -1;
|
||||
pos2 = ftell(f);
|
||||
if ( pos2 < 0 ) return -1;
|
||||
rc = fseek(f, pos1, SEEK_SET);
|
||||
if ( rc != 0 ) return -1;
|
||||
return pos2 - pos1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Remove a managed file. Update quotas accordingly.
|
||||
@@ -1895,6 +1921,53 @@ static int test_quota_glob(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_file_available HANDLE
|
||||
**
|
||||
** Return the number of bytes from the current file point to the end of
|
||||
** the file.
|
||||
*/
|
||||
static int test_quota_file_available(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
quota_FILE *p;
|
||||
sqlite3_int64 x;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
x = sqlite3_quota_file_available(p);
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_ferror HANDLE
|
||||
**
|
||||
** Return true if the file handle is in the error state.
|
||||
*/
|
||||
static int test_quota_ferror(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
quota_FILE *p;
|
||||
int x;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
x = sqlite3_quota_ferror(p);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(x));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine registers the custom TCL commands defined in this
|
||||
** module. This should be the only procedure visible from outside
|
||||
@@ -1924,6 +1997,8 @@ int Sqlitequota_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_quota_file_mtime", test_quota_file_mtime },
|
||||
{ "sqlite3_quota_remove", test_quota_remove },
|
||||
{ "sqlite3_quota_glob", test_quota_glob },
|
||||
{ "sqlite3_quota_file_available",test_quota_file_available },
|
||||
{ "sqlite3_quota_ferror", test_quota_ferror },
|
||||
};
|
||||
int i;
|
||||
|
||||
|
||||
+17
-2
@@ -162,7 +162,7 @@ quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode);
|
||||
** 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*);
|
||||
size_t sqlite3_quota_fwrite(const void*, size_t, size_t, quota_FILE*);
|
||||
|
||||
/*
|
||||
** Flush all written content held in memory buffers out to disk.
|
||||
@@ -190,6 +190,13 @@ int sqlite3_quota_fseek(quota_FILE*, long, int);
|
||||
void sqlite3_quota_rewind(quota_FILE*);
|
||||
long sqlite3_quota_ftell(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Test the error indicator for the given file.
|
||||
**
|
||||
** Return non-zero if the error indicator is set.
|
||||
*/
|
||||
int sqlite3_quota_ferror(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Truncate a file previously opened by sqlite3_quota_fopen(). Return
|
||||
** zero on success and non-zero on any kind of failure.
|
||||
@@ -198,7 +205,7 @@ long sqlite3_quota_ftell(quota_FILE*);
|
||||
** Any attempt to "truncate" a file to a larger size results in
|
||||
** undefined behavior.
|
||||
*/
|
||||
int sqlite3_quota_ftrunate(quota_FILE*, sqlite3_int64 newSize);
|
||||
int sqlite3_quota_ftruncate(quota_FILE*, sqlite3_int64 newSize);
|
||||
|
||||
/*
|
||||
** Return the last modification time of the opened file, in seconds
|
||||
@@ -232,6 +239,14 @@ sqlite3_int64 sqlite3_quota_file_size(quota_FILE*);
|
||||
*/
|
||||
sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Determine the amount of data in bytes available for reading
|
||||
** in the given file.
|
||||
**
|
||||
** Return -1 if the amount cannot be determined for some reason.
|
||||
*/
|
||||
long sqlite3_quota_file_available(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Delete a file from the disk, if that file is under quota management.
|
||||
** Adjust quotas accordingly.
|
||||
|
||||
+1
-1
@@ -339,7 +339,7 @@ end_of_vacuum:
|
||||
|
||||
/* This both clears the schemas and reduces the size of the db->aDb[]
|
||||
** array. */
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
+5
-6
@@ -2747,7 +2747,7 @@ case OP_Savepoint: {
|
||||
}
|
||||
if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
db->flags = (db->flags | SQLITE_InternChanges);
|
||||
}
|
||||
}
|
||||
@@ -3051,7 +3051,7 @@ case OP_VerifyCookie: {
|
||||
** a v-table method.
|
||||
*/
|
||||
if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
|
||||
sqlite3ResetInternalSchema(db, pOp->p1);
|
||||
sqlite3ResetOneSchema(db, pOp->p1);
|
||||
}
|
||||
|
||||
p->expired = 1;
|
||||
@@ -4214,7 +4214,6 @@ case OP_RowData: {
|
||||
assert( pC!=0 );
|
||||
assert( pC->nullRow==0 );
|
||||
assert( pC->pseudoTableReg==0 );
|
||||
assert( !pC->isSorter );
|
||||
assert( pC->pCursor!=0 );
|
||||
pCrsr = pC->pCursor;
|
||||
assert( sqlite3BtreeCursorIsValid(pCrsr) );
|
||||
@@ -4864,7 +4863,7 @@ case OP_ParseSchema: {
|
||||
db->init.busy = 0;
|
||||
}
|
||||
}
|
||||
if( rc ) sqlite3ResetInternalSchema(db, -1);
|
||||
if( rc ) sqlite3ResetAllSchemasOfConnection(db);
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
goto no_mem;
|
||||
}
|
||||
@@ -5511,7 +5510,7 @@ case OP_JournalMode: { /* out2-prerelease */
|
||||
if( !sqlite3PagerOkToChangeJournalMode(pPager) ) eNew = eOld;
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
zFilename = sqlite3PagerFilename(pPager);
|
||||
zFilename = sqlite3PagerFilename(pPager, 1);
|
||||
|
||||
/* Do not allow a transition to journal_mode=WAL for a database
|
||||
** in temporary storage or if the VFS does not support shared memory
|
||||
@@ -6159,7 +6158,7 @@ vdbe_error_halt:
|
||||
if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1;
|
||||
rc = SQLITE_ERROR;
|
||||
if( resetSchemaOnFault>0 ){
|
||||
sqlite3ResetInternalSchema(db, resetSchemaOnFault-1);
|
||||
sqlite3ResetOneSchema(db, resetSchemaOnFault-1);
|
||||
}
|
||||
|
||||
/* This is the only way out of this procedure. We have to
|
||||
|
||||
+51
-24
@@ -22,8 +22,8 @@
|
||||
** are invoked only from within xCreate and xConnect methods.
|
||||
*/
|
||||
struct VtabCtx {
|
||||
Table *pTab;
|
||||
VTable *pVTable;
|
||||
VTable *pVTable; /* The virtual table being constructed */
|
||||
Table *pTab; /* The Table object to which the virtual table belongs */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -38,33 +38,35 @@ static int createModule(
|
||||
void *pAux, /* Context pointer for xCreate/xConnect */
|
||||
void (*xDestroy)(void *) /* Module destructor function */
|
||||
){
|
||||
int rc, nName;
|
||||
Module *pMod;
|
||||
int rc = SQLITE_OK;
|
||||
int nName;
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
nName = sqlite3Strlen30(zName);
|
||||
pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1);
|
||||
if( pMod ){
|
||||
Module *pDel;
|
||||
char *zCopy = (char *)(&pMod[1]);
|
||||
memcpy(zCopy, zName, nName+1);
|
||||
pMod->zName = zCopy;
|
||||
pMod->pModule = pModule;
|
||||
pMod->pAux = pAux;
|
||||
pMod->xDestroy = xDestroy;
|
||||
pDel = (Module *)sqlite3HashInsert(&db->aModule, zCopy, nName, (void*)pMod);
|
||||
if( pDel && pDel->xDestroy ){
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
pDel->xDestroy(pDel->pAux);
|
||||
if( sqlite3HashFind(&db->aModule, zName, nName) ){
|
||||
rc = SQLITE_MISUSE_BKPT;
|
||||
}else{
|
||||
Module *pMod;
|
||||
pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1);
|
||||
if( pMod ){
|
||||
Module *pDel;
|
||||
char *zCopy = (char *)(&pMod[1]);
|
||||
memcpy(zCopy, zName, nName+1);
|
||||
pMod->zName = zCopy;
|
||||
pMod->pModule = pModule;
|
||||
pMod->pAux = pAux;
|
||||
pMod->xDestroy = xDestroy;
|
||||
pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,nName,(void*)pMod);
|
||||
assert( pDel==0 || pDel==pMod );
|
||||
if( pDel ){
|
||||
db->mallocFailed = 1;
|
||||
sqlite3DbFree(db, pDel);
|
||||
}
|
||||
}
|
||||
sqlite3DbFree(db, pDel);
|
||||
if( pDel==pMod ){
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
}else if( xDestroy ){
|
||||
xDestroy(pAux);
|
||||
}
|
||||
rc = sqlite3ApiExit(db, SQLITE_OK);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
if( rc!=SQLITE_OK && xDestroy ) xDestroy(pAux);
|
||||
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
@@ -180,6 +182,31 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Table *p is a virtual table. This function removes the VTable object
|
||||
** for table *p associated with database connection db from the linked
|
||||
** list in p->pVTab. It also decrements the VTable ref count. This is
|
||||
** used when closing database connection db to free all of its VTable
|
||||
** objects without disturbing the rest of the Schema object (which may
|
||||
** be being used by other shared-cache connections).
|
||||
*/
|
||||
void sqlite3VtabDisconnect(sqlite3 *db, Table *p){
|
||||
VTable **ppVTab;
|
||||
|
||||
assert( IsVirtual(p) );
|
||||
assert( sqlite3BtreeHoldsAllMutexes(db) );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
|
||||
for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){
|
||||
if( (*ppVTab)->db==db ){
|
||||
VTable *pVTab = *ppVTab;
|
||||
*ppVTab = pVTab->pNext;
|
||||
sqlite3VtabUnlock(pVTab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Disconnect all the virtual table objects in the sqlite3.pDisconnect list.
|
||||
|
||||
Reference in New Issue
Block a user