Add a general purpose hashtable pattern matcher
[jimtcl.git] / jim-sqlite3.c
blob8d4d8b834a15f2b5e3108ca6d389e5909d492d5d
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"
40 #include "jimautoconf.h"
42 typedef struct JimSqliteHandle
44 sqlite3 *db;
45 } JimSqliteHandle;
47 static void JimSqliteDelProc(Jim_Interp *interp, void *privData)
49 JimSqliteHandle *sh = privData;
51 JIM_NOTUSED(interp);
53 sqlite3_close(sh->db);
54 Jim_Free(sh);
57 static char *JimSqliteQuoteString(const char *str, int len, int *newLenPtr)
59 int i, newLen, c = 0;
60 const char *s;
61 char *d, *buf;
63 for (i = 0; i < len; i++)
64 if (str[i] == '\'')
65 c++;
66 newLen = len + c;
67 s = str;
68 d = buf = Jim_Alloc(newLen);
69 while (len--) {
70 if (*s == '\'')
71 *d++ = '\'';
72 *d++ = *s++;
74 *newLenPtr = newLen;
75 return buf;
78 static Jim_Obj *JimSqliteFormatQuery(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
79 int objc, Jim_Obj *const *objv)
81 const char *fmt;
82 int fmtLen;
83 Jim_Obj *resObjPtr;
85 fmt = Jim_GetString(fmtObjPtr, &fmtLen);
86 resObjPtr = Jim_NewStringObj(interp, "", 0);
87 while (fmtLen) {
88 const char *p = fmt;
89 char spec[2];
91 while (*fmt != '%' && fmtLen) {
92 fmt++;
93 fmtLen--;
95 Jim_AppendString(interp, resObjPtr, p, fmt - p);
96 if (fmtLen == 0)
97 break;
98 fmt++;
99 fmtLen--; /* skip '%' */
100 if (*fmt != '%') {
101 if (objc == 0) {
102 Jim_FreeNewObj(interp, resObjPtr);
103 Jim_SetResultString(interp, "not enough arguments for all format specifiers", -1);
104 return NULL;
106 else {
107 objc--;
110 switch (*fmt) {
111 case 's':
113 const char *str;
114 char *quoted;
115 int len, newLen;
117 str = Jim_GetString(objv[0], &len);
118 quoted = JimSqliteQuoteString(str, len, &newLen);
119 Jim_AppendString(interp, resObjPtr, quoted, newLen);
120 Jim_Free(quoted);
122 objv++;
123 break;
124 case '%':
125 Jim_AppendString(interp, resObjPtr, "%", 1);
126 break;
127 default:
128 spec[1] = *fmt;
129 spec[2] = '\0';
130 Jim_FreeNewObj(interp, resObjPtr);
131 Jim_SetResultFormatted(interp,
132 "bad field specifier \"%s\", only %%s and %%%% are valid", spec);
133 return NULL;
135 fmt++;
136 fmtLen--;
138 return resObjPtr;
141 /* Calls to [sqlite.open] create commands that are implemented by this
142 * C command. */
143 static int JimSqliteHandlerCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
145 JimSqliteHandle *sh = Jim_CmdPrivData(interp);
146 int option;
147 static const char * const options[] = {
148 "close", "query", "lastid", "changes", NULL
150 enum
151 { OPT_CLOSE, OPT_QUERY, OPT_LASTID, OPT_CHANGES };
153 if (argc < 2) {
154 Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
155 return JIM_ERR;
157 if (Jim_GetEnum(interp, argv[1], options, &option, "Sqlite method", JIM_ERRMSG) != JIM_OK)
158 return JIM_ERR;
159 /* CLOSE */
160 if (option == OPT_CLOSE) {
161 if (argc != 2) {
162 Jim_WrongNumArgs(interp, 2, argv, "");
163 return JIM_ERR;
165 Jim_DeleteCommand(interp, Jim_String(argv[0]));
166 return JIM_OK;
168 else if (option == OPT_QUERY) {
169 /* QUERY */
170 Jim_Obj *objPtr, *rowsListPtr;
171 sqlite3_stmt *stmt;
172 const char *query, *tail;
173 int columns, rows, len;
174 char *nullstr;
176 if (argc >= 4 && Jim_CompareStringImmediate(interp, argv[2], "-null")) {
177 nullstr = Jim_StrDup(Jim_String(argv[3]));
178 argv += 2;
179 argc -= 2;
181 else {
182 nullstr = Jim_StrDup("");
184 if (argc < 3) {
185 Jim_WrongNumArgs(interp, 2, argv, "query ?args?");
186 Jim_Free(nullstr);
187 return JIM_ERR;
189 objPtr = JimSqliteFormatQuery(interp, argv[2], argc - 3, argv + 3);
190 if (objPtr == NULL) {
191 Jim_Free(nullstr);
192 return JIM_ERR;
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);
200 Jim_Free(nullstr);
201 return JIM_ERR;
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);
207 rows = 0;
208 columns = sqlite3_column_count(stmt);
209 while (sqlite3_step(stmt) == SQLITE_ROW) {
210 int i;
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)) {
219 case SQLITE_NULL:
220 vObj = Jim_NewStringObj(interp, nullstr, -1);
221 break;
222 case SQLITE_INTEGER:
223 vObj = Jim_NewIntObj(interp, sqlite3_column_int(stmt, i));
224 break;
225 case SQLITE_FLOAT:
226 vObj = Jim_NewDoubleObj(interp, sqlite3_column_double(stmt, i));
227 break;
228 case SQLITE_TEXT:
229 case SQLITE_BLOB:
230 vObj = Jim_NewStringObj(interp,
231 sqlite3_column_blob(stmt, i), sqlite3_column_bytes(stmt, i));
232 break;
234 Jim_ListAppendElement(interp, objPtr, vObj);
236 Jim_ListAppendElement(interp, rowsListPtr, objPtr);
237 rows++;
239 /* Finalize */
240 Jim_Free(nullstr);
241 if (sqlite3_finalize(stmt) != SQLITE_OK) {
242 Jim_DecrRefCount(interp, rowsListPtr);
243 Jim_SetResultString(interp, sqlite3_errmsg(sh->db), -1);
244 return JIM_ERR;
246 Jim_SetResult(interp, rowsListPtr);
247 Jim_DecrRefCount(interp, rowsListPtr);
249 else if (option == OPT_LASTID) {
250 if (argc != 2) {
251 Jim_WrongNumArgs(interp, 2, argv, "");
252 return JIM_ERR;
254 Jim_SetResult(interp, Jim_NewIntObj(interp, sqlite3_last_insert_rowid(sh->db)));
255 return JIM_OK;
257 else if (option == OPT_CHANGES) {
258 if (argc != 2) {
259 Jim_WrongNumArgs(interp, 2, argv, "");
260 return JIM_ERR;
262 Jim_SetResult(interp, Jim_NewIntObj(interp, sqlite3_changes(sh->db)));
263 return JIM_OK;
265 return JIM_OK;
268 static int JimSqliteOpenCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
270 sqlite3 *db;
271 JimSqliteHandle *sh;
272 char buf[60];
273 int r;
275 if (argc != 2) {
276 Jim_WrongNumArgs(interp, 1, argv, "dbname");
277 return JIM_ERR;
279 r = sqlite3_open(Jim_String(argv[1]), &db);
280 if (r != SQLITE_OK) {
281 Jim_SetResultString(interp, sqlite3_errmsg(db), -1);
282 sqlite3_close(db);
283 return JIM_ERR;
285 /* Create the file command */
286 sh = Jim_Alloc(sizeof(*sh));
287 sh->db = db;
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);
291 return JIM_OK;
294 int Jim_sqlite3Init(Jim_Interp *interp)
296 if (Jim_PackageProvide(interp, "sqlite3", "1.0", JIM_ERRMSG))
297 return JIM_ERR;
299 Jim_CreateCommand(interp, "sqlite3.open", JimSqliteOpenCommand, NULL, NULL);
300 return JIM_OK;