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