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