aboutsummaryrefslogtreecommitdiff
path: root/jim-sqlite.c
diff options
context:
space:
mode:
authorantirez <antirez>2005-04-02 21:35:33 +0000
committerantirez <antirez>2005-04-02 21:35:33 +0000
commitcff59fd28863ff91079a710b34de19ab4cad4206 (patch)
tree822b67e9c7e124115d49a75b2c25112f66b61d6c /jim-sqlite.c
parentbd9393e8e256e76f5976df479ba50cd00e0f777f (diff)
downloadjimtcl-cff59fd28863ff91079a710b34de19ab4cad4206.zip
jimtcl-cff59fd28863ff91079a710b34de19ab4cad4206.tar.gz
jimtcl-cff59fd28863ff91079a710b34de19ab4cad4206.tar.bz2
Committed the sqlite extension and documentation.
Diffstat (limited to 'jim-sqlite.c')
-rw-r--r--jim-sqlite.c283
1 files changed, 283 insertions, 0 deletions
diff --git a/jim-sqlite.c b/jim-sqlite.c
new file mode 100644
index 0000000..77586ef
--- /dev/null
+++ b/jim-sqlite.c
@@ -0,0 +1,283 @@
+/* Jim - Sqlite bindings
+ * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
+ *
+ * $Id: jim-sqlite.c,v 1.1 2005/04/02 21:35:33 antirez Exp $
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * A copy of the license is also included in the source distribution
+ * of Jim, as a TXT file name called LICENSE.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sqlite.h>
+
+#define JIM_EXTENSION
+#include "jim.h"
+
+#define SQLITE_CMD_LEN 128
+
+typedef struct JimSqliteHandle {
+ sqlite *db;
+} JimSqliteHandle;
+
+static void JimSqliteDelProc(Jim_Interp *interp, void *privData)
+{
+ JimSqliteHandle *sh = privData;
+ JIM_NOTUSED(interp);
+
+ sqlite_close(sh->db);
+ Jim_Free(sh);
+}
+
+static char *JimSqliteQuoteString(const char *str, int len, int *newLenPtr)
+{
+ int i, newLen, c = 0;
+ const char *s;
+ char *d, *buf;
+
+ for (i = 0; i < len; i++)
+ if (str[i] == '\'')
+ c++;
+ newLen = len+c;
+ s = str;
+ d = buf = Jim_Alloc(newLen);
+ while (len--) {
+ if (*s == '\'')
+ *d++ = '\'';
+ *d++ = *s++;
+ }
+ *newLenPtr = newLen;
+ return buf;
+}
+
+static Jim_Obj *JimSqliteFormatQuery(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
+ int objc, Jim_Obj *const *objv)
+{
+ const char *fmt;
+ int fmtLen;
+ Jim_Obj *resObjPtr;
+
+ fmt = Jim_GetString(fmtObjPtr, &fmtLen);
+ resObjPtr = Jim_NewStringObj(interp, "", 0);
+ while (fmtLen) {
+ const char *p = fmt;
+ char spec[2];
+
+ while (*fmt != '%' && fmtLen) {
+ fmt++; fmtLen--;
+ }
+ Jim_AppendString(interp, resObjPtr, p, fmt-p);
+ if (fmtLen == 0)
+ break;
+ fmt++; fmtLen--; /* skip '%' */
+ if (*fmt != '%') {
+ if (objc == 0) {
+ Jim_FreeNewObj(interp, resObjPtr);
+ Jim_SetResultString(interp,
+ "not enough arguments for all format specifiers", -1);
+ return NULL;
+ } else {
+ objc--;
+ }
+ }
+ switch(*fmt) {
+ case 's':
+ {
+ const char *str;
+ char *quoted;
+ int len, newLen;
+
+ str = Jim_GetString(objv[0], &len);
+ quoted = JimSqliteQuoteString(str, len, &newLen);
+ Jim_AppendString(interp, resObjPtr, quoted, newLen);
+ Jim_Free(quoted);
+ }
+ objv++;
+ break;
+ case '%':
+ Jim_AppendString(interp, resObjPtr, "%" , 1);
+ break;
+ default:
+ spec[1] = *fmt; spec[2] = '\0';
+ Jim_FreeNewObj(interp, resObjPtr);
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings(interp, Jim_GetResult(interp),
+ "bad field specifier \"", spec, "\", ",
+ "only %%s and %%%% are validd", NULL);
+ return NULL;
+ }
+ fmt++;
+ fmtLen--;
+ }
+ return resObjPtr;
+}
+
+/* Calls to [sqlite.open] create commands that are implemented by this
+ * C command. */
+static int JimSqliteHandlerCommand(Jim_Interp *interp, int argc,
+ Jim_Obj *const *argv)
+{
+ JimSqliteHandle *sh = Jim_CmdPrivData(interp);
+ int option;
+ const char *options[] = {
+ "close", "query", "lastid", "changes", NULL
+ };
+ enum {OPT_CLOSE, OPT_QUERY, OPT_LASTID, OPT_CHANGES};
+
+ if (argc < 2) {
+ Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
+ return JIM_ERR;
+ }
+ if (Jim_GetEnum(interp, argv[1], options, &option, "Sqlite method",
+ JIM_ERRMSG) != JIM_OK)
+ return JIM_ERR;
+ /* CLOSE */
+ if (option == OPT_CLOSE) {
+ if (argc != 2) {
+ Jim_WrongNumArgs(interp, 2, argv, "");
+ return JIM_ERR;
+ }
+ Jim_DeleteCommand(interp, Jim_GetString(argv[0], NULL));
+ return JIM_OK;
+ } else if (option == OPT_QUERY) {
+ /* QUERY */
+ Jim_Obj *objPtr, *rowsListPtr;
+ sqlite_vm *vm;
+ char *errMsg;
+ const char *query, *tail, **values, **names;
+ int columns, rows;
+ char *nullstr;
+
+ if (argc >= 4 && Jim_CompareStringImmediate(interp, argv[2], "-null")) {
+ nullstr = Jim_StrDup(Jim_GetString(argv[3], NULL));
+ argv += 2;
+ argc -= 2;
+ } else {
+ nullstr = Jim_StrDup("");
+ }
+ if (argc < 3) {
+ Jim_WrongNumArgs(interp, 2, argv, "query ?args?");
+ Jim_Free(nullstr);
+ return JIM_ERR;
+ }
+ objPtr = JimSqliteFormatQuery(interp, argv[2], argc-3, argv+3);
+ if (objPtr == NULL) {
+ Jim_Free(nullstr);
+ return JIM_ERR;
+ }
+ query = Jim_GetString(objPtr, NULL);
+ Jim_IncrRefCount(objPtr);
+ /* Compile the query into VM code */
+ if (sqlite_compile(sh->db, query, &tail, &vm, &errMsg) != SQLITE_OK) {
+ Jim_DecrRefCount(interp, objPtr);
+ Jim_SetResultString(interp, errMsg, -1);
+ sqlite_freemem(errMsg);
+ Jim_Free(nullstr);
+ return JIM_ERR;
+ }
+ Jim_DecrRefCount(interp, objPtr); /* query no longer needed. */
+ /* Build a list of rows (that are lists in turn) */
+ rowsListPtr = Jim_NewListObj(interp, NULL, 0);
+ Jim_IncrRefCount(rowsListPtr);
+ rows = 0;
+ while (sqlite_step(vm, &columns, &values, &names) == SQLITE_ROW) {
+ int i;
+
+ objPtr = Jim_NewListObj(interp, NULL, 0);
+ for (i = 0; i < columns; i++) {
+ Jim_ListAppendElement(interp, objPtr,
+ Jim_NewStringObj(interp, names[i], -1));
+ Jim_ListAppendElement(interp, objPtr,
+ Jim_NewStringObj(interp,
+ values[i] != NULL ? values[i] : nullstr, -1));
+ }
+ Jim_ListAppendElement(interp, rowsListPtr, objPtr);
+ rows++;
+ }
+ /* Finalize */
+ Jim_Free(nullstr);
+ if (sqlite_finalize(vm, &errMsg) != SQLITE_OK) {
+ Jim_DecrRefCount(interp, rowsListPtr);
+ Jim_SetResultString(interp, errMsg, -1);
+ sqlite_freemem(errMsg);
+ return JIM_ERR;
+ }
+ Jim_SetResult(interp, rowsListPtr);
+ Jim_DecrRefCount(interp, rowsListPtr);
+ } else if (option == OPT_LASTID) {
+ if (argc != 2) {
+ Jim_WrongNumArgs(interp, 2, argv, "");
+ return JIM_ERR;
+ }
+ Jim_SetResult(interp, Jim_NewIntObj(interp,
+ sqlite_last_insert_rowid(sh->db)));
+ return JIM_OK;
+ } else if (option == OPT_CHANGES) {
+ if (argc != 2) {
+ Jim_WrongNumArgs(interp, 2, argv, "");
+ return JIM_ERR;
+ }
+ Jim_SetResult(interp, Jim_NewIntObj(interp,
+ sqlite_changes(sh->db)));
+ return JIM_OK;
+ }
+ return JIM_OK;
+}
+
+static int JimSqliteOpenCommand(Jim_Interp *interp, int argc,
+ Jim_Obj *const *argv)
+{
+ sqlite *db;
+ JimSqliteHandle *sh;
+ char buf[SQLITE_CMD_LEN], *errMsg;
+ Jim_Obj *objPtr;
+ long dbId;
+
+ if (argc != 2) {
+ Jim_WrongNumArgs(interp, 1, argv, "dbname");
+ return JIM_ERR;
+ }
+ db = sqlite_open(Jim_GetString(argv[1], NULL), 0, &errMsg);
+ if (db == NULL) {
+ Jim_SetResultString(interp, errMsg, -1);
+ sqlite_freemem(errMsg);
+ return JIM_ERR;
+ }
+ /* Get the next file id */
+ if (Jim_EvalGlobal(interp,
+ "if {[catch {incr sqlite.dbId}]} {set sqlite.dbId 0}") != JIM_OK)
+ return JIM_ERR;
+ objPtr = Jim_GetVariableStr(interp, "sqlite.dbId", JIM_ERRMSG);
+ if (objPtr == NULL) return JIM_ERR;
+ if (Jim_GetLong(interp, objPtr, &dbId) != JIM_OK) return JIM_ERR;
+
+ /* Create the file command */
+ sh = Jim_Alloc(sizeof(*sh));
+ sh->db = db;
+ sprintf(buf, "sqlite.handle%ld", dbId);
+ Jim_CreateCommand(interp, buf, JimSqliteHandlerCommand, sh,
+ JimSqliteDelProc);
+ Jim_SetResultString(interp, buf, -1);
+ return JIM_OK;
+}
+
+int Jim_OnLoad(Jim_Interp *interp)
+{
+ Jim_InitExtension(interp);
+ if (Jim_PackageProvide(interp, "sqlite", "1.0", JIM_ERRMSG) != JIM_OK)
+ return JIM_ERR;
+ Jim_CreateCommand(interp, "sqlite.open", JimSqliteOpenCommand, NULL, NULL);
+ return JIM_OK;
+}