xref: /sqlite-3.40.0/src/test_blob.c (revision bd41d566)
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, &notUsed);
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