diff options
author | drh <drh@noemail.net> | 2018-01-03 19:03:31 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2018-01-03 19:03:31 +0000 |
commit | 3ec8665e22a6fe5cf6a2b98c04de1d350d519c8c (patch) | |
tree | 089d1b1b514c40d264b89fc9289a3d66d7168889 /src | |
parent | cb7d541d3ae005e24bb0d36bb138d84a3ca3d415 (diff) | |
download | sqlite-3ec8665e22a6fe5cf6a2b98c04de1d350d519c8c.tar.gz sqlite-3ec8665e22a6fe5cf6a2b98c04de1d350d519c8c.zip |
Replace sqlite3_memdb_config() with sqlite3_deserialize(). Remove the
"db memdb" command from the TCL interface, replacing it with "db serialize"
and "db deserialize".
FossilOrigin-Name: 2f6e9df9f0c5a9e5b1acb99cfa9486850cc1822d35b0989e779a7a10f3b1f1ac
Diffstat (limited to 'src')
-rw-r--r-- | src/attach.c | 126 | ||||
-rw-r--r-- | src/memdb.c | 99 | ||||
-rw-r--r-- | src/sqlite.h.in | 25 | ||||
-rw-r--r-- | src/sqliteInt.h | 5 | ||||
-rw-r--r-- | src/tclsqlite.c | 138 |
5 files changed, 187 insertions, 206 deletions
diff --git a/src/attach.c b/src/attach.c index fa38e8415..151b52e7a 100644 --- a/src/attach.c +++ b/src/attach.c @@ -55,6 +55,10 @@ static int resolveAttachExpr(NameContext *pName, Expr *pExpr) ** ** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the ** third argument. +** +** If the db->init.reopenMemdb flags is set, then instead of attaching a +** new database, close the database on db->init.iDb and reopen it as an +** empty MemDB. */ static void attachFunc( sqlite3_context *context, @@ -75,65 +79,79 @@ static void attachFunc( sqlite3_vfs *pVfs; UNUSED_PARAMETER(NotUsed); - zFile = (const char *)sqlite3_value_text(argv[0]); zName = (const char *)sqlite3_value_text(argv[1]); if( zFile==0 ) zFile = ""; if( zName==0 ) zName = ""; - /* Check for the following errors: - ** - ** * Too many attached databases, - ** * Transaction currently open - ** * Specified database name already being used. - */ - if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ - zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", - db->aLimit[SQLITE_LIMIT_ATTACHED] - ); - goto attach_error; - } - for(i=0; i<db->nDb; i++){ - char *z = db->aDb[i].zDbSName; - assert( z && zName ); - if( sqlite3StrICmp(z, zName)==0 ){ - zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName); + if( db->init.reopenMemdb ){ + /* This is not a real ATTACH. Instead, this routine is being called + ** from sqlite3_deserialize() to close database db->init.iDb and + ** reopen it as a MemDB */ + pVfs = sqlite3_vfs_find("memdb"); + if( pVfs==0 ) return; + pNew = &db->aDb[db->init.iDb]; + sqlite3BtreeClose(pNew->pBt); + pNew->pBt = 0; + pNew->pSchema = 0; + rc = sqlite3BtreeOpen(pVfs, "x", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB); + }else{ + /* This is a real ATTACH */ + + /* Check for the following errors: + ** + ** * Too many attached databases, + ** * Transaction currently open + ** * Specified database name already being used. + */ + if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ + zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", + db->aLimit[SQLITE_LIMIT_ATTACHED] + ); goto attach_error; } + for(i=0; i<db->nDb; i++){ + char *z = db->aDb[i].zDbSName; + assert( z && zName ); + if( sqlite3StrICmp(z, zName)==0 ){ + zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName); + goto attach_error; + } + } + + /* Allocate the new entry in the db->aDb[] array and initialize the schema + ** hash tables. + */ + if( db->aDb==db->aDbStatic ){ + aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 ); + if( aNew==0 ) return; + memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); + }else{ + aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); + if( aNew==0 ) return; + } + db->aDb = aNew; + pNew = &db->aDb[db->nDb]; + memset(pNew, 0, sizeof(*pNew)); + + /* Open the database file. If the btree is successfully opened, use + ** it to obtain the database schema. At this point the schema may + ** or may not be initialized. + */ + flags = db->openFlags; + rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); + sqlite3_result_error(context, zErr, -1); + sqlite3_free(zErr); + return; + } + assert( pVfs ); + flags |= SQLITE_OPEN_MAIN_DB; + rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); + sqlite3_free( zPath ); + db->nDb++; } - - /* Allocate the new entry in the db->aDb[] array and initialize the schema - ** hash tables. - */ - if( db->aDb==db->aDbStatic ){ - aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 ); - if( aNew==0 ) return; - memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); - }else{ - aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); - if( aNew==0 ) return; - } - db->aDb = aNew; - pNew = &db->aDb[db->nDb]; - memset(pNew, 0, sizeof(*pNew)); - - /* Open the database file. If the btree is successfully opened, use - ** it to obtain the database schema. At this point the schema may - ** or may not be initialized. - */ - flags = db->openFlags; - rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); - return; - } - assert( pVfs ); - flags |= SQLITE_OPEN_MAIN_DB; - rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); - sqlite3_free( zPath ); - db->nDb++; db->skipBtreeMutex = 0; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; @@ -160,7 +178,7 @@ static void attachFunc( sqlite3BtreeLeave(pNew->pBt); } pNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1; - pNew->zDbSName = sqlite3DbStrDup(db, zName); + if( !db->init.reopenMemdb ) pNew->zDbSName = sqlite3DbStrDup(db, zName); if( rc==SQLITE_OK && pNew->zDbSName==0 ){ rc = SQLITE_NOMEM_BKPT; } @@ -200,8 +218,8 @@ static void attachFunc( /* If the file was opened successfully, read the schema for the new database. ** If this fails, or if opening the file failed, then close the file and - ** remove the entry from the db->aDb[] array. i.e. put everything back the way - ** we found it. + ** remove the entry from the db->aDb[] array. i.e. put everything back the + ** way we found it. */ if( rc==SQLITE_OK ){ sqlite3BtreeEnterAll(db); diff --git a/src/memdb.c b/src/memdb.c index 0a81978cc..5a1f12dcb 100644 --- a/src/memdb.c +++ b/src/memdb.c @@ -12,17 +12,6 @@ ** ** This is an in-memory VFS implementation. The application supplies ** a chunk of memory to hold the database file. -** -** USAGE: -** -** sqlite3_open_v2("whatever", &db, SQLITE_OPEN_READWRITE, "memdb"); -** void *sqlite3_memdb_ptr(db, "main", &sz); -** int sqlite3_memdb_config(db, "main", pMem, szData, szMem, mFlags); -** -** Flags: -** -** SQLITE_MEMDB_FREEONCLOSE Free pMem when closing the connection -** SQLITE_MEMDB_RESIZEABLE Use sqlite3_realloc64() to resize pMem */ #ifdef SQLITE_ENABLE_MEMDB #include "sqliteInt.h" @@ -141,7 +130,7 @@ static const sqlite3_io_methods memdb_io_methods = { */ static int memdbClose(sqlite3_file *pFile){ MemFile *p = (MemFile *)pFile; - if( p->mFlags & SQLITE_MEMDB_FREEONCLOSE ) sqlite3_free(p->aData); + if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ) sqlite3_free(p->aData); return SQLITE_OK; } @@ -169,7 +158,7 @@ static int memdbRead( */ static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){ unsigned char *pNew; - if( (p->mFlags & SQLITE_MEMDB_RESIZEABLE)==0 ) return SQLITE_FULL; + if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 ) return SQLITE_FULL; if( p->nMmap>0 ) return SQLITE_FULL; pNew = sqlite3_realloc64(p->aData, newSz); if( pNew==0 ) return SQLITE_FULL; @@ -334,7 +323,7 @@ static int memdbOpen( MemFile *p = (MemFile*)pFile; memset(p, 0, sizeof(*p)); if( (flags & SQLITE_OPEN_MAIN_DB)==0 ) return SQLITE_CANTOPEN; - p->mFlags = SQLITE_MEMDB_RESIZEABLE | SQLITE_MEMDB_FREEONCLOSE; + p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; *pOutFlags = flags | SQLITE_OPEN_MEMORY; p->base.pMethods = &memdb_io_methods; return SQLITE_OK; @@ -451,37 +440,6 @@ static MemFile *memdbFromDbSchema(sqlite3 *db, const char *zSchema){ } /* -** Reconfigure a memdb database. -*/ -int sqlite3_memdb_config( - sqlite3 *db, - const char *zSchema, - void *aData, - sqlite3_int64 sz, - sqlite3_int64 szMax, - unsigned int mFlags -){ - MemFile *p = memdbFromDbSchema(db, zSchema); - int rc; - if( p==0 ){ - rc = SQLITE_ERROR; - }else if( p->eLock!=SQLITE_LOCK_NONE || p->nMmap>0 ){ - rc = SQLITE_BUSY; - }else{ - if( p->mFlags & SQLITE_MEMDB_FREEONCLOSE ) sqlite3_free(p->aData); - p->aData = aData; - p->sz = sz; - p->szMax = szMax; - p->mFlags = mFlags; - rc = SQLITE_OK; - } - if( rc!=SQLITE_OK && (mFlags & SQLITE_MEMDB_FREEONCLOSE)!=0 ){ - sqlite3_free(aData); - } - return SQLITE_OK; -} - -/* ** Return the serialization of a database */ unsigned char *sqlite3_serialize( @@ -547,6 +505,57 @@ unsigned char *sqlite3_serialize( return pOut; } +/* Convert zSchema to a MemDB and initialize its content. +*/ +int sqlite3_deserialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to reopen with the deserialization */ + unsigned char *pData, /* The serialized database content */ + sqlite3_int64 szDb, /* Number bytes in the deserialization */ + sqlite3_int64 szBuf, /* Total size of buffer pData[] */ + unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ +){ + MemFile *p; + char *zSql; + sqlite3_stmt *pStmt = 0; + int rc; + int iDb; + + sqlite3_mutex_enter(db->mutex); + if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; + iDb = sqlite3FindDbName(db, zSchema); + if( iDb<0 ){ + rc = SQLITE_ERROR; + goto end_deserialize; + } + zSql = sqlite3_mprintf("ATTACH x AS %Q", zSchema); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rc ) goto end_deserialize; + db->init.iDb = (u8)iDb; + db->init.reopenMemdb = 1; + rc = sqlite3_step(pStmt); + db->init.reopenMemdb = 0; + if( rc!=SQLITE_DONE ){ + rc = SQLITE_ERROR; + goto end_deserialize; + } + p = memdbFromDbSchema(db, zSchema); + if( p==0 ){ + rc = SQLITE_ERROR; + }else{ + p->aData = pData; + p->sz = szDb; + p->szMax = szBuf; + p->mFlags = mFlags; + rc = SQLITE_OK; + } +end_deserialize: + sqlite3_finalize(pStmt); + sqlite3_mutex_leave(db->mutex); + return rc; +} + /* ** This routine is called when the extension is loaded. ** Register the new VFS. diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 6b6a5655b..233a2cf60 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -8759,29 +8759,6 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp( SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* -** CAPI3REF: Set the current MEMDB buffer -** EXPERIMENTAL -** -** This interface is only available when SQLite is compiled -** with SQLITE_ENABLE_MEMDB. -** -** The sqlite3_memdb_config(D,S,P,N,M,F) interface initializes a MEMDB database. -** The database identified by D and S must not be in active use when this -** interface is called, or [SQLITE_BUSY] is returned. -*/ -int sqlite3_memdb_config(sqlite3*,const char*,void*,sqlite3_int64,sqlite3_int64,unsigned); - -/* -** CAPI3REF: Flags for configuring MEMDB databases -** EXPERIMENTAL -** -** The following are allowed values for the 6th argument (the "flags" -** argument) of the [sqlite3_memdb_config()] interface. -*/ -#define SQLITE_MEMDB_FREEONCLOSE 0x001 /* Free the memory buffer on close */ -#define SQLITE_MEMDB_RESIZEABLE 0x002 /* Resize using sqlite3_realloc64() */ - -/* ** CAPI3REF: Serialize a database ** EXPERIMENTAL ** @@ -8829,7 +8806,7 @@ unsigned char *sqlite3_serialize( #define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */ /* -** CAPI3REF: Set the current MEMDB buffer +** CAPI3REF: Deserialize a database ** EXPERIMENTAL ** ** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 002fb4e91..35074ee81 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1365,8 +1365,9 @@ struct sqlite3 { int newTnum; /* Rootpage of table being initialized */ u8 iDb; /* Which db file is being initialized */ u8 busy; /* TRUE if currently initializing */ - u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ - u8 imposterTable; /* Building an imposter table */ + u8 orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ + u8 imposterTable : 1; /* Building an imposter table */ + u8 reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ } init; int nVdbeActive; /* Number of VDBEs currently running */ int nVdbeRead; /* Number of active VDBEs that read or write */ diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 2c6e1bbe2..c9b4ff610 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -1852,14 +1852,14 @@ static int SQLITE_TCLAPI DbObjCmd( "complete", "copy", "deserialize", "enable_load_extension", "errorcode", "eval", "exists", "function", "incrblob", - "interrupt", "last_insert_rowid", "memdb", - "nullvalue", "onecolumn", "preupdate", - "profile", "progress", "rekey", - "restore", "rollback_hook", "serialize", - "status", "timeout", "total_changes", - "trace", "trace_v2", "transaction", - "unlock_notify", "update_hook", "version", - "wal_hook", 0 + "interrupt", "last_insert_rowid", "nullvalue", + "onecolumn", "preupdate", "profile", + "progress", "rekey", "restore", + "rollback_hook", "serialize", "status", + "timeout", "total_changes", "trace", + "trace_v2", "transaction", "unlock_notify", + "update_hook", "version", "wal_hook", + 0 }; enum DB_enum { DB_AUTHORIZER, DB_BACKUP, DB_BUSY, @@ -1868,14 +1868,13 @@ static int SQLITE_TCLAPI DbObjCmd( DB_COMPLETE, DB_COPY, DB_DESERIALIZE, DB_ENABLE_LOAD_EXTENSION, DB_ERRORCODE, DB_EVAL, DB_EXISTS, DB_FUNCTION, DB_INCRBLOB, - DB_INTERRUPT, DB_LAST_INSERT_ROWID, DB_MEMDB, - DB_NULLVALUE, DB_ONECOLUMN, DB_PREUPDATE, - DB_PROFILE, DB_PROGRESS, DB_REKEY, - DB_RESTORE, DB_ROLLBACK_HOOK, DB_SERIALIZE, - DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES, - DB_TRACE, DB_TRACE_V2, DB_TRANSACTION, - DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION, - DB_WAL_HOOK + DB_INTERRUPT, DB_LAST_INSERT_ROWID, DB_NULLVALUE, + DB_ONECOLUMN, DB_PREUPDATE, DB_PROFILE, + DB_PROGRESS, DB_REKEY, DB_RESTORE, + DB_ROLLBACK_HOOK, DB_SERIALIZE, DB_STATUS, + DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE, + DB_TRACE_V2, DB_TRANSACTION, DB_UNLOCK_NOTIFY, + DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK }; /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ @@ -2419,7 +2418,44 @@ static int SQLITE_TCLAPI DbObjCmd( ** Reopen DATABASE (default "main") using the content in $VALUE */ case DB_DESERIALIZE: { - rc = TCL_ERROR; /* TBD */ +#ifndef SQLITE_ENABLE_MEMDB + Tcl_AppendResult(interp, "MEMDB not available in this build", + (char*)0); + rc = TCL_ERROR; +#else + const char *zSchema; + Tcl_Obj *pValue; + unsigned char *pBA; + unsigned char *pData; + int len, xrc; + + if( objc==3 ){ + zSchema = 0; + pValue = objv[2]; + }else if( objc==4 ){ + zSchema = Tcl_GetString(objv[2]); + pValue = objv[3]; + }else{ + Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? VALUE"); + rc = TCL_ERROR; + break; + } + pBA = Tcl_GetByteArrayFromObj(pValue, &len); + pData = sqlite3_malloc64( len ); + if( pData==0 ){ + Tcl_AppendResult(interp, "out of memory", (char*)0); + rc = TCL_ERROR; + }else{ + memcpy(pData, pBA, len); + xrc = sqlite3_deserialize(pDb->db, zSchema, pData, len, len, + SQLITE_DESERIALIZE_FREEONCLOSE | + SQLITE_DESERIALIZE_RESIZEABLE); + if( xrc ){ + Tcl_AppendResult(interp, "unable to set MEMDB content", (char*)0); + rc = TCL_ERROR; + } + } +#endif break; } @@ -2679,44 +2715,6 @@ static int SQLITE_TCLAPI DbObjCmd( } /* - ** $db memdb DATABASE ?BLOB? - ** - ** Set or query the content of a MEMDB database. - ** - */ - case DB_MEMDB: { -#ifndef SQLITE_ENABLE_MEMDB - Tcl_AppendResult(interp, "MEMDB not available in this build", - (char*)0); - rc = TCL_ERROR; -#else - const char *zSchema = Tcl_GetString(objv[2]); - unsigned char *pData; - if( objc==4 ){ - int len = 0, xrc; - unsigned char *pBA = Tcl_GetByteArrayFromObj(objv[3], &len); - pData = sqlite3_malloc64( len ); - if( pData==0 ){ - Tcl_AppendResult(interp, "out of memory", (char*)0); - rc = TCL_ERROR; - }else{ - memcpy(pData, pBA, len); - xrc = sqlite3_memdb_config(pDb->db, zSchema, pData, len, len, - SQLITE_MEMDB_FREEONCLOSE|SQLITE_MEMDB_RESIZEABLE); - if( xrc ){ - Tcl_AppendResult(interp, "unable to set MEMDB content", (char*)0); - rc = TCL_ERROR; - } - } - }else{ - Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); - rc = TCL_ERROR; - } -#endif - break; - } - - /* ** $db nullvalue ?STRING? ** ** Change text used when a NULL comes back from the database. If ?STRING? @@ -3481,9 +3479,6 @@ static int SQLITE_TCLAPI DbMain( int nKey = 0; #endif int rc; -#ifdef SQLITE_ENABLE_MEMDB - Tcl_Obj *pDbObj = 0; -#endif /* In normal use, each TCL interpreter runs in a single thread. So ** by default, we can turn off mutexing on SQLite database connections. @@ -3576,10 +3571,6 @@ static int SQLITE_TCLAPI DbMain( }else{ flags &= ~SQLITE_OPEN_URI; } -#ifdef SQLITE_ENABLE_MEMDB - }else if( strcmp(zArg, "-memdb")==0 ){ - pDbObj = objv[i]; -#endif }else{ Tcl_AppendResult(interp, "unknown option: ", zArg, (char*)0); return TCL_ERROR; @@ -3588,25 +3579,10 @@ static int SQLITE_TCLAPI DbMain( zErrMsg = 0; p = (SqliteDb*)Tcl_Alloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); -#ifdef SQLITE_ENABLE_MEMDB - if( pDbObj ){ - rc = sqlite3_open_v2("x", &p->db, flags, "memdb"); - if( rc==SQLITE_OK ){ - int len; - unsigned char *aData = Tcl_GetByteArrayFromObj(pDbObj, &len); - unsigned char *a = sqlite3_malloc64( len ); - memcpy(a, aData, len); - sqlite3_memdb_config(p->db, "main", a, len, sqlite3_msize(a), - SQLITE_MEMDB_FREEONCLOSE | SQLITE_MEMDB_RESIZEABLE); - } - }else -#endif - { - if( zFile==0 ) zFile = ":memory:"; - zFile = Tcl_TranslateFileName(interp, zFile, &translatedFilename); - rc = sqlite3_open_v2(zFile, &p->db, flags, zVfs); - Tcl_DStringFree(&translatedFilename); - } + if( zFile==0 ) zFile = ""; + zFile = Tcl_TranslateFileName(interp, zFile, &translatedFilename); + rc = sqlite3_open_v2(zFile, &p->db, flags, zVfs); + Tcl_DStringFree(&translatedFilename); if( p->db ){ if( SQLITE_OK!=sqlite3_errcode(p->db) ){ zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->db)); |