aio recvfrom was not null terminating the result
[jimtcl.git] / jim-sqlite3.c
blob369e3a51505de912a8789dd664c8677f65fe5397
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"
27 typedef struct JimSqliteHandle
29 sqlite3 *db;
30 } JimSqliteHandle;
32 static void JimSqliteDelProc(Jim_Interp *interp, void *privData)
34 JimSqliteHandle *sh = privData;
36 JIM_NOTUSED(interp);
38 sqlite3_close(sh->db);
39 Jim_Free(sh);
42 static char *JimSqliteQuoteString(const char *str, int len, int *newLenPtr)
44 int i, newLen, c = 0;
45 const char *s;
46 char *d, *buf;
48 for (i = 0; i < len; i++)
49 if (str[i] == '\'')
50 c++;
51 newLen = len + c;
52 s = str;
53 d = buf = Jim_Alloc(newLen);
54 while (len--) {
55 if (*s == '\'')
56 *d++ = '\'';
57 *d++ = *s++;
59 *newLenPtr = newLen;
60 return buf;
63 static Jim_Obj *JimSqliteFormatQuery(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
64 int objc, Jim_Obj *const *objv)
66 const char *fmt;
67 int fmtLen;
68 Jim_Obj *resObjPtr;
70 fmt = Jim_GetString(fmtObjPtr, &fmtLen);
71 resObjPtr = Jim_NewStringObj(interp, "", 0);
72 while (fmtLen) {
73 const char *p = fmt;
74 char spec[2];
76 while (*fmt != '%' && fmtLen) {
77 fmt++;
78 fmtLen--;
80 Jim_AppendString(interp, resObjPtr, p, fmt - p);
81 if (fmtLen == 0)
82 break;
83 fmt++;
84 fmtLen--; /* skip '%' */
85 if (*fmt != '%') {
86 if (objc == 0) {
87 Jim_FreeNewObj(interp, resObjPtr);
88 Jim_SetResultString(interp, "not enough arguments for all format specifiers", -1);
89 return NULL;
91 else {
92 objc--;
95 switch (*fmt) {
96 case 's':
98 const char *str;
99 char *quoted;
100 int len, newLen;
102 str = Jim_GetString(objv[0], &len);
103 quoted = JimSqliteQuoteString(str, len, &newLen);
104 Jim_AppendString(interp, resObjPtr, quoted, newLen);
105 Jim_Free(quoted);
107 objv++;
108 break;
109 case '%':
110 Jim_AppendString(interp, resObjPtr, "%", 1);
111 break;
112 default:
113 spec[1] = *fmt;
114 spec[2] = '\0';
115 Jim_FreeNewObj(interp, resObjPtr);
116 Jim_SetResultFormatted(interp,
117 "bad field specifier \"%s\", only %%s and %%%% are valid", spec);
118 return NULL;
120 fmt++;
121 fmtLen--;
123 return resObjPtr;
126 /* Calls to [sqlite.open] create commands that are implemented by this
127 * C command. */
128 static int JimSqliteHandlerCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
130 JimSqliteHandle *sh = Jim_CmdPrivData(interp);
131 int option;
132 const char *options[] = {
133 "close", "query", "lastid", "changes", NULL
135 enum
136 { OPT_CLOSE, OPT_QUERY, OPT_LASTID, OPT_CHANGES };
138 if (argc < 2) {
139 Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
140 return JIM_ERR;
142 if (Jim_GetEnum(interp, argv[1], options, &option, "Sqlite method", JIM_ERRMSG) != JIM_OK)
143 return JIM_ERR;
144 /* CLOSE */
145 if (option == OPT_CLOSE) {
146 if (argc != 2) {
147 Jim_WrongNumArgs(interp, 2, argv, "");
148 return JIM_ERR;
150 Jim_DeleteCommand(interp, Jim_GetString(argv[0], NULL));
151 return JIM_OK;
153 else if (option == OPT_QUERY) {
154 /* QUERY */
155 Jim_Obj *objPtr, *rowsListPtr;
156 sqlite3_stmt *stmt;
157 const char *query, *tail;
158 int columns, rows, len;
159 char *nullstr;
161 if (argc >= 4 && Jim_CompareStringImmediate(interp, argv[2], "-null")) {
162 nullstr = Jim_StrDup(Jim_GetString(argv[3], NULL));
163 argv += 2;
164 argc -= 2;
166 else {
167 nullstr = Jim_StrDup("");
169 if (argc < 3) {
170 Jim_WrongNumArgs(interp, 2, argv, "query ?args?");
171 Jim_Free(nullstr);
172 return JIM_ERR;
174 objPtr = JimSqliteFormatQuery(interp, argv[2], argc - 3, argv + 3);
175 if (objPtr == NULL) {
176 Jim_Free(nullstr);
177 return JIM_ERR;
179 query = Jim_GetString(objPtr, &len);
180 Jim_IncrRefCount(objPtr);
181 /* Compile the query into VM code */
182 if (sqlite3_prepare(sh->db, query, len, &stmt, &tail) != SQLITE_OK) {
183 Jim_DecrRefCount(interp, objPtr);
184 Jim_SetResultString(interp, sqlite3_errmsg(sh->db), -1);
185 Jim_Free(nullstr);
186 return JIM_ERR;
188 Jim_DecrRefCount(interp, objPtr); /* query no longer needed. */
189 /* Build a list of rows (that are lists in turn) */
190 rowsListPtr = Jim_NewListObj(interp, NULL, 0);
191 Jim_IncrRefCount(rowsListPtr);
192 rows = 0;
193 columns = sqlite3_column_count(stmt);
194 while (sqlite3_step(stmt) == SQLITE_ROW) {
195 int i;
197 objPtr = Jim_NewListObj(interp, NULL, 0);
198 for (i = 0; i < columns; i++) {
199 Jim_Obj *vObj = NULL;
201 Jim_ListAppendElement(interp, objPtr,
202 Jim_NewStringObj(interp, sqlite3_column_name(stmt, i), -1));
203 switch (sqlite3_column_type(stmt, i)) {
204 case SQLITE_NULL:
205 vObj = Jim_NewStringObj(interp, nullstr, -1);
206 break;
207 case SQLITE_INTEGER:
208 vObj = Jim_NewIntObj(interp, sqlite3_column_int(stmt, i));
209 break;
210 case SQLITE_FLOAT:
211 vObj = Jim_NewDoubleObj(interp, sqlite3_column_double(stmt, i));
212 break;
213 case SQLITE_TEXT:
214 case SQLITE_BLOB:
215 vObj = Jim_NewStringObj(interp,
216 sqlite3_column_blob(stmt, i), sqlite3_column_bytes(stmt, i));
217 break;
219 Jim_ListAppendElement(interp, objPtr, vObj);
221 Jim_ListAppendElement(interp, rowsListPtr, objPtr);
222 rows++;
224 /* Finalize */
225 Jim_Free(nullstr);
226 if (sqlite3_finalize(stmt) != SQLITE_OK) {
227 Jim_DecrRefCount(interp, rowsListPtr);
228 Jim_SetResultString(interp, sqlite3_errmsg(sh->db), -1);
229 return JIM_ERR;
231 Jim_SetResult(interp, rowsListPtr);
232 Jim_DecrRefCount(interp, rowsListPtr);
234 else if (option == OPT_LASTID) {
235 if (argc != 2) {
236 Jim_WrongNumArgs(interp, 2, argv, "");
237 return JIM_ERR;
239 Jim_SetResult(interp, Jim_NewIntObj(interp, sqlite3_last_insert_rowid(sh->db)));
240 return JIM_OK;
242 else if (option == OPT_CHANGES) {
243 if (argc != 2) {
244 Jim_WrongNumArgs(interp, 2, argv, "");
245 return JIM_ERR;
247 Jim_SetResult(interp, Jim_NewIntObj(interp, sqlite3_changes(sh->db)));
248 return JIM_OK;
250 return JIM_OK;
253 static int JimSqliteOpenCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
255 sqlite3 *db;
256 JimSqliteHandle *sh;
257 char buf[128];
258 int r;
260 if (argc != 2) {
261 Jim_WrongNumArgs(interp, 1, argv, "dbname");
262 return JIM_ERR;
264 r = sqlite3_open(Jim_GetString(argv[1], NULL), &db);
265 if (r != SQLITE_OK) {
266 Jim_SetResultString(interp, sqlite3_errmsg(db), -1);
267 sqlite3_close(db);
268 return JIM_ERR;
270 /* Create the file command */
271 sh = Jim_Alloc(sizeof(*sh));
272 sh->db = db;
273 snprintf(buf, sizeof(buf), "sqlite.handle%ld", Jim_GetId(interp));
274 Jim_CreateCommand(interp, buf, JimSqliteHandlerCommand, sh, JimSqliteDelProc);
275 Jim_SetResultString(interp, buf, -1);
276 return JIM_OK;
279 int Jim_sqlite3Init(Jim_Interp *interp)
281 Jim_CreateCommand(interp, "sqlite3.open", JimSqliteOpenCommand, NULL, NULL);
282 return JIM_OK;