Add a general purpose hashtable pattern matcher
[jimtcl.git] / jim-sqlite.c
blob019d671240cc435f94869f7661e4c4222eadc916
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 <sqlite.h>
39 #include "jim.h"
40 #include "jimautoconf.h"
42 typedef struct JimSqliteHandle
44 sqlite *db;
45 } JimSqliteHandle;
47 static void JimSqliteDelProc(Jim_Interp *interp, void *privData)
49 JimSqliteHandle *sh = privData;
51 JIM_NOTUSED(interp);
53 sqlite_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 sqlite_vm *vm;
172 char *errMsg;
173 const char *query, *tail, **values, **names;
174 int columns, rows;
175 char *nullstr;
177 if (argc >= 4 && Jim_CompareStringImmediate(interp, argv[2], "-null")) {
178 nullstr = Jim_StrDup(Jim_String(argv[3]));
179 argv += 2;
180 argc -= 2;
182 else {
183 nullstr = Jim_StrDup("");
185 if (argc < 3) {
186 Jim_WrongNumArgs(interp, 2, argv, "query ?args?");
187 Jim_Free(nullstr);
188 return JIM_ERR;
190 objPtr = JimSqliteFormatQuery(interp, argv[2], argc - 3, argv + 3);
191 if (objPtr == NULL) {
192 Jim_Free(nullstr);
193 return JIM_ERR;
195 query = Jim_String(objPtr);
196 Jim_IncrRefCount(objPtr);
197 /* Compile the query into VM code */
198 if (sqlite_compile(sh->db, query, &tail, &vm, &errMsg) != SQLITE_OK) {
199 Jim_DecrRefCount(interp, objPtr);
200 Jim_SetResultString(interp, errMsg, -1);
201 sqlite_freemem(errMsg);
202 Jim_Free(nullstr);
203 return JIM_ERR;
205 Jim_DecrRefCount(interp, objPtr); /* query no longer needed. */
206 /* Build a list of rows (that are lists in turn) */
207 rowsListPtr = Jim_NewListObj(interp, NULL, 0);
208 Jim_IncrRefCount(rowsListPtr);
209 rows = 0;
210 while (sqlite_step(vm, &columns, &values, &names) == SQLITE_ROW) {
211 int i;
213 objPtr = Jim_NewListObj(interp, NULL, 0);
214 for (i = 0; i < columns; i++) {
215 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, names[i], -1));
216 Jim_ListAppendElement(interp, objPtr,
217 Jim_NewStringObj(interp, values[i] != NULL ? values[i] : nullstr, -1));
219 Jim_ListAppendElement(interp, rowsListPtr, objPtr);
220 rows++;
222 /* Finalize */
223 Jim_Free(nullstr);
224 if (sqlite_finalize(vm, &errMsg) != SQLITE_OK) {
225 Jim_DecrRefCount(interp, rowsListPtr);
226 Jim_SetResultString(interp, errMsg, -1);
227 sqlite_freemem(errMsg);
228 return JIM_ERR;
230 Jim_SetResult(interp, rowsListPtr);
231 Jim_DecrRefCount(interp, rowsListPtr);
233 else if (option == OPT_LASTID) {
234 if (argc != 2) {
235 Jim_WrongNumArgs(interp, 2, argv, "");
236 return JIM_ERR;
238 Jim_SetResult(interp, Jim_NewIntObj(interp, sqlite_last_insert_rowid(sh->db)));
239 return JIM_OK;
241 else if (option == OPT_CHANGES) {
242 if (argc != 2) {
243 Jim_WrongNumArgs(interp, 2, argv, "");
244 return JIM_ERR;
246 Jim_SetResult(interp, Jim_NewIntObj(interp, sqlite_changes(sh->db)));
247 return JIM_OK;
249 return JIM_OK;
252 static int JimSqliteOpenCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
254 sqlite *db;
255 JimSqliteHandle *sh;
256 char buf[60], *errMsg;
258 if (argc != 2) {
259 Jim_WrongNumArgs(interp, 1, argv, "dbname");
260 return JIM_ERR;
262 db = sqlite_open(Jim_String(argv[1]), 0, &errMsg);
263 if (db == NULL) {
264 Jim_SetResultString(interp, errMsg, -1);
265 sqlite_freemem(errMsg);
266 return JIM_ERR;
268 /* Create the file command */
269 sh = Jim_Alloc(sizeof(*sh));
270 sh->db = db;
271 snprintf(buf, sizeof(buf), "sqlite.handle%ld", Jim_GetId(interp));
272 Jim_CreateCommand(interp, buf, JimSqliteHandlerCommand, sh, JimSqliteDelProc);
273 Jim_SetResultString(interp, buf, -1);
274 return JIM_OK;
277 int Jim_sqliteInit(Jim_Interp *interp)
279 if (Jim_PackageProvide(interp, "sqlite", "1.0", JIM_ERRMSG))
280 return JIM_ERR;
282 Jim_CreateCommand(interp, "sqlite.open", JimSqliteOpenCommand, NULL, NULL);
283 return JIM_OK;