Missing parens in non-utf8 version of utf8_strlen
[jimtcl.git] / jim-sqlite3.c
blobd621de28df6262b1997ff2d194ba691c0dc69332
2 /* Jim - Sqlite bindings
3 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * A copy of the license is also included in the source distribution
12 * of Jim, as a TXT file name called LICENSE.
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
21 #include <stdio.h>
22 #include <string.h>
23 #include <sqlite3.h>
25 #include "jim.h"
26 #include "jimautoconf.h"
28 typedef struct JimSqliteHandle
30 sqlite3 *db;
31 } JimSqliteHandle;
33 static void JimSqliteDelProc(Jim_Interp *interp, void *privData)
35 JimSqliteHandle *sh = privData;
37 JIM_NOTUSED(interp);
39 sqlite3_close(sh->db);
40 Jim_Free(sh);
43 static char *JimSqliteQuoteString(const char *str, int len, int *newLenPtr)
45 int i, newLen, c = 0;
46 const char *s;
47 char *d, *buf;
49 for (i = 0; i < len; i++)
50 if (str[i] == '\'')
51 c++;
52 newLen = len + c;
53 s = str;
54 d = buf = Jim_Alloc(newLen);
55 while (len--) {
56 if (*s == '\'')
57 *d++ = '\'';
58 *d++ = *s++;
60 *newLenPtr = newLen;
61 return buf;
64 static Jim_Obj *JimSqliteFormatQuery(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
65 int objc, Jim_Obj *const *objv)
67 const char *fmt;
68 int fmtLen;
69 Jim_Obj *resObjPtr;
71 fmt = Jim_GetString(fmtObjPtr, &fmtLen);
72 resObjPtr = Jim_NewStringObj(interp, "", 0);
73 while (fmtLen) {
74 const char *p = fmt;
75 char spec[2];
77 while (*fmt != '%' && fmtLen) {
78 fmt++;
79 fmtLen--;
81 Jim_AppendString(interp, resObjPtr, p, fmt - p);
82 if (fmtLen == 0)
83 break;
84 fmt++;
85 fmtLen--; /* skip '%' */
86 if (*fmt != '%') {
87 if (objc == 0) {
88 Jim_FreeNewObj(interp, resObjPtr);
89 Jim_SetResultString(interp, "not enough arguments for all format specifiers", -1);
90 return NULL;
92 else {
93 objc--;
96 switch (*fmt) {
97 case 's':
99 const char *str;
100 char *quoted;
101 int len, newLen;
103 str = Jim_GetString(objv[0], &len);
104 quoted = JimSqliteQuoteString(str, len, &newLen);
105 Jim_AppendString(interp, resObjPtr, quoted, newLen);
106 Jim_Free(quoted);
108 objv++;
109 break;
110 case '%':
111 Jim_AppendString(interp, resObjPtr, "%", 1);
112 break;
113 default:
114 spec[1] = *fmt;
115 spec[2] = '\0';
116 Jim_FreeNewObj(interp, resObjPtr);
117 Jim_SetResultFormatted(interp,
118 "bad field specifier \"%s\", only %%s and %%%% are valid", spec);
119 return NULL;
121 fmt++;
122 fmtLen--;
124 return resObjPtr;
127 /* Calls to [sqlite.open] create commands that are implemented by this
128 * C command. */
129 static int JimSqliteHandlerCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
131 JimSqliteHandle *sh = Jim_CmdPrivData(interp);
132 int option;
133 static const char * const options[] = {
134 "close", "query", "lastid", "changes", NULL
136 enum
137 { OPT_CLOSE, OPT_QUERY, OPT_LASTID, OPT_CHANGES };
139 if (argc < 2) {
140 Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
141 return JIM_ERR;
143 if (Jim_GetEnum(interp, argv[1], options, &option, "Sqlite method", JIM_ERRMSG) != JIM_OK)
144 return JIM_ERR;
145 /* CLOSE */
146 if (option == OPT_CLOSE) {
147 if (argc != 2) {
148 Jim_WrongNumArgs(interp, 2, argv, "");
149 return JIM_ERR;
151 Jim_DeleteCommand(interp, Jim_String(argv[0]));
152 return JIM_OK;
154 else if (option == OPT_QUERY) {
155 /* QUERY */
156 Jim_Obj *objPtr, *rowsListPtr;
157 sqlite3_stmt *stmt;
158 const char *query, *tail;
159 int columns, rows, len;
160 char *nullstr;
162 if (argc >= 4 && Jim_CompareStringImmediate(interp, argv[2], "-null")) {
163 nullstr = Jim_StrDup(Jim_String(argv[3]));
164 argv += 2;
165 argc -= 2;
167 else {
168 nullstr = Jim_StrDup("");
170 if (argc < 3) {
171 Jim_WrongNumArgs(interp, 2, argv, "query ?args?");
172 Jim_Free(nullstr);
173 return JIM_ERR;
175 objPtr = JimSqliteFormatQuery(interp, argv[2], argc - 3, argv + 3);
176 if (objPtr == NULL) {
177 Jim_Free(nullstr);
178 return JIM_ERR;
180 query = Jim_GetString(objPtr, &len);
181 Jim_IncrRefCount(objPtr);
182 /* Compile the query into VM code */
183 if (sqlite3_prepare(sh->db, query, len, &stmt, &tail) != SQLITE_OK) {
184 Jim_DecrRefCount(interp, objPtr);
185 Jim_SetResultString(interp, sqlite3_errmsg(sh->db), -1);
186 Jim_Free(nullstr);
187 return JIM_ERR;
189 Jim_DecrRefCount(interp, objPtr); /* query no longer needed. */
190 /* Build a list of rows (that are lists in turn) */
191 rowsListPtr = Jim_NewListObj(interp, NULL, 0);
192 Jim_IncrRefCount(rowsListPtr);
193 rows = 0;
194 columns = sqlite3_column_count(stmt);
195 while (sqlite3_step(stmt) == SQLITE_ROW) {
196 int i;
198 objPtr = Jim_NewListObj(interp, NULL, 0);
199 for (i = 0; i < columns; i++) {
200 Jim_Obj *vObj = NULL;
202 Jim_ListAppendElement(interp, objPtr,
203 Jim_NewStringObj(interp, sqlite3_column_name(stmt, i), -1));
204 switch (sqlite3_column_type(stmt, i)) {
205 case SQLITE_NULL:
206 vObj = Jim_NewStringObj(interp, nullstr, -1);
207 break;
208 case SQLITE_INTEGER:
209 vObj = Jim_NewIntObj(interp, sqlite3_column_int(stmt, i));
210 break;
211 case SQLITE_FLOAT:
212 vObj = Jim_NewDoubleObj(interp, sqlite3_column_double(stmt, i));
213 break;
214 case SQLITE_TEXT:
215 case SQLITE_BLOB:
216 vObj = Jim_NewStringObj(interp,
217 sqlite3_column_blob(stmt, i), sqlite3_column_bytes(stmt, i));
218 break;
220 Jim_ListAppendElement(interp, objPtr, vObj);
222 Jim_ListAppendElement(interp, rowsListPtr, objPtr);
223 rows++;
225 /* Finalize */
226 Jim_Free(nullstr);
227 if (sqlite3_finalize(stmt) != SQLITE_OK) {
228 Jim_DecrRefCount(interp, rowsListPtr);
229 Jim_SetResultString(interp, sqlite3_errmsg(sh->db), -1);
230 return JIM_ERR;
232 Jim_SetResult(interp, rowsListPtr);
233 Jim_DecrRefCount(interp, rowsListPtr);
235 else if (option == OPT_LASTID) {
236 if (argc != 2) {
237 Jim_WrongNumArgs(interp, 2, argv, "");
238 return JIM_ERR;
240 Jim_SetResult(interp, Jim_NewIntObj(interp, sqlite3_last_insert_rowid(sh->db)));
241 return JIM_OK;
243 else if (option == OPT_CHANGES) {
244 if (argc != 2) {
245 Jim_WrongNumArgs(interp, 2, argv, "");
246 return JIM_ERR;
248 Jim_SetResult(interp, Jim_NewIntObj(interp, sqlite3_changes(sh->db)));
249 return JIM_OK;
251 return JIM_OK;
254 static int JimSqliteOpenCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
256 sqlite3 *db;
257 JimSqliteHandle *sh;
258 char buf[60];
259 int r;
261 if (argc != 2) {
262 Jim_WrongNumArgs(interp, 1, argv, "dbname");
263 return JIM_ERR;
265 r = sqlite3_open(Jim_String(argv[1]), &db);
266 if (r != SQLITE_OK) {
267 Jim_SetResultString(interp, sqlite3_errmsg(db), -1);
268 sqlite3_close(db);
269 return JIM_ERR;
271 /* Create the file command */
272 sh = Jim_Alloc(sizeof(*sh));
273 sh->db = db;
274 snprintf(buf, sizeof(buf), "sqlite.handle%ld", Jim_GetId(interp));
275 Jim_CreateCommand(interp, buf, JimSqliteHandlerCommand, sh, JimSqliteDelProc);
276 Jim_SetResultString(interp, buf, -1);
277 return JIM_OK;
280 int Jim_sqlite3Init(Jim_Interp *interp)
282 if (Jim_PackageProvide(interp, "sqlite3", "1.0", JIM_ERRMSG))
283 return JIM_ERR;
285 Jim_CreateCommand(interp, "sqlite3.open", JimSqliteOpenCommand, NULL, NULL);
286 return JIM_OK;