Files
libraries/SDD/sdd_sybc.c
Philip Smart af833c09ee Initial upload
2019-11-18 00:29:48 +00:00

1205 lines
39 KiB
C
Executable File

/******************************************************************************
* Product: ##### ###### ###### # ### ######
* # # # # # # # # # #
* # # # # # # # # #
* ##### # # # # # # ######
* # # # # # # # # #
* # # # # # # # # # #
* ##### ###### ###### ####### ####### ### ######
*
* File: sdd_sybc.c
* Description: Server Data-source Driver library driver to handle sybase
* connectivity.
*
* Version: %I%
* Dated: %D%
* Copyright: P.D. Smart, 1996-2019.
*
* History: 1.0 - Initial Release.
*
******************************************************************************
* This source file is free software: you can redistribute it and#or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This source file 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
/* Bring in system header files.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
/* Indicate that we are a C module for any header specifics.
*/
#define SDD_SYBC_C
/* Bring in local specific header files.
*/
#include "sdd.h"
#include <sybfront.h>
#include <sybdb.h>
#include "sdd_sybc.h"
/******************************************************************************
* Function: _SYBC_GetArg
* Description: Function to scan an input buffer and extract a required
* argument from it.
*
* Returns: SDD_FAIL- Couldnt obtain argument.
* SDD_OK - Argument obtained and validated.
******************************************************************************/
int _SYBC_GetArg( UCHAR *szArgType, /* I: Type of Arg to scan for */
UCHAR *snzDataBuf, /* I: Input buffer */
int nDataLen, /* I: Len of data */
UCHAR **pszArg ) /* O: Pointer to Arg */
{
/* Local variables.
*/
int nCmpLen;
int nPos;
int nReturn = SDD_FAIL;
UCHAR *szFunc = "_SYBC_GetArg";
/* Go through the input buffer looking for a 'szArgType' within it. If it
* exists, then note the position just after it as this contains the
* database name.
*/
for(nPos=0, nCmpLen=strlen(szArgType); nPos < nDataLen; nPos++)
{
/* If a match occurs, then setup the pointer to correct location.
*/
if(strncmp(&snzDataBuf[nPos], szArgType, nCmpLen) == 0)
{
/* Make sure that the match is not a sub-match as some of the
* variables we are scanning for are similar.
*/
if( (nPos == 0) ||
(nPos > 0 && snzDataBuf[nPos-1] == '\0') ||
(nPos > 0 && isspace(snzDataBuf[nPos-1])) )
{
nPos += nCmpLen;
break;
}
}
}
/* If the pointer did not reach the end of the buffer then we have
* located a valid name.
*/
if(nPos < nDataLen)
{
/* Setup the callers pointer to point to the correct location
* in the buffer.
*/
*pszArg = &snzDataBuf[nPos];
nReturn=SDD_OK;
}
/* Return result code to caller.
*/
return(nReturn);
}
/******************************************************************************
* Function: _SYBC_RunSql
* Description: Function to execute a given buffer of SQL on the current
* database and return resultant data to the original caller.
*
* Returns: SDD_FAIL- SQL execution failed, see error message.
* SDD_OK - SQL execution succeeded.
******************************************************************************/
int _SYBC_RunSql( UCHAR *snzDataBuf, /* I: Input data */
int nDataLen, /* I: Len of data */
int (*fSendDataCB)(UCHAR *, UINT),
/* I: CB to send reply */
UCHAR *szErrMsg ) /* O: Error text */
{
/* Local variables.
*/
DBINT lResultBufLen;
UINT nFirstRow;
UCHAR *pszTmpBuf;
UCHAR *szFunc = "_SYBC_RunSql";
/* Need to NULL terminate the buffer prior to executing it, so allocate
* some memory and copy the original buffer into it.
*/
if((pszTmpBuf=(UCHAR *)malloc(nDataLen+1)) == NULL)
{
sprintf(szErrMsg,
"%s: Out of memory trying to exec SQL buffer",
SDD_EMSG_MEMORY);
return(SDD_FAIL);
}
memcpy(pszTmpBuf, snzDataBuf, nDataLen);
pszTmpBuf[nDataLen] = '\0';
/* Log if debugging switched on.
*/
Lgr(LOG_MESSAGE, szFunc, "SQL: %s", pszTmpBuf);
/* Load SQL into sybase's SQL buffer.
*/
if(dbcmd(SYB.dbProc, pszTmpBuf) == FAIL)
{
/* Free up memory as it can no longer be used.
*/
free(pszTmpBuf);
sprintf(szErrMsg,
"%s: Couldnt load SQL into sybase exec buffer",
SDD_EMSG_SQLLOAD);
return(SDD_FAIL);
}
/* Free up memory, no longer needed.
*/
free(pszTmpBuf);
/* Execute the SQL on the server, paying attention to the results.
*/
if(dbsqlexec(SYB.dbProc) == FAIL)
{
sprintf(szErrMsg,
"%s: Couldnt execute SQL due to syntax errors",
SDD_EMSG_SQLSYNTAX);
return(SDD_FAIL);
}
/* Initiate sybase into the return-us-results mode, trapping any errors.
*/
if(dbresults(SYB.dbProc) == FAIL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build an error message and exit.
*/
sprintf(szErrMsg,
"%s: Runtime error during SQL execution",
SDD_EMSG_SQLRUNERR);
return(SDD_FAIL);
}
/* Initialise any required variables.
*/
SYB.nAbortPending = FALSE;
/* Loop until we have exhausted the number of rows or an ABORT condition
* occurs.
*/
do {
/* Initialise any loop variables.
*/
nFirstRow = TRUE;
/* Work out the maximum size of a memory buffer we need to hold returned
* results.
*/
lResultBufLen=dbspr1rowlen(SYB.dbProc);
/* Allocate memory to hold returned results.
*/
if((pszTmpBuf=(UCHAR *)malloc(lResultBufLen+1)) == NULL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: Out of memory trying to alloc result buffer",
SDD_EMSG_MEMORY);
return(SDD_FAIL);
}
do {
/* If this is the first row then get the column header, else get
* the actual rows.
*/
if( nFirstRow == TRUE )
{
/* Fetch a list of all the columns we are returning.
*/
if(dbsprhead(SYB.dbProc, pszTmpBuf, lResultBufLen) == FAIL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Free up memory used.
*/
free(pszTmpBuf);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: Error in fetching row header from server",
SDD_EMSG_FETCHHEAD);
return(SDD_FAIL);
}
/* Update flag so we dont do this again.
*/
nFirstRow = FALSE;
} else
{
/* Fetch one row of data from the server.
*/
if(dbspr1row(SYB.dbProc, pszTmpBuf, lResultBufLen) == FAIL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Free up memory used.
*/
free(pszTmpBuf);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: Error in fetching a result row from server",
SDD_EMSG_FETCHROW);
return(SDD_FAIL);
}
}
/* Log row if debugging switched on.
*/
Lgr(LOG_DEBUG, szFunc, "%s", pszTmpBuf);
/* Call function to transmit row to original caller.
*/
if(fSendDataCB(pszTmpBuf, strlen(pszTmpBuf)) == SDD_FAIL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Free up memory used.
*/
free(pszTmpBuf);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: Failed to send row back to client",
SDD_EMSG_SENDROW);
return(SDD_FAIL);
}
} while( (SYB.nAbortPending == FALSE) &&
(dbnextrow(SYB.dbProc) != NO_MORE_ROWS) );
/* Free up memory used.
*/
free(pszTmpBuf);
} while(SYB.nAbortPending == FALSE &&
dbresults(SYB.dbProc) != NO_MORE_RESULTS);
/* If an abort command arrived halfway through processing then tidy up
* and exit.
*/
if(SYB.nAbortPending == TRUE)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: ABORT received, query abandoned",
SDD_EMSG_ABORTRCVD);
return(SDD_FAIL);
}
/* Return success to caller as everything worked.
*/
return(SDD_OK);
}
/******************************************************************************
* Function: sybc_InitService
* Description: Entry point which initialises the driver into a defined state.
* It is mandatory that this function is called before any other
* in order for the driver to function correctly. The caller
* provides it with two types of data, 1) A structure containing
* data for it to use in initialising itself, 2) a pointer to a
* buffer which the driver uses to place an error message should
* it not be able to complete initialisation.
*
* Returns: SDD_FAIL- An error occurred in initialising the driver and an
* error message is stored in szErrStr.
* SDD_OK - Driver initialised successfully.
******************************************************************************/
int sybc_InitService( SERVICEDETAILS *sServiceDet, /* I: Init data */
UCHAR *szErrStr ) /* O: Error message */
{
/* Local variables.
*/
int nReturn = SDD_OK;
UCHAR *szFunc = "sybc_InitService";
/* Copy all configuration data out of the service structure.
*/
strcpy(SYB.szUserName, sServiceDet->uServiceInfo.sSybaseInfo.szUser);
strcpy(SYB.szPassword, sServiceDet->uServiceInfo.sSybaseInfo.szPassword);
strcpy(SYB.szServer, sServiceDet->uServiceInfo.sSybaseInfo.szServer);
strcpy(SYB.szDatabase, sServiceDet->uServiceInfo.sSybaseInfo.szDatabase);
/* Initialise the sybase db library.
*/
if( dbinit() == FAIL )
{
sprintf(szErrStr,
"%s: Couldnt initialise DBLIBRARY",
SDD_EMSG_SRCINIT);
return(SDD_FAIL);
}
/* Initialise the login maintence record.
*/
if( (SYB.sLoginRec=dblogin()) == (LOGINREC *)NULL )
{
sprintf(szErrStr,
"%s: Couldnt initialise Login Maintenance Record",
SDD_EMSG_SRCINIT);
return(SDD_FAIL);
}
/* Setup options and user id info within the login record.
*/
DBSETLUSER(SYB.sLoginRec, SYB.szUserName);
DBSETLPWD(SYB.sLoginRec, SYB.szPassword);
DBSETLAPP(SYB.sLoginRec, DEF_APPNAME);
/* Now perform the actual login to the server.
*/
SYB.dbProc = dbopen(SYB.sLoginRec, SYB.szServer);
if( SYB.dbProc == (DBPROCESS *)NULL )
{
sprintf(szErrStr,
"%s: Couldnt login to named data server (%s)",
SDD_EMSG_BADLOGIN, SYB.szServer);
return(SDD_FAIL);
}
/* Setup options on the database specific for this modules usage.
*/
dbsetopt(SYB.dbProc, DBPRCOLSEP, DEF_COLSEP, 1);
dbsetopt(SYB.dbProc, DBPRLINELEN, "1024", 4);
dbsetopt(SYB.dbProc, DBPRPAD, DEF_PADCHR, DBPADON);
/* OK, server connection is valid, so lets connect to the required db.
*/
if( dbuse(SYB.dbProc, SYB.szDatabase) == FAIL )
{
sprintf(szErrStr,
"%s: Couldnt open named database (%s)",
SDD_EMSG_BADDBASE, SYB.szDatabase);
return(SDD_FAIL);
}
/* Soak up any/all init results.
*/
while(dbresults(SYB.dbProc) != NO_MORE_RESULTS)
{
while(dbnextrow(SYB.dbProc) != NO_MORE_ROWS);
}
/* Log if debugging switched on.
*/
Lgr(LOG_MESSAGE, szFunc,
"Sybase Driver: Initialised: (User=%s, Pwd=<hidden>, Srv=%s, DB=%s)",
SYB.szUserName, SYB.szServer, SYB.szDatabase);
/* Return result code to caller.
*/
return(nReturn);
}
/******************************************************************************
* Function: _SYBC_ListDB
* Description: Function to list all the names of databases available on the
* currently open data source.
*
* Returns: SDD_FAIL- SQL execution failed, see error message.
* SDD_OK - SQL execution succeeded.
******************************************************************************/
int _SYBC_ListDB( int (*fSendDataCB)(UCHAR *, UINT),
/* I: CB to send reply */
UCHAR *szErrMsg ) /* O: Error text */
{
/* Local variables.
*/
int nReturn = SDD_OK;
ULNG lResultBufLen;
UCHAR szDbName[MAX_SYBC_DBNAME];
UCHAR *pszTmpBuf;
UCHAR *szFunc = "_SYBC_ListDB";
/* Load SQL into sybase's SQL buffer which requests a list of all known
* databases.
*/
if(dbcmd(SYB.dbProc, "sp_databases") == FAIL)
{
/* Build error message to indicate problem.
*/
sprintf(szErrMsg,
"%s: Couldnt issue request to Sybase", SDD_EMSG_SQLLOAD);
return(SDD_FAIL);
}
/* Execute the SQL on the server, paying attention to the results.
*/
if(dbsqlexec(SYB.dbProc) == FAIL)
{
sprintf(szErrMsg,
"%s: Issued request to sybase generated syntax errors",
SDD_EMSG_SQLSYNTAX);
return(SDD_FAIL);
}
/* Initiate sybase into the return-us-results mode, trapping any errors.
*/
if(dbresults(SYB.dbProc) == FAIL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build an error message and exit.
*/
sprintf(szErrMsg, "%s: Runtime error during request execution",
SDD_EMSG_SQLRUNERR);
return(SDD_FAIL);
}
/* Initialise any required variables.
*/
SYB.nAbortPending = FALSE;
/* Work out the maximum size of a memory buffer we need to hold returned
* results.
*/
lResultBufLen=dbspr1rowlen(SYB.dbProc);
/* Allocate memory to hold returned results.
*/
if((pszTmpBuf=(UCHAR *)malloc(lResultBufLen+1)) == NULL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: Out of memory trying to alloc result buffer",
SDD_EMSG_MEMORY);
return(SDD_FAIL);
}
/* Loop until we have exhausted the number of result sets or an ABORT
* condition occurs.
*/
do {
/* Bind to relevant return columns.
*/
dbbind(SYB.dbProc, 1, NTBSTRINGBIND, (DBINT)0, szDbName);
/* Loop to extract all rows returned.
*/
while( (SYB.nAbortPending == FALSE) &&
(dbnextrow(SYB.dbProc) != NO_MORE_ROWS) )
{
/* Build up output line.
*/
sprintf(pszTmpBuf, "%s", szDbName);
/* Call function to transmit row to original caller.
*/
if(fSendDataCB(pszTmpBuf, strlen(pszTmpBuf)) == SDD_FAIL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Free up memory used.
*/
free(pszTmpBuf);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: Failed to send row back to client",
SDD_EMSG_SENDROW);
return(SDD_FAIL);
}
}
} while(SYB.nAbortPending == FALSE &&
dbresults(SYB.dbProc) != NO_MORE_RESULTS);
/* Free up memory used.
*/
free(pszTmpBuf);
/* If an abort command arrived halfway through processing then tidy up
* and exit.
*/
if(SYB.nAbortPending == TRUE)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: ABORT received, query abandoned",
SDD_EMSG_ABORTRCVD);
return(SDD_FAIL);
}
/* Return success/fail to caller depending on execution results.
*/
return(nReturn);
}
/******************************************************************************
* Function: _SYBC_ListTables
* Description: Function to list all names of tables in a given database
* (or current database if no database name given).
*
* Returns: SDD_FAIL- SQL execution failed, see error message.
* SDD_OK - SQL execution succeeded.
******************************************************************************/
int _SYBC_ListTables( UCHAR *snzDataBuf, /* I: Input data */
int nDataLen, /* I: Len of data */
int (*fSendDataCB)(UCHAR *, UINT),
/* I: CB to send reply */
UCHAR *szErrMsg ) /* O: Error text */
{
/* Local variables.
*/
int nReturn = SDD_OK;
UCHAR *pszDbName;
UCHAR szTableName[MAX_SYBC_TABLENAME];
UCHAR szTableType[MAX_SYBC_TABLENAME];
ULNG lResultBufLen;
UCHAR *pszTmpBuf;
UCHAR *szFunc = "_SYBC_ListTables";
/* See if the caller has provided the name of a new database. If HE hasnt
* then assume current database.
*/
if(_SYBC_GetArg(SYBC_DBNAME, snzDataBuf, nDataLen, &pszDbName) == SDD_FAIL)
{
/* If the length of the input buffer indicates that there is data within
* it, yet it does not contain a database name, then it is illegal.
*/
if(nDataLen > 1)
{
sprintf(szErrMsg, "%s: Invalid DATABASE NAME provided",
SDD_EMSG_BADCMD);
return(SDD_FAIL);
} else
{
/* Setup the variable so that the select below will work on the
* current database.
*/
pszDbName = NULL;
}
} else
{
/* AAAAAHHHHH! Database name switching not yet supported... tee
* hee, message for the sucker!!
*/
sprintf(szErrMsg,
"%s: Sybase switch to database function not yet implemented",
SDD_EMSG_NOTYI);
return(SDD_FAIL);
}
/* Load SQL into sybase's SQL buffer which requests a list of all known
* tables.
*/
if(dbcmd(SYB.dbProc, "sp_tables") == FAIL)
{
/* Build error message to indicate problem.
*/
sprintf(szErrMsg,
"%s: Couldnt issue request to Sybase", SDD_EMSG_SQLLOAD);
return(SDD_FAIL);
}
/* Execute the SQL on the server, paying attention to the results.
*/
if(dbsqlexec(SYB.dbProc) == FAIL)
{
sprintf(szErrMsg,
"%s: Issued request to sybase generated syntax errors",
SDD_EMSG_SQLSYNTAX);
return(SDD_FAIL);
}
/* Initiate sybase into the return-us-results mode, trapping any errors.
*/
if(dbresults(SYB.dbProc) == FAIL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build an error message and exit.
*/
sprintf(szErrMsg, "%s: Runtime error during request execution",
SDD_EMSG_SQLRUNERR);
return(SDD_FAIL);
}
/* Initialise any required variables.
*/
SYB.nAbortPending = FALSE;
/* Work out the maximum size of a memory buffer we need to hold returned
* results.
*/
lResultBufLen=dbspr1rowlen(SYB.dbProc);
/* Allocate memory to hold returned results.
*/
if((pszTmpBuf=(UCHAR *)malloc(lResultBufLen+1)) == NULL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: Out of memory trying to alloc result buffer",
SDD_EMSG_MEMORY);
return(SDD_FAIL);
}
/* Loop until we have exhausted the number of result sets or an ABORT
* condition occurs.
*/
do {
/* Bind to relevant return columns.
*/
dbbind(SYB.dbProc, 3, NTBSTRINGBIND, (DBINT)0, szTableName);
dbbind(SYB.dbProc, 4, NTBSTRINGBIND, (DBINT)0, szTableType);
/* Loop to extract all rows returned.
*/
while( (SYB.nAbortPending == FALSE) &&
(dbnextrow(SYB.dbProc) != NO_MORE_ROWS) )
{
/* Build up output line.
*/
sprintf(pszTmpBuf, "%s%s%s", szTableName, DEF_COLSEP, szTableType);
/* Call function to transmit row to original caller.
*/
if(fSendDataCB(pszTmpBuf, strlen(pszTmpBuf)) == SDD_FAIL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Free up memory used.
*/
free(pszTmpBuf);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: Failed to send row back to client",
SDD_EMSG_SENDROW);
return(SDD_FAIL);
}
}
} while(SYB.nAbortPending == FALSE &&
dbresults(SYB.dbProc) != NO_MORE_RESULTS);
/* Free up memory used.
*/
free(pszTmpBuf);
/* If an abort command arrived halfway through processing then tidy up
* and exit.
*/
if(SYB.nAbortPending == TRUE)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: ABORT received, query abandoned",
SDD_EMSG_ABORTRCVD);
return(SDD_FAIL);
}
/* Return success/fail to caller depending on execution results.
*/
return(nReturn);
}
/******************************************************************************
* Function: _SYBC_ListCols
* Description: Function to list all names and attributes of columns in a
* given table in a given database (or current database/table if
* no database name given).
*
* Returns: SDD_FAIL- SQL execution failed, see error message.
* SDD_OK - SQL execution succeeded.
******************************************************************************/
int _SYBC_ListCols( UCHAR *snzDataBuf, /* I: Input data */
int nDataLen, /* I: Len of data */
int (*fSendDataCB)(UCHAR *, UINT),
/* I: CB to send reply */
UCHAR *szErrMsg ) /* O: Error text */
{
/* Local variables.
*/
int nNullable;
int nRadix;
int nScale;
int nReturn = SDD_OK;
UINT nRowCount = 0;
long lLength;
long lPrecision;
ULNG lResultBufLen;
UCHAR szColumnName[MAX_SYBC_COLNAME];
UCHAR szTypeName[MAX_SYBC_TYPENAME];
UCHAR *pszColFilter;
UCHAR *pszDbName;
UCHAR *pszTableName;
UCHAR *pszTmpBuf;
UCHAR *szFunc = "_SYBC_ListCols";
/* See if the caller has provided the name of a new database. If HE hasnt
* then assume current database.
*/
if(_SYBC_GetArg(SYBC_DBNAME, snzDataBuf, nDataLen, &pszDbName) == SDD_FAIL)
{
/* Setup the variable so that the select below will work on the
* current database.
*/
pszDbName = NULL;
} else
{
/* AAAAAHHHHH! Database name switching not yet supported... tee
* hee, message for the sucker!!
*/
sprintf(szErrMsg,
"%s: Sybase switch to database function not yet implemented",
SDD_EMSG_NOTYI);
return(SDD_FAIL);
}
/* Caller must provide the name of a table... else what do we scan for...
* a French Empire, vapourware... tell me more!!
*/
if(_SYBC_GetArg(SYBC_TABLENAME, snzDataBuf, nDataLen, &pszTableName)
== SDD_FAIL)
{
/* Always generate an error... French Empire.. bah humbug!
*/
sprintf(szErrMsg, "%s: Invalid TABLE NAME provided",
SDD_EMSG_BADARGS);
return(SDD_FAIL);
}
/* See if the caller has provided the name of a specific column. If HE has
* then he requires data limited to just that one column.
*/
if(_SYBC_GetArg(SYBC_COLFILTER,snzDataBuf,nDataLen,&pszColFilter)==SDD_FAIL)
{
/* Setup the variable so that the column filter logic below wont work
* as no specific column name has been provided, hence the caller
* wants all columns.
*/
pszColFilter = NULL;
}
/* Load SQL into sybase's SQL buffer which requests a list of all known
* columns for a given table.
*/
if(dbfcmd(SYB.dbProc, "sp_columns %s", pszTableName) == FAIL)
{
/* Build error message to indicate problem.
*/
sprintf(szErrMsg,
"%s: Couldnt issue request to list columns for %s",
SDD_EMSG_SQLLOAD, pszTableName);
return(SDD_FAIL);
}
/* Execute the SQL on the server, paying attention to the results.
*/
if(dbsqlexec(SYB.dbProc) == FAIL)
{
sprintf(szErrMsg,
"%s: Issued request to sybase generated syntax errors",
SDD_EMSG_SQLSYNTAX);
return(SDD_FAIL);
}
/* Initiate sybase into the return-us-results mode, trapping any errors.
*/
if(dbresults(SYB.dbProc) == FAIL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build an error message and exit.
*/
sprintf(szErrMsg, "%s: Runtime error during request execution",
SDD_EMSG_SQLRUNERR);
return(SDD_FAIL);
}
/* Initialise any required variables.
*/
SYB.nAbortPending = FALSE;
/* Work out the maximum size of a memory buffer we need to hold returned
* results.
*/
lResultBufLen=dbspr1rowlen(SYB.dbProc);
/* Allocate memory to hold returned results.
*/
if((pszTmpBuf=(UCHAR *)malloc(lResultBufLen+1)) == NULL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: Out of memory trying to alloc result buffer",
SDD_EMSG_MEMORY);
return(SDD_FAIL);
}
/* Loop until we have exhausted the number of result sets or an ABORT
* condition occurs.
*/
do {
/* Bind to relevant return columns.
*/
dbbind(SYB.dbProc, 4, NTBSTRINGBIND, (DBINT)0, szColumnName);
dbbind(SYB.dbProc, 6, NTBSTRINGBIND, (DBINT)0, szTypeName);
dbbind(SYB.dbProc, 7, INTBIND, (DBINT)0, (BYTE *)&lPrecision);
dbbind(SYB.dbProc, 8, INTBIND, (DBINT)0, (BYTE *)&lLength);
dbbind(SYB.dbProc, 9, INTBIND, (DBINT)0, (BYTE *)&nScale);
dbbind(SYB.dbProc, 10, INTBIND, (DBINT)0, (BYTE *)&nRadix);
dbbind(SYB.dbProc, 11, INTBIND, (DBINT)0, (BYTE *)&nNullable);
/* Loop to extract rows returned.
*/
while( (SYB.nAbortPending == FALSE) &&
(dbnextrow(SYB.dbProc) != NO_MORE_ROWS) )
{
/* If there is any data to be transmitted the run through the
* column filter if applicable and transmit it to the client.
*/
if( (pszColFilter != NULL && strcmp(pszColFilter, szColumnName)==0)
||
(pszColFilter == NULL) )
{
/* Place the appropriate data in the return buffer.
*/
sprintf(pszTmpBuf,
"%s%s%s%s%ld%s%ld%s%d%s%d%s%d",
szColumnName, DEF_COLSEP,
szTypeName, DEF_COLSEP,
lPrecision, DEF_COLSEP,
lLength, DEF_COLSEP,
nScale, DEF_COLSEP,
nRadix, DEF_COLSEP,
nNullable);
/* Call function to transmit row to original caller.
*/
if(fSendDataCB(pszTmpBuf, strlen(pszTmpBuf)) == SDD_FAIL)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Free up memory used.
*/
free(pszTmpBuf);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: Failed to send row back to client",
SDD_EMSG_SENDROW);
return(SDD_FAIL);
} else
{
/* Increment row counter to indicate number of rows
* returned.
*/
nRowCount++;
}
}
}
} while(SYB.nAbortPending == FALSE &&
dbresults(SYB.dbProc) != NO_MORE_RESULTS);
/* Free up memory used.
*/
free(pszTmpBuf);
/* If no rows where detected or returned then there was an error with
* the column, table or database name.
*/
if(nRowCount == 0)
{
/* Build error message for return.
*/
sprintf(szErrMsg,
(pszColFilter == NULL ? "%s: Table name not known by system" :
"%s: Column name not valid for table"),
SDD_EMSG_SQLRUNERR);
/* Setting error code to indicate condition.
*/
nReturn = SDD_FAIL;
}
/* If an abort command arrived halfway through processing then tidy up
* for exit.
*/
if(SYB.nAbortPending == TRUE)
{
/* Cancel the query to avoid future results-pending errors.
*/
dbcancel(SYB.dbProc);
/* Build error message for return.
*/
sprintf(szErrMsg,
"%s: ABORT received, query abandoned",
SDD_EMSG_ABORTRCVD);
nReturn = SDD_FAIL;
}
/* Return success/fail to caller depending on execution results.
*/
return(nReturn);
}
/******************************************************************************
* Function: sybc_CloseService
* Description: Entry point which performs a drive closedown. The closedown
* procedure ensure that the driver returns to a virgin state
* (ie.like at power up) so that InitService can be called again.
*
* Returns: SDD_FAIL- An error occurred in closing the driver and an
* error message is stored in szErrStr.
* SDD_OK - Driver successfully closed.
******************************************************************************/
int sybc_CloseService( UCHAR *szErrMsg ) /* O: Error message if failed */
{
/* Local variables.
*/
int nReturn = SDD_OK;
UCHAR *szFunc = "sybc_CloseService";
/* Shutdown any dbase connection.
*/
dbexit();
/* Tidy up variables.
*/
SYB.nAbortPending = FALSE;
strcpy(SYB.szUserName, "");
strcpy(SYB.szPassword, "");
strcpy(SYB.szServer, "");
strcpy(SYB.szDatabase, "");
/* Log if debugging switched on.
*/
Lgr(LOG_MESSAGE, szFunc, "Sybase Driver: Closed.");
/* Return result code to caller.
*/
return(nReturn);
}
/******************************************************************************
* Function: sybc_ProcessRequest
* Description: Entry point into driver to initiate the driver into
* processing a request. A data block is passed as a parameter
* to the driver which represents a request with relevant
* parameters. The data within the structure is only relevant
* to the original client and this driver code.
*
* Returns: SDD_FAIL- An error occurred within the driver whilst trying to
* process the request, see error text.
* SDD_OK - Request processed successfully.
******************************************************************************/
int sybc_ProcessRequest( UCHAR *snzDataBuf, /* I: Input data */
int nDataLen, /* I: Len of data */
int (*fSendDataCB)(UCHAR *, UINT),
/* I: CB to send reply */
UCHAR *szErrMsg ) /* O: Error text */
{
/* Local variables.
*/
int nReturn = SDD_OK;
UCHAR *szFunc = "sybc_ProcessRequest";
/* If the request block doesnt contain any data, something went wrong
* somewhere??
*/
if(nDataLen < 1)
{
sprintf(szErrMsg,
"%s: Illegal request, has size of %dBytes",
SDD_EMSG_BADREQ, nDataLen);
return(SDD_FAIL);
}
/* First byte of the request data block indicates actions required, so
* decipher it.
*/
switch(snzDataBuf[0])
{
/* Request to execute a block of SQL code. SQL immediately follows the
* command byte in the input buffer.
*/
case SDD_RUNSQL:
/* Log if debugging switched on.
*/
Lgr(LOG_DEBUG, szFunc,
"Sybase Driver: Requested to execute SQL");
/* Execute the SQL.
*/
nReturn=_SYBC_RunSql(&snzDataBuf[1],nDataLen,fSendDataCB,szErrMsg);
break;
/* Request to list all the names of the databases on the current open
* data source.
*/
case SDD_LIST_DB:
/* Log if debugging switched on.
*/
Lgr(LOG_DEBUG, szFunc,
"Sybase Driver: Requested to list all Databases on source");
/* Call function to extract names of all databases in data source.
*/
nReturn=_SYBC_ListDB(fSendDataCB, szErrMsg);
break;
/* Request to list all the table names of a given database on the
* current open data source.
*/
case SDD_LIST_TABLES:
/* Log if debugging switched on.
*/
Lgr(LOG_DEBUG, szFunc,
"Sybase Driver: Requested to list all columns on a table");
/* Call function to extract names of all tables in a database.
*/
nReturn=_SYBC_ListTables(&snzDataBuf[1], nDataLen, fSendDataCB,
szErrMsg);
break;
/* Request to list all the column names and their attributes of a
* given database/table.
*/
case SDD_LIST_COLS:
/* Log if debugging switched on.
*/
Lgr(LOG_DEBUG, szFunc,
"Sybase Driver: Requested to list all tables in a database");
/* Call function to extract details of all columns in a table.
*/
nReturn=_SYBC_ListCols(&snzDataBuf[1], nDataLen, fSendDataCB,
szErrMsg);
break;
default:
sprintf(szErrMsg,
"%s: Illegal command in request buffer (%x)",
SDD_EMSG_BADCMD, snzDataBuf[0]);
return(SDD_FAIL);
}
/* Return result code to caller.
*/
return(nReturn);
}
/******************************************************************************
* Function: sybc_ProcessOOB
* Description: Entry point into driver to process an out of band command
* that may or may not be relevant to current state of
* operation. The task of this function is to decipher the
* command and act on it immediately, ie. a cancel command
* would abort any ProcessRequest that is in process and
* clean up.
*
* Returns: No returns.
******************************************************************************/
void sybc_ProcessOOB( UCHAR nCommand ) /* I: OOB Command */
{
/* Local variables.
*/
/* Decipher command and perform actions accordingly.
*/
switch(nCommand)
{
/* Request to abort current ProcessRequest command and return daemon
* to a waiting-for-request state.
*/
case SDD_ABORT:
SYB.nAbortPending = TRUE;
break;
/* Request to close down and exit.
*/
case SDD_EXIT:
break;
default:
break;
}
/* Return to caller.
*/
return;
}