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.
27 #define SQLITE_CMD_LEN 128
29 typedef struct JimSqliteHandle
{
33 static void JimSqliteDelProc(Jim_Interp
*interp
, void *privData
)
35 JimSqliteHandle
*sh
= privData
;
38 sqlite3_close(sh
->db
);
42 static char *JimSqliteQuoteString(const char *str
, int len
, int *newLenPtr
)
48 for (i
= 0; i
< len
; i
++)
53 d
= buf
= Jim_Alloc(newLen
);
63 static Jim_Obj
*JimSqliteFormatQuery(Jim_Interp
*interp
, Jim_Obj
*fmtObjPtr
,
64 int objc
, Jim_Obj
*const *objv
)
70 fmt
= Jim_GetString(fmtObjPtr
, &fmtLen
);
71 resObjPtr
= Jim_NewStringObj(interp
, "", 0);
76 while (*fmt
!= '%' && fmtLen
) {
79 Jim_AppendString(interp
, resObjPtr
, p
, fmt
-p
);
82 fmt
++; fmtLen
--; /* skip '%' */
85 Jim_FreeNewObj(interp
, resObjPtr
);
86 Jim_SetResultString(interp
,
87 "not enough arguments for all format specifiers", -1);
100 str
= Jim_GetString(objv
[0], &len
);
101 quoted
= JimSqliteQuoteString(str
, len
, &newLen
);
102 Jim_AppendString(interp
, resObjPtr
, quoted
, newLen
);
108 Jim_AppendString(interp
, resObjPtr
, "%" , 1);
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
);
125 /* Calls to [sqlite.open] create commands that are implemented by this
127 static int JimSqliteHandlerCommand(Jim_Interp
*interp
, int argc
,
128 Jim_Obj
*const *argv
)
130 JimSqliteHandle
*sh
= Jim_CmdPrivData(interp
);
132 const char *options
[] = {
133 "close", "query", "lastid", "changes", NULL
135 enum {OPT_CLOSE
, OPT_QUERY
, OPT_LASTID
, OPT_CHANGES
};
138 Jim_WrongNumArgs(interp
, 1, argv
, "method ?args ...?");
141 if (Jim_GetEnum(interp
, argv
[1], options
, &option
, "Sqlite method",
142 JIM_ERRMSG
) != JIM_OK
)
145 if (option
== OPT_CLOSE
) {
147 Jim_WrongNumArgs(interp
, 2, argv
, "");
150 Jim_DeleteCommand(interp
, Jim_GetString(argv
[0], NULL
));
152 } else if (option
== OPT_QUERY
) {
154 Jim_Obj
*objPtr
, *rowsListPtr
;
156 const char *query
, *tail
;
157 int columns
, rows
, len
;
160 if (argc
>= 4 && Jim_CompareStringImmediate(interp
, argv
[2], "-null")) {
161 nullstr
= Jim_StrDup(Jim_GetString(argv
[3], NULL
));
165 nullstr
= Jim_StrDup("");
168 Jim_WrongNumArgs(interp
, 2, argv
, "query ?args?");
172 objPtr
= JimSqliteFormatQuery(interp
, argv
[2], argc
-3, argv
+3);
173 if (objPtr
== NULL
) {
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);
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
);
191 columns
= sqlite3_column_count(stmt
);
192 while (sqlite3_step(stmt
) == SQLITE_ROW
) {
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
)) {
202 vObj
= Jim_NewStringObj(interp
, nullstr
, -1);
205 vObj
= Jim_NewIntObj(interp
, sqlite3_column_int(stmt
, i
));
208 vObj
= Jim_NewDoubleObj(interp
, sqlite3_column_double(stmt
, i
));
212 vObj
= Jim_NewStringObj(interp
,
213 (const unsigned char *)sqlite3_column_blob(stmt
, i
),
214 sqlite3_column_bytes(stmt
, i
));
217 Jim_ListAppendElement(interp
, objPtr
, vObj
);
219 Jim_ListAppendElement(interp
, rowsListPtr
, objPtr
);
224 if (sqlite3_finalize(stmt
) != SQLITE_OK
) {
225 Jim_DecrRefCount(interp
, rowsListPtr
);
226 Jim_SetResultString(interp
, sqlite3_errmsg(sh
->db
), -1);
229 Jim_SetResult(interp
, rowsListPtr
);
230 Jim_DecrRefCount(interp
, rowsListPtr
);
231 } else if (option
== OPT_LASTID
) {
233 Jim_WrongNumArgs(interp
, 2, argv
, "");
236 Jim_SetResult(interp
, Jim_NewIntObj(interp
,
237 sqlite3_last_insert_rowid(sh
->db
)));
239 } else if (option
== OPT_CHANGES
) {
241 Jim_WrongNumArgs(interp
, 2, argv
, "");
244 Jim_SetResult(interp
, Jim_NewIntObj(interp
,
245 sqlite3_changes(sh
->db
)));
251 static int JimSqliteOpenCommand(Jim_Interp
*interp
, int argc
,
252 Jim_Obj
*const *argv
)
256 char buf
[SQLITE_CMD_LEN
];
262 Jim_WrongNumArgs(interp
, 1, argv
, "dbname");
265 r
= sqlite3_open(Jim_GetString(argv
[1], NULL
), &db
);
266 if (r
!= SQLITE_OK
) {
267 Jim_SetResultString(interp
, sqlite3_errmsg(db
), -1);
271 /* Get the next file id */
272 if (Jim_EvalGlobal(interp
,
273 "if {[catch {incr sqlite.dbId}]} {set sqlite.dbId 0}") != JIM_OK
)
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
));
282 sprintf(buf
, "sqlite.handle%ld", dbId
);
283 Jim_CreateCommand(interp
, buf
, JimSqliteHandlerCommand
, sh
,
285 Jim_SetResultString(interp
, buf
, -1);
290 Jim_OnLoad(Jim_Interp
*interp
)
292 Jim_InitExtension(interp
);
293 if (Jim_PackageProvide(interp
, "sqlite3", "1.0", JIM_ERRMSG
) != JIM_OK
)
295 Jim_CreateCommand(interp
, "sqlite3.open", JimSqliteOpenCommand
, NULL
, NULL
);