1 /* 2 ** 2014 October 30 3 ** 4 ** The author disclaims copyright to this source code. In place of 5 ** a legal notice, here is a blessing: 6 ** 7 ** May you do good and not evil. 8 ** May you find forgiveness for yourself and forgive others. 9 ** May you share freely, never taking more than you give. 10 ** 11 ************************************************************************* 12 ** 13 */ 14 #include "sqliteInt.h" 15 #include "tcl.h" 16 #include <stdlib.h> 17 #include <string.h> 18 #include <assert.h> 19 20 /* These functions are implemented in main.c. */ 21 extern const char *sqlite3ErrName(int); 22 23 /* From test1.c: */ 24 extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); 25 extern void *sqlite3TestTextToPtr(const char *z); 26 27 /* 28 ** Return a pointer to a buffer containing a text representation of the 29 ** pointer passed as the only argument. The original pointer may be extracted 30 ** from the text using sqlite3TestTextToPtr(). 31 */ 32 static char *ptrToText(void *p){ 33 static char buf[100]; 34 sqlite3_snprintf(sizeof(buf)-1, buf, "%p", p); 35 return buf; 36 } 37 38 /* 39 ** Attempt to extract a blob handle (type sqlite3_blob*) from the Tcl 40 ** object passed as the second argument. If successful, set *ppBlob to 41 ** point to the blob handle and return TCL_OK. Otherwise, store an error 42 ** message in the tcl interpreter and return TCL_ERROR. The final value 43 ** of *ppBlob is undefined in this case. 44 ** 45 ** If the object contains a string that begins with "incrblob_", then it 46 ** is assumed to be the name of a Tcl channel opened using the [db incrblob] 47 ** command (see tclsqlite.c). Otherwise, it is assumed to be a pointer 48 ** encoded using the ptrToText() routine or similar. 49 */ 50 static int blobHandleFromObj( 51 Tcl_Interp *interp, 52 Tcl_Obj *pObj, 53 sqlite3_blob **ppBlob 54 ){ 55 char *z; 56 int n; 57 58 z = Tcl_GetStringFromObj(pObj, &n); 59 if( n==0 ){ 60 *ppBlob = 0; 61 }else if( n>9 && 0==memcmp("incrblob_", z, 9) ){ 62 int notUsed; 63 Tcl_Channel channel; 64 ClientData instanceData; 65 66 channel = Tcl_GetChannel(interp, z, ¬Used); 67 if( !channel ) return TCL_ERROR; 68 69 Tcl_Flush(channel); 70 Tcl_Seek(channel, 0, SEEK_SET); 71 72 instanceData = Tcl_GetChannelInstanceData(channel); 73 *ppBlob = *((sqlite3_blob **)instanceData); 74 }else{ 75 *ppBlob = (sqlite3_blob*)sqlite3TestTextToPtr(z); 76 } 77 78 return TCL_OK; 79 } 80 81 /* 82 ** Like Tcl_GetString(), except that if the string is 0 bytes in size, a 83 ** NULL Pointer is returned. 84 */ 85 static char *blobStringFromObj(Tcl_Obj *pObj){ 86 int n; 87 char *z; 88 z = Tcl_GetStringFromObj(pObj, &n); 89 return (n ? z : 0); 90 } 91 92 /* 93 ** sqlite3_blob_open DB DATABASE TABLE COLUMN ROWID FLAGS VARNAME 94 ** 95 ** Tcl test harness for the sqlite3_blob_open() function. 96 */ 97 static int test_blob_open( 98 ClientData clientData, /* Not used */ 99 Tcl_Interp *interp, /* Calling TCL interpreter */ 100 int objc, /* Number of arguments */ 101 Tcl_Obj *CONST objv[] /* Command arguments */ 102 ){ 103 sqlite3 *db; 104 const char *zDb; 105 const char *zTable; 106 const char *zColumn; 107 sqlite_int64 iRowid; 108 int flags; 109 const char *zVarname; 110 int nVarname; 111 112 sqlite3_blob *pBlob = (sqlite3_blob*)0xFFFFFFFF; 113 int rc; 114 115 if( objc!=8 ){ 116 const char *zUsage = "DB DATABASE TABLE COLUMN ROWID FLAGS VARNAME"; 117 Tcl_WrongNumArgs(interp, 1, objv, zUsage); 118 return TCL_ERROR; 119 } 120 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; 121 zDb = Tcl_GetString(objv[2]); 122 zTable = blobStringFromObj(objv[3]); 123 zColumn = Tcl_GetString(objv[4]); 124 if( Tcl_GetWideIntFromObj(interp, objv[5], &iRowid) ) return TCL_ERROR; 125 if( Tcl_GetIntFromObj(interp, objv[6], &flags) ) return TCL_ERROR; 126 zVarname = Tcl_GetStringFromObj(objv[7], &nVarname); 127 128 if( nVarname>0 ){ 129 rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRowid, flags, &pBlob); 130 Tcl_SetVar(interp, zVarname, ptrToText(pBlob), 0); 131 }else{ 132 rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRowid, flags, 0); 133 } 134 135 if( rc==SQLITE_OK ){ 136 Tcl_ResetResult(interp); 137 }else{ 138 Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE); 139 return TCL_ERROR; 140 } 141 return TCL_OK; 142 } 143 144 145 /* 146 ** sqlite3_blob_close HANDLE 147 */ 148 static int test_blob_close( 149 ClientData clientData, /* Not used */ 150 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 151 int objc, /* Number of arguments */ 152 Tcl_Obj *CONST objv[] /* Command arguments */ 153 ){ 154 sqlite3_blob *pBlob; 155 int rc; 156 157 if( objc!=2 ){ 158 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); 159 return TCL_ERROR; 160 } 161 162 if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR; 163 rc = sqlite3_blob_close(pBlob); 164 165 if( rc ){ 166 Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE); 167 }else{ 168 Tcl_ResetResult(interp); 169 } 170 return TCL_OK; 171 } 172 173 /* 174 ** sqlite3_blob_bytes HANDLE 175 */ 176 static int test_blob_bytes( 177 ClientData clientData, /* Not used */ 178 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 179 int objc, /* Number of arguments */ 180 Tcl_Obj *CONST objv[] /* Command arguments */ 181 ){ 182 sqlite3_blob *pBlob; 183 int nByte; 184 185 if( objc!=2 ){ 186 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); 187 return TCL_ERROR; 188 } 189 190 if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR; 191 nByte = sqlite3_blob_bytes(pBlob); 192 Tcl_SetObjResult(interp, Tcl_NewIntObj(nByte)); 193 194 return TCL_OK; 195 } 196 197 /* 198 ** sqlite3_blob_read CHANNEL OFFSET N 199 ** 200 ** This command is used to test the sqlite3_blob_read() in ways that 201 ** the Tcl channel interface does not. The first argument should 202 ** be the name of a valid channel created by the [incrblob] method 203 ** of a database handle. This function calls sqlite3_blob_read() 204 ** to read N bytes from offset OFFSET from the underlying SQLite 205 ** blob handle. 206 ** 207 ** On success, a byte-array object containing the read data is 208 ** returned. On failure, the interpreter result is set to the 209 ** text representation of the returned error code (i.e. "SQLITE_NOMEM") 210 ** and a Tcl exception is thrown. 211 */ 212 static int test_blob_read( 213 ClientData clientData, /* Not used */ 214 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 215 int objc, /* Number of arguments */ 216 Tcl_Obj *CONST objv[] /* Command arguments */ 217 ){ 218 sqlite3_blob *pBlob; 219 int nByte; 220 int iOffset; 221 unsigned char *zBuf = 0; 222 int rc; 223 224 if( objc!=4 ){ 225 Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N"); 226 return TCL_ERROR; 227 } 228 229 if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR; 230 if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) 231 || TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte) 232 ){ 233 return TCL_ERROR; 234 } 235 236 if( nByte>0 ){ 237 zBuf = (unsigned char *)Tcl_Alloc(nByte); 238 } 239 rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset); 240 if( rc==SQLITE_OK ){ 241 Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte)); 242 }else{ 243 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); 244 } 245 Tcl_Free((char *)zBuf); 246 247 return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); 248 } 249 250 /* 251 ** sqlite3_blob_write HANDLE OFFSET DATA ?NDATA? 252 ** 253 ** This command is used to test the sqlite3_blob_write() in ways that 254 ** the Tcl channel interface does not. The first argument should 255 ** be the name of a valid channel created by the [incrblob] method 256 ** of a database handle. This function calls sqlite3_blob_write() 257 ** to write the DATA byte-array to the underlying SQLite blob handle. 258 ** at offset OFFSET. 259 ** 260 ** On success, an empty string is returned. On failure, the interpreter 261 ** result is set to the text representation of the returned error code 262 ** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown. 263 */ 264 static int test_blob_write( 265 ClientData clientData, /* Not used */ 266 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 267 int objc, /* Number of arguments */ 268 Tcl_Obj *CONST objv[] /* Command arguments */ 269 ){ 270 sqlite3_blob *pBlob; 271 int iOffset; 272 int rc; 273 274 unsigned char *zBuf; 275 int nBuf; 276 277 if( objc!=4 && objc!=5 ){ 278 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET DATA ?NDATA?"); 279 return TCL_ERROR; 280 } 281 282 if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR; 283 if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){ 284 return TCL_ERROR; 285 } 286 287 zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf); 288 if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){ 289 return TCL_ERROR; 290 } 291 rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset); 292 if( rc!=SQLITE_OK ){ 293 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); 294 } 295 296 return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); 297 } 298 299 300 /* 301 ** Register commands with the TCL interpreter. 302 */ 303 int Sqlitetest_blob_Init(Tcl_Interp *interp){ 304 static struct { 305 char *zName; 306 Tcl_ObjCmdProc *xProc; 307 } aObjCmd[] = { 308 { "sqlite3_blob_open", test_blob_open }, 309 { "sqlite3_blob_close", test_blob_close }, 310 { "sqlite3_blob_bytes", test_blob_bytes }, 311 { "sqlite3_blob_read", test_blob_read }, 312 { "sqlite3_blob_write", test_blob_write }, 313 }; 314 int i; 315 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ 316 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0); 317 } 318 return TCL_OK; 319 } 320