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