Add basic support for [format %b]
[jimtcl.git] / jim-sqlite3.c
blob62d8fa9f9beee29bd0e579499c3fba52d51adab7
1 /*
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
8 * are met:
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.
35 #include <stdio.h>
36 #include <string.h>
37 #include <sqlite3.h>
39 #include <jim.h>
41 typedef struct JimSqliteHandle
43 sqlite3 *db;
44 } JimSqliteHandle;
46 static void JimSqliteDelProc(Jim_Interp *interp, void *privData)
48 JimSqliteHandle *sh = privData;
50 JIM_NOTUSED(interp);
52 sqlite3_close(sh->db);
53 Jim_Free(sh);
56 static char *JimSqliteQuoteString(const char *str, int len, int *newLenPtr)
58 int i, newLen, c = 0;
59 const char *s;
60 char *d, *buf;
62 for (i = 0; i < len; i++)
63 if (str[i] == '\'')
64 c++;
65 newLen = len + c;
66 s = str;
67 d = buf = Jim_Alloc(newLen);
68 while (len--) {
69 if (*s == '\'')
70 *d++ = '\'';
71 *d++ = *s++;
73 *newLenPtr = newLen;
74 return buf;
77 static Jim_Obj *JimSqliteFormatQuery(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
78 int objc, Jim_Obj *const *objv)
80 const char *fmt;
81 int fmtLen;
82 Jim_Obj *resObjPtr;
84 fmt = Jim_GetString(fmtObjPtr, &fmtLen);
85 resObjPtr = Jim_NewStringObj(interp, "", 0);
86 while (fmtLen) {
87 const char *p = fmt;
88 char spec[2];
90 while (*fmt != '%' && fmtLen) {
91 fmt++;
92 fmtLen--;
94 Jim_AppendString(interp, resObjPtr, p, fmt - p);
95 if (fmtLen == 0)
96 break;
97 fmt++;
98 fmtLen--; /* skip '%' */
99 if (*fmt != '%') {
100 if (objc == 0) {
101 Jim_FreeNewObj(interp, resObjPtr);
102 Jim_SetResultString(interp, "not enough arguments for all format specifiers", -1);
103 return NULL;
105 else {
106 objc--;
109 switch (*fmt) {
110 case 's':
112 const char *str;
113 char *quoted;
114 int len, newLen;
116 str = Jim_GetString(objv[0], &len);
117 quoted = JimSqliteQuoteString(str, len, &newLen);
118 Jim_AppendString(interp, resObjPtr, quoted, newLen);
119 Jim_Free(quoted);
121 objv++;
122 break;
123 case '%':
124 Jim_AppendString(interp, resObjPtr, "%", 1);
125 break;
126 default:
127 spec[0] = *fmt;
128 spec[1] = '\0';
129 Jim_FreeNewObj(interp, resObjPtr);
130 Jim_SetResultFormatted(interp,
131 "bad field specifier \"%s\", only %%s and %%%% are valid", spec);
132 return NULL;
134 fmt++;
135 fmtLen--;
137 return resObjPtr;
140 /* Calls to [sqlite.open] create commands that are implemented by this
141 * C command. */
142 static int JimSqliteHandlerCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
144 JimSqliteHandle *sh = Jim_CmdPrivData(interp);
145 int option;
146 static const char * const options[] = {
147 "close", "query", "lastid", "changes", NULL
149 enum
150 { OPT_CLOSE, OPT_QUERY, OPT_LASTID, OPT_CHANGES };
152 if (argc < 2) {
153 Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
154 return JIM_ERR;
156 if (Jim_GetEnum(interp, argv[1], options, &option, "Sqlite method", JIM_ERRMSG) != JIM_OK)
157 return JIM_ERR;
158 /* CLOSE */
159 if (option == OPT_CLOSE) {
160 if (argc != 2) {
161 Jim_WrongNumArgs(interp, 2, argv, "");
162 return JIM_ERR;
164 Jim_DeleteCommand(interp, Jim_String(argv[0]));
165 return JIM_OK;
167 else if (option == OPT_QUERY) {
168 /* QUERY */
169 Jim_Obj *objPtr, *rowsListPtr;
170 sqlite3_stmt *stmt;
171 const char *query, *tail;
172 int columns, rows, len;
173 char *nullstr;
175 if (argc >= 4 && Jim_CompareStringImmediate(interp, argv[2], "-null")) {
176 nullstr = Jim_StrDup(Jim_String(argv[3]));
177 argv += 2;
178 argc -= 2;
180 else {
181 nullstr = Jim_StrDup("");
183 if (argc < 3) {
184 Jim_WrongNumArgs(interp, 2, argv, "query ?args?");
185 Jim_Free(nullstr);
186 return JIM_ERR;
188 objPtr = JimSqliteFormatQuery(interp, argv[2], argc - 3, argv + 3);
189 if (objPtr == NULL) {
190 Jim_Free(nullstr);
191 return JIM_ERR;
193 query = Jim_GetString(objPtr, &len);
194 Jim_IncrRefCount(objPtr);
195 /* Compile the query into VM code */
196 if (sqlite3_prepare_v2(sh->db, query, len, &stmt, &tail) != SQLITE_OK) {
197 Jim_DecrRefCount(interp, objPtr);
198 Jim_SetResultString(interp, sqlite3_errmsg(sh->db), -1);
199 Jim_Free(nullstr);
200 return JIM_ERR;
202 Jim_DecrRefCount(interp, objPtr); /* query no longer needed. */
203 /* Build a list of rows (that are lists in turn) */
204 rowsListPtr = Jim_NewListObj(interp, NULL, 0);
205 Jim_IncrRefCount(rowsListPtr);
206 rows = 0;
207 columns = sqlite3_column_count(stmt);
208 while (sqlite3_step(stmt) == SQLITE_ROW) {
209 int i;
211 objPtr = Jim_NewListObj(interp, NULL, 0);
212 for (i = 0; i < columns; i++) {
213 Jim_Obj *vObj = NULL;
215 Jim_ListAppendElement(interp, objPtr,
216 Jim_NewStringObj(interp, sqlite3_column_name(stmt, i), -1));
217 switch (sqlite3_column_type(stmt, i)) {
218 case SQLITE_NULL:
219 vObj = Jim_NewStringObj(interp, nullstr, -1);
220 break;
221 case SQLITE_INTEGER:
222 vObj = Jim_NewIntObj(interp, sqlite3_column_int(stmt, i));
223 break;
224 case SQLITE_FLOAT:
225 vObj = Jim_NewDoubleObj(interp, sqlite3_column_double(stmt, i));
226 break;
227 case SQLITE_TEXT:
228 case SQLITE_BLOB:
229 vObj = Jim_NewStringObj(interp,
230 sqlite3_column_blob(stmt, i), sqlite3_column_bytes(stmt, i));
231 break;
233 Jim_ListAppendElement(interp, objPtr, vObj);
235 Jim_ListAppendElement(interp, rowsListPtr, objPtr);
236 rows++;
238 /* Finalize */
239 Jim_Free(nullstr);
240 if (sqlite3_finalize(stmt) != SQLITE_OK) {
241 Jim_DecrRefCount(interp, rowsListPtr);
242 Jim_SetResultString(interp, sqlite3_errmsg(sh->db), -1);
243 return JIM_ERR;
245 Jim_SetResult(interp, rowsListPtr);
246 Jim_DecrRefCount(interp, rowsListPtr);
248 else if (option == OPT_LASTID) {
249 if (argc != 2) {
250 Jim_WrongNumArgs(interp, 2, argv, "");
251 return JIM_ERR;
253 Jim_SetResult(interp, Jim_NewIntObj(interp, sqlite3_last_insert_rowid(sh->db)));
254 return JIM_OK;
256 else if (option == OPT_CHANGES) {
257 if (argc != 2) {
258 Jim_WrongNumArgs(interp, 2, argv, "");
259 return JIM_ERR;
261 Jim_SetResult(interp, Jim_NewIntObj(interp, sqlite3_changes(sh->db)));
262 return JIM_OK;
264 return JIM_OK;
267 static int JimSqliteOpenCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
269 sqlite3 *db;
270 JimSqliteHandle *sh;
271 char buf[60];
272 int r;
274 if (argc != 2) {
275 Jim_WrongNumArgs(interp, 1, argv, "dbname");
276 return JIM_ERR;
278 r = sqlite3_open(Jim_String(argv[1]), &db);
279 if (r != SQLITE_OK) {
280 Jim_SetResultString(interp, sqlite3_errmsg(db), -1);
281 sqlite3_close(db);
282 return JIM_ERR;
284 /* Create the file command */
285 sh = Jim_Alloc(sizeof(*sh));
286 sh->db = db;
287 snprintf(buf, sizeof(buf), "sqlite.handle%ld", Jim_GetId(interp));
288 Jim_CreateCommand(interp, buf, JimSqliteHandlerCommand, sh, JimSqliteDelProc);
289 Jim_SetResultString(interp, buf, -1);
290 return JIM_OK;
293 int Jim_sqlite3Init(Jim_Interp *interp)
295 if (Jim_PackageProvide(interp, "sqlite3", "1.0", JIM_ERRMSG))
296 return JIM_ERR;
298 Jim_CreateCommand(interp, "sqlite3.open", JimSqliteOpenCommand, NULL, NULL);
299 return JIM_OK;