Add new test file e_blobopen.test, containing tests for sqlite3_blob_open().
FossilOrigin-Name: ecbccd0e594d22b3ae7fabc8037951dc49570bc3
This commit is contained in:
parent
97d3898335
commit
b391b94431
1
main.mk
1
main.mk
@ -246,6 +246,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_autoext.c \
|
||||
$(TOP)/src/test_async.c \
|
||||
$(TOP)/src/test_backup.c \
|
||||
$(TOP)/src/test_blob.c \
|
||||
$(TOP)/src/test_btree.c \
|
||||
$(TOP)/src/test_config.c \
|
||||
$(TOP)/src/test_demovfs.c \
|
||||
|
30
manifest
30
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\sanother\sharmless\scomment\stypo.
|
||||
D 2014-11-07T14:37:32.349
|
||||
C Add\snew\stest\sfile\se_blobopen.test,\scontaining\stests\sfor\ssqlite3_blob_open().
|
||||
D 2014-11-07T14:41:11.404
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -151,7 +151,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk bbc8b6000ed143a1a8d31d3b4995c359a3188fa1
|
||||
F main.mk 3fececc835c5c23637c3e0af970e27a81a9ba476
|
||||
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
|
||||
F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
@ -229,15 +229,15 @@ F src/resolve.c 4965007d6497b6a4d7a6d98751cc39712885f952
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c 428165951748151e87a15295b7357221433e311b
|
||||
F src/shell.c 0500307f63e3675dc4ab21112c6086b4d146fc08
|
||||
F src/sqlite.h.in f433227d7f619887a1064913fa66cefa3da4349a
|
||||
F src/sqlite.h.in 5abfd7d6c9bd2054105820de5ce12b1e491b1dc9
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
|
||||
F src/sqliteInt.h 71b0bf1a7fc55b5cb374f7579fd140e730a6e0f4
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 81712116e826b0089bb221b018929536b2b5406f
|
||||
F src/table.c f142bba7903e93ca8d113a5b8877a108ad1a27dc
|
||||
F src/tclsqlite.c 7cdd4dd3c2a4183483feca260070d73d6e22cd47
|
||||
F src/test1.c ac7f3bad83ef4508d5efc85b32e86da48db8ed7e
|
||||
F src/tclsqlite.c 0a874655dd39a9875e39c5d3c464db662171d228
|
||||
F src/test1.c cce6ad0effe8c66a62d2634d9714ffdc7372ef11
|
||||
F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712
|
||||
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
|
||||
F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
|
||||
@ -249,8 +249,9 @@ F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
|
||||
F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8
|
||||
F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
|
||||
F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e
|
||||
F src/test_blob.c 1f2e3e25255b731c4fcf15ee7990d06347cb6c09
|
||||
F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
|
||||
F src/test_config.c c8b8b50bf2fe5102de10e4c7100b746d7f6bf62f
|
||||
F src/test_config.c 035c17a173937d019b8dfc1d524f9d3fc8123504
|
||||
F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
|
||||
@ -289,7 +290,7 @@ F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
|
||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||
F src/util.c 3b627daa45c7308c1e36e3dbaa3f9ce7e5c7fa73
|
||||
F src/vacuum.c 9b30ec729337dd012ed88d4c292922c8ef9cf00c
|
||||
F src/vdbe.c 3fd4ebd3e87b63175bfd2be747608bae1670b4df
|
||||
F src/vdbe.c d5dab22208e36e5689e9fb553aea3613921054ec
|
||||
F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3
|
||||
F src/vdbeInt.h 9bb69ff2447c34b6ccc58b34ec35b615f86ead78
|
||||
F src/vdbeapi.c 07acb615d1e4170e71fc1b0d087f3c53a1ad8e83
|
||||
@ -451,6 +452,7 @@ F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
||||
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
|
||||
F test/distinct.test 086e70c765f172e8974e9f83b9ac5ca03c154e77
|
||||
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
|
||||
F test/e_blobopen.test 234f960d90235a9b51ec3ca1e062e8541dd558d8 w test/e_blob.test
|
||||
F test/e_changes.test fd66105385153dbf21fdb35eb8ef6c3e1eade579
|
||||
F test/e_createtable.test c7e67b49e6cf92473c8fb30ab26143e9e2128cf7
|
||||
F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a
|
||||
@ -491,7 +493,7 @@ F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
|
||||
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
|
||||
F test/fkey5.test 8a1fde4e7721ae00b05b3178888833726ca2df8d
|
||||
F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48
|
||||
F test/fkey7.test e31d0e71a41c1d29349a16448d6c420e2c53a8fc
|
||||
F test/fkey7.test 72e915890ee4a005daaf3002cb208e8fe973ac13
|
||||
F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749
|
||||
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
|
||||
F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c
|
||||
@ -1155,7 +1157,7 @@ F test/without_rowid1.test 7862e605753c8d25329f665fa09072e842183151
|
||||
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
|
||||
F test/without_rowid3.test 1081aabf60a1e1123b7f9a8f6ae19954351843b0
|
||||
F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
|
||||
F test/without_rowid5.test b4a639a367f04d382d20e8f44fc1be4f2d57d107
|
||||
F test/without_rowid5.test 61256715b686359df48ca1742db50cc7e3e7b862
|
||||
F test/wordcount.c 9915e06cb33d8ca8109b8700791afe80d305afda
|
||||
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
|
||||
F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac
|
||||
@ -1211,7 +1213,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 5700508535c35ac6b158b527e1d47e529e8e28ab
|
||||
R 95ce072bedb368f2e0f6da4bf10906cf
|
||||
U drh
|
||||
Z 8c052ffec320f3367f2daa07942512b6
|
||||
P b45bc80bb16f07192d84fd14433bb724a84d4146
|
||||
R 0ead7b89f3ed1286937ae097bba351c9
|
||||
U dan
|
||||
Z d785e78de9d268050b7beac3067db32e
|
||||
|
@ -1 +1 @@
|
||||
b45bc80bb16f07192d84fd14433bb724a84d4146
|
||||
ecbccd0e594d22b3ae7fabc8037951dc49570bc3
|
@ -5657,26 +5657,42 @@ typedef struct sqlite3_blob sqlite3_blob;
|
||||
** SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
|
||||
** </pre>)^
|
||||
**
|
||||
** ^(Parameter zDb is not the filename that contains the database, but
|
||||
** rather the symbolic name of the database. For attached databases, this is
|
||||
** the name that appears after the AS keyword in the [ATTACH] statement.
|
||||
** For the main database file, the database name is "main". For TEMP
|
||||
** tables, the database name is "temp".)^
|
||||
**
|
||||
** ^If the flags parameter is non-zero, then the BLOB is opened for read
|
||||
** and write access. ^If it is zero, the BLOB is opened for read access.
|
||||
** ^It is not possible to open a column that is part of an index or primary
|
||||
** key for writing. ^If [foreign key constraints] are enabled, it is
|
||||
** not possible to open a column that is part of a [child key] for writing.
|
||||
** and write access. ^If the flags parameter is zero, the BLOB is opened for
|
||||
** read-only access.
|
||||
**
|
||||
** ^Note that the database name is not the filename that contains
|
||||
** the database but rather the symbolic name of the database that
|
||||
** appears after the AS keyword when the database is connected using [ATTACH].
|
||||
** ^For the main database file, the database name is "main".
|
||||
** ^For TEMP tables, the database name is "temp".
|
||||
** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored
|
||||
** in *ppBlob. Otherwise an [error code] is returned and, unless the error
|
||||
** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
|
||||
** the API is not misused, it is always safe to call [sqlite3_blob_close()]
|
||||
** on *ppBlob after this function it returns.
|
||||
**
|
||||
** This function fails with SQLITE_ERROR if any of the following are true:
|
||||
** <ul>
|
||||
** <li> ^(Database zDb does not exist)^,
|
||||
** <li> ^(Table zTable does not exist within database zDb)^,
|
||||
** <li> ^(Table zTable is a WITHOUT ROWID table)^,
|
||||
** <li> ^(Column zColumn does not exist)^,
|
||||
** <li> ^(Row iRow is not present in the table)^,
|
||||
** <li> ^(The specified column of row iRow contains a value that is not
|
||||
** a TEXT or BLOB value)^,
|
||||
** <li> ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE
|
||||
** constraint and the blob is being opened for read/write access)^,
|
||||
** <li> ^([foreign key constraints | Foreign key constraints] are enabled,
|
||||
** column zColumn is part of a [child key] definition and the blob is
|
||||
** being opened for read/write access)^.
|
||||
** </ul>
|
||||
**
|
||||
** ^Unless it returns SQLITE_MISUSE, this function sets the
|
||||
** [database connection] error code and message accessible via
|
||||
** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
|
||||
**
|
||||
** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written
|
||||
** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set
|
||||
** to be a null pointer.)^
|
||||
** ^This function sets the [database connection] error code and message
|
||||
** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related
|
||||
** functions. ^Note that the *ppBlob variable is always initialized in a
|
||||
** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob
|
||||
** regardless of the success or failure of this routine.
|
||||
**
|
||||
** ^(If the row that a BLOB handle points to is modified by an
|
||||
** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects
|
||||
@ -5694,13 +5710,9 @@ typedef struct sqlite3_blob sqlite3_blob;
|
||||
** interface. Use the [UPDATE] SQL command to change the size of a
|
||||
** blob.
|
||||
**
|
||||
** ^The [sqlite3_blob_open()] interface will fail for a [WITHOUT ROWID]
|
||||
** table. Incremental BLOB I/O is not possible on [WITHOUT ROWID] tables.
|
||||
**
|
||||
** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces
|
||||
** and the built-in [zeroblob] SQL function can be used, if desired,
|
||||
** to create an empty, zero-filled blob in which to read or write using
|
||||
** this interface.
|
||||
** and the built-in [zeroblob] SQL function may be used to create a
|
||||
** zero-filled blob to read or write using the incremental-blob interface.
|
||||
**
|
||||
** To avoid a resource leak, every open [BLOB handle] should eventually
|
||||
** be released by a call to [sqlite3_blob_close()].
|
||||
|
@ -3725,6 +3725,7 @@ static void init_all(Tcl_Interp *interp){
|
||||
extern int Sqlitetest9_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestasync_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_autoext_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_blob_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_demovfs_Init(Tcl_Interp *);
|
||||
extern int Sqlitetest_func_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_hexio_Init(Tcl_Interp*);
|
||||
@ -3768,6 +3769,7 @@ static void init_all(Tcl_Interp *interp){
|
||||
Sqlitetest9_Init(interp);
|
||||
Sqlitetestasync_Init(interp);
|
||||
Sqlitetest_autoext_Init(interp);
|
||||
Sqlitetest_blob_Init(interp);
|
||||
Sqlitetest_demovfs_Init(interp);
|
||||
Sqlitetest_func_Init(interp);
|
||||
Sqlitetest_hexio_Init(interp);
|
||||
|
152
src/test1.c
152
src/test1.c
@ -1651,154 +1651,6 @@ static int blobHandleFromObj(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_bytes CHANNEL
|
||||
*/
|
||||
static int test_blob_bytes(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int nByte;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
nByte = sqlite3_blob_bytes(pBlob);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(nByte));
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_close CHANNEL
|
||||
*/
|
||||
static int test_blob_close(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
sqlite3_blob_close(pBlob);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_read CHANNEL OFFSET N
|
||||
**
|
||||
** This command is used to test the sqlite3_blob_read() in ways that
|
||||
** the Tcl channel interface does not. The first argument should
|
||||
** be the name of a valid channel created by the [incrblob] method
|
||||
** of a database handle. This function calls sqlite3_blob_read()
|
||||
** to read N bytes from offset OFFSET from the underlying SQLite
|
||||
** blob handle.
|
||||
**
|
||||
** On success, a byte-array object containing the read data is
|
||||
** returned. On failure, the interpreter result is set to the
|
||||
** text representation of the returned error code (i.e. "SQLITE_NOMEM")
|
||||
** and a Tcl exception is thrown.
|
||||
*/
|
||||
static int test_blob_read(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int nByte;
|
||||
int iOffset;
|
||||
unsigned char *zBuf = 0;
|
||||
int rc;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte)
|
||||
){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( nByte>0 ){
|
||||
zBuf = (unsigned char *)Tcl_Alloc(nByte);
|
||||
}
|
||||
rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset);
|
||||
if( rc==SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte));
|
||||
}else{
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}
|
||||
Tcl_Free((char *)zBuf);
|
||||
|
||||
return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_write CHANNEL OFFSET DATA ?NDATA?
|
||||
**
|
||||
** This command is used to test the sqlite3_blob_write() in ways that
|
||||
** the Tcl channel interface does not. The first argument should
|
||||
** be the name of a valid channel created by the [incrblob] method
|
||||
** of a database handle. This function calls sqlite3_blob_write()
|
||||
** to write the DATA byte-array to the underlying SQLite blob handle.
|
||||
** at offset OFFSET.
|
||||
**
|
||||
** On success, an empty string is returned. On failure, the interpreter
|
||||
** result is set to the text representation of the returned error code
|
||||
** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown.
|
||||
*/
|
||||
static int test_blob_write(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int iOffset;
|
||||
int rc;
|
||||
|
||||
unsigned char *zBuf;
|
||||
int nBuf;
|
||||
|
||||
if( objc!=4 && objc!=5 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET DATA ?NDATA?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
|
||||
if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}
|
||||
|
||||
return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
|
||||
}
|
||||
|
||||
static int test_blob_reopen(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
@ -6910,11 +6762,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_table_column_metadata", test_table_column_metadata, 0 },
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
{ "sqlite3_blob_read", test_blob_read, 0 },
|
||||
{ "sqlite3_blob_write", test_blob_write, 0 },
|
||||
{ "sqlite3_blob_reopen", test_blob_reopen, 0 },
|
||||
{ "sqlite3_blob_bytes", test_blob_bytes, 0 },
|
||||
{ "sqlite3_blob_close", test_blob_close, 0 },
|
||||
#endif
|
||||
{ "pcache_stats", test_pcache_stats, 0 },
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
|
319
src/test_blob.c
Normal file
319
src/test_blob.c
Normal file
@ -0,0 +1,319 @@
|
||||
/*
|
||||
** 2014 October 30
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* These functions are implemented in main.c. */
|
||||
extern const char *sqlite3ErrName(int);
|
||||
|
||||
/* From test1.c: */
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
extern void *sqlite3TestTextToPtr(const char *z);
|
||||
|
||||
/*
|
||||
** Return a pointer to a buffer containing a text representation of the
|
||||
** pointer passed as the only argument. The original pointer may be extracted
|
||||
** from the text using sqlite3TestTextToPtr().
|
||||
*/
|
||||
static char *ptrToText(void *p){
|
||||
static char buf[100];
|
||||
sqlite3_snprintf(sizeof(buf)-1, buf, "%p", p);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to extract a blob handle (type sqlite3_blob*) from the Tcl
|
||||
** object passed as the second argument. If successful, set *ppBlob to
|
||||
** point to the blob handle and return TCL_OK. Otherwise, store an error
|
||||
** message in the tcl interpreter and return TCL_ERROR. The final value
|
||||
** of *ppBlob is undefined in this case.
|
||||
**
|
||||
** If the object contains a string that begins with "incrblob_", then it
|
||||
** is assumed to be the name of a Tcl channel opened using the [db incrblob]
|
||||
** command (see tclsqlite.c). Otherwise, it is assumed to be a pointer
|
||||
** encoded using the ptrToText() routine or similar.
|
||||
*/
|
||||
static int blobHandleFromObj(
|
||||
Tcl_Interp *interp,
|
||||
Tcl_Obj *pObj,
|
||||
sqlite3_blob **ppBlob
|
||||
){
|
||||
char *z;
|
||||
int n;
|
||||
|
||||
z = Tcl_GetStringFromObj(pObj, &n);
|
||||
if( n==0 ){
|
||||
*ppBlob = 0;
|
||||
}else if( n>9 && 0==memcmp("incrblob_", z, 9) ){
|
||||
int notUsed;
|
||||
Tcl_Channel channel;
|
||||
ClientData instanceData;
|
||||
|
||||
channel = Tcl_GetChannel(interp, z, ¬Used);
|
||||
if( !channel ) return TCL_ERROR;
|
||||
|
||||
Tcl_Flush(channel);
|
||||
Tcl_Seek(channel, 0, SEEK_SET);
|
||||
|
||||
instanceData = Tcl_GetChannelInstanceData(channel);
|
||||
*ppBlob = *((sqlite3_blob **)instanceData);
|
||||
}else{
|
||||
*ppBlob = (sqlite3_blob*)sqlite3TestTextToPtr(z);
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Like Tcl_GetString(), except that if the string is 0 bytes in size, a
|
||||
** NULL Pointer is returned.
|
||||
*/
|
||||
static char *blobStringFromObj(Tcl_Obj *pObj){
|
||||
int n;
|
||||
char *z;
|
||||
z = Tcl_GetStringFromObj(pObj, &n);
|
||||
return (n ? z : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_open DB DATABASE TABLE COLUMN ROWID FLAGS VARNAME
|
||||
**
|
||||
** Tcl test harness for the sqlite3_blob_open() function.
|
||||
*/
|
||||
static int test_blob_open(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* Calling TCL interpreter */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zDb;
|
||||
const char *zTable;
|
||||
const char *zColumn;
|
||||
sqlite_int64 iRowid;
|
||||
int flags;
|
||||
const char *zVarname;
|
||||
int nVarname;
|
||||
|
||||
sqlite3_blob *pBlob = (sqlite3_blob*)0xFFFFFFFF;
|
||||
int rc;
|
||||
|
||||
if( objc!=8 ){
|
||||
const char *zUsage = "DB DATABASE TABLE COLUMN ROWID FLAGS VARNAME";
|
||||
Tcl_WrongNumArgs(interp, 1, objv, zUsage);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zDb = Tcl_GetString(objv[2]);
|
||||
zTable = blobStringFromObj(objv[3]);
|
||||
zColumn = Tcl_GetString(objv[4]);
|
||||
if( Tcl_GetWideIntFromObj(interp, objv[5], &iRowid) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[6], &flags) ) return TCL_ERROR;
|
||||
zVarname = Tcl_GetStringFromObj(objv[7], &nVarname);
|
||||
|
||||
if( nVarname>0 ){
|
||||
rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRowid, flags, &pBlob);
|
||||
Tcl_SetVar(interp, zVarname, ptrToText(pBlob), 0);
|
||||
}else{
|
||||
rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRowid, flags, 0);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
Tcl_ResetResult(interp);
|
||||
}else{
|
||||
Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** sqlite3_blob_close HANDLE
|
||||
*/
|
||||
static int test_blob_close(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
rc = sqlite3_blob_close(pBlob);
|
||||
|
||||
if( rc ){
|
||||
Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}else{
|
||||
Tcl_ResetResult(interp);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_bytes HANDLE
|
||||
*/
|
||||
static int test_blob_bytes(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int nByte;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
nByte = sqlite3_blob_bytes(pBlob);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(nByte));
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_read CHANNEL OFFSET N
|
||||
**
|
||||
** This command is used to test the sqlite3_blob_read() in ways that
|
||||
** the Tcl channel interface does not. The first argument should
|
||||
** be the name of a valid channel created by the [incrblob] method
|
||||
** of a database handle. This function calls sqlite3_blob_read()
|
||||
** to read N bytes from offset OFFSET from the underlying SQLite
|
||||
** blob handle.
|
||||
**
|
||||
** On success, a byte-array object containing the read data is
|
||||
** returned. On failure, the interpreter result is set to the
|
||||
** text representation of the returned error code (i.e. "SQLITE_NOMEM")
|
||||
** and a Tcl exception is thrown.
|
||||
*/
|
||||
static int test_blob_read(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int nByte;
|
||||
int iOffset;
|
||||
unsigned char *zBuf = 0;
|
||||
int rc;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte)
|
||||
){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( nByte>0 ){
|
||||
zBuf = (unsigned char *)Tcl_Alloc(nByte);
|
||||
}
|
||||
rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset);
|
||||
if( rc==SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte));
|
||||
}else{
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}
|
||||
Tcl_Free((char *)zBuf);
|
||||
|
||||
return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_write HANDLE OFFSET DATA ?NDATA?
|
||||
**
|
||||
** This command is used to test the sqlite3_blob_write() in ways that
|
||||
** the Tcl channel interface does not. The first argument should
|
||||
** be the name of a valid channel created by the [incrblob] method
|
||||
** of a database handle. This function calls sqlite3_blob_write()
|
||||
** to write the DATA byte-array to the underlying SQLite blob handle.
|
||||
** at offset OFFSET.
|
||||
**
|
||||
** On success, an empty string is returned. On failure, the interpreter
|
||||
** result is set to the text representation of the returned error code
|
||||
** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown.
|
||||
*/
|
||||
static int test_blob_write(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int iOffset;
|
||||
int rc;
|
||||
|
||||
unsigned char *zBuf;
|
||||
int nBuf;
|
||||
|
||||
if( objc!=4 && objc!=5 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET DATA ?NDATA?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
|
||||
if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}
|
||||
|
||||
return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetest_blob_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aObjCmd[] = {
|
||||
{ "sqlite3_blob_open", test_blob_open },
|
||||
{ "sqlite3_blob_close", test_blob_close },
|
||||
{ "sqlite3_blob_bytes", test_blob_bytes },
|
||||
{ "sqlite3_blob_read", test_blob_read },
|
||||
{ "sqlite3_blob_write", test_blob_write },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
@ -155,6 +155,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "analyze", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
Tcl_SetVar2(interp, "sqlite_options", "api_armor", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "api_armor", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
Tcl_SetVar2(interp, "sqlite_options", "atomicwrite", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
|
@ -3798,10 +3798,11 @@ case OP_Found: { /* jump, in3 */
|
||||
}else{
|
||||
pIdxKey = sqlite3VdbeAllocUnpackedRecord(
|
||||
pC->pKeyInfo, aTempRec, sizeof(aTempRec), &pFree
|
||||
);
|
||||
);
|
||||
if( pIdxKey==0 ) goto no_mem;
|
||||
assert( pIn3->flags & MEM_Blob );
|
||||
assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */
|
||||
/* assert( (pIn3->flags & MEM_Zero)==0 ); // zeroblobs already expanded */
|
||||
ExpandBlob(pIn3);
|
||||
sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
|
||||
}
|
||||
pIdxKey->default_rc = 0;
|
||||
|
549
test/e_blobopen.test
Normal file
549
test/e_blobopen.test
Normal file
@ -0,0 +1,549 @@
|
||||
# 2014 October 30
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix e_blobopen
|
||||
|
||||
forcedelete test.db2
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
|
||||
CREATE TABLE main.t1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
CREATE TEMP TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
|
||||
CREATE TABLE main.x1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
CREATE TEMP TABLE x2(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
CREATE TABLE aux.x3(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
|
||||
INSERT INTO main.t1 VALUES(1, 'main one', X'0101');
|
||||
INSERT INTO main.t1 VALUES(2, 'main two', X'0102');
|
||||
INSERT INTO main.t1 VALUES(3, 'main three', X'0103');
|
||||
INSERT INTO main.t1 VALUES(4, 'main four', X'0104');
|
||||
INSERT INTO main.t1 VALUES(5, 'main five', X'0105');
|
||||
|
||||
INSERT INTO main.x1 VALUES(1, 'x main one', X'000101');
|
||||
INSERT INTO main.x1 VALUES(2, 'x main two', X'000102');
|
||||
INSERT INTO main.x1 VALUES(3, 'x main three', X'000103');
|
||||
INSERT INTO main.x1 VALUES(4, 'x main four', X'000104');
|
||||
INSERT INTO main.x1 VALUES(5, 'x main five', X'000105');
|
||||
|
||||
INSERT INTO temp.t1 VALUES(1, 'temp one', X'0201');
|
||||
INSERT INTO temp.t1 VALUES(2, 'temp two', X'0202');
|
||||
INSERT INTO temp.t1 VALUES(3, 'temp three', X'0203');
|
||||
INSERT INTO temp.t1 VALUES(4, 'temp four', X'0204');
|
||||
INSERT INTO temp.t1 VALUES(5, 'temp five', X'0205');
|
||||
|
||||
INSERT INTO temp.x2 VALUES(1, 'x temp one', X'000201');
|
||||
INSERT INTO temp.x2 VALUES(2, 'x temp two', X'000202');
|
||||
INSERT INTO temp.x2 VALUES(3, 'x temp three', X'000203');
|
||||
INSERT INTO temp.x2 VALUES(4, 'x temp four', X'000204');
|
||||
INSERT INTO temp.x2 VALUES(5, 'x temp five', X'000205');
|
||||
|
||||
INSERT INTO aux.t1 VALUES(1, 'aux one', X'0301');
|
||||
INSERT INTO aux.t1 VALUES(2, 'aux two', X'0302');
|
||||
INSERT INTO aux.t1 VALUES(3, 'aux three', X'0303');
|
||||
INSERT INTO aux.t1 VALUES(4, 'aux four', X'0304');
|
||||
INSERT INTO aux.t1 VALUES(5, 'aux five', X'0305');
|
||||
|
||||
INSERT INTO aux.x3 VALUES(1, 'x aux one', X'000301');
|
||||
INSERT INTO aux.x3 VALUES(2, 'x aux two', X'000302');
|
||||
INSERT INTO aux.x3 VALUES(3, 'x aux three', X'000303');
|
||||
INSERT INTO aux.x3 VALUES(4, 'x aux four', X'000304');
|
||||
INSERT INTO aux.x3 VALUES(5, 'x aux five', X'000305');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-37639-55938 This interfaces opens a handle to the BLOB
|
||||
# located in row iRow, column zColumn, table zTable in database zDb; in
|
||||
# other words, the same BLOB that would be selected by: SELECT zColumn
|
||||
# FROM zDb.zTable WHERE rowid = iRow;
|
||||
#
|
||||
proc read_blob {zDb zTab zCol iRow} {
|
||||
sqlite3_blob_open db $zDb $zTab $zCol $iRow 0 B
|
||||
set nByte [sqlite3_blob_bytes $B]
|
||||
set data [sqlite3_blob_read $B 0 $nByte]
|
||||
sqlite3_blob_close $B
|
||||
return $data
|
||||
}
|
||||
|
||||
do_test 1.1.1 { read_blob main t1 b 1 } "main one"
|
||||
do_test 1.1.2 { read_blob main t1 c 1 } "\01\01"
|
||||
do_test 1.1.3 { read_blob temp t1 b 1 } "temp one"
|
||||
do_test 1.1.4 { read_blob temp t1 c 1 } "\02\01"
|
||||
do_test 1.1.6 { read_blob aux t1 b 1 } "aux one"
|
||||
do_test 1.1.7 { read_blob aux t1 c 1 } "\03\01"
|
||||
|
||||
do_test 1.2.1 { read_blob main t1 b 4 } "main four"
|
||||
do_test 1.2.2 { read_blob main t1 c 4 } "\01\04"
|
||||
do_test 1.2.3 { read_blob temp t1 b 4 } "temp four"
|
||||
do_test 1.2.4 { read_blob temp t1 c 4 } "\02\04"
|
||||
do_test 1.2.6 { read_blob aux t1 b 4 } "aux four"
|
||||
do_test 1.2.7 { read_blob aux t1 c 4 } "\03\04"
|
||||
|
||||
do_test 1.3.1 { read_blob main x1 b 2 } "x main two"
|
||||
do_test 1.3.2 { read_blob main x1 c 2 } "\00\01\02"
|
||||
do_test 1.3.3 { read_blob temp x2 b 2 } "x temp two"
|
||||
do_test 1.3.4 { read_blob temp x2 c 2 } "\00\02\02"
|
||||
do_test 1.3.6 { read_blob aux x3 b 2 } "x aux two"
|
||||
do_test 1.3.7 { read_blob aux x3 c 2 } "\00\03\02"
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-27234-05761 Parameter zDb is not the filename that
|
||||
# contains the database, but rather the symbolic name of the database.
|
||||
# For attached databases, this is the name that appears after the AS
|
||||
# keyword in the ATTACH statement. For the main database file, the
|
||||
# database name is "main". For TEMP tables, the database name is "temp".
|
||||
#
|
||||
# The test cases immediately above demonstrate that the database name
|
||||
# for the main db, for TEMP tables and for those in attached databases
|
||||
# is correct. The following tests check that filenames cannot be
|
||||
# used as well.
|
||||
#
|
||||
do_test 2.1 {
|
||||
list [catch { sqlite3_blob_open db "test.db" t1 b 1 0 B } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test 2.2 {
|
||||
list [catch { sqlite3_blob_open db "test.db2" t1 b 1 0 B } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-50854-53979 If the flags parameter is non-zero, then
|
||||
# the BLOB is opened for read and write access.
|
||||
#
|
||||
# EVIDENCE-OF: R-03922-41160 If the flags parameter is zero, the BLOB is
|
||||
# opened for read-only access.
|
||||
#
|
||||
foreach {tn iRow flags} {
|
||||
1 1 0
|
||||
2 2 1
|
||||
3 3 -1
|
||||
4 4 2147483647
|
||||
5 5 -2147483648
|
||||
} {
|
||||
do_test 3.$tn.1 {
|
||||
sqlite3_blob_open db main x1 c $iRow $flags B
|
||||
set n [sqlite3_blob_bytes $B]
|
||||
sqlite3_blob_read $B 0 $n
|
||||
} [binary format ccc 0 1 $iRow]
|
||||
|
||||
if {$flags==0} {
|
||||
# Blob was opened for read-only access - writing returns an error.
|
||||
do_test 3.$tn.2 {
|
||||
list [catch { sqlite3_blob_write $B 0 xxx 3 } msg] $msg
|
||||
} {1 SQLITE_READONLY}
|
||||
|
||||
do_execsql_test 3.$tn.3 {
|
||||
SELECT c FROM x1 WHERE a=$iRow;
|
||||
} [binary format ccc 0 1 $iRow]
|
||||
} else {
|
||||
# Blob was opened for read/write access - writing succeeds
|
||||
do_test 3.$tn.4 {
|
||||
list [catch { sqlite3_blob_write $B 0 xxx 3 } msg] $msg
|
||||
} {0 {}}
|
||||
|
||||
do_execsql_test 3.$tn.5 {
|
||||
SELECT c FROM x1 WHERE a=$iRow;
|
||||
} {xxx}
|
||||
}
|
||||
|
||||
sqlite3_blob_close $B
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(x, y);
|
||||
INSERT INTO t1 VALUES('abcd', 152);
|
||||
INSERT INTO t1 VALUES(NULL, X'00010203');
|
||||
INSERT INTO t1 VALUES('', 154.2);
|
||||
|
||||
CREATE TABLE t2(x PRIMARY KEY, y) WITHOUT ROWID;
|
||||
INSERT INTO t2 VALUES(1, 'blob');
|
||||
|
||||
CREATE TABLE t3(a PRIMARY KEY, b, c, d, e, f, UNIQUE(e, f));
|
||||
INSERT INTO t3 VALUES('aaaa', 'bbbb', 'cccc', 'dddd', 'eeee', 'ffff');
|
||||
CREATE INDEX t3b ON t3(b);
|
||||
|
||||
CREATE TABLE p1(x PRIMARY KEY);
|
||||
INSERT INTO p1 VALUES('abc');
|
||||
|
||||
CREATE TABLE c1(a INTEGER PRIMARY KEY, b REFERENCES p1);
|
||||
INSERT INTO c1 VALUES(45, 'abc');
|
||||
}
|
||||
|
||||
proc test_blob_open {tn zDb zTab zCol iRow flags errcode errmsg} {
|
||||
global B
|
||||
set B "0x1234"
|
||||
|
||||
if {$errcode=="SQLITE_OK"} {
|
||||
set expected "0 {}"
|
||||
} else {
|
||||
set expected "1 $errcode"
|
||||
}
|
||||
|
||||
set ::res [list [
|
||||
catch { sqlite3_blob_open db $zDb $zTab $zCol $iRow $flags B } msg
|
||||
] $msg]
|
||||
do_test 4.$tn.1 { set ::res } $expected
|
||||
|
||||
# EVIDENCE-OF: R-08940-21305 Unless it returns SQLITE_MISUSE, this
|
||||
# function sets the database connection error code and message
|
||||
# accessible via sqlite3_errcode() and sqlite3_errmsg() and related
|
||||
# functions.
|
||||
#
|
||||
# This proc (test_blob_open) is used below to test various error and
|
||||
# non-error conditions. But never SQLITE_MISUSE conditions. So these
|
||||
# test cases are considered as partly verifying the requirement above.
|
||||
# See below for a test of the SQLITE_MISUSE case.
|
||||
#
|
||||
do_test 4.$tn.2 {
|
||||
sqlite3_errcode db
|
||||
} $errcode
|
||||
do_test 4.$tn.3 {
|
||||
sqlite3_errmsg db
|
||||
} $errmsg
|
||||
|
||||
# EVIDENCE-OF: R-31086-35521 On success, SQLITE_OK is returned and the
|
||||
# new BLOB handle is stored in *ppBlob. Otherwise an error code is
|
||||
# returned and, unless the error code is SQLITE_MISUSE, *ppBlob is set
|
||||
# to NULL.
|
||||
#
|
||||
do_test 4.$tn.4 {
|
||||
expr {$B == "0"}
|
||||
} [expr {$errcode != "SQLITE_OK"}]
|
||||
|
||||
# EVIDENCE-OF: R-63421-15521 This means that, provided the API is not
|
||||
# misused, it is always safe to call sqlite3_blob_close() on *ppBlob
|
||||
# after this function it returns.
|
||||
do_test 4.$tn.5 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-31204-44780 Database zDb does not exist
|
||||
test_blob_open 1 nosuchdb t1 x 1 0 SQLITE_ERROR "no such table: nosuchdb.t1"
|
||||
|
||||
# EVIDENCE-OF: R-28676-08005 Table zTable does not exist within database zDb
|
||||
test_blob_open 2 main tt1 x 1 0 SQLITE_ERROR "no such table: main.tt1"
|
||||
|
||||
# EVIDENCE-OF: R-40134-30296 Table zTable is a WITHOUT ROWID table
|
||||
test_blob_open 3 main t2 y 1 0 SQLITE_ERROR \
|
||||
"cannot open table without rowid: t2"
|
||||
|
||||
# EVIDENCE-OF: R-56376-21261 Column zColumn does not exist
|
||||
test_blob_open 4 main t1 z 2 0 SQLITE_ERROR "no such column: \"z\""
|
||||
|
||||
# EVIDENCE-OF: R-28258-23166 Row iRow is not present in the table
|
||||
test_blob_open 5 main t1 y 6 0 SQLITE_ERROR "no such rowid: 6"
|
||||
|
||||
# EVIDENCE-OF: R-11683-62380 The specified column of row iRow contains a
|
||||
# value that is not a TEXT or BLOB value
|
||||
test_blob_open 6 main t1 x 2 0 SQLITE_ERROR "cannot open value of type null"
|
||||
test_blob_open 7 main t1 y 1 0 SQLITE_ERROR "cannot open value of type integer"
|
||||
test_blob_open 8 main t1 y 3 0 SQLITE_ERROR "cannot open value of type real"
|
||||
|
||||
# EVIDENCE-OF: R-34146-30782 Column zColumn is part of an index, PRIMARY
|
||||
# KEY or UNIQUE constraint and the blob is being opened for read/write
|
||||
# access
|
||||
#
|
||||
# Test cases 8.1.* show that such columns can be opened for read-access.
|
||||
# Tests 8.2.* show that read-write access is different. Columns "c" and "c"
|
||||
# are not part of an index, PK or UNIQUE constraint, so they work in both
|
||||
# cases.
|
||||
#
|
||||
test_blob_open 8.1.1 main t3 a 1 0 SQLITE_OK "not an error"
|
||||
test_blob_open 8.1.2 main t3 b 1 0 SQLITE_OK "not an error"
|
||||
test_blob_open 8.1.3 main t3 c 1 0 SQLITE_OK "not an error"
|
||||
test_blob_open 8.1.4 main t3 d 1 0 SQLITE_OK "not an error"
|
||||
test_blob_open 8.1.5 main t3 e 1 0 SQLITE_OK "not an error"
|
||||
test_blob_open 8.1.6 main t3 f 1 0 SQLITE_OK "not an error"
|
||||
|
||||
set cannot "cannot open indexed column for writing"
|
||||
test_blob_open 8.2.1 main t3 a 1 8 SQLITE_ERROR $cannot
|
||||
test_blob_open 8.2.2 main t3 b 1 8 SQLITE_ERROR $cannot
|
||||
test_blob_open 8.2.3 main t3 c 1 8 SQLITE_OK "not an error"
|
||||
test_blob_open 8.2.4 main t3 d 1 8 SQLITE_OK "not an error"
|
||||
test_blob_open 8.2.5 main t3 e 1 8 SQLITE_ERROR $cannot
|
||||
test_blob_open 8.2.6 main t3 f 1 8 SQLITE_ERROR $cannot
|
||||
|
||||
# EVIDENCE-OF: R-50117-55204 Foreign key constraints are enabled, column
|
||||
# zColumn is part of a child key definition and the blob is being opened
|
||||
# for read/write access
|
||||
#
|
||||
# 9.1: FK disabled, read-only access.
|
||||
# 9.2: FK disabled, read-only access.
|
||||
# 9.3: FK enabled, read/write access.
|
||||
# 9.4: FK enabled, read/write access.
|
||||
#
|
||||
test_blob_open 9.1 main c1 b 45 0 SQLITE_OK "not an error"
|
||||
test_blob_open 9.2 main c1 b 45 1 SQLITE_OK "not an error"
|
||||
execsql { PRAGMA foreign_keys = ON }
|
||||
test_blob_open 9.3 main c1 b 45 0 SQLITE_OK "not an error"
|
||||
test_blob_open 9.4 main c1 b 45 1 SQLITE_ERROR \
|
||||
"cannot open foreign key column for writing"
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-08940-21305 Unless it returns SQLITE_MISUSE, this
|
||||
# function sets the database connection error code and message
|
||||
# accessible via sqlite3_errcode() and sqlite3_errmsg() and related
|
||||
# functions.
|
||||
#
|
||||
# This requirement is partially verified by the many uses of test
|
||||
# command [test_blob_open] above. All that is left is to verify the
|
||||
# SQLITE_MISUSE case.
|
||||
#
|
||||
# SQLITE_MISUSE is only returned if SQLITE_ENABLE_API_ARMOR is defined
|
||||
# during compilation.
|
||||
#
|
||||
ifcapable api_armor {
|
||||
sqlite3_blob_open db main t1 x 1 0 B
|
||||
|
||||
do_test 10.1.1 {
|
||||
list [catch {sqlite3_blob_open $B main t1 x 1 0 B2} msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
do_test 10.1.2 {
|
||||
list [sqlite3_errcode db] [sqlite3_errmsg db]
|
||||
} {SQLITE_OK {not an error}}
|
||||
sqlite3_blob_close $B
|
||||
|
||||
do_test 10.2.1 {
|
||||
list [catch {sqlite3_blob_open db main {} x 1 0 B} msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
do_test 10.2.2 {
|
||||
list [sqlite3_errcode db] [sqlite3_errmsg db]
|
||||
} {SQLITE_OK {not an error}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-50542-62589 If the row that a BLOB handle points to is
|
||||
# modified by an UPDATE, DELETE, or by ON CONFLICT side-effects then the
|
||||
# BLOB handle is marked as "expired". This is true if any column of the
|
||||
# row is changed, even a column other than the one the BLOB handle is
|
||||
# open on.
|
||||
#
|
||||
# EVIDENCE-OF: R-48367-20048 Calls to sqlite3_blob_read() and
|
||||
# sqlite3_blob_write() for an expired BLOB handle fail with a return
|
||||
# code of SQLITE_ABORT.
|
||||
#
|
||||
# 11.2: read-only handle, DELETE.
|
||||
# 11.3: read-only handle, UPDATE.
|
||||
# 11.4: read-only handle, REPLACE.
|
||||
# 11.5: read/write handle, DELETE.
|
||||
# 11.6: read/write handle, UPDATE.
|
||||
# 11.7: read/write handle, REPLACE.
|
||||
#
|
||||
do_execsql_test 11.1 {
|
||||
CREATE TABLE b1(a INTEGER PRIMARY KEY, b, c UNIQUE);
|
||||
INSERT INTO b1 VALUES(1, '1234567890', 1);
|
||||
INSERT INTO b1 VALUES(2, '1234567890', 2);
|
||||
INSERT INTO b1 VALUES(3, '1234567890', 3);
|
||||
INSERT INTO b1 VALUES(4, '1234567890', 4);
|
||||
INSERT INTO b1 VALUES(5, '1234567890', 5);
|
||||
INSERT INTO b1 VALUES(6, '1234567890', 6);
|
||||
|
||||
CREATE TABLE b2(a INTEGER PRIMARY KEY, b, c UNIQUE);
|
||||
INSERT INTO b2 VALUES(1, '1234567890', 1);
|
||||
INSERT INTO b2 VALUES(2, '1234567890', 2);
|
||||
INSERT INTO b2 VALUES(3, '1234567890', 3);
|
||||
INSERT INTO b2 VALUES(4, '1234567890', 4);
|
||||
INSERT INTO b2 VALUES(5, '1234567890', 5);
|
||||
INSERT INTO b2 VALUES(6, '1234567890', 6);
|
||||
}
|
||||
|
||||
do_test 11.2.1 {
|
||||
sqlite3_blob_open db main b1 b 2 0 B
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.2.2 {
|
||||
# Deleting a different row does not invalidate the blob handle.
|
||||
execsql { DELETE FROM b1 WHERE a = 1 }
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.2.3 {
|
||||
execsql { DELETE FROM b1 WHERE a = 2 }
|
||||
list [catch { sqlite3_blob_read $B 0 10 } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.2.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
do_test 11.3.1 {
|
||||
sqlite3_blob_open db main b1 b 3 0 B
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.3.2 {
|
||||
# Updating a different row
|
||||
execsql { UPDATE b1 SET c = 42 WHERE a=4 }
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.3.3 {
|
||||
execsql { UPDATE b1 SET c = 43 WHERE a=3 }
|
||||
list [catch { sqlite3_blob_read $B 0 10 } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.3.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
do_test 11.4.1 {
|
||||
sqlite3_blob_open db main b1 b 6 0 B
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.4.2 {
|
||||
# Replace a different row
|
||||
execsql { INSERT OR REPLACE INTO b1 VALUES(10, 'abcdefghij', 5) }
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.4.3 {
|
||||
execsql { INSERT OR REPLACE INTO b1 VALUES(11, 'abcdefghij', 6) }
|
||||
list [catch { sqlite3_blob_read $B 0 10 } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.4.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
do_test 11.4.1 {
|
||||
sqlite3_blob_open db main b2 b 2 1 B
|
||||
sqlite3_blob_write $B 0 "abcdefghij"
|
||||
} {}
|
||||
do_test 11.4.2 {
|
||||
# Deleting a different row does not invalidate the blob handle.
|
||||
execsql { DELETE FROM b2 WHERE a = 1 }
|
||||
sqlite3_blob_write $B 0 "ABCDEFGHIJ"
|
||||
} {}
|
||||
do_test 11.4.3 {
|
||||
execsql { DELETE FROM b2 WHERE a = 2 }
|
||||
list [catch { sqlite3_blob_write $B 0 "0987654321" } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.4.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
do_test 11.5.1 {
|
||||
sqlite3_blob_open db main b2 b 3 1 B
|
||||
sqlite3_blob_write $B 0 "abcdefghij"
|
||||
} {}
|
||||
do_test 11.5.2 {
|
||||
# Updating a different row
|
||||
execsql { UPDATE b2 SET c = 42 WHERE a=4 }
|
||||
sqlite3_blob_write $B 0 "ABCDEFGHIJ"
|
||||
} {}
|
||||
do_test 11.5.3 {
|
||||
execsql { UPDATE b2 SET c = 43 WHERE a=3 }
|
||||
list [catch { sqlite3_blob_write $B 0 "0987654321" } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.5.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
do_test 11.6.1 {
|
||||
sqlite3_blob_open db main b2 b 6 1 B
|
||||
sqlite3_blob_write $B 0 "abcdefghij"
|
||||
} {}
|
||||
do_test 11.6.2 {
|
||||
# Replace a different row
|
||||
execsql { INSERT OR REPLACE INTO b2 VALUES(10, 'abcdefghij', 5) }
|
||||
sqlite3_blob_write $B 0 "ABCDEFGHIJ"
|
||||
} {}
|
||||
do_test 11.6.3 {
|
||||
execsql { INSERT OR REPLACE INTO b2 VALUES(11, 'abcdefghij', 6) }
|
||||
list [catch { sqlite3_blob_write $B 0 "0987654321" } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.6.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-45408-40694 Changes written into a BLOB prior to the
|
||||
# BLOB expiring are not rolled back by the expiration of the BLOB. Such
|
||||
# changes will eventually commit if the transaction continues to
|
||||
# completion.
|
||||
#
|
||||
do_execsql_test 12.1 {
|
||||
CREATE TABLE b3(x INTEGER PRIMARY KEY, y TEXT, z INTEGER);
|
||||
INSERT INTO b3 VALUES(22, '..........', NULL);
|
||||
}
|
||||
do_test 12.2 {
|
||||
sqlite3_blob_open db main b3 y 22 1 B
|
||||
sqlite3_blob_write $B 0 "xxxxx" 5
|
||||
} {}
|
||||
do_execsql_test 12.3 {
|
||||
UPDATE b3 SET z = 'not null';
|
||||
}
|
||||
do_test 12.4 {
|
||||
list [catch {sqlite3_blob_write $B 5 "xxxxx" 5} msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_execsql_test 12.5 {
|
||||
SELECT * FROM b3;
|
||||
} {22 xxxxx..... {not null}}
|
||||
do_test 12.5 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
do_execsql_test 12.6 {
|
||||
SELECT * FROM b3;
|
||||
} {22 xxxxx..... {not null}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-58813-55036 The sqlite3_bind_zeroblob() and
|
||||
# sqlite3_result_zeroblob() interfaces and the built-in zeroblob SQL
|
||||
# function may be used to create a zero-filled blob to read or write
|
||||
# using the incremental-blob interface.
|
||||
#
|
||||
do_execsql_test 13.1 {
|
||||
CREATE TABLE c2(i INTEGER PRIMARY KEY, j);
|
||||
INSERT INTO c2 VALUES(10, zeroblob(24));
|
||||
}
|
||||
|
||||
do_test 13.2 {
|
||||
set stmt [sqlite3_prepare_v2 db "INSERT INTO c2 VALUES(11, ?)" -1]
|
||||
sqlite3_bind_zeroblob $stmt 1 45
|
||||
sqlite3_step $stmt
|
||||
sqlite3_finalize $stmt
|
||||
} {SQLITE_OK}
|
||||
|
||||
# The blobs can be read:
|
||||
#
|
||||
do_test 13.3.1 {
|
||||
sqlite3_blob_open db main c2 j 10 1 B
|
||||
sqlite3_blob_open db main c2 j 11 1 B2
|
||||
list [sqlite3_blob_bytes $B] [sqlite3_blob_bytes $B2]
|
||||
} {24 45}
|
||||
do_test 13.3.2 {
|
||||
sqlite3_blob_read $B 0 24
|
||||
} [string repeat [binary format c 0] 24]
|
||||
do_test 13.3.3 {
|
||||
sqlite3_blob_read $B2 0 45
|
||||
} [string repeat [binary format c 0] 45]
|
||||
|
||||
# And also written:
|
||||
#
|
||||
do_test 13.4.1 {
|
||||
sqlite3_blob_write $B 0 [string repeat [binary format c 1] 24]
|
||||
} {}
|
||||
do_test 13.4.2 {
|
||||
sqlite3_blob_write $B2 0 [string repeat [binary format c 1] 45]
|
||||
} {}
|
||||
do_test 13.5 {
|
||||
sqlite3_blob_close $B
|
||||
sqlite3_blob_close $B2
|
||||
execsql { SELECT j FROM c2 }
|
||||
} [list \
|
||||
[string repeat [binary format c 1] 24] \
|
||||
[string repeat [binary format c 1] 45] \
|
||||
]
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -50,5 +50,22 @@ do_tblsread_test 1.3 { UPDATE par SET a=? WHERE b=? } {c1 c2 par}
|
||||
do_tblsread_test 1.4 { UPDATE par SET c=? WHERE b=? } {c3 par}
|
||||
do_tblsread_test 1.5 { UPDATE par SET a=?,b=?,c=? WHERE b=? } {c1 c2 c3 par s1}
|
||||
|
||||
ifcapable incrblob {
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE pX(x PRIMARY KEY);
|
||||
CREATE TABLE cX(a INTEGER PRIMARY KEY, b REFERENCES pX);
|
||||
}
|
||||
|
||||
do_catchsql_test 2.1 {
|
||||
INSERT INTO cX VALUES(11, zeroblob(40));
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_test 2.2 {
|
||||
set stmt [sqlite3_prepare_v2 db "INSERT INTO cX VALUES(11, ?)" -1]
|
||||
sqlite3_bind_zeroblob $stmt 1 45
|
||||
sqlite3_step $stmt
|
||||
sqlite3_finalize $stmt
|
||||
} {SQLITE_CONSTRAINT}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
@ -185,8 +185,7 @@ do_execsql_test without_rowid5-5.9 {
|
||||
# EVIDENCE-OF: R-12643-30541 The incremental blob I/O mechanism does not
|
||||
# work for WITHOUT ROWID tables.
|
||||
#
|
||||
# EVIDENCE-OF: R-25760-33257 The sqlite3_blob_open() interface will fail
|
||||
# for a WITHOUT ROWID table.
|
||||
# EVIDENCE-OF: R-40134-30296 Table zTable is a WITHOUT ROWID table
|
||||
#
|
||||
do_execsql_test without_rowid5-6.1 {
|
||||
CREATE TABLE b1(a INTEGER PRIMARY KEY, b BLOB) WITHOUT ROWID;
|
||||
|
Loading…
x
Reference in New Issue
Block a user