Make manual page rendering easier.
[jimtcl/wkoszek.git] / jim-sqlite3.c
blob1c12c55edea4a776a23810b8d6570165b3aad874
1 /* Jim - Sqlite bindings
2 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * A copy of the license is also included in the source distribution
11 * of Jim, as a TXT file name called LICENSE.
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 #include <stdio.h>
21 #include <string.h>
22 #include <sqlite3.h>
24 #define JIM_EXTENSION
25 #include "jim.h"
27 #define SQLITE_CMD_LEN 128
29 typedef struct JimSqliteHandle {
30 sqlite3 *db;
31 } JimSqliteHandle;
33 static void JimSqliteDelProc(Jim_Interp *interp, void *privData)
35 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++; fmtLen--;
79 Jim_AppendString(interp, resObjPtr, p, fmt-p);
80 if (fmtLen == 0)
81 break;
82 fmt++; fmtLen--; /* skip '%' */
83 if (*fmt != '%') {
84 if (objc == 0) {
85 Jim_FreeNewObj(interp, resObjPtr);
86 Jim_SetResultString(interp,
87 "not enough arguments for all format specifiers", -1);
88 return NULL;
89 } else {
90 objc--;
93 switch(*fmt) {
94 case 's':
96 const char *str;
97 char *quoted;
98 int len, newLen;
100 str = Jim_GetString(objv[0], &len);
101 quoted = JimSqliteQuoteString(str, len, &newLen);
102 Jim_AppendString(interp, resObjPtr, quoted, newLen);
103 Jim_Free(quoted);
105 objv++;
106 break;
107 case '%':
108 Jim_AppendString(interp, resObjPtr, "%" , 1);
109 break;
110 default:
111 spec[1] = *fmt; spec[2] = '\0';
112 Jim_FreeNewObj(interp, resObjPtr);
113 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
114 Jim_AppendStrings(interp, Jim_GetResult(interp),
115 "bad field specifier \"", spec, "\", ",
116 "only %%s and %%%% are validd", NULL);
117 return NULL;
119 fmt++;
120 fmtLen--;
122 return resObjPtr;
125 /* Calls to [sqlite.open] create commands that are implemented by this
126 * C command. */
127 static int JimSqliteHandlerCommand(Jim_Interp *interp, int argc,
128 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 {OPT_CLOSE, OPT_QUERY, OPT_LASTID, OPT_CHANGES};
137 if (argc < 2) {
138 Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
139 return JIM_ERR;
141 if (Jim_GetEnum(interp, argv[1], options, &option, "Sqlite method",
142 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;
152 } else if (option == OPT_QUERY) {
153 /* QUERY */
154 Jim_Obj *objPtr, *rowsListPtr;
155 sqlite3_stmt *stmt;
156 const char *query, *tail;
157 int columns, rows, len;
158 char *nullstr;
160 if (argc >= 4 && Jim_CompareStringImmediate(interp, argv[2], "-null")) {
161 nullstr = Jim_StrDup(Jim_GetString(argv[3], NULL));
162 argv += 2;
163 argc -= 2;
164 } else {
165 nullstr = Jim_StrDup("");
167 if (argc < 3) {
168 Jim_WrongNumArgs(interp, 2, argv, "query ?args?");
169 Jim_Free(nullstr);
170 return JIM_ERR;
172 objPtr = JimSqliteFormatQuery(interp, argv[2], argc-3, argv+3);
173 if (objPtr == NULL) {
174 Jim_Free(nullstr);
175 return JIM_ERR;
177 query = Jim_GetString(objPtr, &len);
178 Jim_IncrRefCount(objPtr);
179 /* Compile the query into VM code */
180 if (sqlite3_prepare(sh->db, query, len, &stmt, &tail) != SQLITE_OK) {
181 Jim_DecrRefCount(interp, objPtr);
182 Jim_SetResultString(interp, sqlite3_errmsg(sh->db), -1);
183 Jim_Free(nullstr);
184 return JIM_ERR;
186 Jim_DecrRefCount(interp, objPtr); /* query no longer needed. */
187 /* Build a list of rows (that are lists in turn) */
188 rowsListPtr = Jim_NewListObj(interp, NULL, 0);
189 Jim_IncrRefCount(rowsListPtr);
190 rows = 0;
191 columns = sqlite3_column_count(stmt);
192 while (sqlite3_step(stmt) == SQLITE_ROW) {
193 int i;
195 objPtr = Jim_NewListObj(interp, NULL, 0);
196 for (i = 0; i < columns; i++) {
197 Jim_Obj *vObj = NULL;
198 Jim_ListAppendElement(interp, objPtr,
199 Jim_NewStringObj(interp, sqlite3_column_name(stmt, i), -1));
200 switch (sqlite3_column_type(stmt, i)) {
201 case SQLITE_NULL:
202 vObj = Jim_NewStringObj(interp, nullstr, -1);
203 break;
204 case SQLITE_INTEGER:
205 vObj = Jim_NewIntObj(interp, sqlite3_column_int(stmt, i));
206 break;
207 case SQLITE_FLOAT:
208 vObj = Jim_NewDoubleObj(interp, sqlite3_column_double(stmt, i));
209 break;
210 case SQLITE_TEXT:
211 case SQLITE_BLOB:
212 vObj = Jim_NewStringObj(interp,
213 (const unsigned char *)sqlite3_column_blob(stmt, i),
214 sqlite3_column_bytes(stmt, i));
215 break;
217 Jim_ListAppendElement(interp, objPtr, vObj);
219 Jim_ListAppendElement(interp, rowsListPtr, objPtr);
220 rows++;
222 /* Finalize */
223 Jim_Free(nullstr);
224 if (sqlite3_finalize(stmt) != SQLITE_OK) {
225 Jim_DecrRefCount(interp, rowsListPtr);
226 Jim_SetResultString(interp, sqlite3_errmsg(sh->db), -1);
227 return JIM_ERR;
229 Jim_SetResult(interp, rowsListPtr);
230 Jim_DecrRefCount(interp, rowsListPtr);
231 } else if (option == OPT_LASTID) {
232 if (argc != 2) {
233 Jim_WrongNumArgs(interp, 2, argv, "");
234 return JIM_ERR;
236 Jim_SetResult(interp, Jim_NewIntObj(interp,
237 sqlite3_last_insert_rowid(sh->db)));
238 return JIM_OK;
239 } else if (option == OPT_CHANGES) {
240 if (argc != 2) {
241 Jim_WrongNumArgs(interp, 2, argv, "");
242 return JIM_ERR;
244 Jim_SetResult(interp, Jim_NewIntObj(interp,
245 sqlite3_changes(sh->db)));
246 return JIM_OK;
248 return JIM_OK;
251 static int JimSqliteOpenCommand(Jim_Interp *interp, int argc,
252 Jim_Obj *const *argv)
254 sqlite3 *db;
255 JimSqliteHandle *sh;
256 char buf[SQLITE_CMD_LEN];
257 Jim_Obj *objPtr;
258 long dbId;
259 int r;
261 if (argc != 2) {
262 Jim_WrongNumArgs(interp, 1, argv, "dbname");
263 return JIM_ERR;
265 r = sqlite3_open(Jim_GetString(argv[1], NULL), &db);
266 if (r != SQLITE_OK) {
267 Jim_SetResultString(interp, sqlite3_errmsg(db), -1);
268 sqlite3_close(db);
269 return JIM_ERR;
271 /* Get the next file id */
272 if (Jim_EvalGlobal(interp,
273 "if {[catch {incr sqlite.dbId}]} {set sqlite.dbId 0}") != JIM_OK)
274 return JIM_ERR;
275 objPtr = Jim_GetVariableStr(interp, "sqlite.dbId", JIM_ERRMSG);
276 if (objPtr == NULL) return JIM_ERR;
277 if (Jim_GetLong(interp, objPtr, &dbId) != JIM_OK) return JIM_ERR;
279 /* Create the file command */
280 sh = Jim_Alloc(sizeof(*sh));
281 sh->db = db;
282 sprintf(buf, "sqlite.handle%ld", dbId);
283 Jim_CreateCommand(interp, buf, JimSqliteHandlerCommand, sh,
284 JimSqliteDelProc);
285 Jim_SetResultString(interp, buf, -1);
286 return JIM_OK;
289 DLLEXPORT int
290 Jim_OnLoad(Jim_Interp *interp)
292 Jim_InitExtension(interp);
293 if (Jim_PackageProvide(interp, "sqlite3", "1.0", JIM_ERRMSG) != JIM_OK)
294 return JIM_ERR;
295 Jim_CreateCommand(interp, "sqlite3.open", JimSqliteOpenCommand, NULL, NULL);
296 return JIM_OK;