diff options
author | drh <drh@noemail.net> | 2018-01-03 16:49:52 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2018-01-03 16:49:52 +0000 |
commit | cb7d541d3ae005e24bb0d36bb138d84a3ca3d415 (patch) | |
tree | c594ecd7b9f257a531db3fc9b8712527814d68dc /src | |
parent | 4dcac40e3d4cf02513c84c95ccd4198ef35f2320 (diff) | |
download | sqlite-cb7d541d3ae005e24bb0d36bb138d84a3ca3d415.tar.gz sqlite-cb7d541d3ae005e24bb0d36bb138d84a3ca3d415.zip |
Replace the sqlite3_memdb_ptr() interface with the more general
sqlite3_serialize() interface.
FossilOrigin-Name: 8cf2ed4eff6d2e0958656e23384b05ead2128b678b0b69a591878af4190cd077
Diffstat (limited to 'src')
-rw-r--r-- | src/memdb.c | 81 | ||||
-rw-r--r-- | src/sqlite.h.in | 111 | ||||
-rw-r--r-- | src/tclsqlite.c | 108 |
3 files changed, 233 insertions, 67 deletions
diff --git a/src/memdb.c b/src/memdb.c index 215bfbb24..0a81978cc 100644 --- a/src/memdb.c +++ b/src/memdb.c @@ -157,6 +157,7 @@ static int memdbRead( MemFile *p = (MemFile *)pFile; if( iOfst+iAmt>p->sz ){ memset(zBuf, 0, iAmt); + if( iOfst<p->sz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst); return SQLITE_IOERR_SHORT_READ; } memcpy(zBuf, p->aData+iOfst, iAmt); @@ -450,20 +451,6 @@ static MemFile *memdbFromDbSchema(sqlite3 *db, const char *zSchema){ } /* -** Return a pointer to the memory used to hold the database. -** Return NULL if the arguments do not describe a memdb database. -*/ -void *sqlite3_memdb_ptr(sqlite3 *db, const char *zSchema, sqlite3_int64 *pSz){ - MemFile *p = memdbFromDbSchema(db, zSchema); - if( p==0 ){ - *pSz = 0; - return 0; - } - *pSz = p->sz; - return p->aData; -} - -/* ** Reconfigure a memdb database. */ int sqlite3_memdb_config( @@ -494,6 +481,72 @@ int sqlite3_memdb_config( return SQLITE_OK; } +/* +** Return the serialization of a database +*/ +unsigned char *sqlite3_serialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which database within the connection */ + sqlite3_int64 *piSize, /* Write size here, if not NULL */ + unsigned int mFlags /* Maybe SQLITE_SERIALIZE_NOCOPY */ +){ + MemFile *p = memdbFromDbSchema(db, zSchema); + int iDb = sqlite3FindDbName(db, zSchema); + Btree *pBt; + sqlite3_int64 sz; + int szPage = 0; + sqlite3_stmt *pStmt = 0; + unsigned char *pOut; + char *zSql; + int rc; + + if( piSize ) *piSize = -1; + if( iDb<0 ) return 0; + if( p ){ + if( piSize ) *piSize = p->sz; + if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ + pOut = p->aData; + }else{ + pOut = sqlite3_malloc64( p->sz ); + if( pOut ) memcpy(pOut, p->aData, p->sz); + } + return pOut; + } + pBt = db->aDb[iDb].pBt; + if( pBt==0 ) return 0; + szPage = sqlite3BtreeGetPageSize(pBt); + zSql = sqlite3_mprintf("PRAGMA \"%w\".page_count", zSchema); + rc = zSql ? sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) : SQLITE_NOMEM; + sqlite3_free(zSql); + if( rc ) return 0; + sqlite3_step(pStmt); + sz = sqlite3_column_int64(pStmt, 0)*szPage; + if( piSize ) *piSize = sz; + if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ + pOut = 0; + }else{ + pOut = sqlite3_malloc64( sz ); + if( pOut ){ + int nPage = sqlite3_column_int(pStmt, 0); + Pager *pPager = sqlite3BtreePager(pBt); + int pgno; + for(pgno=1; pgno<=nPage; pgno++){ + DbPage *pPage = 0; + unsigned char *pTo = pOut + szPage*(sqlite3_int64)(pgno-1); + rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pPage, 0); + if( rc==SQLITE_OK ){ + memcpy(pTo, sqlite3PagerGetData(pPage), szPage); + }else{ + memset(pTo, 0, szPage); + } + sqlite3PagerUnref(pPage); + } + } + } + sqlite3_finalize(pStmt); + return pOut; +} + /* ** 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 143448553..6b6a5655b 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -8759,22 +8759,6 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp( SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* -** CAPI3REF: Retrieve the current MEMDB buffer -** EXPERIMENTAL -** -** This interface is only available when SQLite is compiled -** with SQLITE_ENABLE_MEMDB. -** -** The sqlite3_memdb_ptr(D,S,P) interface returns a pointer to the -** memory buffer that is the database file used for [database connection] D -** and schema S. If schema S of database connection D is not a MEMDB -** database, then this routine returns NULL. If P is not NULL, then it must -** be a pointer to a 64-bit signed integer into which the size of the -** database file is written. -*/ -SQLITE_EXPERIMENTAL void *sqlite3_memdb_ptr(sqlite3*,const char*,sqlite3_int64*); - -/* ** CAPI3REF: Set the current MEMDB buffer ** EXPERIMENTAL ** @@ -8785,7 +8769,7 @@ SQLITE_EXPERIMENTAL void *sqlite3_memdb_ptr(sqlite3*,const char*,sqlite3_int64*) ** The database identified by D and S must not be in active use when this ** interface is called, or [SQLITE_BUSY] is returned. */ -SQLITE_EXPERIMENTAL int sqlite3_memdb_config(sqlite3*,const char*,void*,sqlite3_int64,sqlite3_int64,unsigned); +int sqlite3_memdb_config(sqlite3*,const char*,void*,sqlite3_int64,sqlite3_int64,unsigned); /* ** CAPI3REF: Flags for configuring MEMDB databases @@ -8797,6 +8781,99 @@ SQLITE_EXPERIMENTAL int sqlite3_memdb_config(sqlite3*,const char*,void*,sqlite3_ #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 +** +** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory +** that is a serialization of the S database on [database connection] D. +** If P is not a NULL pointer, then the size of the database in bytes +** is written into *P. +** +** For an ordinary on-disk database file, the serialization is just a +** copy of the disk file. For an in-memory database or a "TEMP" database, +** the serialization is the same sequence of bytes which would be written +** to disk if that database where backed up to disk. +** +** The usual case is that sqlite3_serialize() copies the serialization of +** the database into memory obtained from [sqlite3_malloc64()] and returns +** a pointer to that memory. The caller is responsible for freeing the +** returned value to avoid a memory leak. However, if the F argument +** contains the SQLITE_SERIALIZE_NOCOPY bit, then no memory allocations +** are made, and the sqlite3_serialize() function will return a pointer +** to the contiguous memory representation of the database that SQLite +** is currently using for that database, or NULL if the no such contiguous +** memory representation of the database exists. A contigous memory +** representation of the database will usually only exist if there has +** been a prior call to [sqlite3_deserialize(D,S,...)] with the same +** values of D and S. +** The size of the database is written into *P even if the +** SQLITE_SERIALIZE_NOCOPY bit is set but no contigious copy +** of the database exists. +** +** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the +** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory +** allocation error occurs. +*/ +unsigned char *sqlite3_serialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to serialize. ex: "main", "temp", ... */ + sqlite3_int64 *piSize, /* Write size of the DB here, if not NULL */ + unsigned int mFlags /* Zero or more SQLITE_SERIALIZE_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3_serialize +** EXPERIMENTAL +*/ +#define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */ + +/* +** CAPI3REF: Set the current MEMDB buffer +** EXPERIMENTAL +** +** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the +** [database connection] D to disconnection from database S and then +** reopen S as an in-memory database based on the serialization contained +** in P. The serialized database P is N bytes in size. M is the size of +** the buffer P, which might be larger than N. If M is larger than N, and +** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is +** permitted to add content to the in-memory database as long as the total +** size does not exceed M bytes. +** +** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will +** invoke sqlite3_free() on the serialization buffer when the database +** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then +** SQLite will try to increase the buffer size using sqlite3_realloc64() +** if writes on the database cause it to grow larger than M bytes. +** +** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the +** database is currently in a read transaction or is involved in a backup +** operation. +** +** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the +** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then +** [sqlite3_free()] is invoked on argument P prior to returning. +*/ +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 */ +); + +/* +** CAPI3REF: Flags for sqlite3_deserialize() +** EXPERIMENTAL +** +** The following are allowed values for the 6th argument (the "flags" or "F" +** argument) of the [sqlite3_deserialize()] interface. +*/ +#define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */ +#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */ +#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ /* ** Undo the hack that converts floating point types to integer for diff --git a/src/tclsqlite.c b/src/tclsqlite.c index d02d94baf..2c6e1bbe2 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -1846,34 +1846,36 @@ static int SQLITE_TCLAPI DbObjCmd( int choice; int rc = TCL_OK; static const char *DB_strs[] = { - "authorizer", "backup", "busy", - "cache", "changes", "close", - "collate", "collation_needed", "commit_hook", - "complete", "copy", "enable_load_extension", - "errorcode", "eval", "exists", - "function", "incrblob", "interrupt", - "last_insert_rowid", "memdb", "nullvalue", - "onecolumn", "preupdate", "profile", - "progress", "rekey", "restore", - "rollback_hook", "status", "timeout", - "total_changes", "trace", "trace_v2", - "transaction", "unlock_notify", "update_hook", - "version", "wal_hook", 0 + "authorizer", "backup", "busy", + "cache", "changes", "close", + "collate", "collation_needed", "commit_hook", + "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 }; enum DB_enum { - DB_AUTHORIZER, DB_BACKUP, DB_BUSY, - DB_CACHE, DB_CHANGES, DB_CLOSE, - DB_COLLATE, DB_COLLATION_NEEDED, DB_COMMIT_HOOK, - DB_COMPLETE, DB_COPY, 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_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_AUTHORIZER, DB_BACKUP, DB_BUSY, + DB_CACHE, DB_CHANGES, DB_CLOSE, + DB_COLLATE, DB_COLLATION_NEEDED, DB_COMMIT_HOOK, + 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 }; /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ @@ -2412,6 +2414,16 @@ static int SQLITE_TCLAPI DbObjCmd( } /* + ** $db deserialize ?DATABASE? VALUE + ** + ** Reopen DATABASE (default "main") using the content in $VALUE + */ + case DB_DESERIALIZE: { + rc = TCL_ERROR; /* TBD */ + break; + } + + /* ** $db enable_load_extension BOOLEAN ** ** Turn the extension loading feature on or off. It if off by @@ -2679,17 +2691,8 @@ static int SQLITE_TCLAPI DbObjCmd( rc = TCL_ERROR; #else const char *zSchema = Tcl_GetString(objv[2]); - sqlite3_int64 sz = 0; unsigned char *pData; - if( objc==3 ){ - pData = sqlite3_memdb_ptr(pDb->db, zSchema, &sz); - if( pData==0 ){ - Tcl_AppendResult(interp, "not a MEMDB database", (char*)0); - rc = TCL_ERROR; - }else{ - Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pData,sz)); - } - }else if( objc==4 ){ + if( objc==4 ){ int len = 0, xrc; unsigned char *pBA = Tcl_GetByteArrayFromObj(objv[3], &len); pData = sqlite3_malloc64( len ); @@ -2934,6 +2937,39 @@ static int SQLITE_TCLAPI DbObjCmd( } /* + ** $db serialize ?DATABASE? + ** + ** Return a serialization of a database. + */ + case DB_SERIALIZE: { +#ifndef SQLITE_ENABLE_MEMDB + Tcl_AppendResult(interp, "MEMDB not available in this build", + (char*)0); + rc = TCL_ERROR; +#else + const char *zSchema = objc>=3 ? Tcl_GetString(objv[2]) : "main"; + sqlite3_int64 sz = 0; + unsigned char *pData; + if( objc!=2 && objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE?"); + rc = TCL_ERROR; + }else{ + int needFree; + pData = sqlite3_serialize(pDb->db, zSchema, &sz, SQLITE_SERIALIZE_NOCOPY); + if( pData ){ + needFree = 0; + }else{ + pData = sqlite3_serialize(pDb->db, zSchema, &sz, 0); + needFree = 1; + } + Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pData,sz)); + if( needFree ) sqlite3_free(pData); + } +#endif + break; + } + + /* ** $db status (step|sort|autoindex|vmstep) ** ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or |