2 * Jim - Sqlite bindings
4 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * The views and conclusions contained in the software and documentation
31 * are those of the authors and should not be interpreted as representing
32 * official policies, either expressed or implied, of the Jim Tcl Project.
40 #include "jimautoconf.h"
42 typedef struct JimSqliteHandle
47 static void JimSqliteDelProc(Jim_Interp
*interp
, void *privData
)
49 JimSqliteHandle
*sh
= privData
;
53 sqlite3_close(sh
->db
);
57 static char *JimSqliteQuoteString(const char *str
, int len
, int *newLenPtr
)
63 for (i
= 0; i
< len
; i
++)
68 d
= buf
= Jim_Alloc(newLen
);
78 static Jim_Obj
*JimSqliteFormatQuery(Jim_Interp
*interp
, Jim_Obj
*fmtObjPtr
,
79 int objc
, Jim_Obj
*const *objv
)
85 fmt
= Jim_GetString(fmtObjPtr
, &fmtLen
);
86 resObjPtr
= Jim_NewStringObj(interp
, "", 0);
91 while (*fmt
!= '%' && fmtLen
) {
95 Jim_AppendString(interp
, resObjPtr
, p
, fmt
- p
);
99 fmtLen
--; /* skip '%' */
102 Jim_FreeNewObj(interp
, resObjPtr
);
103 Jim_SetResultString(interp
, "not enough arguments for all format specifiers", -1);
117 str
= Jim_GetString(objv
[0], &len
);
118 quoted
= JimSqliteQuoteString(str
, len
, &newLen
);
119 Jim_AppendString(interp
, resObjPtr
, quoted
, newLen
);
125 Jim_AppendString(interp
, resObjPtr
, "%", 1);
130 Jim_FreeNewObj(interp
, resObjPtr
);
131 Jim_SetResultFormatted(interp
,
132 "bad field specifier \"%s\", only %%s and %%%% are valid", spec
);
141 /* Calls to [sqlite.open] create commands that are implemented by this
143 static int JimSqliteHandlerCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
145 JimSqliteHandle
*sh
= Jim_CmdPrivData(interp
);
147 static const char * const options
[] = {
148 "close", "query", "lastid", "changes", NULL
151 { OPT_CLOSE
, OPT_QUERY
, OPT_LASTID
, OPT_CHANGES
};
154 Jim_WrongNumArgs(interp
, 1, argv
, "method ?args ...?");
157 if (Jim_GetEnum(interp
, argv
[1], options
, &option
, "Sqlite method", JIM_ERRMSG
) != JIM_OK
)
160 if (option
== OPT_CLOSE
) {
162 Jim_WrongNumArgs(interp
, 2, argv
, "");
165 Jim_DeleteCommand(interp
, Jim_String(argv
[0]));
168 else if (option
== OPT_QUERY
) {
170 Jim_Obj
*objPtr
, *rowsListPtr
;
172 const char *query
, *tail
;
173 int columns
, rows
, len
;
176 if (argc
>= 4 && Jim_CompareStringImmediate(interp
, argv
[2], "-null")) {
177 nullstr
= Jim_StrDup(Jim_String(argv
[3]));
182 nullstr
= Jim_StrDup("");
185 Jim_WrongNumArgs(interp
, 2, argv
, "query ?args?");
189 objPtr
= JimSqliteFormatQuery(interp
, argv
[2], argc
- 3, argv
+ 3);
190 if (objPtr
== NULL
) {
194 query
= Jim_GetString(objPtr
, &len
);
195 Jim_IncrRefCount(objPtr
);
196 /* Compile the query into VM code */
197 if (sqlite3_prepare(sh
->db
, query
, len
, &stmt
, &tail
) != SQLITE_OK
) {
198 Jim_DecrRefCount(interp
, objPtr
);
199 Jim_SetResultString(interp
, sqlite3_errmsg(sh
->db
), -1);
203 Jim_DecrRefCount(interp
, objPtr
); /* query no longer needed. */
204 /* Build a list of rows (that are lists in turn) */
205 rowsListPtr
= Jim_NewListObj(interp
, NULL
, 0);
206 Jim_IncrRefCount(rowsListPtr
);
208 columns
= sqlite3_column_count(stmt
);
209 while (sqlite3_step(stmt
) == SQLITE_ROW
) {
212 objPtr
= Jim_NewListObj(interp
, NULL
, 0);
213 for (i
= 0; i
< columns
; i
++) {
214 Jim_Obj
*vObj
= NULL
;
216 Jim_ListAppendElement(interp
, objPtr
,
217 Jim_NewStringObj(interp
, sqlite3_column_name(stmt
, i
), -1));
218 switch (sqlite3_column_type(stmt
, i
)) {
220 vObj
= Jim_NewStringObj(interp
, nullstr
, -1);
223 vObj
= Jim_NewIntObj(interp
, sqlite3_column_int(stmt
, i
));
226 vObj
= Jim_NewDoubleObj(interp
, sqlite3_column_double(stmt
, i
));
230 vObj
= Jim_NewStringObj(interp
,
231 sqlite3_column_blob(stmt
, i
), sqlite3_column_bytes(stmt
, i
));
234 Jim_ListAppendElement(interp
, objPtr
, vObj
);
236 Jim_ListAppendElement(interp
, rowsListPtr
, objPtr
);
241 if (sqlite3_finalize(stmt
) != SQLITE_OK
) {
242 Jim_DecrRefCount(interp
, rowsListPtr
);
243 Jim_SetResultString(interp
, sqlite3_errmsg(sh
->db
), -1);
246 Jim_SetResult(interp
, rowsListPtr
);
247 Jim_DecrRefCount(interp
, rowsListPtr
);
249 else if (option
== OPT_LASTID
) {
251 Jim_WrongNumArgs(interp
, 2, argv
, "");
254 Jim_SetResult(interp
, Jim_NewIntObj(interp
, sqlite3_last_insert_rowid(sh
->db
)));
257 else if (option
== OPT_CHANGES
) {
259 Jim_WrongNumArgs(interp
, 2, argv
, "");
262 Jim_SetResult(interp
, Jim_NewIntObj(interp
, sqlite3_changes(sh
->db
)));
268 static int JimSqliteOpenCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
276 Jim_WrongNumArgs(interp
, 1, argv
, "dbname");
279 r
= sqlite3_open(Jim_String(argv
[1]), &db
);
280 if (r
!= SQLITE_OK
) {
281 Jim_SetResultString(interp
, sqlite3_errmsg(db
), -1);
285 /* Create the file command */
286 sh
= Jim_Alloc(sizeof(*sh
));
288 snprintf(buf
, sizeof(buf
), "sqlite.handle%ld", Jim_GetId(interp
));
289 Jim_CreateCommand(interp
, buf
, JimSqliteHandlerCommand
, sh
, JimSqliteDelProc
);
290 Jim_SetResultString(interp
, buf
, -1);
294 int Jim_sqlite3Init(Jim_Interp
*interp
)
296 if (Jim_PackageProvide(interp
, "sqlite3", "1.0", JIM_ERRMSG
))
299 Jim_CreateCommand(interp
, "sqlite3.open", JimSqliteOpenCommand
, NULL
, NULL
);