/* File: odbc_xsb.c ** Author(s): Lily Dong, David S. Warren ** Contact: xsb-contact@cs.sunysb.edu ** ** Copyright (C) The Research Foundation of SUNY, 1986, 1993-1998 ** ** XSB is free software; you can redistribute it and/or modify it under the ** terms of the GNU Library General Public License as published by the Free ** Software Foundation; either version 2 of the License, or (at your option) ** any later version. ** ** XSB is distributed in the hope that it will be useful, but WITHOUT ANY ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ** FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for ** more details. ** ** You should have received a copy of the GNU Library General Public License ** along with XSB; if not, write to the Free Software Foundation, ** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ** ** $Id: odbc_xsb.c,v 1.29 2003/04/16 14:11:31 lfcastro Exp $ ** */ #include "xsb_config.h" #include "cell_xsb.h" #ifdef CYGWIN #define FAR #include "sql.h" #include "sqlext.h" #include "odbc_string.h" #else #ifdef WIN_NT #include #endif #include #include #include #endif #include #include #include #include "cinterf.h" #include "deref.h" #include "error_xsb.h" #include "export.h" #include "register.h" #include "ptoc_tag_xsb_i.h" #include "io_builtins_xsb.h" #define MAXCURSORNUM 25 #define MAXVARSTRLEN 2000 #define MAXI(a,b) ((a)>(b)?(a):(b)) static Cell nullStrAtom; static int serverConnected = 0; /* static int numberOfCursors = 0; */ static long SQL_NTSval = SQL_NTS; static HENV henv = NULL; /*HDBC hdbc;*/ UCHAR uid[128]; struct Cursor { struct Cursor *NCursor; /* Next Cursor in cursor chain*/ struct Cursor *PCursor; /* Prev Cursor in cursor chain*/ HDBC hdbc; /* connection handle for this cursor*/ int Status; /* status of the cursor*/ UCHAR *Sql; /* pointer to the sql statement*/ HSTMT hstmt; /* the statement handle*/ int NumBindVars; /* number of bind values*/ UCHAR **BindList; /* pointer to array of pointers to the bind values*/ int *BindTypes; /* types of the bind values*/ SWORD NumCols; /* number of columns selected*/ SWORD *ColTypes; /* pointer to array of column types*/ UDWORD *ColLen; /* pointer to array of max column lengths*/ UDWORD *OutLen; /* pointer to array of actual column lengths*/ UCHAR **Data; /* pointer to array of pointers to data*/ }; /* global cursor table*/ struct Cursor *FCursor; /* root of curser chain*/ struct Cursor *LCursor; /* tail of curser chain*/ /* Number of Cursors per Connection */ struct NumberofCursors{ HDBC hdbc; int CursorCount; struct NumberofCursors *NCurNum; }; struct NumberofCursors *FCurNum; /* First in the list of Number of Cursors */ SWORD ODBCToXSBType(SWORD odbcType) { switch (odbcType) { case SQL_TINYINT: case SQL_SMALLINT: case SQL_INTEGER: return SQL_C_SLONG; case SQL_DECIMAL: case SQL_NUMERIC: case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: return SQL_C_FLOAT; case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: return SQL_BINARY; case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: case SQL_CHAR: case SQL_VARCHAR: default: return SQL_C_CHAR; } } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* PrintErrorMsg()*/ /* PARAMETERS:*/ /* struct Cursor *c - pointer to a cursor, or NULL*/ /* NOTES:*/ /* PrintErrorMsg() prints out the error message that associates*/ /* with the statement handler of cursor i.*/ /*-----------------------------------------------------------------------------*/ int PrintErrorMsg(struct Cursor *cur) { UCHAR FAR *szsqlstate; SDWORD FAR *pfnativeerror; UCHAR FAR *szerrormsg; SWORD cberrormsgmax; SWORD FAR *pcberrormsg; RETCODE rc; szsqlstate=(UCHAR FAR *)malloc(sizeof(UCHAR FAR)*10); pfnativeerror=(SDWORD FAR *)malloc(sizeof(SDWORD FAR)); szerrormsg=(UCHAR FAR *)malloc(sizeof(UCHAR FAR)*SQL_MAX_MESSAGE_LENGTH); pcberrormsg=(SWORD FAR *)malloc(sizeof(SWORD FAR)); cberrormsgmax=SQL_MAX_MESSAGE_LENGTH-1; if (cur != NULL) rc = SQLError(SQL_NULL_HENV, cur->hdbc, cur->hstmt, szsqlstate, pfnativeerror, szerrormsg,cberrormsgmax,pcberrormsg); else rc = SQLError(SQL_NULL_HENV, NULL, SQL_NULL_HSTMT, szsqlstate, pfnativeerror, szerrormsg,cberrormsgmax,pcberrormsg); if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO)) { printf("ODBC SYSCALL ERROR (CODE %s): %s\n", szsqlstate, szerrormsg); } free(szsqlstate); free(pfnativeerror); free(szerrormsg); free(pcberrormsg); return 1; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* SetCursorClose()*/ /* PARAMETER:*/ /* struct Cursor *cur - pointer to current cursor*/ /* NOTES:*/ /* free all the memory resource allocated for cursor cur*/ /*-----------------------------------------------------------------------------*/ void SetCursorClose(struct Cursor *cur) { int j; SQLFreeStmt(cur->hstmt, SQL_CLOSE); /* free statement handler*/ if (cur->NumBindVars) { /* free bind variable list*/ for (j = 0; j < cur->NumBindVars; j++) if (cur->BindTypes[j] != 2) free((void *)cur->BindList[j]); free(cur->BindList); free(cur->BindTypes); } if (cur->NumCols) { /* free the resulting row set*/ for (j = 0; j < cur->NumCols; j++) free(cur->Data[j]); free(cur->ColTypes); free(cur->ColLen); free(cur->OutLen); free(cur->Data); } /* free memory for the sql statement associated w/ this cursor*/ if (cur->Sql) free(cur->Sql); /* initialize the variables. set them to the right value*/ cur->Sql = 0; cur->NumCols = cur->Status = cur->NumBindVars = 0; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* ODBCConnect()*/ /* PARAMETERS:*/ /* R1: 1*/ /* R2: 0 -> call connect with server/user/pw */ /* 1 -> call driver_connect with full connection string */ /* R3: Server or connection_string*/ /* R4: User name or don't care*/ /* R5: Password or don't care*/ /* R6: var, for returned Connection ID.*/ /* NOTES:*/ /* This function is called when a user wants to start a db session,*/ /* assuming that she doesn't have one open. It initializes system*/ /* resources for the new session, including allocations of various things:*/ /* environment handler, connection handler, statement handlers and then*/ /* tries to connect to the database, either by server,userid,pw if R2=0,*/ /* or thrugh a driver by using r3 as the connections tring, if R2=1. */ /* If any of these allocations or connection fails, function returns a*/ /* failure code 1. Otherwise 0. */ /*-----------------------------------------------------------------------------*/ void ODBCConnect() { UCHAR *server; UCHAR *pwd; UCHAR *connectIn; HDBC hdbc = NULL; RETCODE rc; /* if we don't yet have an environment, allocate one.*/ if (!henv) { /* allocate environment handler*/ rc = SQLAllocEnv(&henv); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { xsb_error("Environment allocation failed"); ctop_int(6, 0); return; } /* SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC2, SQL_IS_UINTEGER); */ LCursor = FCursor = NULL; FCurNum = NULL; nullStrAtom = makestring(string_find("NULL",1)); } /* allocate connection handler*/ rc = SQLAllocConnect(henv, &hdbc); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { xsb_error("Connection Resources Allocation Failed"); ctop_int(6, 0); return; } if (!ptoc_int(2)) { /* get server name, user id and password*/ server = (UCHAR *)ptoc_string(3); strcpy(uid, (UCHAR *)ptoc_string(4)); pwd = (UCHAR *)ptoc_string(5); /* connect to database*/ rc = SQLConnect(hdbc, server, SQL_NTS, uid, SQL_NTS, pwd, SQL_NTS); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { SQLFreeConnect(hdbc); xsb_error("Connection to server %s failed", server); ctop_int(6, 0); return; } } else { /* connecting through driver using a connection string */ connectIn = (UCHAR *)ptoc_longstring(3); rc = SQLDriverConnect(hdbc, NULL, connectIn, SQL_NTS, NULL, 0, NULL,SQL_DRIVER_NOPROMPT); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { SQLFreeConnect(hdbc); xsb_error("Connection to driver failed: %s", connectIn); ctop_int(6, 0); return; } } serverConnected = 1; ctop_int(6, (long)hdbc); return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* ODBCDisconnect() */ /* PARAMETERS:*/ /* R1: 2*/ /* R2: hdbc if closing a particular connection*/ /* 0 if closing entire ODBC session.*/ /* NOTES:*/ /* Disconnect us from the server and free all the system resources we*/ /* allocated for the session - statement handlers, connection handler,*/ /* environment handler and memory space. */ /*-----------------------------------------------------------------------------*/ void ODBCDisconnect() { struct Cursor *cur = FCursor; struct Cursor *tcur; struct NumberofCursors *numi = FCurNum, *numj = FCurNum; HDBC hdbc = (HDBC)ptoc_int(2); if (!serverConnected) return; if (hdbc == NULL) { /* close entire connection*/ if (FCursor != NULL) xsb_abort("Must close all connections before shutting down"); SQLFreeEnv(henv); serverConnected = 0; return; } /* only free cursors associated with this connection (hdbc)*/ while((numj != NULL) && (numj->hdbc != hdbc)){ if(numj != FCurNum) numi=numi->NCurNum; numj=numj->NCurNum; } if(numj != NULL){ if(numj == FCurNum) FCurNum=numj->NCurNum; else numi->NCurNum=numj->NCurNum; free(numj); } while (cur != NULL) { if (cur->hdbc == hdbc) { tcur = cur->NCursor; if (cur->Status != 0) SetCursorClose(cur); SQLFreeStmt(cur->hstmt,SQL_DROP); if (cur->PCursor) (cur->PCursor)->NCursor = cur->NCursor; else FCursor = cur->NCursor; if (cur->NCursor) (cur->NCursor)->PCursor = cur->PCursor; else LCursor = cur->PCursor; free(cur); /* (num->CursorCount)-- */ cur = tcur; } else cur = cur->NCursor; } SQLDisconnect(hdbc); SQLFreeConnect(hdbc); /* SQLFreeEnv(henv);*/ serverConnected = 0; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* FindFreeCursor()*/ /* PARAMETERS:*/ /* R1: 7*/ /* R2: Connection Handle*/ /* R3: SQL Statement*/ /* R4: var, in which Cursor addr is returned*/ /* NOTES:*/ /* Find a free statement handler and return its index number into the*/ /* global cursor table. It gives priority to a closed cursor with same*/ /* stantement number over ordinary closed cursors. If there is no handler*/ /* left, function returns NULL. possible cursor status values are*/ /* 0 - never been used - no resource associated w/ the cursor */ /* 1 - used before but having been closed-the cursor has all the resource*/ /* 2 - reusing a used cursor w/ the same statement number, no resource*/ /* needs to be allocated*/ /* 3 - using a cursor that has no resource - it needs to be allocated*/ /*-----------------------------------------------------------------------------*/ void FindFreeCursor() { struct Cursor *curi = FCursor, *curj = NULL, *curk = NULL; struct NumberofCursors *num = FCurNum; HDBC hdbc = (HDBC)ptoc_int(2); char *Sql_stmt = ptoc_longstring(3); RETCODE rc; /* search */ while (curi != NULL) { if (curi->hdbc == hdbc) { /* only look at stmt handles for this connection */ if (curi->Status == 0) curj = curi; /* cursor never been used*/ else { if (curi->Status == 1) { /* a closed cursor*/ /* same statement as this one, so grab and return it*/ if (!strcmp(curi->Sql,Sql_stmt)) { if (curi != FCursor) { (curi->PCursor)->NCursor = curi->NCursor; if (curi == LCursor) LCursor = curi->PCursor; else (curi->NCursor)->PCursor = curi->PCursor; FCursor->PCursor = curi; curi->PCursor = NULL; curi->NCursor = FCursor; FCursor = curi; } curi->Status = 2; ctop_int(4, (long)curi); /*printf("reuse cursor: %p\n",curi);*/ return; } else { curk = curi; /* otherwise just record it*/ } } } } curi = curi->NCursor; } /* done w/ the search; see what was found*/ if (curj != NULL) { /* give priority to an unused cursor*/ curi = curj; /*printf("take unused cursor: %p\n",curi);*/ } else { while((num != NULL) && (num->hdbc != hdbc)){ num=num->NCurNum; } if(num == NULL){ num = (struct NumberofCursors *)malloc(sizeof(struct NumberofCursors)); num->hdbc = hdbc; num->NCurNum=FCurNum; FCurNum=num; num->CursorCount=0; } if (num->CursorCount < MAXCURSORNUM) { /* allocate a new cursor if allowed*/ /* problem here: should have numberOfCursors for each connection */ curi = (struct Cursor *)calloc(sizeof(struct Cursor),1); curi->PCursor = NULL; curi->NCursor = FCursor; if (FCursor == NULL) LCursor = curi; else FCursor->PCursor = curi; FCursor = curi; rc = SQLAllocStmt(hdbc,&(curi->hstmt)); if (!((rc==SQL_SUCCESS) || (rc==SQL_SUCCESS_WITH_INFO))) { free(curi); /* numberOfCursors--; */ xsb_abort("while trying to allocate ODBC statement\n"); } num->CursorCount++; /*printf("allocate a new cursor: %p\n",curi);*/ } else if (curk == NULL) { /* no cursor left*/ ctop_int(4, 0); return; } else { /* steal a cursor*/ curi = curk; SetCursorClose(curi); /*printf("steal a cursor: %p\n",curi);*/ } } /* move to front of list.*/ if (curi != FCursor) { (curi->PCursor)->NCursor = curi->NCursor; if (curi == LCursor) LCursor = curi->PCursor; else (curi->NCursor)->PCursor = curi->PCursor; FCursor->PCursor = curi; curi->PCursor = NULL; curi->NCursor = FCursor; FCursor = curi; } curi->hdbc = hdbc; curi->Sql = (UCHAR *)strdup(Sql_stmt); if (!curi->Sql) xsb_exit("Not enough memory for SQL stmt in FindFreeCursor!"); curi->Status = 3; ctop_int(4, (long)curi); return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* SetBindVarNum() */ /* PARAMETERS:*/ /* R1: 3*/ /* R2: Cursor Address*/ /* R3: Number of bind values*/ /* NOTES:*/ /* set the number of bind variables. Note that the memory to*/ /* store their values is not allocated here since we don't know*/ /* their type: no information on how much memory is needed. If*/ /* we're reusing an old statement handler we don't have to worry*/ /* about these things. All we need to do is to make sure that*/ /* the statement is indeed the same statement w/ the same bind*/ /* variable number.*/ /*-----------------------------------------------------------------------------*/ void SetBindVarNum() { struct Cursor *cur = (struct Cursor *)ptoc_int(2); int NumBindVars = ptoc_int(3); if (cur->Status == 2) { if (cur->NumBindVars != NumBindVars) xsb_exit("Number of Bind values provided does not agree with query\n"); return; } cur->NumBindVars = NumBindVars; cur->BindList = malloc(sizeof(UCHAR *) * NumBindVars); if (!cur->BindList) xsb_exit("Not enough memory for cur->BindList!"); cur->BindTypes = malloc(sizeof(int) * NumBindVars); if (!cur->BindTypes) xsb_exit("Not enough memory for cur->BindTypes!"); } void write_canonical_term(Cell prologterm); extern char *wcan_string; extern int wcan_disp; extern int letter_flag; #define MAX_BIND_VALS 30 char *term_string[MAX_BIND_VALS] = {0}; /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* SetBindVal() */ /* PARAMETERS:*/ /* R1: 6*/ /* R2: Cursor Addr*/ /* R3: Bind variable index (starting from 0)*/ /* R4: Value to give the bind variable*/ /* R5: var, for returned status*/ /* NOTES:*/ /* set the bind variables' values. */ /* allocate memory if it is needed(status == 3)*/ /*-----------------------------------------------------------------------------*/ void SetBindVal() { RETCODE rc; struct Cursor *cur = (struct Cursor *)ptoc_int(2); int j = ptoc_int(3); Cell BindVal = ptoc_tag(4); if (!((j >= 0) && (j < cur->NumBindVars))) xsb_exit("Abnormal argument in SetBindVal!"); /* if we're reusing an opened cursor w/ the statement number*/ /* reallocate BindVar if type has changed (May not be such a good idea?)*/ if (cur->Status == 2) { if (isinteger(BindVal)) { if (cur->BindTypes[j] != 0) { if (cur->BindTypes[j] != 2) free((void *)cur->BindList[j]); cur->BindList[j] = (UCHAR *)malloc(sizeof(int)); cur->BindTypes[j] = 0; rc = SQLBindParameter(cur->hstmt, (short)(j+1), SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, (int *)(cur->BindList[j]), 0, NULL); if (rc != SQL_SUCCESS) { ctop_int(5,PrintErrorMsg(cur)); SetCursorClose(cur); return; } } *((int *)cur->BindList[j]) = oint_val(BindVal); } else if (isfloat(BindVal)) { if (cur->BindTypes[j] != 1) { /*printf("ODBC: Changing Type: flt to %d\n",cur->BindTypes[j]);*/ if (cur->BindTypes[j] != 2) free((void *)cur->BindList[j]); cur->BindList[j] = (UCHAR *)malloc(sizeof(float)); cur->BindTypes[j] = 1; rc = SQLBindParameter(cur->hstmt, (short)(j+1), SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, 0, 0, (float *)(cur->BindList[j]), 0, NULL); if (rc != SQL_SUCCESS) { ctop_int(5,PrintErrorMsg(cur)); SetCursorClose(cur); return; } } *((float *)cur->BindList[j]) = (float)float_val(BindVal); } else if (isstring(BindVal)) { if (cur->BindTypes[j] != 2) { /*printf("ODBC: Changing Type: str to %d\n",cur->BindTypes[j]);*/ free((void *)cur->BindList[j]); cur->BindTypes[j] = 2; /* SQLBindParameter will be done anyway*/ } cur->BindList[j] = string_val(BindVal); } else if (isconstr(BindVal) && get_arity(get_str_psc(BindVal))==1) { letter_flag = 1; wcan_disp = 0; write_canonical_term(p2p_arg(BindVal,1)); if (term_string[j]) free(term_string[j]); term_string[j] = malloc(wcan_disp+1); strncpy(term_string[j],wcan_string,wcan_disp); term_string[j][wcan_disp] = '\0'; cur->BindTypes[j] = 2; cur->BindList[j] = term_string[j]; } else { xsb_exit("Unknown bind variable type, %d", cur->BindTypes[j]); } ctop_int(5,0); return; } /* otherwise, memory needs to be allocated in this case*/ if (isinteger(BindVal)) { cur->BindTypes[j] = 0; cur->BindList[j] = (UCHAR *)malloc(sizeof(int)); if (!cur->BindList[j]) xsb_exit("Not enough memory for an int in SetBindVal!"); *((int *)cur->BindList[j]) = oint_val(BindVal); } else if (isfloat(BindVal)) { cur->BindTypes[j] = 1; cur->BindList[j] = (UCHAR *)malloc(sizeof(float)); if (!cur->BindList[j]) xsb_exit("Not enough memory for a float in SetBindVal!"); *((float *)cur->BindList[j]) = (float)float_val(BindVal); } else if (isstring(BindVal)) { cur->BindTypes[j] = 2; cur->BindList[j] = string_val(BindVal); } else if (isconstr(BindVal) && get_arity(get_str_psc(BindVal))==1) { letter_flag = 1; wcan_disp = 0; write_canonical_term(p2p_arg(BindVal,1)); if (term_string[j]) free(term_string[j]); term_string[j] = malloc(wcan_disp+1); strncpy(term_string[j],wcan_string,wcan_disp); term_string[j][wcan_disp] = '\0'; cur->BindTypes[j] = 2; cur->BindList[j] = term_string[j]; } else { xsb_exit("Unknown bind variable type, %d", cur->BindTypes[j]); } ctop_int(5,0); return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* Parse()*/ /* PARAMETERS:*/ /* R1: 2*/ /* R2: the SQL statement for the cursor*/ /* R3: var, returned cursor address*/ /* NOTES:*/ /* parse the sql statement and submit it to DBMS to execute. if all these*/ /* succeed, then prepare for resulting row fetching. this includes*/ /* determination of column number in the resulting rowset and the length*/ /* of each column and memory allocation which is used to store each row.*/ /*-----------------------------------------------------------------------------*/ void Parse() { int j; struct Cursor *cur = (struct Cursor *)ptoc_int(2); RETCODE rc; if (cur->Status == 2) { /* reusing opened cursor*/ rc = SQLFreeStmt(cur->hstmt,SQL_CLOSE); if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) { ctop_int(3, PrintErrorMsg(cur)); SetCursorClose(cur); return; } /* reset just char select vars, since they store addr of chars*/ for (j = 0; j < cur->NumBindVars; j++) { if (cur->BindTypes[j] == 2) rc = SQLBindParameter(cur->hstmt, (short)(j+1), SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0,(char *) cur->BindList[j], 0, &SQL_NTSval); } } else { if (SQLPrepare(cur->hstmt, cur->Sql, SQL_NTS) != SQL_SUCCESS) { ctop_int(3,PrintErrorMsg(cur)); SetCursorClose(cur); return; } /* set the bind variables*/ for (j = 0; j < cur->NumBindVars; j++) { if (cur->BindTypes[j] == 2) /* we're sloppy here. it's ok for us to use the default values*/ rc = SQLBindParameter(cur->hstmt, (short)(j+1), SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0,(char *)cur->BindList[j], 0, &SQL_NTSval); else if (cur->BindTypes[j] == 1) { rc = SQLBindParameter(cur->hstmt, (short)(j+1), SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, 0, 0, (float *)cur->BindList[j], 0, NULL); } else rc = SQLBindParameter(cur->hstmt, (short)(j+1), SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, (int *)(cur->BindList[j]), 0, NULL); if (rc != SQL_SUCCESS) { ctop_int(3,PrintErrorMsg(cur)); SetCursorClose(cur); return; } } } /* submit it for execution*/ if (SQLExecute(cur->hstmt) != SQL_SUCCESS) { ctop_int(3,PrintErrorMsg(cur)); SetCursorClose(cur); return; } ctop_int(3,0); return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* ODBCCommit()*/ /* PARAMETERS:*/ /* R1: 10*/ /* R2: Connection Handle*/ /* R3: var, returned status, 0 if successful*/ /*-----------------------------------------------------------------------------*/ void ODBCCommit() { struct Cursor *cur = FCursor; HDBC hdbc = (HDBC)ptoc_int(2); RETCODE rc; if (((rc=SQLTransact(henv,hdbc,SQL_COMMIT)) == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO)) { /* close only those with right hdbc....*/ while (cur != NULL) { if (cur->hdbc == hdbc && cur->Status != 0) SetCursorClose(cur); cur = cur->NCursor; } ctop_int(3,0); } else ctop_int(3,PrintErrorMsg(NULL)); return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* ODBCRollback()*/ /* PARAMETERS:*/ /* R1: 11*/ /* R2: Connection Handle*/ /* R3: var, for returned status: 0 if successful*/ /*-----------------------------------------------------------------------------*/ void ODBCRollback() { struct Cursor *cur = FCursor; HDBC hdbc = (HDBC)ptoc_int(2); RETCODE rc; if (((rc=SQLTransact(henv,hdbc,SQL_ROLLBACK)) == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO)) { /* only close those with right hdbc*/ while (cur != NULL) { if (cur->hdbc == hdbc && cur->Status != 0) SetCursorClose(cur); cur = cur->NCursor; } ctop_int(3,0); } else ctop_int(3, PrintErrorMsg(NULL)); return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* ODBCColumns()*/ /* PARAMETERS:*/ /* R1: 12*/ /* R2: Cursor Index*/ /* R3: Table name*/ /* R4: var for returned status: 0 if successful*/ /*-----------------------------------------------------------------------------*/ void ODBCColumns() { struct Cursor *cur = (struct Cursor *)ptoc_int(2); char tmpstr[255]; char *str1, *str2, *str3; RETCODE rc; strcpy(tmpstr,ptoc_string(3)); str1 = strtok(tmpstr,"."); str2 = str3 = NULL; if (str1) str2 = strtok(NULL,"."); if (str2) str3 = strtok(NULL,"."); if (!str3 && !str2) {str3 = str1; str1 = NULL;} else if (!str3) {str3 = str2; str2 = NULL;} /* printf("str1 %s, str2 %s, str3 %s\n",str1,str2,str3);*/ if (((rc=SQLColumns(cur->hstmt, str1, SQL_NTS, str2, SQL_NTS, str3, SQL_NTS, NULL,0)) == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO)) { ctop_int(4,0); } else { ctop_int(4,PrintErrorMsg(cur)); SetCursorClose(cur); } return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* ODBCTables()*/ /* PARAMETERS:*/ /* R1: 13*/ /* R2: Cursor*/ /* R3: var, returned status: 0 if successful*/ /*-----------------------------------------------------------------------------*/ void ODBCTables() { struct Cursor *cur = (struct Cursor *)ptoc_int(2); RETCODE rc; if (cur->Status == 2) { /* reusing opened cursor*/ rc = SQLFreeStmt(cur->hstmt,SQL_CLOSE); if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) { ctop_int(3, PrintErrorMsg(cur)); SetCursorClose(cur); return; } } if (((rc=SQLTables(cur->hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0)) == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO)) { ctop_int(3,0); } else { ctop_int(3,PrintErrorMsg(cur)); SetCursorClose(cur); } return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* ODBCUserTables()*/ /* PARAMETERS:*/ /* R1: 14*/ /* R2: Cursor*/ /* R3: var, for returned status: 0 if successful*/ /*-----------------------------------------------------------------------------*/ void ODBCUserTables() { struct Cursor *cur = (struct Cursor *)ptoc_int(2); UWORD TablePrivilegeExists; RETCODE rc; /* since some ODBC drivers don't implement the function SQLTablePrivileges*/ /* we check it first*/ SQLGetFunctions(cur->hdbc,SQL_API_SQLTABLEPRIVILEGES,&TablePrivilegeExists); if (!TablePrivilegeExists) { printf("Privilege concept does not exist in this DVMS: you probably can access any of the existing tables\n"); ctop_int(3, 2); return; } if (((rc=SQLTablePrivileges(cur->hstmt, NULL, 0, NULL, 0, NULL, 0)) == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO)) ctop_int(3,0); else { ctop_int(3,PrintErrorMsg(cur)); SetCursorClose(cur); } return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* DisplayColSize()*/ /* PARAMETERS:*/ /* SWORD coltype - column type which is a single word.*/ /* UDWORD collen - column length which is returned by SQLDescribeCol*/ /* UCHAR *colname - pointer to column name string*/ /* RETURN VALUE:*/ /* column length - the size of memory that is needed to store the column*/ /* value for supported column types*/ /* 0 - otherwise*/ /*-----------------------------------------------------------------------------*/ UDWORD DisplayColSize(SWORD coltype, UDWORD collen, UCHAR *colname) { switch (ODBCToXSBType(coltype)) { case SQL_C_SLONG: return sizeof(long *); case SQL_C_CHAR: { UDWORD tmp = MAXI(collen+1, strlen((char *) colname)); if (tmp < MAXVARSTRLEN) return tmp; else return MAXVARSTRLEN; } case SQL_C_BINARY: { return MAXVARSTRLEN; } case SQL_C_FLOAT: return sizeof(float *); default: printf("Illegal ODBC Type: %d\n",coltype); return 0; } } extern xsbBool unify(Cell, Cell); /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* ODBCDataSources()*/ /* PARAMETERS:*/ /* R1: 1 - first call; 2 - subsequent calls */ /* R2: var, returns DSN */ /* R3: var, returns DS description */ /* R4: var, returns status */ /* NOTES:*/ /*-----------------------------------------------------------------------------*/ void ODBCDataSources() { static SQLCHAR DSN[SQL_MAX_DSN_LENGTH+1]; static SQLCHAR Description[SQL_MAX_DSN_LENGTH+1]; RETCODE rc; int seq; SWORD dsn_size, descr_size; Cell op2 = ptoc_tag(3); Cell op3 = ptoc_tag(4); if (!henv) { /* allocate environment handler*/ rc = SQLAllocEnv(&henv); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { xsb_error("Environment allocation failed"); ctop_int(5,1); return; } LCursor = FCursor = NULL; FCurNum = NULL; nullStrAtom = makestring(string_find("NULL",1)); } seq = ptoc_int(2); if (seq == 1) { rc = SQLDataSources(henv,SQL_FETCH_FIRST,DSN, SQL_MAX_DSN_LENGTH,&dsn_size, Description,SQL_MAX_DSN_LENGTH, &descr_size); if (rc == SQL_NO_DATA_FOUND) { ctop_int(5,2); return; } if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { xsb_error("Environment allocation failed"); ctop_int(5,1); return; } } else { rc = SQLDataSources(henv,SQL_FETCH_NEXT,DSN, SQL_MAX_DSN_LENGTH,&dsn_size, Description,SQL_MAX_DSN_LENGTH, &descr_size); if (rc == SQL_NO_DATA_FOUND) { ctop_int(5,2); return; } if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { xsb_error("Environment allocation failed"); ctop_int(5,1); return; } } XSB_Deref(op2); if (isref(op2)) unify(op2, makestring(string_find(DSN,1))); else { xsb_error("[ODBCDataSources] Param 2 should be a free variable."); ctop_int(5,1); return; } XSB_Deref(op3); if (isref(op3)) unify(op3, makestring(string_find(Description,1))); else { xsb_error("[ODBCDataSources] Param 3 should be a free variable."); ctop_int(5,1); return; } ctop_int(5,0); return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* DescribeSelect()*/ /* PARAMETERS:*/ /* R1: 15*/ /* R2: Ptr to cursor record*/ /* R3: var, for returned status:*/ /* 0 - the result row has at least one column and*/ /* 1 - something goes wrong, we can't retrieve column information, */ /* memory allocation fails (if this happens program stops).*/ /* 2 - no column is affected*/ /* NOTES:*/ /* memory is also allocated for future data storage*/ /*-----------------------------------------------------------------------------*/ void ODBCDescribeSelect() { int j; UCHAR colname[50]; SWORD colnamelen; SWORD scale; SWORD nullable; UDWORD collen; struct Cursor *cur = (struct Cursor *)ptoc_int(2); cur->NumCols = 0; SQLNumResultCols(cur->hstmt, (SQLSMALLINT*)&(cur->NumCols)); if (!(cur->NumCols)) { /* no columns are affected, set cursor status to unused */ cur->Status = 1; ctop_int(3,2); return; } /* if we aren't reusing a closed statement handle, we need to get*/ /* resulting rowset info and allocate memory for it*/ if (cur->Status != 2) { cur->ColTypes = (SWORD *)malloc(sizeof(SWORD) * cur->NumCols); if (!cur->ColTypes) xsb_exit("Not enough memory for ColTypes!"); cur->Data = (UCHAR **)malloc(sizeof(char *) * cur->NumCols); if (!cur->Data) xsb_exit("Not enough memory for Data!"); cur->OutLen = (UDWORD *)malloc(sizeof(UDWORD) * cur->NumCols); if (!cur->OutLen) xsb_exit("Not enough memory for OutLen!"); cur->ColLen = (UDWORD *)malloc(sizeof(UDWORD) * cur->NumCols); if (!cur->ColLen) xsb_exit("Not enough memory for ColLen!"); for (j = 0; j < cur->NumCols; j++) { SQLDescribeCol(cur->hstmt, (short)(j+1), (UCHAR FAR*)colname, sizeof(colname), &colnamelen, &(cur->ColTypes[j]), &collen, &scale, &nullable); /* SQLServer returns this wierd type for a system table, treat it as varchar?*/ if (cur->ColTypes[j] == -9) cur->ColTypes[j] = SQL_VARCHAR; colnamelen = (colnamelen > 49) ? 49 : colnamelen; colname[colnamelen] = '\0'; if (!(cur->ColLen[j] = DisplayColSize(cur->ColTypes[j],collen,colname))) { /* let SetCursorClose function correctly free all the memory allocated*/ /* for Data storage: cur->Data[j]'s*/ cur->NumCols = j; /* set so close frees memory allocated thus far*/ SetCursorClose(cur); /* return(1);*/ ctop_int(3,1); return; } cur->Data[j] = (UCHAR *) malloc(((unsigned) cur->ColLen[j]+1)*sizeof(UCHAR)); if (!cur->Data[j]) xsb_exit("Not enough memory for Data[j]!"); } } /* bind them*/ for (j = 0; j < cur->NumCols; j++) { SQLBindCol(cur->hstmt, (short)(j+1), ODBCToXSBType(cur->ColTypes[j]), cur->Data[j], cur->ColLen[j], (SDWORD FAR *)(&(cur->OutLen[j]))); } /* return 0;*/ ctop_int(3,0); return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* FetchNextRow()*/ /* PARAMETERS:*/ /* R1: 4*/ /* R2: Cursor Address*/ /* R3: var, for returned status*/ /* 0 => successful, a row is read and available*/ /* 1 => successful, no row avaliable*/ /* 2 => unsuccessful, an error occurred in fetching.*/ /* NOTES:*/ /* fetch next row of result rowset.*/ /*-----------------------------------------------------------------------------*/ void FetchNextRow() { struct Cursor *cur = (struct Cursor *)ptoc_int(2); RETCODE rc; if (!serverConnected || cur->Status == 0) { ctop_int(3,2); return; } rc = SQLFetch(cur->hstmt); if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO)) ctop_int(3,0); else if (rc == SQL_NO_DATA_FOUND){ cur->Status = 1; /* done w/fetching. set cursor status to unused */ ctop_int(3,1); } else { SetCursorClose(cur); /* error occured in fetching*/ ctop_int(3,2); } return; } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* ODBCConnectOption() */ /* PARAMETERS:*/ /* R1: 16*/ /* R2: Connection Handle*/ /* R3: 0 -> get; 1 -> set*/ /* R4: Option indicator (int)*/ /* R5: if R2=0 -> variable that attr val is returned in*/ /* if R2=1 -> value used to set attr*/ /* R6: Return Code*/ /* NOTES:*/ /* either gets value of a connection option, or sets it.*/ /*-----------------------------------------------------------------------------*/ void ODBCConnectOption() { HDBC hdbc = (HDBC)ptoc_int(2); int set = ptoc_int(3); long value = 0; RETCODE rc; if (set) { rc = SQLSetConnectOption(hdbc,(UWORD)ptoc_int(4),(UDWORD)ptoc_int(5)); } else { rc = SQLGetConnectOption(hdbc,(UWORD)ptoc_int(4),(PTR)&value); ctop_int(5,value); } if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO)) ctop_int(6,0); else ctop_int(6,PrintErrorMsg(NULL)); } /*-----------------------------------------------------------------------------*/ /* FUNCTION NAME:*/ /* GetColumn() */ /* PARAMETERS:*/ /* R1: 5*/ /* R2: Cursor*/ /* R3: Column Index*/ /* R4: Column Value returned*/ /* R5: Return Code*/ /* NOTES:*/ /* get the indicated column.*/ /*-----------------------------------------------------------------------------*/ int GetColumn() { struct Cursor *cur = (struct Cursor *)ptoc_int(2); int ColCurNum = ptoc_int(3); Cell op1; Cell op = ptoc_tag(4); UDWORD len; if (ColCurNum < 0 || ColCurNum >= cur->NumCols) { /* no more columns in the result row*/ ctop_int(5,1); return TRUE; } ctop_int(5,0); /* get the data*/ if (cur->OutLen[ColCurNum] == SQL_NULL_DATA) { /* column value is NULL*/ return unify(op,nullStrAtom); } /* convert the string to either integer, float or string*/ /* according to the column type and pass it back to XSB*/ switch (ODBCToXSBType(cur->ColTypes[ColCurNum])) { case SQL_C_CHAR: /* convert the column string to a C string */ len = ((cur->ColLen[ColCurNum] < cur->OutLen[ColCurNum])? cur->ColLen[ColCurNum]:cur->OutLen[ColCurNum]); *(cur->Data[ColCurNum]+len) = '\0'; /* compare strings here, so don't intern strings unnecessarily*/ XSB_Deref(op); if (isref(op)) return unify(op, makestring(string_find(cur->Data[ColCurNum],1))); if (isconstr(op) && get_arity(get_str_psc(op)) == 1) { STRFILE strfile; op1 = cell(clref_val(op)+1); XSB_Deref(op1); strfile.strcnt = strlen(cur->Data[ColCurNum]); strfile.strptr = strfile.strbase = cur->Data[ColCurNum]; read_canonical_term(NULL,&strfile,op1); /* terminating '.'? */ return TRUE; } if (!isstring(op)) return FALSE; if (strcmp(string_val(op),cur->Data[ColCurNum])) return FALSE; return TRUE; case SQL_C_BINARY: /* convert the column string to a C string */ len = ((cur->ColLen[ColCurNum] < cur->OutLen[ColCurNum])? cur->ColLen[ColCurNum]:cur->OutLen[ColCurNum]); *(cur->Data[ColCurNum]+len) = '\0'; /* compare strings here, so don't intern strings unnecessarily*/ XSB_Deref(op); if (isref(op)) return unify(op, makestring(string_find(cur->Data[ColCurNum],1))); if (isconstr(op) && get_arity(get_str_psc(op)) == 1) { STRFILE strfile; op1 = cell(clref_val(op)+1); XSB_Deref(op1); strfile.strcnt = strlen(cur->Data[ColCurNum]); strfile.strptr = strfile.strbase = cur->Data[ColCurNum]; read_canonical_term(NULL,&strfile,op1); /* terminating '.'? */ return TRUE; } if (!isstring(op)) return FALSE; if (strcmp(string_val(op),cur->Data[ColCurNum])) return FALSE; return TRUE; case SQL_C_SLONG: return unify(op,makeint(*(long *)(cur->Data[ColCurNum]))); case SQL_C_FLOAT: return unify(op,makefloat(*(float *)(cur->Data[ColCurNum]))); } return FALSE; }