ref: 866d74c0c4bb50e85e9e8bb95140c10d409e53be
dir: /tools/db/infdb.c/
// // infdb - NT data base daemon for Inferno // // Copyright 1997 Lucent Technologies // // May 1997 // #include <stdio.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #ifdef WIN32 #include <winsock.h> #include <SQL.h> #include <SQLEXT.h> #else #include <unistd.h> #include <sql.h> #include <sqlext.h> #define max(a, b) ((a) > (b) ? (a) : (b)) #define strnicmp strncasecmp #endif #define MAXCOLS 100 #define BUFSIZE 8192 #define REQ_HEADER_SIZE 18 #define RES_HEADER_SIZE 22 #define OFFSET_LENGTH 2 #define OFFSET_STREAM 14 #define OFFSET_REQ_DATA 18 #define OFFSET_RETURN 18 #define OFFSET_RES_DATA 22 #define CONN_ALLOC_FAIL 1 #define STREAM_ALLOC_FAIL 2 #define STREAM_BAD_ID 3 #define LAST_ERROR_NO 4 // // Deal with one connection. Use stdin and stdout to read and write messages. // Each incoming message is answered before reading the next incoming message. // typedef int STATUS; #define OK 0 #define WARN -1 #define ERR -2 typedef struct { int state; #define SQLC_FREE 0 #define SQLC_INUSE 1 int connid; int refcount; UCHAR user[48]; UCHAR passwd[48]; UCHAR dbname[48]; UCHAR errmsg[256]; HDBC hdbc; } SQLConn; typedef struct { int state; #define SQLS_FREE 0 #define SQLS_INUSE 1 int streamid; int connid; HSTMT hstmt; UCHAR errmsg[256]; UCHAR colname[MAXCOLS][32]; SWORD coltype[MAXCOLS]; SWORD colnamelen; SWORD nullable; UDWORD collen[MAXCOLS]; SWORD scale; SDWORD outlen[MAXCOLS]; UCHAR *data[MAXCOLS]; SWORD nresultcols; SDWORD rowcount; SWORD rownum; RETCODE rc; UCHAR *setdata[MAXCOLS]; SDWORD setdatalen[MAXCOLS]; } SQLStream; typedef struct { HENV henv; int maxconn; int numconn; SQLConn **scarray; int maxstream; int numstream; SQLStream **ssarray; } SQLEnv; typedef struct { char mtype; char version; int nbytes; int sstream; int retcode; int bytesNotRead; char *data; } DBMSG, *DBMSGP; int getCommand (DBMSGP msgp, UCHAR *buf, int bufsiz); void sendResponse (char type, int lendata, int sstream, int retcode, char *data); void sendError (char *errmsg, int sstream); void print_err (SQLEnv *sqle, int connid, int streamid, UCHAR * buf, int bufsiz); UDWORD display_size (SWORD coltype, UDWORD collen, UCHAR *colname); STATUS newSqlEnv (SQLEnv **sqle); STATUS freeSqlEnv (SQLEnv **sqle); STATUS newSqlConn (SQLEnv *sqle, char *info, int *connid); STATUS mapSqlConn (SQLEnv *sqle, int connid, SQLConn **sqlc); STATUS freeSqlConn (SQLEnv *sqle, int connid); STATUS newSqlStream (SQLEnv *sqle, int connid, int *streamid); STATUS mapSqlStream (SQLEnv *sqle, int streamid, SQLStream **sqls); STATUS freeSqlStream (SQLEnv *sqle, int streamid); STATUS parseConnInfo (SQLConn *sqlc, char *info); char *iError[] = { "INFDB: DB connection allocation failed", "INFDB: couldn't allocate SQL stream", "INFDB: bad SQL stream identifier" }; int main(int argc, char *argv[]) { int i; int notdone = 1; int infErrno; DBMSG msg; SQLEnv *sqle = NULL; SQLStream *sqls; char buf[BUFSIZE]; char outbuf[BUFSIZE]; char errmsg[256]; STATUS rc; // We just have to talk to stdin and stdout. However, stdout may be open // in text mode, which is bad for data. Set it to binary mode. #ifdef WIN32 _setmode(0, _O_BINARY); _setmode(1, _O_BINARY); #endif rc = newSqlEnv(&sqle); if ( rc != OK ) { sendError("INFDB: Failed to allocate SQL environment.", -1); return -1; } while ( notdone ) { int bytesRead; bytesRead = 0; if ( (bytesRead = getCommand(&msg, buf, sizeof(buf))) <= 0 ) { continue; } msg.retcode = 0; infErrno = 0; switch ( msg.mtype ) { // Initiate a new connection. case 'I': { int connid; rc = newSqlConn(sqle, msg.data, &connid); if ( rc != OK ) { infErrno = CONN_ALLOC_FAIL; break; } /* // Need a new SQLStream to make subsequent requests. rc = newSqlStream(sqle, connid, &streamid); if ( rc != OK ) { infErrno = STREAM_ALLOC_FAIL; break; } sprintf(outbuf, "%d", streamid); */ sprintf(outbuf, "%d", connid); sendResponse('i', strlen(outbuf), msg.sstream, 0, outbuf); break; } case 'O': { // open an SQL stream. int connid, streamid; connid = atoi(msg.data); rc = newSqlStream(sqle, connid, &streamid); if (rc != OK) { infErrno = STREAM_ALLOC_FAIL; break; } sprintf(outbuf, "%d", streamid); sendResponse('o', strlen(outbuf), msg.sstream, 0, outbuf); break; } case 'K': // klose an SQL stream rc = freeSqlStream(sqle, msg.sstream); sendResponse('k', 0, msg.sstream, 0, ""); break; case 'C': // request number of columns rc = mapSqlStream(sqle, msg.sstream, &sqls); if ( rc != OK ) { infErrno = STREAM_BAD_ID; break; } sprintf(outbuf, "%d", sqls->nresultcols); sendResponse('c', strlen(outbuf), msg.sstream, 0, outbuf); break; case 'N': // fetch next row rc = mapSqlStream(sqle, msg.sstream, &sqls); if ( rc != OK ) { infErrno = STREAM_BAD_ID; break; } sqls->errmsg[0] = '\0'; rc = SQLFetch(sqls->hstmt); if ( rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO ) { sqls->rownum++; // if ( rc == SQL_SUCCESS_WITH_INFO ) { // fprintf(stderr, "SQLFetch got SQL_SUCCESS_WITH_INFO\n"); // } /* Get the data for all columns */ for ( i = 0; i < sqls->nresultcols; i++ ) { rc = SQLGetData(sqls->hstmt, (UWORD)(i+1), (sqls->coltype[i] == SQL_LONGVARBINARY || sqls->coltype[i] == SQL_LONGVARCHAR) ? SQL_C_DEFAULT : SQL_C_CHAR, sqls->data[i], sqls->collen[i], &sqls->outlen[i]); if ( rc == SQL_SUCCESS_WITH_INFO && (UDWORD) sqls->outlen[i] > sqls->collen[i] ) { UCHAR *tmp; tmp = (UCHAR *) realloc(sqls->data[i], sqls->outlen[i]+1); if ( tmp != NULL ) { SDWORD dummy; sqls->data[i] = tmp; rc = SQLGetData(sqls->hstmt, (UWORD)(i+1), SQL_C_DEFAULT, &tmp[sqls->collen[i]], sqls->outlen[i], &dummy); sqls->collen[i] = sqls->outlen[i]; sqls->data[i][sqls->outlen[i]] = 0; } } else if ( rc != SQL_SUCCESS ) { sprintf(sqls->errmsg, "Problem retrieving data from data base, col %d", i+1); msg.retcode = 2; } } } else if ( rc == SQL_NO_DATA_FOUND ) { sqls->rownum = 0; msg.retcode = 1; } else { sqls->rownum = -1; // Probably should get some status from ODBC for message sprintf(sqls->errmsg, "Error occurred in fetching data"); } if ( sqls->rownum < 0 ) { sendError(errmsg, msg.sstream); } else { sprintf(outbuf, "%d", sqls->rownum); // rownum should be <= rowcount sendResponse('n', strlen(outbuf), msg.sstream, msg.retcode, outbuf); } break; case 'H': // request an error message, if any rc = mapSqlStream(sqle, msg.sstream, &sqls); if ( rc != OK ) { infErrno = STREAM_BAD_ID; break; } sendError(sqls->errmsg, msg.sstream); sqls->errmsg[0] = 0; break; case 'P': // request write data // in Inferno, param nums start at 0; in ODBC/SQL, they start at 1 // This leaves outdatalen[0] unused, hence available as final SQLBindParameter arg. rc = mapSqlStream(sqle, msg.sstream, &sqls); if ( rc != OK ) { infErrno = STREAM_BAD_ID; break; } sqls->errmsg[0] = 0; if ( (i = atoi(msg.data) + 1) < 1 || i >= MAXCOLS ) { sendError("Illegal param number", msg.sstream); } else { int len; char *p = msg.data + 4; // data points to param number len = msg.nbytes - 4; // number of data chars if ( len < 0 ) { sendError("Write phase error II", msg.sstream); break; } if ( sqls->setdata[i] != NULL ) { free(sqls->setdata[i]); } sqls->setdata[i] = (char *) malloc(len + 1); if ( sqls->setdata[i] == NULL ) { sendError("Allocation error in server", msg.sstream); break; } // Copy data we have into buffer, and if we don't have it all yet, // try to get the rest. sqls->setdatalen[i] = len++; // adjust len for trailing \n bytesRead = &buf[bytesRead] - p; // number data bytes we have read memcpy(sqls->setdata[i], p, bytesRead); len -= bytesRead; // number bytes still to read, while ( len > 0 ) { int n; if ( (n = read(0, sqls->setdata[i] + bytesRead, len)) <= 0 ) { break; } bytesRead += n; len -= n; } if ( len > 0 ) { sendError("Couldn't read all of parameter", msg.sstream); break; } rc = SQLBindParameter(sqls->hstmt, (UWORD)i, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, sqls->setdatalen[i], 0, (PTR) i, 0, sqls->setdatalen); if ( rc != SQL_SUCCESS ) { sendError("BindParameter failed: maybe not supported", msg.sstream); break; } sqls->setdatalen[0] = SQL_LEN_DATA_AT_EXEC(0); sprintf(outbuf, "%d", bytesRead - 1); sendResponse('p', strlen(outbuf), msg.sstream, 0, outbuf); } break; case 'R': // request read data rc = mapSqlStream(sqle, msg.sstream, &sqls); if ( rc != OK ) { infErrno = STREAM_BAD_ID; break; } if ( (i = atoi(msg.data)) < 0 || i >= sqls->nresultcols || sqls->rownum <= 0 ) { sendError(sqls->rownum <= 0 ? "No current row" : "Illegal column number", msg.sstream); } else if ( sqls->outlen[i] == SQL_NULL_DATA || sqls->outlen[i] == SQL_NO_TOTAL ) { sendResponse('r', 0, msg.sstream, 0, ""); } else { if ( sqls->coltype[i] == SQL_VARCHAR ) { sqls->outlen[i] = strlen(sqls->data[i]); } sendResponse('r', sqls->outlen[i], msg.sstream, 0, sqls->data[i]); } break; case 'T': // request column title rc = mapSqlStream(sqle, msg.sstream, &sqls); if ( rc != OK ) { infErrno = STREAM_BAD_ID; break; } if ( (i = atoi(msg.data)) < 0 || i >= sqls->nresultcols ) { sendError("Illegal column number", msg.sstream); } else { sendResponse('t', strlen(sqls->colname[i]), msg.sstream, 0, sqls->colname[i]); } break; case 'W': // execute command rc = mapSqlStream(sqle, msg.sstream, &sqls); if ( rc != OK ) { infErrno = STREAM_BAD_ID; break; } if ( sqls->hstmt ) { SQLFreeStmt(sqls->hstmt, SQL_CLOSE); SQLFreeStmt(sqls->hstmt, SQL_UNBIND); } // Look for special extensions if ( strnicmp(msg.data, "commit", 6) == 0 ) { SQLConn *sqlc; rc = mapSqlConn(sqle, sqls->connid, &sqlc); rc = SQLTransact(SQL_NULL_HENV, sqlc->hdbc, SQL_COMMIT); } else if ( strnicmp(msg.data, "rollback", 8) == 0 ) { SQLConn *sqlc; rc = mapSqlConn(sqle, sqls->connid, &sqlc); rc = SQLTransact(SQL_NULL_HENV, sqlc->hdbc, SQL_ROLLBACK); } else if ( strnicmp(msg.data, "tables", 6) == 0 ) { rc = SQLTables(sqls->hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0); } else if ( strnicmp(msg.data, "columns", 7) == 0 ) { UCHAR *tbl; for ( tbl = msg.data+8; *tbl == ' ' || *tbl == '\t'; tbl++ ) { } rc = SQLColumns(sqls->hstmt, NULL, 0, NULL, 0, tbl, SQL_NTS, NULL, 0); } else { rc = SQLExecDirect(sqls->hstmt, msg.data, SQL_NTS); } outbuf[0] = '\0'; while ( rc == SQL_NEED_DATA ) { PTR pToken; // SDWORD pnum; rc = SQLParamData(sqls->hstmt, &pToken); // pnum = (SDWORD) pToken; #define pnum (int)pToken if ( rc == SQL_NEED_DATA ) { int retcode; if ( sqls->setdata[pnum] == NULL || sqls->setdatalen[pnum] <= 0 ) { sprintf(outbuf, "Parameter %d not set\n", pnum); break; } for ( i = 0; i < sqls->setdatalen[pnum]; ) { int togo; togo = 1024; if ( sqls->setdatalen[pnum] - i < 1024 ) { togo = sqls->setdatalen[pnum] - i; } retcode = SQLPutData(sqls->hstmt, sqls->setdata[pnum] + i, togo); i += togo; if ( retcode != SQL_SUCCESS ) { print_err(sqle, -1, msg.sstream, &outbuf[strlen(outbuf)], sizeof (outbuf) - strlen(outbuf)); break; } } if ( retcode != SQL_SUCCESS /* && retcode != SQL_SUCCESS_WITH_INFO */) { break; } } } if ( rc != SQL_SUCCESS ) { strcat(outbuf, "Command execution failed\n"); switch ( rc ) { case SQL_SUCCESS_WITH_INFO: strcat(outbuf, ": SQL_SUCCESS_WITH_INFO"); print_err(sqle, -1, msg.sstream, &outbuf[strlen(outbuf)], sizeof (outbuf) - strlen(outbuf)); break; case SQL_ERROR: strcat(outbuf, ": SQL_ERROR"); print_err(sqle, -1, msg.sstream, &outbuf[strlen(outbuf)], sizeof (outbuf) - strlen(outbuf)); break; case SQL_NEED_DATA: strcat(outbuf, ": SQL_NEED_DATA"); break; case SQL_STILL_EXECUTING: strcat(outbuf, ": SQL_STILL_EXECUTING"); break; case SQL_INVALID_HANDLE: strcat(outbuf, ": SQL_INVALID_HANDLE"); break; } sendError(outbuf, msg.sstream); break; } SQLNumResultCols(sqls->hstmt, &sqls->nresultcols); if ( sqls->nresultcols == 0 ) { // was not 'select' command SQLRowCount(sqls->hstmt, &sqls->rowcount); // we don't use this, do we? } else { // get the column labels, save for later for ( i = 0; i < sqls->nresultcols; i++ ) { int newlen; SQLDescribeCol(sqls->hstmt, (UWORD) (i+1), sqls->colname[i], (SWORD)sizeof(sqls->colname[i]), &sqls->colnamelen, &sqls->coltype[i], &sqls->collen[i], &sqls->scale, &sqls->nullable); sqls->colname[i][sqls->colnamelen] = 0; // Adjust the length, since we are converting everything to strings. if ( (newlen = display_size(sqls->coltype[i], sqls->collen[i], sqls->colname[i])) != 0 ) { sqls->collen[i] = newlen; } if ( sqls->collen[i] == 0 ) { sqls->collen[i] = BUFSIZE; } sqls->data[i] = (UCHAR *) malloc(sqls->collen[i] + 1); /* SQLBindCol(sqls->hstmt, (UWORD) (i+1), newlen > 0 ? SQL_C_CHAR : SQL_C_DEFAULT, sqls->data[i], sqls->collen[i], &sqls->outlen[i]); */ } sqls->rownum = 0; } sendResponse('w', 0, msg.sstream, 0, ""); break; case 'X': notdone = 0; break; default: sprintf(sqls->errmsg, "Unknown command: %c", msg.mtype); sendError(sqls->errmsg, msg.sstream); sqls->errmsg[0] = '\0'; break; } // end of switch (msg.mtype) if ( infErrno > 0 && infErrno < LAST_ERROR_NO ) { sendError(iError[infErrno - 1], msg.sstream); } } // end of while (notdone) rc = freeSqlEnv(&sqle); return 0; } // // All the incoming commands should end with a newline character. // We read until we get one. Then we verify that we have read as // many bytes as the count in message says we should. // int getCommand(DBMSGP msgp, UCHAR *buf, int bufsiz) { int bytesRead = 0; int rc = 0; msgp->mtype = '\0'; while ( bufsiz > 0 && (rc = read(0, &buf[bytesRead], bufsiz)) > 0 ) { bytesRead += rc; bufsiz -= rc; msgp->bytesNotRead -= rc; if ( msgp->mtype == '\0' && bytesRead >= REQ_HEADER_SIZE ) { if ( (msgp->version = buf[1]) != '1' ) { // wrong version, give up char *wrong_version = "Message has wrong version number"; sendResponse('h', strlen(wrong_version), 0, 0, wrong_version); return -1; } msgp->mtype = buf[0]; msgp->nbytes = atoi(buf+OFFSET_LENGTH); msgp->sstream = atoi(buf+OFFSET_STREAM); msgp->data = buf+OFFSET_REQ_DATA; msgp->bytesNotRead = REQ_HEADER_SIZE + msgp->nbytes + 1 - bytesRead; if ( bufsiz > msgp->bytesNotRead ) { bufsiz = msgp->bytesNotRead; } } } if ( rc < 0 ) { // log a problem // fprintf(stderr, "Problem reading from client\n"); return rc; } if ( msgp->bytesNotRead == 0 ) { msgp->data[msgp->nbytes] = 0; // discard final newline } return bytesRead; } void sendResponse(char type, int lendata, int sstream, int retcode, char *data) { char hdr[RES_HEADER_SIZE+2]; sprintf(hdr, "%c1%11d %3d %3d ", type, lendata, sstream, retcode); write(1, hdr, RES_HEADER_SIZE); write(1, data, lendata); write(1, "\n", 1); } void sendError(char *errmsg, int sstream) { sendResponse('h', strlen(errmsg), sstream, 0, errmsg); } void print_err(SQLEnv *sqle, int connid, int streamid, UCHAR * buf, int bufsiz) { RETCODE rc; UCHAR stateString[40]; SDWORD native; SWORD msglen; SQLConn *sqlc; SQLStream *sqls; HENV *henv; HDBC *hdbc; HSTMT *hstmt; henv = sqle->henv; rc = mapSqlConn(sqle, connid, &sqlc); hdbc = rc == OK ? sqlc->hdbc : SQL_NULL_HDBC; rc = mapSqlStream(sqle, streamid, &sqls); hstmt = rc == OK ? sqls->hstmt : SQL_NULL_HSTMT; rc = SQLError(henv, hdbc, hstmt, stateString, &native, buf, (SWORD) bufsiz, &msglen); } #define MAX_NUM_PRECISION 15 /* Define max length of char string representation of number as: */ /* = max(precision) + leading sign + E + exp sign + max exp length */ /* = 15 + 1 + 1 + 1 + 2 */ /* = 15 + 5 */ #define MAX_NUM_STRING_SIZE (MAX_NUM_PRECISION + 5) UDWORD display_size(SWORD coltype, UDWORD collen, UCHAR *colname) { switch (coltype) { case SQL_CHAR: case SQL_VARCHAR: return max(collen, strlen(colname)); case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: return max(6, strlen(colname)); case SQL_INTEGER: return max(11, strlen(colname)); case SQL_BIGINT: return max(30, strlen(colname)); case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return max(50, strlen(colname)); case SQL_DECIMAL: case SQL_NUMERIC: case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: return(max(MAX_NUM_STRING_SIZE, strlen(colname))); case SQL_LONGVARBINARY: case SQL_LONGVARCHAR: return BUFSIZE; /* Note that this function only supports the core data types. */ /* For unknown data types, the caller should assume binary data */ default: /* fprintf(stderr, "Unknown datatype, %d\n", coltype); */ return 0; } } STATUS newSqlEnv(SQLEnv **sqle) { SQLEnv *newenv; STATUS rc; newenv = (SQLEnv *) calloc(1, sizeof(SQLEnv)); if (newenv == NULL) { return ERR; } rc = SQLAllocEnv(&newenv->henv); if ( rc != SQL_SUCCESS) { free (newenv); return ERR; } *sqle = newenv; return OK; } STATUS freeSqlEnv(SQLEnv **sqle) { int i; STATUS rc; for (i = 0; i < (*sqle)->maxstream; i++) { // Free this stream. // Connection will be freed automatically. rc = freeSqlStream(*sqle, i); } // dealloc the stream structures // dealloc the connect structures SQLFreeEnv((*sqle)->henv); // dealloc the env structure return OK; } STATUS mapSqlConn(SQLEnv *sqle, int connid, SQLConn **sqlc) { if ( connid >= 0 && connid < sqle->maxconn ) { *sqlc = sqle->scarray[connid]; if ( (*sqlc)->state == SQLC_INUSE ) return OK; } return ERR; } STATUS newSqlConn(SQLEnv *sqle, char *info, int *connid) { SQLConn **newarray, *sqlc; int newid = -1, i; STATUS rc; *connid = -1; // Connect to the database. // Search for an available connection structure to reuse for ( i = 0; i < sqle->maxconn; i++ ) { sqlc = sqle->scarray[i]; if ( sqlc != NULL && sqlc->state == SQLC_FREE ) { newid = i; break; } } if ( newid == -1 ) { // Assign a new connection id newid = sqle->maxconn++; // Extend the connection pointer array newarray = (SQLConn **) realloc((char *) sqle->scarray, sqle->maxconn * sizeof(SQLConn*)); if ( newarray == NULL ) { return ERR; } sqle->scarray = newarray; // Allocate a new connection structure sqlc = (SQLConn *) calloc(1, sizeof(SQLConn)); if ( sqlc == NULL ) { return ERR; } sqle->scarray[newid] = sqlc; } // Ask ODBC for a new connection handle rc = SQLAllocConnect(sqle->henv, &sqlc->hdbc); if (rc == SQL_ERROR) { return ERR; } sqlc->refcount = 0; sqlc->state = SQLC_INUSE; // Extract the username, password, and database name rc = parseConnInfo(sqlc, info); if ( rc != OK ) { return ERR; } // Request an ODBC connection to the database rc = SQLConnect(sqlc->hdbc, sqlc->dbname, SQL_NTS, sqlc->user, SQL_NTS, sqlc->passwd, SQL_NTS); if ( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO ) { // log error? // Should try to get something more specific from ODBC sprintf(sqlc->errmsg, "Connect failed: user = %s, passwd = %s, dbname = %s", sqlc->user, sqlc->passwd, sqlc->dbname); SQLDisconnect(sqlc->hdbc); SQLFreeConnect(sqlc->hdbc); return ERR; } *connid = newid; // Set connect option to disable auto commit rc = SQLSetConnectOption(sqlc->hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF); if ( rc != SQL_SUCCESS ) { return WARN; } return OK; } STATUS freeSqlConn(SQLEnv *sqle, int connid) { SQLConn *sqlc; STATUS rc; rc = mapSqlConn(sqle, connid, &sqlc); if ( rc != OK ) { return WARN; } SQLDisconnect(sqlc->hdbc); SQLFreeConnect(sqlc->hdbc); sqlc->state = SQLC_FREE; return OK; } STATUS mapSqlStream(SQLEnv *sqle, int streamid, SQLStream **sqls) { if ( streamid >= 0 && streamid < sqle->maxstream ) { *sqls = sqle->ssarray[streamid]; if ( (*sqls)->state == SQLS_INUSE ) return OK; } return ERR; } STATUS newSqlStream(SQLEnv *sqle, int connid, int *streamid) { HSTMT hstmt; SQLConn *sqlc; SQLStream **newarray, *sqls; int newid = -1, i; STATUS rc; rc = mapSqlConn(sqle, connid, &sqlc); if (rc != OK) { return ERR; } // Search for an available stream structure to reuse for ( i = 0; i < sqle->maxstream; i++ ) { sqls = sqle->ssarray[i]; if ( sqls != NULL && sqls->state == SQLS_FREE ) { newid = i; break; } } if ( newid == -1 ) { // Assign a new stream id newid = sqle->maxstream++; // Extend the stream pointer array newarray = (SQLStream **) realloc((char *) sqle->ssarray, sqle->maxstream * sizeof(SQLStream*)); if ( newarray == NULL ) { return ERR; } sqle->ssarray = newarray; // Allocate a new stream structure sqls = (SQLStream *) calloc(1, sizeof(SQLStream)); if ( sqls == NULL ) { return ERR; } sqle->ssarray[newid] = sqls; } // Associate new stream with specified connection sqls->connid = connid; sqlc->refcount++; // Ask ODBC to allocate a new statement handle rc = SQLAllocStmt(sqlc->hdbc, &hstmt); if (rc == SQL_ERROR) { return ERR; } sqls->hstmt = hstmt; sqls->state = SQLS_INUSE; *streamid = newid; return OK; } STATUS freeSqlStream(SQLEnv *sqle, int streamid) { SQLConn *sqlc; SQLStream *sqls; STATUS rc; rc = mapSqlStream(sqle, streamid, &sqls); if ( rc != OK ) { return WARN; } sqls->state = SQLS_FREE; rc = SQLFreeStmt(sqls->hstmt, SQL_DROP); rc = mapSqlConn(sqle, sqls->connid, &sqlc); if ( rc != OK ) { return WARN; } if ( --sqlc->refcount == 0 ) { rc = freeSqlConn(sqle, sqls->connid); if (rc != OK) { return WARN; } } return OK; } STATUS parseConnInfo(SQLConn *sqlc, char *info) { UCHAR *temp; // The argument 'info' points to a buffer containing a string // of the form "username/password/dbname\n". We will use 'strtok' // to tokenize the string into the parts we need, keeping // copies in the 'sqlc' structure. temp = strtok(info, "/\n"); if ( temp == NULL ) { return ERR; } strncpy(sqlc->user, temp, 48); temp = strtok(NULL, "/\n"); if ( temp == NULL ) { return ERR; } strncpy(sqlc->passwd, temp, 48); temp = strtok(NULL, "/\n"); if ( temp == NULL ) { return ERR; } strncpy(sqlc->dbname, temp, 48); return OK; }