--- /dev/null
+/*
+ * $Eid: mysqltcl.c,v 1.2 2002/02/15 18:52:08 artur Exp $
+ *
+ * MYSQL interface to Tcl
+ *
+ * Hakan Soderstrom, hs@soderstrom.se
+ *
+ */
+
+/*
+ * Copyright (c) 1994, 1995 Hakan Soderstrom and Tom Poindexter
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice and this permission notice
+ * appear in all copies of the software and related documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL HAKAN SODERSTROM OR SODERSTROM PROGRAMVARUVERKSTAD
+ * AB BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL
+ * DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY
+ * OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN
+ * CONNECTON WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifdef _WINDOWS
+ #include <windows.h>
+ #define PACKAGE "mysqltcl"
+ #define VERSION "2.14"
+#endif
+
+#include <tcl.h>
+#include <mysql.h>
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+
+/* A few macros for making the code more readable */
+
+#define DECLARE_CMD(func) \
+static int func _ANSI_ARGS_((ClientData clientData, \
+ Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]))
+
+#define DEFINE_CMD(func) \
+static int func(clientData, interp, objc, objv) \
+ ClientData clientData; \
+ Tcl_Interp *interp; \
+ int objc; \
+ Tcl_Obj *CONST objv[];
+
+#define ADD_CMD(cmdName, cmdProc) \
+Tcl_CreateObjCommand(interp, #cmdName, cmdProc, NULL, Mysqltcl_Kill)
+
+/* Compile-time constants */
+
+#define MYSQL_SMALL_SIZE TCL_RESULT_SIZE /* Smaller buffer size. */
+#define MYSQL_NAME_LEN 80 /* Max. host, database name length. */
+
+typedef struct MysqlTclHandle {
+ MYSQL * connection ; /* Connection handle, if connected; -1 otherwise. */
+ char host[MYSQL_NAME_LEN] ; /* Host name, if connected. */
+ char database[MYSQL_NAME_LEN] ; /* Db name, if selected; NULL otherwise. */
+ MYSQL_RES* result ; /* Stored result, if any; NULL otherwise. */
+ int res_count ; /* Count of unfetched rows in result. */
+ int col_count ; /* Column count in result, if any. */
+ int number;
+ int isquery;
+} MysqlTclHandle;
+
+/* one global Hash for mysql handles */
+static Tcl_HashTable handleTable;
+
+static char *MysqlHandlePrefix = "mysql";
+/* Prefix string used to identify handles.
+ * The following must be strlen(MysqlHandlePrefix).
+ */
+#define MYSQL_HPREFIX_LEN 5
+
+/* Array for status info, and its elements. */
+#define MYSQL_STATUS_ARR "mysqlstatus"
+
+#define MYSQL_STATUS_CODE "code"
+#define MYSQL_STATUS_CMD "command"
+#define MYSQL_STATUS_MSG "message"
+#define MYSQL_STATUS_NULLV "nullvalue"
+
+/* C variable corresponding to mysqlstatus(nullvalue) */
+static char *MysqlNullvalue = NULL ;
+#define MYSQL_NULLV_INIT ""
+
+/* Check Level for mysql_prologue */
+#define CL_PLAIN 0
+#define CL_CONN 1
+#define CL_DB 2
+#define CL_RES 3
+
+/* Prototypes for all functions. */
+
+DECLARE_CMD(Mysqltcl_Connect);
+DECLARE_CMD(Mysqltcl_Use);
+DECLARE_CMD(Mysqltcl_Escape);
+DECLARE_CMD(Mysqltcl_Sel);
+DECLARE_CMD(Mysqltcl_Next);
+DECLARE_CMD(Mysqltcl_Seek);
+DECLARE_CMD(Mysqltcl_Map);
+DECLARE_CMD(Mysqltcl_Exec);
+DECLARE_CMD(Mysqltcl_Close);
+DECLARE_CMD(Mysqltcl_Info);
+DECLARE_CMD(Mysqltcl_Result);
+DECLARE_CMD(Mysqltcl_Col);
+DECLARE_CMD(Mysqltcl_State);
+DECLARE_CMD(Mysqltcl_InsertId);
+DECLARE_CMD(Mysqltcl_Query);
+
+static int MysqlHandleSet _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr));
+static void MysqlHandleFree _ANSI_ARGS_((Tcl_Obj *objPtr));
+
+
+/* handle object type
+ * This section defince funtions for Handling new Tcl_Obj type */
+
+Tcl_ObjType mysqlHandleType = {
+ "mysqlhandle",
+ MysqlHandleFree,
+ (Tcl_DupInternalRepProc *) NULL,
+ NULL,
+ MysqlHandleSet
+};
+
+static int
+MysqlHandleSet(interp, objPtr)
+ Tcl_Interp *interp; /* Used for error reporting if not NULL. */
+ register Tcl_Obj *objPtr; /* The object to convert. */
+{
+ Tcl_ObjType *oldTypePtr = objPtr->typePtr;
+ char *string;
+ MysqlTclHandle *handle;
+ Tcl_HashEntry *entryPtr;
+
+ string=Tcl_GetStringFromObj(objPtr, NULL);
+ entryPtr = Tcl_FindHashEntry(&handleTable,string);
+ if (entryPtr == NULL) {
+ handle=0;
+ } else {
+ handle=(MysqlTclHandle *)Tcl_GetHashValue(entryPtr);
+ }
+ if (!handle) {
+ if (interp != NULL)
+ return TCL_ERROR;
+ }
+ if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
+ oldTypePtr->freeIntRepProc(objPtr);
+ }
+
+ objPtr->internalRep.otherValuePtr = (MysqlTclHandle *) handle;
+ objPtr->typePtr = &mysqlHandleType;
+ Tcl_Preserve((char *)handle);
+ // printf("p set obj handle %i %x\n",handle->isquery,handle);
+
+ return TCL_OK;
+}
+static void
+MysqlHandleFree(Tcl_Obj *obj)
+{
+ MysqlTclHandle *handle = (MysqlTclHandle *)obj->internalRep.otherValuePtr;
+ Tcl_Release((char *)handle);
+ // printf("r free obj handle %i %x\n",handle->isquery,handle);
+}
+
+static int
+GetHandleFromObj(interp, objPtr, handlePtr)
+ Tcl_Interp *interp;
+ register Tcl_Obj *objPtr;
+ register MysqlTclHandle **handlePtr;
+{
+ if (Tcl_ConvertToType (interp, objPtr, &mysqlHandleType) != TCL_OK)
+ return TCL_ERROR;
+ *handlePtr = (MysqlTclHandle *)objPtr->internalRep.otherValuePtr;
+ return TCL_OK;
+}
+
+static Tcl_Obj *
+Tcl_NewHandleObj(handle)
+ register MysqlTclHandle* handle;
+{
+ register Tcl_Obj *objPtr;
+ char buffer[MYSQL_HPREFIX_LEN+TCL_DOUBLE_SPACE+1];
+ register int len;
+ Tcl_HashEntry *entryPtr;
+ int newflag;
+
+ objPtr=Tcl_NewObj();
+ /* the string for "query" can not be longer as MysqlHandlePrefix see buf variable */
+ len=sprintf(buffer, "%s%d", (handle->isquery) ? "query" : MysqlHandlePrefix,handle->number);
+ objPtr->bytes = Tcl_Alloc((unsigned) len + 1);
+ strcpy(objPtr->bytes, buffer);
+ objPtr->length = len;
+
+ entryPtr=Tcl_CreateHashEntry(&handleTable,buffer,&newflag);
+ Tcl_SetHashValue(entryPtr,handle);
+
+ objPtr->internalRep.otherValuePtr = handle;
+ objPtr->typePtr = &mysqlHandleType;
+
+ Tcl_Preserve((char *)handle);
+ // printf("p new obj handle %i %x\n",handle->isquery,handle);
+
+ return objPtr;
+}
+
+
+/* CONFLICT HANDLING
+ *
+ * Every command begins by calling 'mysql_prologue'.
+ * This function resets mysqlstatus(code) to zero; the other array elements
+ * retain their previous values.
+ * The function also saves objc/objv in global variables.
+ * After this the command processing proper begins.
+ *
+ * If there is a conflict, the message is taken from one of the following
+ * sources,
+ * -- this code (mysql_prim_confl),
+ * -- the database server (mysql_server_confl),
+ * A complete message is put together from the above plus the name of the
+ * command where the conflict was detected.
+ * The complete message is returned as the Tcl result and is also stored in
+ * mysqlstatus(message).
+ * mysqlstatus(code) is set to "-1" for a primitive conflict or to mysql_errno
+ * for a server conflict
+ * In addition, the whole command where the conflict was detected is put
+ * together from the saved objc/objv and is copied into mysqlstatus(command).
+ */
+
+/*
+ *-----------------------------------------------------------
+ * set_statusArr
+ * Help procedure to set Tcl global array with mysqltcl internal
+ * informations
+ */
+
+static void
+set_statusArr(Tcl_Interp *interp,char *elem_name,Tcl_Obj *tobj)
+{
+ Tcl_SetVar2Ex (interp,MYSQL_STATUS_ARR,elem_name,tobj,TCL_GLOBAL_ONLY);
+}
+
+/*
+ *----------------------------------------------------------------------
+ * clear_msg
+ *
+ * Clears all error and message elements in the global array variable.
+ *
+ */
+
+static void
+clear_msg(Tcl_Interp *interp)
+{
+ set_statusArr(interp,MYSQL_STATUS_CODE,Tcl_NewIntObj(0));
+ set_statusArr(interp,MYSQL_STATUS_CMD,Tcl_NewObj());
+ set_statusArr(interp,MYSQL_STATUS_MSG,Tcl_NewObj());
+}
+
+/*
+ *----------------------------------------------------------------------
+ * mysql_reassemble
+ * Reassembles the current command from the saved objv; copies it into
+ * mysqlstatus(command).
+ */
+
+static void
+mysql_reassemble (Tcl_Interp *interp,int objc,Tcl_Obj *CONST objv[])
+{
+ set_statusArr(interp,MYSQL_STATUS_CMD,Tcl_NewListObj(objc, objv));
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ * mysql_prim_confl
+ * Conflict handling after a primitive conflict.
+ *
+ */
+
+static int
+mysql_prim_confl (Tcl_Interp *interp,int objc,Tcl_Obj *CONST objv[],char *msg)
+{
+ set_statusArr(interp,MYSQL_STATUS_CODE,Tcl_NewIntObj(-1));
+
+ Tcl_ResetResult (interp) ;
+ Tcl_AppendStringsToObj (Tcl_GetObjResult(interp),
+ Tcl_GetString(objv[0]), ": ", msg, (char*)NULL);
+
+ set_statusArr(interp,MYSQL_STATUS_MSG,Tcl_GetObjResult(interp));
+
+ mysql_reassemble (interp,objc,objv) ;
+ return TCL_ERROR ;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ * mysql_server_confl
+ * Conflict handling after an mySQL conflict.
+ *
+ */
+
+static int
+mysql_server_confl (Tcl_Interp *interp,int objc,Tcl_Obj *CONST objv[],MYSQL * connection)
+{
+ char* mysql_errorMsg;
+
+ mysql_errorMsg = mysql_error(connection);
+
+ set_statusArr(interp,MYSQL_STATUS_CODE,Tcl_NewIntObj(mysql_errno(connection)));
+
+ Tcl_ResetResult (interp) ;
+ Tcl_AppendStringsToObj (Tcl_GetObjResult(interp),
+ Tcl_GetString(objv[0]), "/db server: ",
+ (mysql_errorMsg == NULL) ? "" : mysql_errorMsg,
+ (char*)NULL) ;
+
+ set_statusArr(interp,MYSQL_STATUS_MSG,Tcl_GetObjResult(interp));
+
+ mysql_reassemble (interp,objc,objv) ;
+ return TCL_ERROR ;
+}
+
+static MysqlTclHandle *
+get_handle (Tcl_Interp *interp,int objc,Tcl_Obj *CONST objv[],int check_level)
+{
+ MysqlTclHandle *handle;
+ if (GetHandleFromObj(interp, objv[1], &handle) != TCL_OK) {
+ mysql_prim_confl (interp,objc,objv,"not mysqltcl handle") ;
+ return NULL;
+ }
+ if (check_level==CL_PLAIN) return handle;
+ if (handle->connection == 0) {
+ mysql_prim_confl (interp,objc,objv,"handle already closed (dangling pointer)") ;
+ return NULL;
+ }
+ if (check_level==CL_CONN) return handle;
+ if (check_level!=CL_RES) {
+ if (handle->database[0] == '\0') {
+ mysql_prim_confl (interp,objc,objv,"no current database") ;
+ return NULL;
+ }
+ if (check_level==CL_DB) return handle;
+ }
+ if (handle->result == NULL) {
+ mysql_prim_confl (interp,objc,objv,"no result pending") ;
+ return NULL;
+ }
+ return handle;
+}
+
+/*----------------------------------------------------------------------
+ * mysql_QueryTclObj
+ * getRowCellAsObject
+ * This to method control how tcl data is transfered to mysql and
+ * how data is imported into tcl from mysql
+ */
+static int mysql_QueryTclObj(MysqlTclHandle *handle,Tcl_Obj *obj)
+{
+ char *query;
+ int result,queryLen;
+ Tcl_DString queryDS;
+
+ query=Tcl_GetStringFromObj(obj, &queryLen);
+ Tcl_UtfToExternalDString(NULL, query, -1, &queryDS);
+ queryLen = Tcl_DStringLength(&queryDS);
+ result = mysql_real_query(handle->connection,Tcl_DStringValue(&queryDS),queryLen);
+ Tcl_DStringFree(&queryDS);
+ return result;
+}
+static Tcl_Obj *getRowCellAsObject(MYSQL_ROW row,int length)
+{
+ Tcl_Obj *obj;
+ if (*row) {
+ obj = Tcl_NewByteArrayObj(*row,length);
+ } else {
+ obj = Tcl_NewStringObj(MysqlNullvalue,-1);
+ }
+ return obj;
+}
+
+static MysqlTclHandle *createMysqlHandle()
+{
+ static int HandleNum=0;
+ MysqlTclHandle *handle;
+ handle=(MysqlTclHandle *)Tcl_Alloc(sizeof(MysqlTclHandle));
+ if (handle == 0) {
+ panic("no memory for handle");
+ return handle;
+ }
+ handle->connection = (MYSQL *)0 ;
+ handle->host[0] = '\0' ;
+ handle->database[0] = '\0' ;
+ handle->result = NULL ;
+ handle->res_count = 0 ;
+ handle->col_count = 0 ;
+ handle->isquery = 0;
+
+ //printf("p create handle %i %x\n",handle->isquery,handle);
+
+ /* not MT-safe, static */
+ handle->number=HandleNum++;
+ return handle;
+}
+
+static MysqlTclHandle *createQueryHandleFrom(MysqlTclHandle *handle)
+{
+ int number;
+ MysqlTclHandle *qhandle;
+ qhandle = createMysqlHandle();
+ number = qhandle->number;
+ if (!qhandle) return qhandle;
+ memcpy(qhandle,handle,sizeof(MysqlTclHandle));
+ qhandle->isquery=1;
+ qhandle->number=number;
+ return qhandle;
+}
+static void closeHandle(MysqlTclHandle *handle)
+{
+ handle->connection = (MYSQL *)0;
+ // printf("r close handle %i %x\n",handle->isquery,handle);
+ Tcl_EventuallyFree((char *)handle,TCL_DYNAMIC);
+}
+
+/*
+ *----------------------------------------------------------------------
+ * mysql_prologue
+ *
+ * Does most of standard command prologue; required for all commands
+ * having conflict handling.
+ * 'req_min_args' must be the minimum number of arguments for the command,
+ * including the command word.
+ * 'req_max_args' must be the maximum number of arguments for the command,
+ * including the command word.
+ * 'usage_msg' must be a usage message, leaving out the command name.
+ * Checks the handle assumed to be present in objv[1] if 'check' is not NULL.
+ * RETURNS: Handle index or -1 on failure.
+ * SIDE EFFECT: Sets the Tcl result on failure.
+ */
+
+static MysqlTclHandle *
+mysql_prologue (interp, objc, objv, req_min_args, req_max_args, check_level, usage_msg)
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST objv[];
+ int req_min_args;
+ int req_max_args;
+ int check_level;
+ char *usage_msg;
+{
+ /* Check number of args. */
+ if (objc < req_min_args || objc > req_max_args) {
+ Tcl_WrongNumArgs(interp, 1, objv, usage_msg);
+ return NULL;
+ }
+
+ /* Reset mysqlstatus(code). */
+ set_statusArr(interp,MYSQL_STATUS_CODE,Tcl_NewIntObj(0));
+
+ /* Check the handle.
+ * The function is assumed to set the status array on conflict.
+ */
+ return (get_handle(interp,objc,objv,check_level));
+}
+
+/*
+ *----------------------------------------------------------------------
+ * mysql_colinfo
+ *
+ * Given an MYSQL_FIELD struct and a string keyword appends a piece of
+ * column info (one item) to the Tcl result.
+ * ASSUMES 'fld' is non-null.
+ * RETURNS 0 on success, 1 otherwise.
+ * SIDE EFFECT: Sets the result and status on failure.
+ */
+
+static Tcl_Obj *
+mysql_colinfo (interp,objc,objv,fld,keyw)
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST objv[];
+ MYSQL_FIELD* fld ;
+ Tcl_Obj * keyw ;
+{
+ char buf[MYSQL_SMALL_SIZE];
+ int idx ;
+
+ static CONST char* MysqlColkey[] =
+ {
+ "table", "name", "type", "length", "prim_key", "non_null", "numeric", "decimals", NULL
+ };
+ enum coloptions {
+ MYSQL_COL_TABLE_K, MYSQL_COL_NAME_K, MYSQL_COL_TYPE_K, MYSQL_COL_LENGTH_K, MYSQL_COL_PRIMKEY_K,
+ MYSQL_COL_NONNULL_K, MYSQL_COL_NUMERIC_K, MYSQL_COL_DECIMALS_K};
+
+ if (Tcl_GetIndexFromObj(interp, keyw, (char **)MysqlColkey, "option",
+ TCL_EXACT, &idx) != TCL_OK)
+ return NULL;
+
+ switch (idx)
+ {
+ case MYSQL_COL_TABLE_K:
+ return Tcl_NewStringObj(fld->table, -1) ;
+ case MYSQL_COL_NAME_K:
+ return Tcl_NewStringObj(fld->name, -1) ;
+ case MYSQL_COL_TYPE_K:
+ switch (fld->type)
+ {
+ case FIELD_TYPE_DECIMAL:
+ return Tcl_NewStringObj("decimal", -1);
+ case FIELD_TYPE_TINY:
+ return Tcl_NewStringObj("tiny", -1);
+ case FIELD_TYPE_SHORT:
+ return Tcl_NewStringObj("short", -1);
+ case FIELD_TYPE_LONG:
+ return Tcl_NewStringObj("long", -1) ;
+ case FIELD_TYPE_FLOAT:
+ return Tcl_NewStringObj("float", -1);
+ case FIELD_TYPE_DOUBLE:
+ return Tcl_NewStringObj("double", -1);
+ case FIELD_TYPE_NULL:
+ return Tcl_NewStringObj("null", -1);
+ case FIELD_TYPE_TIMESTAMP:
+ return Tcl_NewStringObj("timestamp", -1);
+ case FIELD_TYPE_LONGLONG:
+ return Tcl_NewStringObj("long long", -1);
+ case FIELD_TYPE_INT24:
+ return Tcl_NewStringObj("int24", -1);
+ case FIELD_TYPE_DATE:
+ return Tcl_NewStringObj("date", -1);
+ case FIELD_TYPE_TIME:
+ return Tcl_NewStringObj("time", -1);
+ case FIELD_TYPE_DATETIME:
+ return Tcl_NewStringObj("date time", -1);
+ case FIELD_TYPE_YEAR:
+ return Tcl_NewStringObj("year", -1);
+ case FIELD_TYPE_NEWDATE:
+ return Tcl_NewStringObj("new date", -1);
+ case FIELD_TYPE_ENUM:
+ return Tcl_NewStringObj("enum", -1); /* fyll p忿? */
+ case FIELD_TYPE_SET: /* samma */
+ return Tcl_NewStringObj("set", -1);
+ case FIELD_TYPE_TINY_BLOB:
+ return Tcl_NewStringObj("tiny blob", -1);
+ case FIELD_TYPE_MEDIUM_BLOB:
+ return Tcl_NewStringObj("medium blob", -1);
+ case FIELD_TYPE_LONG_BLOB:
+ return Tcl_NewStringObj("long blob", -1);
+ case FIELD_TYPE_BLOB:
+ return Tcl_NewStringObj("blob", -1);
+ case FIELD_TYPE_VAR_STRING:
+ return Tcl_NewStringObj("var string", -1);
+ case FIELD_TYPE_STRING:
+ return Tcl_NewStringObj("string", -1) ;
+ default:
+ sprintf (buf, "column '%s' has weird datatype", fld->name) ;
+ mysql_prim_confl (interp,objc,objv,buf) ;
+ return NULL ;
+ }
+ break ;
+ case MYSQL_COL_LENGTH_K:
+ return Tcl_NewIntObj(fld->length) ;
+ case MYSQL_COL_PRIMKEY_K:
+ return Tcl_NewBooleanObj(IS_PRI_KEY(fld->flags)) ;
+ case MYSQL_COL_NONNULL_K:
+ return Tcl_NewBooleanObj(IS_NOT_NULL(fld->flags)) ;
+ case MYSQL_COL_NUMERIC_K:
+ return Tcl_NewBooleanObj(IS_NUM(fld->type));
+ case MYSQL_COL_DECIMALS_K:
+ return IS_NUM(fld->type)? Tcl_NewIntObj(fld->decimals): Tcl_NewIntObj(-1);
+ default: /* should never happen */
+ mysql_prim_confl (interp,objc,objv,"weirdness in mysql_colinfo") ;
+ return NULL ;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ * Mysqltcl_Kill
+ * Close all connections.
+ *
+ */
+
+static void
+Mysqltcl_Kill (clientData)
+ ClientData clientData;
+{
+ Tcl_HashEntry *entryPtr;
+ Tcl_HashSearch search;
+ MysqlTclHandle *handle;
+ int wasdeleted=0;
+
+ for (entryPtr=Tcl_FirstHashEntry(&handleTable,&search);
+ entryPtr!=NULL;
+ entryPtr=Tcl_NextHashEntry(&search)) {
+ wasdeleted=1;
+ handle=(MysqlTclHandle *)Tcl_GetHashValue(entryPtr);
+
+ if (handle->connection == 0) continue;
+
+ if (handle->result != NULL)
+ mysql_free_result (handle->result) ;
+
+ mysql_close (handle->connection);
+ closeHandle(handle);
+ }
+ if (wasdeleted) {
+ Tcl_DeleteHashTable(&handleTable);
+ Tcl_InitHashTable (&handleTable, TCL_STRING_KEYS);
+ }
+}
+
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Connect
+ * Implements the mysqlconnect command:
+ * usage: mysqlconnect ?option value ...?
+ *
+ * Results:
+ * handle - a character string of newly open handle
+ * TCL_OK - connect successful
+ * TCL_ERROR - connect not successful - error message returned
+ */
+
+DEFINE_CMD(Mysqltcl_Connect)
+{
+ int i, idx;
+ char *hostname = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *db = NULL;
+ int port = 0;
+ char *socket = NULL;
+ MysqlTclHandle *handle;
+ const char *groupname = "mysqltcl";
+
+ static CONST char* MysqlConnectOpt[] =
+ {
+ "-host", "-user", "-password", "-db", "-port", "-socket", NULL
+ };
+ enum connectoption {
+ MYSQL_CONNHOST_OPT, MYSQL_CONNUSER_OPT, MYSQL_CONNPASSWORD_OPT,
+ MYSQL_CONNDB_OPT, MYSQL_CONNPORT_OPT, MYSQL_CONNSOCKET_OPT
+ };
+ if (!(objc & 1) ||
+ objc>(sizeof(MysqlConnectOpt)/sizeof(MysqlConnectOpt[0]-1)*2+1)) {
+ Tcl_WrongNumArgs(interp, 1, objv, "[-user xxx] [-db mysql] [-port 3306] [-host localhost] [-socket sock] [-password pass]");
+ return TCL_ERROR;
+ }
+
+ for (i = 1; i < objc; i++) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], (char **)MysqlConnectOpt, "option",
+ 0, &idx) != TCL_OK)
+ return TCL_ERROR;
+
+ switch (idx)
+ {
+ case MYSQL_CONNHOST_OPT:
+ hostname = Tcl_GetStringFromObj(objv[++i],NULL);
+ break;
+ case MYSQL_CONNUSER_OPT:
+ user = Tcl_GetStringFromObj(objv[++i],NULL);
+ break;
+ case MYSQL_CONNPASSWORD_OPT:
+ password = Tcl_GetStringFromObj(objv[++i],NULL);
+ break;
+ case MYSQL_CONNDB_OPT:
+ db = Tcl_GetStringFromObj(objv[++i],NULL);
+ break;
+ case MYSQL_CONNPORT_OPT:
+ if(Tcl_GetIntFromObj(interp, objv[++i], &port) != TCL_OK)
+ return TCL_ERROR;
+ break;
+ case MYSQL_CONNSOCKET_OPT:
+ socket = Tcl_GetStringFromObj(objv[++i],NULL);
+ break;
+ default:
+ return mysql_prim_confl(interp,objc,objv,"Weirdness in options");
+ }
+ }
+
+ handle = createMysqlHandle();
+
+ if (handle == 0) {
+ panic("no memory for handle");
+ return TCL_ERROR;
+ }
+
+ handle->connection = mysql_init(NULL);
+
+ mysql_options(handle->connection,MYSQL_READ_DEFAULT_GROUP,groupname);
+
+ if (!mysql_real_connect (handle->connection, hostname, user,
+ password, db, port, socket, 0)) {
+ mysql_server_confl (interp,objc,objv,handle->connection);
+ mysql_close (handle->connection);
+ closeHandle(handle);
+ return TCL_ERROR;
+ }
+
+ if (hostname) {
+ strncpy (handle->host, hostname, MYSQL_NAME_LEN) ;
+ handle->host[MYSQL_NAME_LEN - 1] = '\0' ;
+ } else {
+ strcpy (handle->host, "localhost");
+ }
+
+ if (db) {
+ strncpy (handle->database, db, MYSQL_NAME_LEN) ;
+ handle->database[MYSQL_NAME_LEN - 1] = '\0' ;
+ }
+
+ Tcl_SetObjResult(interp, Tcl_NewHandleObj(handle));
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Use
+ * Implements the mysqluse command:
+ * usage: mysqluse handle dbname
+ *
+ * results:
+ * Sets current database to dbname.
+ */
+
+DEFINE_CMD(Mysqltcl_Use)
+{
+ int len;
+ char *db;
+ MysqlTclHandle *handle;
+
+ if ((handle = mysql_prologue(interp, objc, objv, 3, 3, CL_CONN,
+ "handle dbname")) == 0)
+ return TCL_ERROR;
+
+ db=Tcl_GetStringFromObj(objv[2], &len);
+ if (len >= MYSQL_NAME_LEN)
+ return mysql_prim_confl (interp,objc,objv,"database name too long") ;
+ if (mysql_select_db (handle->connection, db) < 0)
+ return mysql_server_confl (interp,objc,objv,handle->connection) ;
+
+ strcpy (handle->database, db) ;
+ return TCL_OK;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Escape
+ * Implements the mysqlescape command:
+ * usage: mysqlescape string
+ *
+ * results:
+ * Escaped string for use in queries.
+ */
+
+DEFINE_CMD(Mysqltcl_Escape)
+{
+ int len;
+ char *inString, *outString;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "string");
+ return TCL_ERROR;
+ }
+ /* !!! here the real_escape command should be used
+ this need a additional parameter connection */
+
+ inString=Tcl_GetStringFromObj(objv[1], &len);
+ outString=Tcl_Alloc((len<<1) + 1);
+ len=mysql_escape_string(outString, inString, len);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), outString, len);
+ Tcl_Free(outString);
+ return TCL_OK;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Sel
+ * Implements the mysqlsel command:
+ * usage: mysqlsel handle sel-query ?-list|-flatlist?
+ *
+ * results:
+ *
+ * SIDE EFFECT: Flushes any pending result, even in case of conflict.
+ * Stores new results.
+ */
+
+DEFINE_CMD(Mysqltcl_Sel)
+{
+ Tcl_Obj *res, *resList;
+ MYSQL_ROW row;
+ MysqlTclHandle *handle;
+ unsigned long *lengths;
+ static char* selOptions[] = {"-list", "-flatlist", NULL};
+ /* Warning !! no option number */
+ int i,selOption=2,colCount;
+
+ if ((handle = mysql_prologue(interp, objc, objv, 3, 4, CL_CONN,
+ "handle sel-query ?-list|-flatlist?")) == 0)
+ return TCL_ERROR;
+
+
+ if (objc==4) {
+ if (Tcl_GetIndexFromObj(interp, objv[3], (char **)selOptions, "option",
+ TCL_EXACT, &selOption) != TCL_OK)
+ return TCL_ERROR;
+ }
+
+ /* Flush any previous result. */
+ if (handle->result != NULL) {
+ mysql_free_result (handle->result) ;
+ handle->result = NULL ;
+ }
+
+ if (mysql_QueryTclObj(handle,objv[2])) {
+ return mysql_server_confl (interp,objc,objv,handle->connection);
+ }
+
+ if ((handle->result = mysql_store_result (handle->connection)) == NULL) {
+ if (selOption==2) Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
+ } else {
+ colCount = handle->col_count = mysql_num_fields (handle->result) ;
+ res = Tcl_GetObjResult(interp);
+ handle->res_count = 0;
+ switch (selOption) {
+ case 0: /* -list */
+ while ((row = mysql_fetch_row (handle->result)) != NULL) {
+ resList = Tcl_NewListObj(0, NULL);
+ lengths = mysql_fetch_lengths(handle->result);
+ for (i=0; i< colCount; i++, row++) {
+ Tcl_ListObjAppendElement (interp, resList,getRowCellAsObject(row,lengths[i]));
+ }
+ Tcl_ListObjAppendElement (interp, res, resList);
+ }
+ break;
+ case 1: /* -flatlist */
+ while ((row = mysql_fetch_row (handle->result)) != NULL) {
+ lengths = mysql_fetch_lengths(handle->result);
+ for (i=0; i< colCount; i++, row++) {
+ Tcl_ListObjAppendElement (interp, res,getRowCellAsObject(row,lengths[i]));
+ }
+ }
+ break;
+ case 2: /* No option */
+ handle->res_count = mysql_num_rows (handle->result);
+ Tcl_SetIntObj(res, handle->res_count);
+ break;
+ }
+ }
+ return TCL_OK;
+}
+/*
+ * Mysqltcl_Query
+ * Works as mysqltclsel but return an $query handle that allow to build
+ * nested queries on simple handle
+ */
+DEFINE_CMD(Mysqltcl_Query)
+{
+ MYSQL_RES *result;
+ MysqlTclHandle *handle, *qhandle;
+
+ if ((handle = mysql_prologue(interp, objc, objv, 3, 3, CL_CONN,
+ "handle sqlstatement")) == 0)
+ return TCL_ERROR;
+
+ if (mysql_QueryTclObj(handle,objv[2])) {
+ return mysql_server_confl (interp,objc,objv,handle->connection);
+ }
+
+ if ((result = mysql_store_result (handle->connection)) == NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
+ return TCL_OK;
+ }
+ if ((qhandle = createQueryHandleFrom(handle)) == NULL) return TCL_ERROR;
+ qhandle->result = result;
+ qhandle->col_count = mysql_num_fields (qhandle->result) ;
+ qhandle->res_count = mysql_num_rows (qhandle->result);
+ Tcl_SetObjResult(interp, Tcl_NewHandleObj(qhandle));
+ return TCL_OK;
+}
+DEFINE_CMD(Mysqltcl_EndQuery)
+{
+ Tcl_HashEntry *entryPtr;
+ MysqlTclHandle *handle;
+
+ if ((handle = mysql_prologue(interp, objc, objv, 2, 2, CL_CONN,
+ "queryhanle")) == 0)
+ return TCL_ERROR;
+
+ if (handle->result != NULL) {
+ mysql_free_result (handle->result) ;
+ handle->result = NULL ;
+ }
+ if (handle->isquery) {
+ entryPtr = Tcl_FindHashEntry(&handleTable,Tcl_GetStringFromObj(objv[1],NULL));
+ if (entryPtr) {
+ Tcl_DeleteHashEntry(entryPtr);
+ }
+ closeHandle(handle);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Exec
+ * Implements the mysqlexec command:
+ * usage: mysqlexec handle sql-statement
+ *
+ * Results:
+ * Number of affected rows on INSERT, UPDATE or DELETE, 0 otherwise.
+ *
+ * SIDE EFFECT: Flushes any pending result, even in case of conflict.
+ */
+
+DEFINE_CMD(Mysqltcl_Exec)
+{
+ MysqlTclHandle *handle;
+ int affected;
+
+ if ((handle = mysql_prologue(interp, objc, objv, 3, 3, CL_CONN,
+ "handle sql-statement")) == 0)
+ return TCL_ERROR;
+
+ /* Flush any previous result. */
+ if (handle->result != NULL) {
+ mysql_free_result (handle->result) ;
+ handle->result = NULL ;
+ }
+
+ if (mysql_QueryTclObj(handle,objv[2]))
+ return mysql_server_confl (interp,objc,objv,handle->connection);
+
+ if ((affected=mysql_affected_rows(handle->connection)) < 0) affected=0;
+ Tcl_SetIntObj(Tcl_GetObjResult(interp),affected);
+ return TCL_OK ;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Next
+ * Implements the mysqlnext command:
+ * usage: mysqlnext handle
+ *
+ * results:
+ * next row from pending results as tcl list, or null list.
+ */
+
+DEFINE_CMD(Mysqltcl_Next)
+{
+ MysqlTclHandle *handle;
+ int idx ;
+ MYSQL_ROW row ;
+ Tcl_Obj *resList;
+ unsigned long *lengths;
+
+ if ((handle = mysql_prologue(interp, objc, objv, 2, 2, CL_RES,
+ "handle")) == 0)
+ return TCL_ERROR;
+
+
+ if (handle->res_count == 0)
+ return TCL_OK ;
+ else if ((row = mysql_fetch_row (handle->result)) == NULL) {
+ handle->res_count = 0 ;
+ return mysql_prim_confl (interp,objc,objv,"result counter out of sync") ;
+ } else
+ handle->res_count-- ;
+
+ lengths = mysql_fetch_lengths(handle->result);
+
+ resList = Tcl_GetObjResult(interp);
+ for (idx = 0 ; idx < handle->col_count ; idx++, row++) {
+ Tcl_ListObjAppendElement (interp, resList,getRowCellAsObject(row,lengths[idx]));
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Seek
+ * Implements the mysqlseek command:
+ * usage: mysqlseek handle rownumber
+ *
+ * results:
+ * number of remaining rows
+ */
+
+DEFINE_CMD(Mysqltcl_Seek)
+{
+ MysqlTclHandle *handle;
+ int row;
+ int total;
+
+ if ((handle = mysql_prologue(interp, objc, objv, 3, 3, CL_RES,
+ " handle row-index")) == 0)
+ return TCL_ERROR;
+
+ if (Tcl_GetIntFromObj (interp, objv[2], &row) != TCL_OK)
+ return TCL_ERROR;
+
+ total = mysql_num_rows (handle->result);
+
+ if (total + row < 0) {
+ mysql_data_seek (handle->result, 0);
+ handle->res_count = total;
+ } else if (row < 0) {
+ mysql_data_seek (handle->result, total + row);
+ handle->res_count = -row;
+ } else if (row >= total) {
+ mysql_data_seek (handle->result, row);
+ handle->res_count = 0;
+ } else {
+ mysql_data_seek (handle->result, row);
+ handle->res_count = total - row;
+ }
+
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(handle->res_count)) ;
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Map
+ * Implements the mysqlmap command:
+ * usage: mysqlmap handle binding-list script
+ *
+ * Results:
+ * SIDE EFFECT: For each row the column values are bound to the variables
+ * in the binding list and the script is evaluated.
+ * The variables are created in the current context.
+ * NOTE: mysqlmap works very much like a 'foreach' construct.
+ * The 'continue' and 'break' commands may be used with their usual effect.
+ */
+
+DEFINE_CMD(Mysqltcl_Map)
+{
+ int code ;
+ int count ;
+ MysqlTclHandle *handle;
+ int idx ;
+ int listObjc ;
+ Tcl_Obj** listObjv ;
+ MYSQL_ROW row ;
+ int *val;
+ unsigned long *lengths;
+
+
+ if ((handle = mysql_prologue(interp, objc, objv, 4, 4, CL_RES,
+ "handle binding-list script")) == 0)
+ return TCL_ERROR;
+
+ if (Tcl_ListObjGetElements (interp, objv[2], &listObjc, &listObjv) != TCL_OK)
+ return TCL_ERROR ;
+
+ if (listObjc > handle->col_count)
+ {
+ return mysql_prim_confl (interp,objc,objv,"too many variables in binding list") ;
+ }
+ else
+ count = (listObjc < handle->col_count)?listObjc
+ :handle->col_count ;
+
+ val=(int*)Tcl_Alloc((count * sizeof (int)));
+ for (idx=0; idx<count; idx++) {
+ if (Tcl_GetStringFromObj(listObjv[idx],0)[0] != '-')
+ val[idx]=1;
+ else
+ val[idx]=0;
+ }
+
+ while (handle->res_count > 0) {
+ /* Get next row, decrement row counter. */
+ if ((row = mysql_fetch_row (handle->result)) == NULL) {
+ handle->res_count = 0 ;
+ Tcl_Free((char *)val);
+ return mysql_prim_confl (interp,objc,objv,"result counter out of sync") ;
+ } else
+ handle->res_count-- ;
+
+ /* Bind variables to column values. */
+ for (idx = 0; idx < count; idx++, row++) {
+ lengths = mysql_fetch_lengths(handle->result);
+ if (val[idx]) {
+ if (Tcl_ObjSetVar2 (interp,
+ listObjv[idx], NULL,getRowCellAsObject(row,lengths[idx]),
+ TCL_LEAVE_ERR_MSG) == NULL) {
+ Tcl_Free((char *)val);
+ return TCL_ERROR ;
+ }
+ }
+ }
+
+ /* Evaluate the script. */
+ switch(code=Tcl_EvalObjEx(interp, objv[3],0)) {
+ case TCL_CONTINUE:
+ case TCL_OK:
+ break ;
+ case TCL_BREAK:
+ Tcl_Free((char *)val);
+ return TCL_OK ;
+ default:
+ Tcl_Free((char *)val);
+ return code ;
+ }
+ }
+ Tcl_Free((char *)val);
+ return TCL_OK ;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Info
+ * Implements the mysqlinfo command:
+ * usage: mysqlinfo handle option
+ *
+ */
+
+DEFINE_CMD(Mysqltcl_Info)
+{
+ int count ;
+ MysqlTclHandle *handle;
+ int idx ;
+ MYSQL_RES* list ;
+ MYSQL_ROW row ;
+ char* val ;
+ Tcl_Obj *resList;
+ static CONST char* MysqlDbOpt[] =
+ {
+ "dbname", "dbname?", "tables", "host", "host?", "databases","info", NULL
+ };
+ enum dboption {
+ MYSQL_INFNAME_OPT, MYSQL_INFNAMEQ_OPT, MYSQL_INFTABLES_OPT,
+ MYSQL_INFHOST_OPT, MYSQL_INFHOSTQ_OPT, MYSQL_INFLIST_OPT, MYSQL_INFO
+ };
+
+ /* We can't fully check the handle at this stage. */
+ if ((handle = mysql_prologue(interp, objc, objv, 3, 3, CL_PLAIN,
+ "handle option")) == 0)
+ return TCL_ERROR;
+
+ if (Tcl_GetIndexFromObj(interp, objv[2], (char **)MysqlDbOpt, "option",
+ TCL_EXACT, &idx) != TCL_OK)
+ return TCL_ERROR;
+
+ /* First check the handle. Checking depends on the option. */
+ switch (idx) {
+ case MYSQL_INFNAMEQ_OPT:
+ if ((handle = get_handle(interp,objc,objv,CL_CONN))!=NULL) {
+ if (handle->database[0] == '\0')
+ return TCL_OK ; /* Return empty string if no current db. */
+ }
+ break ;
+ case MYSQL_INFNAME_OPT:
+ case MYSQL_INFTABLES_OPT:
+ case MYSQL_INFHOST_OPT:
+ case MYSQL_INFLIST_OPT:
+ /* !!! */
+ handle = get_handle(interp,objc,objv,CL_CONN);
+ break ;
+ case MYSQL_INFHOSTQ_OPT:
+ if (handle->connection == 0)
+ return TCL_OK ; /* Return empty string if not connected. */
+ break ;
+ case MYSQL_INFO:
+ if (handle->connection == 0)
+ return TCL_OK ; /* Return empty string if not connected. */
+ break;
+ default: /* should never happen */
+ return mysql_prim_confl (interp,objc,objv,"weirdness in Mysqltcl_Info") ;
+ }
+
+ if (handle == 0) return TCL_ERROR ;
+
+ /* Handle OK, return the requested info. */
+ switch (idx) {
+ case MYSQL_INFNAME_OPT:
+ case MYSQL_INFNAMEQ_OPT:
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(handle->database, -1));
+ break ;
+ case MYSQL_INFTABLES_OPT:
+ if ((list = mysql_list_tables (handle->connection,(char*)NULL)) == NULL)
+ return mysql_server_confl (interp,objc,objv,handle->connection);
+
+ resList = Tcl_GetObjResult(interp);
+ for (count = mysql_num_rows (list); count > 0; count--) {
+ val = *(row = mysql_fetch_row (list)) ;
+ Tcl_ListObjAppendElement (interp, resList, Tcl_NewStringObj((val == NULL)?"":val,-1));
+ }
+ mysql_free_result (list) ;
+ break ;
+ case MYSQL_INFHOST_OPT:
+ case MYSQL_INFHOSTQ_OPT:
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(handle->host, -1));
+ break ;
+ case MYSQL_INFLIST_OPT:
+ if ((list = mysql_list_dbs (handle->connection,(char*)NULL)) == NULL)
+ return mysql_server_confl (interp,objc,objv,handle->connection);
+
+ resList = Tcl_GetObjResult(interp);
+ for (count = mysql_num_rows (list); count > 0; count--) {
+ val = *(row = mysql_fetch_row (list)) ;
+ Tcl_ListObjAppendElement (interp, resList,
+ Tcl_NewStringObj((val == NULL)?"":val,-1));
+ }
+ mysql_free_result (list) ;
+ break ;
+ case MYSQL_INFO:
+ val = mysql_info(handle->connection);
+ if (val!=NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(val,-1));
+ }
+ break;
+ default: /* should never happen */
+ return mysql_prim_confl (interp,objc,objv,"weirdness in Mysqltcl_Info") ;
+ }
+ return TCL_OK ;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Result
+ * Implements the mysqlresult command:
+ * usage: mysqlresult handle option
+ *
+ */
+
+DEFINE_CMD(Mysqltcl_Result)
+{
+ int idx ;
+ MysqlTclHandle *handle;
+ static CONST char* MysqlResultOpt[] =
+ {
+ "rows", "rows?", "cols", "cols?", "current", "current?", NULL
+ };
+ enum resultoption {
+ MYSQL_RESROWS_OPT, MYSQL_RESROWSQ_OPT, MYSQL_RESCOLS_OPT,
+ MYSQL_RESCOLSQ_OPT, MYSQL_RESCUR_OPT, MYSQL_RESCURQ_OPT
+ };
+ /* We can't fully check the handle at this stage. */
+ if ((handle = mysql_prologue(interp, objc, objv, 3, 3, CL_PLAIN,
+ " handle option")) == 0)
+ return TCL_ERROR;
+
+ if (Tcl_GetIndexFromObj(interp, objv[2], (char **)MysqlResultOpt, "option",
+ TCL_EXACT, &idx) != TCL_OK)
+ return TCL_ERROR;
+
+ /* First check the handle. Checking depends on the option. */
+ switch (idx) {
+ case MYSQL_RESROWS_OPT:
+ case MYSQL_RESCOLS_OPT:
+ case MYSQL_RESCUR_OPT:
+ handle = get_handle (interp,objc,objv,CL_RES) ;
+ break ;
+ case MYSQL_RESROWSQ_OPT:
+ case MYSQL_RESCOLSQ_OPT:
+ case MYSQL_RESCURQ_OPT:
+ if ((handle = get_handle (interp,objc,objv,CL_RES))== NULL)
+ return TCL_OK ; /* Return empty string if no pending result. */
+ break ;
+ default: /* should never happen */
+ return mysql_prim_confl (interp,objc,objv,"weirdness in Mysqltcl_Result") ;
+ }
+
+
+ if (handle == 0)
+ return TCL_ERROR ;
+
+ /* Handle OK; return requested info. */
+ switch (idx) {
+ case MYSQL_RESROWS_OPT:
+ case MYSQL_RESROWSQ_OPT:
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(handle->res_count));
+ break ;
+ case MYSQL_RESCOLS_OPT:
+ case MYSQL_RESCOLSQ_OPT:
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(handle->col_count));
+ break ;
+ case MYSQL_RESCUR_OPT:
+ case MYSQL_RESCURQ_OPT:
+ Tcl_SetObjResult(interp,
+ Tcl_NewIntObj(mysql_num_rows (handle->result)
+ - handle->res_count)) ;
+ break ;
+ default:
+ return mysql_prim_confl (interp,objc,objv,"weirdness in Mysqltcl_Result");
+ }
+ return TCL_OK ;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Col
+ * Implements the mysqlcol command:
+ * usage: mysqlcol handle table-name option ?option ...?
+ * mysqlcol handle -current option ?option ...?
+ * '-current' can only be used if there is a pending result.
+ *
+ * results:
+ * List of lists containing column attributes.
+ * If a single attribute is requested the result is a simple list.
+ *
+ * SIDE EFFECT: '-current' disturbs the field position of the result.
+ */
+
+DEFINE_CMD(Mysqltcl_Col)
+{
+ int coln ;
+ int current_db ;
+ MysqlTclHandle *handle;
+ int idx ;
+ int listObjc ;
+ Tcl_Obj **listObjv, *colinfo, *resList, *resSubList;
+ MYSQL_FIELD* fld ;
+ MYSQL_RES* result ;
+ char *argv ;
+
+ /* This check is enough only without '-current'. */
+ if ((handle = mysql_prologue(interp, objc, objv, 4, 99, CL_CONN,
+ "handle table-name option ?option ...?")) == 0)
+ return TCL_ERROR;
+
+ /* Fetch column info.
+ * Two ways: explicit database and table names, or current.
+ */
+ argv=Tcl_GetStringFromObj(objv[2],NULL);
+ current_db = strcmp (argv, "-current") == 0;
+
+ if (current_db) {
+ if ((handle = get_handle (interp,objc,objv,CL_RES)) == 0)
+ return TCL_ERROR ;
+ else
+ result = handle->result ;
+ } else {
+ if ((result = mysql_list_fields (handle->connection, argv, (char*)NULL)) == NULL) {
+ return mysql_server_confl (interp,objc,objv,handle->connection) ;
+ }
+ }
+ /* Must examine the first specifier at this point. */
+ if (Tcl_ListObjGetElements (interp, objv[3], &listObjc, &listObjv) != TCL_OK)
+ return TCL_ERROR ;
+ resList = Tcl_GetObjResult(interp);
+ if (objc == 4 && listObjc == 1) {
+ mysql_field_seek (result, 0) ;
+ while ((fld = mysql_fetch_field (result)) != NULL)
+ if ((colinfo = mysql_colinfo (interp,objc,objv,fld, objv[3])) != NULL) {
+ Tcl_ListObjAppendElement (interp, resList, colinfo);
+ } else {
+ goto conflict;
+ }
+ } else if (objc == 4 && listObjc > 1) {
+ mysql_field_seek (result, 0) ;
+ while ((fld = mysql_fetch_field (result)) != NULL) {
+ resSubList = Tcl_NewListObj(0, NULL);
+ for (coln = 0; coln < listObjc; coln++)
+ if ((colinfo = mysql_colinfo (interp,objc,objv,fld, listObjv[coln])) != NULL) {
+ Tcl_ListObjAppendElement (interp, resSubList, colinfo);
+ } else {
+ goto conflict;
+ }
+ Tcl_ListObjAppendElement (interp, resList, resSubList);
+ }
+ } else {
+ for (idx = 3; idx < objc; idx++) {
+ resSubList = Tcl_NewListObj(0, NULL);
+ mysql_field_seek (result, 0) ;
+ while ((fld = mysql_fetch_field (result)) != NULL)
+ if ((colinfo = mysql_colinfo (interp,objc,objv,fld, objv[idx])) != NULL) {
+ Tcl_ListObjAppendElement (interp, resSubList, colinfo);
+ } else {
+ goto conflict;
+ }
+ Tcl_ListObjAppendElement (interp, resList, resSubList);
+ }
+ }
+ if (!current_db) mysql_free_result (result) ;
+ return TCL_OK;
+
+ conflict:
+ if (!current_db) mysql_free_result (result) ;
+ return TCL_ERROR;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_State
+ * Implements the mysqlstate command:
+ * usage: mysqlstate handle ?-numeric?
+ *
+ */
+
+DEFINE_CMD(Mysqltcl_State)
+{
+ MysqlTclHandle *handle;
+ int numeric=0 ;
+ Tcl_Obj *res;
+
+ if (mysql_prologue(interp, objc, objv, 2, 3, NULL, "handle ?-numeric?") == 0)
+ return TCL_ERROR;
+
+ if (objc==3) {
+ if (strcmp (Tcl_GetStringFromObj(objv[2],NULL), "-numeric"))
+ return mysql_prim_confl (interp,objc,objv,"last parameter should be -numeric") ;
+ else
+ numeric=1;
+ }
+
+ if (GetHandleFromObj(NULL, objv[1], &handle) != TCL_OK)
+ res = (numeric)?Tcl_NewIntObj(0):Tcl_NewStringObj("NOT_A_HANDLE",-1) ;
+ else if (handle->connection == 0)
+ res = (numeric)?Tcl_NewIntObj(1):Tcl_NewStringObj("UNCONNECTED",-1) ;
+ else if (handle->database[0] == '\0')
+ res = (numeric)?Tcl_NewIntObj(2):Tcl_NewStringObj("CONNECTED",-1) ;
+ else if (handle->result == NULL)
+ res = (numeric)?Tcl_NewIntObj(3):Tcl_NewStringObj("IN_USE",-1) ;
+ else
+ res = (numeric)?Tcl_NewIntObj(4):Tcl_NewStringObj("RESULT_PENDING",-1) ;
+
+ Tcl_SetObjResult(interp, res);
+ return TCL_OK ;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_InsertId
+ * Implements the mysqlstate command:
+ * usage: mysqlinsertid handle
+ * Returns the auto increment id of the last INSERT statement
+ *
+ */
+
+DEFINE_CMD(Mysqltcl_InsertId)
+{
+ MysqlTclHandle *handle;
+
+ if ((handle = mysql_prologue(interp, objc, objv, 2, 2, CL_CONN,
+ "handle")) == 0)
+ return TCL_ERROR;
+
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(mysql_insert_id(handle->connection)));
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Mysqltcl_Close --
+ * Implements the mysqlclose command:
+ * usage: mysqlclose ?handle?
+ *
+ * results:
+ * null string
+ */
+
+DEFINE_CMD(Mysqltcl_Close)
+{
+ MysqlTclHandle *handle,*thandle;
+ Tcl_HashEntry *entryPtr;
+ Tcl_HashEntry *qentries[16];
+ Tcl_HashSearch search;
+
+ int i,qfound = 0;
+
+ /* If handle omitted, close all connections. */
+ if (objc == 1) {
+ Mysqltcl_Kill ((ClientData)NULL) ;
+ return TCL_OK ;
+ }
+
+ if ((handle = mysql_prologue(interp, objc, objv, 2, 2, CL_CONN,
+ "?handle?")) == 0)
+ return TCL_ERROR;
+
+ if (handle->result != NULL)
+ mysql_free_result (handle->result) ;
+
+ /* Search all queries on this handle and close those */
+ if (!handle->isquery) {
+ mysql_close(handle->connection);
+ while (1) {
+ for (entryPtr=Tcl_FirstHashEntry(&handleTable,&search);
+ entryPtr!=NULL;
+ entryPtr=Tcl_NextHashEntry(&search)) {
+
+ thandle=(MysqlTclHandle *)Tcl_GetHashValue(entryPtr);
+ if (thandle->connection == handle->connection &&
+ thandle->isquery) {
+ qentries[qfound++] = entryPtr;
+ }
+ if (qfound==16) break;
+ }
+ if (qfound>0) {
+ for(i=0;i<qfound;i++) {
+ entryPtr=qentries[i];
+ thandle=(MysqlTclHandle *)Tcl_GetHashValue(entryPtr);
+ Tcl_DeleteHashEntry(entryPtr);
+ if (thandle->result != NULL)
+ mysql_free_result (thandle->result);
+ closeHandle(thandle);
+ }
+ }
+ if (qfound!=16) break;
+ qfound = 0;
+ }
+ }
+ entryPtr = Tcl_FindHashEntry(&handleTable,Tcl_GetStringFromObj(objv[1],NULL));
+ if (entryPtr) Tcl_DeleteHashEntry(entryPtr);
+ closeHandle(handle);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ * Mysqltcl_Init
+ * Perform all initialization for the MYSQL to Tcl interface.
+ * Adds additional commands to interp, creates message array, initializes
+ * all handles.
+ *
+ * A call to Mysqltcl_Init should exist in Tcl_CreateInterp or
+ * Tcl_CreateExtendedInterp.
+ */
+
+#ifdef _WINDOWS
+__declspec( dllexport )
+#endif
+int Mysqltcl_Init (interp)
+ Tcl_Interp *interp;
+{
+ char nbuf[MYSQL_SMALL_SIZE];
+
+ if (Tcl_InitStubs(interp, "8.1", 0) == NULL)
+ return TCL_ERROR;
+ if (Tcl_PkgRequire(interp, "Tcl", "8.1", 0) == NULL)
+ return TCL_ERROR;
+ if (Tcl_PkgProvide(interp, "mysqltcl" , VERSION) != TCL_OK)
+ return TCL_ERROR;
+ /*
+ * Initialize the new Tcl commands.
+ * Deleting any command will close all connections.
+ */
+ ADD_CMD(mysqlconnect, Mysqltcl_Connect);
+ ADD_CMD(mysqluse, Mysqltcl_Use);
+ ADD_CMD(mysqlescape, Mysqltcl_Escape);
+ ADD_CMD(mysqlsel, Mysqltcl_Sel);
+ ADD_CMD(mysqlnext, Mysqltcl_Next);
+ ADD_CMD(mysqlseek, Mysqltcl_Seek);
+ ADD_CMD(mysqlmap, Mysqltcl_Map);
+ ADD_CMD(mysqlexec, Mysqltcl_Exec);
+ ADD_CMD(mysqlclose, Mysqltcl_Close);
+ ADD_CMD(mysqlinfo, Mysqltcl_Info);
+ ADD_CMD(mysqlresult, Mysqltcl_Result);
+ ADD_CMD(mysqlcol, Mysqltcl_Col);
+ ADD_CMD(mysqlstate, Mysqltcl_State);
+ ADD_CMD(mysqlinsertid, Mysqltcl_InsertId);
+ ADD_CMD(mysqlquery, Mysqltcl_Query);
+ ADD_CMD(mysqlendquery, Mysqltcl_EndQuery);
+
+ /* Initialize mysqlstatus global array. */
+
+ clear_msg(interp);
+
+ /* Initialize HashTable for mysql handles */
+ Tcl_InitHashTable (&handleTable, TCL_STRING_KEYS);
+
+ /* Link the null value element to the corresponding C variable. */
+ if ((MysqlNullvalue = (char*)Tcl_Alloc (12)) == NULL) return TCL_ERROR ;
+ strcpy (MysqlNullvalue, MYSQL_NULLV_INIT);
+ sprintf (nbuf, "%s(%s)", MYSQL_STATUS_ARR, MYSQL_STATUS_NULLV) ;
+
+ if (Tcl_LinkVar (interp,nbuf,(char *)&MysqlNullvalue, TCL_LINK_STRING) != TCL_OK)
+ return TCL_ERROR;
+
+ /* Register the handle object type */
+ Tcl_RegisterObjType(&mysqlHandleType);
+
+ /* A little sanity check.
+ * If this message appears you must change the source code and recompile.
+ */
+ if (strlen (MysqlHandlePrefix) == MYSQL_HPREFIX_LEN)
+ return TCL_OK;
+ else {
+ panic("*** mysqltcl (mysqltcl.c): handle prefix inconsistency!\n");
+ return TCL_ERROR ;
+ }
+}