Commit d91b4451 authored by Hiroshi Inoue's avatar Hiroshi Inoue

*** empty log message ***

parent 3b3b7307
This directory is to save the changes about psqlodbc driver under
win32 while the main development tree(the parent directory) is
nearly freezed(i.e. during beta, a while after the release until
the next branch is made etc). The changes would be reflected
to the main directory later after all. However note that the
trial binary version would be sometimes made using the source
and put on the ftp server for download.
Hiroshi Inoue inoue@postgresql.org
/*-------
* Module: bind.c
*
* Description: This module contains routines related to binding
* columns and parameters.
*
* Classes: BindInfoClass, ParameterInfoClass
*
* API functions: SQLBindParameter, SQLBindCol, SQLDescribeParam, SQLNumParams,
* SQLParamOptions(NI)
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "bind.h"
#include "environ.h"
#include "statement.h"
#include "qresult.h"
#include "pgtypes.h"
#include <stdlib.h>
#include <string.h>
#include "pgapifunc.h"
/* Bind parameters on a statement handle */
RETCODE SQL_API
PGAPI_BindParameter(
HSTMT hstmt,
UWORD ipar,
SWORD fParamType,
SWORD fCType,
SWORD fSqlType,
UDWORD cbColDef,
SWORD ibScale,
PTR rgbValue,
SDWORD cbValueMax,
SDWORD FAR * pcbValue)
{
StatementClass *stmt = (StatementClass *) hstmt;
static char *func = "PGAPI_BindParameter";
mylog("%s: entering...\n", func);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if (stmt->parameters_allocated < ipar)
{
ParameterInfoClass *old_parameters;
int i,
old_parameters_allocated;
old_parameters = stmt->parameters;
old_parameters_allocated = stmt->parameters_allocated;
stmt->parameters = (ParameterInfoClass *) malloc(sizeof(ParameterInfoClass) * (ipar));
if (!stmt->parameters)
{
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Could not allocate memory for statement parameters";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
stmt->parameters_allocated = ipar;
/* copy the old parameters over */
for (i = 0; i < old_parameters_allocated; i++)
{
/* a structure copy should work */
stmt->parameters[i] = old_parameters[i];
}
/* get rid of the old parameters, if there were any */
if (old_parameters)
free(old_parameters);
/*
* zero out the newly allocated parameters (in case they skipped
* some,
*/
/* so we don't accidentally try to use them later) */
for (; i < stmt->parameters_allocated; i++)
{
stmt->parameters[i].buflen = 0;
stmt->parameters[i].buffer = 0;
stmt->parameters[i].used = 0;
stmt->parameters[i].paramType = 0;
stmt->parameters[i].CType = 0;
stmt->parameters[i].SQLType = 0;
stmt->parameters[i].precision = 0;
stmt->parameters[i].scale = 0;
stmt->parameters[i].data_at_exec = FALSE;
stmt->parameters[i].lobj_oid = 0;
stmt->parameters[i].EXEC_used = NULL;
stmt->parameters[i].EXEC_buffer = NULL;
}
}
/* use zero based column numbers for the below part */
ipar--;
/* store the given info */
stmt->parameters[ipar].buflen = cbValueMax;
stmt->parameters[ipar].buffer = rgbValue;
stmt->parameters[ipar].used = pcbValue;
stmt->parameters[ipar].paramType = fParamType;
stmt->parameters[ipar].CType = fCType;
stmt->parameters[ipar].SQLType = fSqlType;
stmt->parameters[ipar].precision = cbColDef;
stmt->parameters[ipar].scale = ibScale;
/*
* If rebinding a parameter that had data-at-exec stuff in it, then
* free that stuff
*/
if (stmt->parameters[ipar].EXEC_used)
{
free(stmt->parameters[ipar].EXEC_used);
stmt->parameters[ipar].EXEC_used = NULL;
}
if (stmt->parameters[ipar].EXEC_buffer)
{
if (stmt->parameters[ipar].SQLType != SQL_LONGVARBINARY)
free(stmt->parameters[ipar].EXEC_buffer);
stmt->parameters[ipar].EXEC_buffer = NULL;
}
/* Data at exec macro only valid for C char/binary data */
if (pcbValue && (*pcbValue == SQL_DATA_AT_EXEC ||
*pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET))
stmt->parameters[ipar].data_at_exec = TRUE;
else
stmt->parameters[ipar].data_at_exec = FALSE;
/* Clear premature result */
if (stmt->status == STMT_PREMATURE)
SC_recycle_statement(stmt);
mylog("PGAPI_BindParamater: ipar=%d, paramType=%d, fCType=%d, fSqlType=%d, cbColDef=%d, ibScale=%d, rgbValue=%d, *pcbValue = %d, data_at_exec = %d\n", ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, pcbValue ? *pcbValue : -777, stmt->parameters[ipar].data_at_exec);
return SQL_SUCCESS;
}
/* Associate a user-supplied buffer with a database column. */
RETCODE SQL_API
PGAPI_BindCol(
HSTMT hstmt,
UWORD icol,
SWORD fCType,
PTR rgbValue,
SDWORD cbValueMax,
SDWORD FAR * pcbValue)
{
StatementClass *stmt = (StatementClass *) hstmt;
static char *func = "PGAPI_BindCol";
mylog("%s: entering...\n", func);
mylog("**** PGAPI_BindCol: stmt = %u, icol = %d\n", stmt, icol);
mylog("**** : fCType=%d rgb=%x valusMax=%d pcb=%x\n", fCType, rgbValue, cbValueMax, pcbValue);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if (stmt->status == STMT_EXECUTING)
{
stmt->errormsg = "Can't bind columns while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/* If the bookmark column is being bound, then just save it */
if (icol == 0)
{
if (rgbValue == NULL)
{
stmt->bookmark.buffer = NULL;
stmt->bookmark.used = NULL;
}
else
{
/* Make sure it is the bookmark data type */
if (fCType != SQL_C_BOOKMARK)
{
stmt->errormsg = "Column 0 is not of type SQL_C_BOOKMARK";
stmt->errornumber = STMT_PROGRAM_TYPE_OUT_OF_RANGE;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
stmt->bookmark.buffer = rgbValue;
stmt->bookmark.used = pcbValue;
}
return SQL_SUCCESS;
}
/*
* Allocate enough bindings if not already done. Most likely,
* execution of a statement would have setup the necessary bindings.
* But some apps call BindCol before any statement is executed.
*/
if (icol > stmt->bindings_allocated)
extend_bindings(stmt, icol);
/* check to see if the bindings were allocated */
if (!stmt->bindings)
{
stmt->errormsg = "Could not allocate memory for bindings.";
stmt->errornumber = STMT_NO_MEMORY_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/* use zero based col numbers from here out */
icol--;
/* Reset for SQLGetData */
stmt->bindings[icol].data_left = -1;
if (rgbValue == NULL)
{
/* we have to unbind the column */
stmt->bindings[icol].buflen = 0;
stmt->bindings[icol].buffer = NULL;
stmt->bindings[icol].used = NULL;
stmt->bindings[icol].returntype = SQL_C_CHAR;
}
else
{
/* ok, bind that column */
stmt->bindings[icol].buflen = cbValueMax;
stmt->bindings[icol].buffer = rgbValue;
stmt->bindings[icol].used = pcbValue;
stmt->bindings[icol].returntype = fCType;
mylog(" bound buffer[%d] = %u\n", icol, stmt->bindings[icol].buffer);
}
return SQL_SUCCESS;
}
/*
* Returns the description of a parameter marker.
* This function is listed as not being supported by SQLGetFunctions() because it is
* used to describe "parameter markers" (not bound parameters), in which case,
* the dbms should return info on the markers. Since Postgres doesn't support that,
* it is best to say this function is not supported and let the application assume a
* data type (most likely varchar).
*/
RETCODE SQL_API
PGAPI_DescribeParam(
HSTMT hstmt,
UWORD ipar,
SWORD FAR * pfSqlType,
UDWORD FAR * pcbColDef,
SWORD FAR * pibScale,
SWORD FAR * pfNullable)
{
StatementClass *stmt = (StatementClass *) hstmt;
static char *func = "PGAPI_DescribeParam";
mylog("%s: entering...\n", func);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if ((ipar < 1) || (ipar > stmt->parameters_allocated))
{
stmt->errormsg = "Invalid parameter number for PGAPI_DescribeParam.";
stmt->errornumber = STMT_BAD_PARAMETER_NUMBER_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
ipar--;
/*
* This implementation is not very good, since it is supposed to
* describe
*/
/* parameter markers, not bound parameters. */
if (pfSqlType)
*pfSqlType = stmt->parameters[ipar].SQLType;
if (pcbColDef)
*pcbColDef = stmt->parameters[ipar].precision;
if (pibScale)
*pibScale = stmt->parameters[ipar].scale;
if (pfNullable)
*pfNullable = pgtype_nullable(stmt, stmt->parameters[ipar].paramType);
return SQL_SUCCESS;
}
/* Sets multiple values (arrays) for the set of parameter markers. */
RETCODE SQL_API
PGAPI_ParamOptions(
HSTMT hstmt,
UDWORD crow,
UDWORD FAR * pirow)
{
static char *func = "PGAPI_ParamOptions";
StatementClass *stmt = (StatementClass *) hstmt;
mylog("%s: entering... %d %x\n", func, crow, pirow);
if (crow == 1) /* temporary solution and must be
* rewritten later */
{
if (pirow)
*pirow = 1;
return SQL_SUCCESS;
}
stmt->errornumber = CONN_UNSUPPORTED_OPTION;
stmt->errormsg = "Function not implemented";
SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
return SQL_ERROR;
}
/*
* This function should really talk to the dbms to determine the number of
* "parameter markers" (not bound parameters) in the statement. But, since
* Postgres doesn't support that, the driver should just count the number of markers
* and return that. The reason the driver just can't say this function is unsupported
* like it does for SQLDescribeParam is that some applications don't care and try
* to call it anyway.
* If the statement does not have parameters, it should just return 0.
*/
RETCODE SQL_API
PGAPI_NumParams(
HSTMT hstmt,
SWORD FAR * pcpar)
{
StatementClass *stmt = (StatementClass *) hstmt;
char in_quote = FALSE;
unsigned int i;
static char *func = "PGAPI_NumParams";
mylog("%s: entering...\n", func);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if (pcpar)
*pcpar = 0;
else
{
SC_log_error(func, "pcpar was null", stmt);
return SQL_ERROR;
}
if (!stmt->statement)
{
/* no statement has been allocated */
stmt->errormsg = "PGAPI_NumParams called with no statement ready.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
else
{
for (i = 0; i < strlen(stmt->statement); i++)
{
if (stmt->statement[i] == '?' && !in_quote)
(*pcpar)++;
else
{
if (stmt->statement[i] == '\'')
in_quote = (in_quote ? FALSE : TRUE);
}
}
return SQL_SUCCESS;
}
}
/*
* Bindings Implementation
*/
BindInfoClass *
create_empty_bindings(int num_columns)
{
BindInfoClass *new_bindings;
int i;
new_bindings = (BindInfoClass *) malloc(num_columns * sizeof(BindInfoClass));
if (!new_bindings)
return 0;
for (i = 0; i < num_columns; i++)
{
new_bindings[i].buflen = 0;
new_bindings[i].buffer = NULL;
new_bindings[i].used = NULL;
new_bindings[i].data_left = -1;
new_bindings[i].ttlbuf = NULL;
new_bindings[i].ttlbuflen = 0;
}
return new_bindings;
}
void
extend_bindings(StatementClass *stmt, int num_columns)
{
static char *func = "extend_bindings";
BindInfoClass *new_bindings;
int i;
mylog("%s: entering ... stmt=%u, bindings_allocated=%d, num_columns=%d\n", func, stmt, stmt->bindings_allocated, num_columns);
/*
* if we have too few, allocate room for more, and copy the old
* entries into the new structure
*/
if (stmt->bindings_allocated < num_columns)
{
new_bindings = create_empty_bindings(num_columns);
if (!new_bindings)
{
mylog("%s: unable to create %d new bindings from %d old bindings\n", func, num_columns, stmt->bindings_allocated);
if (stmt->bindings)
{
free(stmt->bindings);
stmt->bindings = NULL;
}
stmt->bindings_allocated = 0;
return;
}
if (stmt->bindings)
{
for (i = 0; i < stmt->bindings_allocated; i++)
new_bindings[i] = stmt->bindings[i];
free(stmt->bindings);
}
stmt->bindings = new_bindings;
stmt->bindings_allocated = num_columns;
}
/*
* There is no reason to zero out extra bindings if there are more
* than needed. If an app has allocated extra bindings, let it worry
* about it by unbinding those columns.
*/
/* SQLBindCol(1..) ... SQLBindCol(10...) # got 10 bindings */
/* SQLExecDirect(...) # returns 5 cols */
/* SQLExecDirect(...) # returns 10 cols (now OK) */
mylog("exit extend_bindings\n");
}
/* File: bind.h
*
* Description: See "bind.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __BIND_H__
#define __BIND_H__
#include "psqlodbc.h"
/*
* BindInfoClass -- stores information about a bound column
*/
struct BindInfoClass_
{
Int4 buflen; /* size of buffer */
Int4 data_left; /* amount of data left to read
* (SQLGetData) */
char *buffer; /* pointer to the buffer */
Int4 *used; /* used space in the buffer (for strings
* not counting the '\0') */
char *ttlbuf; /* to save the large result */
Int4 ttlbuflen; /* the buffer length */
Int2 returntype; /* kind of conversion to be applied when
* returning (SQL_C_DEFAULT,
* SQL_C_CHAR...) */
};
/*
* ParameterInfoClass -- stores information about a bound parameter
*/
struct ParameterInfoClass_
{
Int4 buflen;
char *buffer;
Int4 *used;
Int2 paramType;
Int2 CType;
Int2 SQLType;
UInt4 precision;
Int2 scale;
Oid lobj_oid;
Int4 *EXEC_used; /* amount of data OR the oid of the large
* object */
char *EXEC_buffer; /* the data or the FD of the large object */
char data_at_exec;
};
BindInfoClass *create_empty_bindings(int num_columns);
void extend_bindings(StatementClass *stmt, int num_columns);
#endif
/*-------
* Module: columninfo.c
*
* Description: This module contains routines related to
* reading and storing the field information from a query.
*
* Classes: ColumnInfoClass (Functions prefix: "CI_")
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "pgtypes.h"
#include "columninfo.h"
#include "connection.h"
#include "socket.h"
#include <stdlib.h>
#include <string.h>
#include "pgapifunc.h"
ColumnInfoClass *
CI_Constructor()
{
ColumnInfoClass *rv;
rv = (ColumnInfoClass *) malloc(sizeof(ColumnInfoClass));
if (rv)
{
rv->num_fields = 0;
rv->name = NULL;
rv->adtid = NULL;
rv->adtsize = NULL;
rv->display_size = NULL;
rv->atttypmod = NULL;
}
return rv;
}
void
CI_Destructor(ColumnInfoClass *self)
{
CI_free_memory(self);
free(self);
}
/*
* Read in field descriptions.
* If self is not null, then also store the information.
* If self is null, then just read, don't store.
*/
char
CI_read_fields(ColumnInfoClass *self, ConnectionClass *conn)
{
Int2 lf;
int new_num_fields;
Oid new_adtid;
Int2 new_adtsize;
Int4 new_atttypmod = -1;
/* MAX_COLUMN_LEN may be sufficient but for safety */
char new_field_name[2 * MAX_COLUMN_LEN + 1];
SocketClass *sock;
ConnInfo *ci;
sock = CC_get_socket(conn);
ci = &conn->connInfo;
/* at first read in the number of fields that are in the query */
new_num_fields = (Int2) SOCK_get_int(sock, sizeof(Int2));
mylog("num_fields = %d\n", new_num_fields);
if (self)
/* according to that allocate memory */
CI_set_num_fields(self, new_num_fields);
/* now read in the descriptions */
for (lf = 0; lf < new_num_fields; lf++)
{
SOCK_get_string(sock, new_field_name, 2 * MAX_COLUMN_LEN);
new_adtid = (Oid) SOCK_get_int(sock, 4);
new_adtsize = (Int2) SOCK_get_int(sock, 2);
/* If 6.4 protocol, then read the atttypmod field */
if (PG_VERSION_GE(conn, 6.4))
{
mylog("READING ATTTYPMOD\n");
new_atttypmod = (Int4) SOCK_get_int(sock, 4);
/* Subtract the header length */
switch (new_adtid)
{
case PG_TYPE_DATETIME:
case PG_TYPE_TIMESTAMP_NO_TMZONE:
case PG_TYPE_TIME:
case PG_TYPE_TIME_WITH_TMZONE:
break;
default:
new_atttypmod -= 4;
}
if (new_atttypmod < 0)
new_atttypmod = -1;
}
mylog("CI_read_fields: fieldname='%s', adtid=%d, adtsize=%d, atttypmod=%d\n", new_field_name, new_adtid, new_adtsize, new_atttypmod);
if (self)
CI_set_field_info(self, lf, new_field_name, new_adtid, new_adtsize, new_atttypmod);
}
return (SOCK_get_errcode(sock) == 0);
}
void
CI_free_memory(ColumnInfoClass *self)
{
register Int2 lf;
int num_fields = self->num_fields;
for (lf = 0; lf < num_fields; lf++)
{
if (self->name[lf])
{
free(self->name[lf]);
self->name[lf] = NULL;
}
}
/* Safe to call even if null */
self->num_fields = 0;
if (self->name)
free(self->name);
self->name = NULL;
if (self->adtid)
free(self->adtid);
self->adtid = NULL;
if (self->adtsize)
free(self->adtsize);
self->adtsize = NULL;
if (self->display_size)
free(self->display_size);
self->display_size = NULL;
if (self->atttypmod)
free(self->atttypmod);
self->atttypmod = NULL;
}
void
CI_set_num_fields(ColumnInfoClass *self, int new_num_fields)
{
CI_free_memory(self); /* always safe to call */
self->num_fields = new_num_fields;
self->name = (char **) malloc(sizeof(char *) * self->num_fields);
memset(self->name, 0, sizeof(char *) * self->num_fields);
self->adtid = (Oid *) malloc(sizeof(Oid) * self->num_fields);
self->adtsize = (Int2 *) malloc(sizeof(Int2) * self->num_fields);
self->display_size = (Int2 *) malloc(sizeof(Int2) * self->num_fields);
self->atttypmod = (Int4 *) malloc(sizeof(Int4) * self->num_fields);
}
void
CI_set_field_info(ColumnInfoClass *self, int field_num, char *new_name,
Oid new_adtid, Int2 new_adtsize, Int4 new_atttypmod)
{
/* check bounds */
if ((field_num < 0) || (field_num >= self->num_fields))
return;
/* store the info */
self->name[field_num] = strdup(new_name);
self->adtid[field_num] = new_adtid;
self->adtsize[field_num] = new_adtsize;
self->atttypmod[field_num] = new_atttypmod;
self->display_size[field_num] = 0;
}
/* File: columninfo.h
*
* Description: See "columninfo.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __COLUMNINFO_H__
#define __COLUMNINFO_H__
#include "psqlodbc.h"
struct ColumnInfoClass_
{
Int2 num_fields;
char **name; /* list of type names */
Oid *adtid; /* list of type ids */
Int2 *adtsize; /* list type sizes */
Int2 *display_size; /* the display size (longest row) */
Int4 *atttypmod; /* the length of bpchar/varchar */
};
#define CI_get_num_fields(self) (self->num_fields)
#define CI_get_oid(self, col) (self->adtid[col])
#define CI_get_fieldname(self, col) (self->name[col])
#define CI_get_fieldsize(self, col) (self->adtsize[col])
#define CI_get_display_size(self, col) (self->display_size[col])
#define CI_get_atttypmod(self, col) (self->atttypmod[col])
ColumnInfoClass *CI_Constructor(void);
void CI_Destructor(ColumnInfoClass *self);
void CI_free_memory(ColumnInfoClass *self);
char CI_read_fields(ColumnInfoClass *self, ConnectionClass *conn);
/* functions for setting up the fields from within the program, */
/* without reading from a socket */
void CI_set_num_fields(ColumnInfoClass *self, int new_num_fields);
void CI_set_field_info(ColumnInfoClass *self, int field_num, char *new_name,
Oid new_adtid, Int2 new_adtsize, Int4 atttypmod);
#endif
/*------
* Module: connection.c
*
* Description: This module contains routines related to
* connecting to and disconnecting from the Postgres DBMS.
*
* Classes: ConnectionClass (Functions prefix: "CC_")
*
* API functions: SQLAllocConnect, SQLConnect, SQLDisconnect, SQLFreeConnect,
* SQLBrowseConnect(NI)
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
/* Multibyte support Eiji Tokuya 2001-03-15 */
#include "connection.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "environ.h"
#include "socket.h"
#include "statement.h"
#include "qresult.h"
#include "lobj.h"
#include "dlg_specific.h"
#ifdef MULTIBYTE
#include "multibyte.h"
#endif
#include "pgapifunc.h"
#include "md5.h"
#define STMT_INCREMENT 16 /* how many statement holders to allocate
* at a time */
#define PRN_NULLCHECK
extern GLOBAL_VALUES globals;
RETCODE SQL_API
PGAPI_AllocConnect(
HENV henv,
HDBC FAR * phdbc)
{
EnvironmentClass *env = (EnvironmentClass *) henv;
ConnectionClass *conn;
static char *func = "PGAPI_AllocConnect";
mylog("%s: entering...\n", func);
conn = CC_Constructor();
mylog("**** %s: henv = %u, conn = %u\n", func, henv, conn);
if (!conn)
{
env->errormsg = "Couldn't allocate memory for Connection object.";
env->errornumber = ENV_ALLOC_ERROR;
*phdbc = SQL_NULL_HDBC;
EN_log_error(func, "", env);
return SQL_ERROR;
}
if (!EN_add_connection(env, conn))
{
env->errormsg = "Maximum number of connections exceeded.";
env->errornumber = ENV_ALLOC_ERROR;
CC_Destructor(conn);
*phdbc = SQL_NULL_HDBC;
EN_log_error(func, "", env);
return SQL_ERROR;
}
*phdbc = (HDBC) conn;
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_Connect(
HDBC hdbc,
UCHAR FAR * szDSN,
SWORD cbDSN,
UCHAR FAR * szUID,
SWORD cbUID,
UCHAR FAR * szAuthStr,
SWORD cbAuthStr)
{
ConnectionClass *conn = (ConnectionClass *) hdbc;
ConnInfo *ci;
static char *func = "PGAPI_Connect";
mylog("%s: entering...\n", func);
if (!conn)
{
CC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
ci = &conn->connInfo;
make_string(szDSN, cbDSN, ci->dsn);
/* get the values for the DSN from the registry */
getDSNinfo(ci, CONN_OVERWRITE);
logs_on_off(1, ci->drivers.debug, ci->drivers.commlog);
/* initialize pg_version from connInfo.protocol */
CC_initialize_pg_version(conn);
/*
* override values from DSN info with UID and authStr(pwd) This only
* occurs if the values are actually there.
*/
make_string(szUID, cbUID, ci->username);
make_string(szAuthStr, cbAuthStr, ci->password);
/* fill in any defaults */
getDSNdefaults(ci);
qlog("conn = %u, %s(DSN='%s', UID='%s', PWD='%s')\n", conn, func, ci->dsn, ci->username, ci->password);
if (CC_connect(conn, FALSE) <= 0)
{
/* Error messages are filled in */
CC_log_error(func, "Error on CC_connect", conn);
return SQL_ERROR;
}
mylog("%s: returning...\n", func);
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_BrowseConnect(
HDBC hdbc,
UCHAR FAR * szConnStrIn,
SWORD cbConnStrIn,
UCHAR FAR * szConnStrOut,
SWORD cbConnStrOutMax,
SWORD FAR * pcbConnStrOut)
{
static char *func = "PGAPI_BrowseConnect";
mylog("%s: entering...\n", func);
return SQL_SUCCESS;
}
/* Drop any hstmts open on hdbc and disconnect from database */
RETCODE SQL_API
PGAPI_Disconnect(
HDBC hdbc)
{
ConnectionClass *conn = (ConnectionClass *) hdbc;
static char *func = "PGAPI_Disconnect";
mylog("%s: entering...\n", func);
if (!conn)
{
CC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
qlog("conn=%u, %s\n", conn, func);
if (conn->status == CONN_EXECUTING)
{
conn->errornumber = CONN_IN_USE;
conn->errormsg = "A transaction is currently being executed";
CC_log_error(func, "", conn);
return SQL_ERROR;
}
logs_on_off(-1, conn->connInfo.drivers.debug, conn->connInfo.drivers.commlog);
mylog("%s: about to CC_cleanup\n", func);
/* Close the connection and free statements */
CC_cleanup(conn);
mylog("%s: done CC_cleanup\n", func);
mylog("%s: returning...\n", func);
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_FreeConnect(
HDBC hdbc)
{
ConnectionClass *conn = (ConnectionClass *) hdbc;
static char *func = "PGAPI_FreeConnect";
mylog("%s: entering...\n", func);
mylog("**** in %s: hdbc=%u\n", func, hdbc);
if (!conn)
{
CC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
/* Remove the connection from the environment */
if (!EN_remove_connection(conn->henv, conn))
{
conn->errornumber = CONN_IN_USE;
conn->errormsg = "A transaction is currently being executed";
CC_log_error(func, "", conn);
return SQL_ERROR;
}
CC_Destructor(conn);
mylog("%s: returning...\n", func);
return SQL_SUCCESS;
}
/*
* IMPLEMENTATION CONNECTION CLASS
*/
ConnectionClass *
CC_Constructor()
{
ConnectionClass *rv;
rv = (ConnectionClass *) malloc(sizeof(ConnectionClass));
if (rv != NULL)
{
rv->henv = NULL; /* not yet associated with an environment */
rv->errormsg = NULL;
rv->errornumber = 0;
rv->errormsg_created = FALSE;
rv->status = CONN_NOT_CONNECTED;
rv->transact_status = CONN_IN_AUTOCOMMIT; /* autocommit by default */
memset(&rv->connInfo, 0, sizeof(ConnInfo));
#ifdef DRIVER_CURSOR_IMPLEMENT
rv->connInfo.updatable_cursors = 1;
#endif /* DRIVER_CURSOR_IMPLEMENT */
memcpy(&(rv->connInfo.drivers), &globals, sizeof(globals));
rv->sock = SOCK_Constructor(rv);
if (!rv->sock)
return NULL;
rv->stmts = (StatementClass **) malloc(sizeof(StatementClass *) * STMT_INCREMENT);
if (!rv->stmts)
return NULL;
memset(rv->stmts, 0, sizeof(StatementClass *) * STMT_INCREMENT);
rv->num_stmts = STMT_INCREMENT;
rv->lobj_type = PG_TYPE_LO;
rv->ntables = 0;
rv->col_info = NULL;
rv->translation_option = 0;
rv->translation_handle = NULL;
rv->DataSourceToDriver = NULL;
rv->DriverToDataSource = NULL;
rv->driver_version = ODBCVER;
memset(rv->pg_version, 0, sizeof(rv->pg_version));
rv->pg_version_number = .0;
rv->pg_version_major = 0;
rv->pg_version_minor = 0;
rv->ms_jet = 0;
#ifdef MULTIBYTE
rv->client_encoding = NULL;
rv->server_encoding = NULL;
#endif /* MULTIBYTE */
/* Initialize statement options to defaults */
/* Statements under this conn will inherit these options */
InitializeStatementOptions(&rv->stmtOptions);
}
return rv;
}
char
CC_Destructor(ConnectionClass *self)
{
mylog("enter CC_Destructor, self=%u\n", self);
if (self->status == CONN_EXECUTING)
return 0;
CC_cleanup(self); /* cleanup socket and statements */
mylog("after CC_Cleanup\n");
#ifdef MULTIBYTE
if (self->client_encoding)
free(self->client_encoding);
if (self->server_encoding)
free(self->server_encoding);
#endif /* MULTIBYTE */
/* Free up statement holders */
if (self->stmts)
{
free(self->stmts);
self->stmts = NULL;
}
mylog("after free statement holders\n");
/* Free cached table info */
if (self->col_info)
{
int i;
for (i = 0; i < self->ntables; i++)
{
if (self->col_info[i]->result) /* Free the SQLColumns
* result structure */
QR_Destructor(self->col_info[i]->result);
free(self->col_info[i]);
}
free(self->col_info);
}
free(self);
mylog("exit CC_Destructor\n");
return 1;
}
/* Return how many cursors are opened on this connection */
int
CC_cursor_count(ConnectionClass *self)
{
StatementClass *stmt;
int i,
count = 0;
mylog("CC_cursor_count: self=%u, num_stmts=%d\n", self, self->num_stmts);
for (i = 0; i < self->num_stmts; i++)
{
stmt = self->stmts[i];
if (stmt && stmt->result && stmt->result->cursor)
count++;
}
mylog("CC_cursor_count: returning %d\n", count);
return count;
}
void
CC_clear_error(ConnectionClass *self)
{
self->errornumber = 0;
self->errormsg = NULL;
self->errormsg_created = FALSE;
}
/*
* Used to cancel a transaction.
* We are almost always in the middle of a transaction.
*/
char
CC_abort(ConnectionClass *self)
{
QResultClass *res;
if (CC_is_in_trans(self))
{
res = NULL;
mylog("CC_abort: sending ABORT!\n");
res = CC_send_query(self, "ABORT", NULL);
CC_set_no_trans(self);
if (res != NULL)
QR_Destructor(res);
else
return FALSE;
}
return TRUE;
}
/* This is called by SQLDisconnect also */
char
CC_cleanup(ConnectionClass *self)
{
int i;
StatementClass *stmt;
if (self->status == CONN_EXECUTING)
return FALSE;
mylog("in CC_Cleanup, self=%u\n", self);
/* Cancel an ongoing transaction */
/* We are always in the middle of a transaction, */
/* even if we are in auto commit. */
if (self->sock)
CC_abort(self);
mylog("after CC_abort\n");
/* This actually closes the connection to the dbase */
if (self->sock)
{
SOCK_Destructor(self->sock);
self->sock = NULL;
}
mylog("after SOCK destructor\n");
/* Free all the stmts on this connection */
for (i = 0; i < self->num_stmts; i++)
{
stmt = self->stmts[i];
if (stmt)
{
stmt->hdbc = NULL; /* prevent any more dbase interactions */
SC_Destructor(stmt);
self->stmts[i] = NULL;
}
}
/* Check for translation dll */
#ifdef WIN32
if (self->translation_handle)
{
FreeLibrary(self->translation_handle);
self->translation_handle = NULL;
}
#endif
mylog("exit CC_Cleanup\n");
return TRUE;
}
int
CC_set_translation(ConnectionClass *self)
{
#ifdef WIN32
if (self->translation_handle != NULL)
{
FreeLibrary(self->translation_handle);
self->translation_handle = NULL;
}
if (self->connInfo.translation_dll[0] == 0)
return TRUE;
self->translation_option = atoi(self->connInfo.translation_option);
self->translation_handle = LoadLibrary(self->connInfo.translation_dll);
if (self->translation_handle == NULL)
{
self->errornumber = CONN_UNABLE_TO_LOAD_DLL;
self->errormsg = "Could not load the translation DLL.";
return FALSE;
}
self->DataSourceToDriver
= (DataSourceToDriverProc) GetProcAddress(self->translation_handle,
"SQLDataSourceToDriver");
self->DriverToDataSource
= (DriverToDataSourceProc) GetProcAddress(self->translation_handle,
"SQLDriverToDataSource");
if (self->DataSourceToDriver == NULL || self->DriverToDataSource == NULL)
{
self->errornumber = CONN_UNABLE_TO_LOAD_DLL;
self->errormsg = "Could not find translation DLL functions.";
return FALSE;
}
#endif
return TRUE;
}
static int
md5_auth_send(ConnectionClass *self, const char *salt)
{
char *pwd1 = NULL, *pwd2 = NULL;
ConnInfo *ci = &(self->connInfo);
SocketClass *sock = self->sock;
mylog("MD5 user=%s password=%s\n", ci->username, ci->password);
if (!(pwd1 = malloc(MD5_PASSWD_LEN + 1)))
return 1;
if (!EncryptMD5(ci->password, ci->username, strlen(ci->username), pwd1))
{
free(pwd1);
return 1;
}
if (!(pwd2 = malloc(MD5_PASSWD_LEN + 1)))
{
free(pwd1);
return 1;
}
if (!EncryptMD5(pwd1 + strlen("md5"), salt, 4, pwd2))
{
free(pwd2);
free(pwd1);
return 1;
}
free(pwd1);
SOCK_put_int(sock, 4 + strlen(pwd2) + 1, 4);
SOCK_put_n_char(sock, pwd2, strlen(pwd2) + 1);
SOCK_flush_output(sock);
free(pwd2);
return 0;
}
char
CC_connect(ConnectionClass *self, char do_password)
{
StartupPacket sp;
StartupPacket6_2 sp62;
QResultClass *res;
SocketClass *sock;
ConnInfo *ci = &(self->connInfo);
int areq = -1;
int beresp;
char msgbuffer[ERROR_MSG_LENGTH];
char salt[5];
static char *func = "CC_connect";
#ifdef MULTIBYTE
char *encoding;
#endif /* MULTIBYTE */
mylog("%s: entering...\n", func);
if (do_password)
sock = self->sock; /* already connected, just authenticate */
else
{
qlog("Global Options: Version='%s', fetch=%d, socket=%d, unknown_sizes=%d, max_varchar_size=%d, max_longvarchar_size=%d\n",
POSTGRESDRIVERVERSION,
ci->drivers.fetch_max,
ci->drivers.socket_buffersize,
ci->drivers.unknown_sizes,
ci->drivers.max_varchar_size,
ci->drivers.max_longvarchar_size);
qlog(" disable_optimizer=%d, ksqo=%d, unique_index=%d, use_declarefetch=%d\n",
ci->drivers.disable_optimizer,
ci->drivers.ksqo,
ci->drivers.unique_index,
ci->drivers.use_declarefetch);
qlog(" text_as_longvarchar=%d, unknowns_as_longvarchar=%d, bools_as_char=%d\n",
ci->drivers.text_as_longvarchar,
ci->drivers.unknowns_as_longvarchar,
ci->drivers.bools_as_char);
#ifdef MULTIBYTE
encoding = check_client_encoding(ci->conn_settings);
if (encoding && strcmp(encoding, "OTHER"))
self->client_encoding = strdup(encoding);
else
{
encoding = check_client_encoding(ci->drivers.conn_settings);
if (encoding && strcmp(encoding, "OTHER"))
self->client_encoding = strdup(encoding);
}
qlog(" extra_systable_prefixes='%s', conn_settings='%s' conn_encoding='%s'\n",
ci->drivers.extra_systable_prefixes,
ci->drivers.conn_settings,
encoding ? encoding : "");
#else
qlog(" extra_systable_prefixes='%s', conn_settings='%s'\n",
ci->drivers.extra_systable_prefixes,
ci->drivers.conn_settings);
#endif
if (self->status != CONN_NOT_CONNECTED)
{
self->errormsg = "Already connected.";
self->errornumber = CONN_OPENDB_ERROR;
return 0;
}
if (ci->server[0] == '\0' || ci->port[0] == '\0' || ci->database[0] == '\0')
{
self->errornumber = CONN_INIREAD_ERROR;
self->errormsg = "Missing server name, port, or database name in call to CC_connect.";
return 0;
}
mylog("CC_connect(): DSN = '%s', server = '%s', port = '%s', database = '%s', username = '%s', password='%s'\n", ci->dsn, ci->server, ci->port, ci->database, ci->username, ci->password);
another_version_retry:
/*
* If the socket was closed for some reason (like a SQLDisconnect,
* but no SQLFreeConnect then create a socket now.
*/
if (!self->sock)
{
self->sock = SOCK_Constructor(self);
if (!self->sock)
{
self->errornumber = CONNECTION_SERVER_NOT_REACHED;
self->errormsg = "Could not open a socket to the server";
return 0;
}
}
sock = self->sock;
mylog("connecting to the server socket...\n");
SOCK_connect_to(sock, (short) atoi(ci->port), ci->server);
if (SOCK_get_errcode(sock) != 0)
{
mylog("connection to the server socket failed.\n");
self->errornumber = CONNECTION_SERVER_NOT_REACHED;
self->errormsg = "Could not connect to the server";
return 0;
}
mylog("connection to the server socket succeeded.\n");
if (PROTOCOL_62(ci))
{
sock->reverse = TRUE; /* make put_int and get_int work
* for 6.2 */
memset(&sp62, 0, sizeof(StartupPacket6_2));
SOCK_put_int(sock, htonl(4 + sizeof(StartupPacket6_2)), 4);
sp62.authtype = htonl(NO_AUTHENTICATION);
strncpy(sp62.database, ci->database, PATH_SIZE);
strncpy(sp62.user, ci->username, NAMEDATALEN);
SOCK_put_n_char(sock, (char *) &sp62, sizeof(StartupPacket6_2));
SOCK_flush_output(sock);
}
else
{
memset(&sp, 0, sizeof(StartupPacket));
mylog("sizeof startup packet = %d\n", sizeof(StartupPacket));
/* Send length of Authentication Block */
SOCK_put_int(sock, 4 + sizeof(StartupPacket), 4);
if (PROTOCOL_63(ci))
sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_63);
else
sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_LATEST);
strncpy(sp.database, ci->database, SM_DATABASE);
strncpy(sp.user, ci->username, SM_USER);
SOCK_put_n_char(sock, (char *) &sp, sizeof(StartupPacket));
SOCK_flush_output(sock);
}
mylog("sent the authentication block.\n");
if (sock->errornumber != 0)
{
mylog("couldn't send the authentication block properly.\n");
self->errornumber = CONN_INVALID_AUTHENTICATION;
self->errormsg = "Sending the authentication packet failed";
return 0;
}
mylog("sent the authentication block successfully.\n");
}
mylog("gonna do authentication\n");
/*
* Now get the authentication request from backend
*/
if (!PROTOCOL_62(ci))
{
BOOL before_64 = PG_VERSION_LT(self, 6.4),
ReadyForQuery = FALSE;
do
{
if (do_password)
beresp = 'R';
else
{
beresp = SOCK_get_char(sock);
mylog("auth got '%c'\n", beresp);
}
switch (beresp)
{
case 'E':
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
self->errornumber = CONN_INVALID_AUTHENTICATION;
self->errormsg = msgbuffer;
qlog("ERROR from backend during authentication: '%s'\n", self->errormsg);
if (strncmp(msgbuffer, "Unsupported frontend protocol", 29) == 0)
{ /* retry older version */
if (PROTOCOL_63(ci))
strcpy(ci->protocol, PG62);
else
strcpy(ci->protocol, PG63);
SOCK_Destructor(sock);
self->sock = (SocketClass *) 0;
CC_initialize_pg_version(self);
goto another_version_retry;
}
return 0;
case 'R':
if (do_password)
{
mylog("in 'R' do_password\n");
areq = AUTH_REQ_PASSWORD;
do_password = FALSE;
}
else
{
areq = SOCK_get_int(sock, 4);
if (areq == AUTH_REQ_MD5)
SOCK_get_n_char(sock, salt, 4);
if (areq == AUTH_REQ_CRYPT)
SOCK_get_n_char(sock, salt, 2);
mylog("areq = %d\n", areq);
}
switch (areq)
{
case AUTH_REQ_OK:
break;
case AUTH_REQ_KRB4:
self->errormsg = "Kerberos 4 authentication not supported";
self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
return 0;
case AUTH_REQ_KRB5:
self->errormsg = "Kerberos 5 authentication not supported";
self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
return 0;
case AUTH_REQ_PASSWORD:
mylog("in AUTH_REQ_PASSWORD\n");
if (ci->password[0] == '\0')
{
self->errornumber = CONNECTION_NEED_PASSWORD;
self->errormsg = "A password is required for this connection.";
return -1; /* need password */
}
mylog("past need password\n");
SOCK_put_int(sock, 4 + strlen(ci->password) + 1, 4);
SOCK_put_n_char(sock, ci->password, strlen(ci->password) + 1);
SOCK_flush_output(sock);
mylog("past flush\n");
break;
case AUTH_REQ_CRYPT:
self->errormsg = "Password crypt authentication not supported";
self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
return 0;
case AUTH_REQ_MD5:
mylog("in AUTH_REQ_MD5\n");
if (ci->password[0] == '\0')
{
self->errornumber = CONNECTION_NEED_PASSWORD;
self->errormsg = "A password is required for this connection.";
return -1; /* need password */
}
if (md5_auth_send(self, salt))
{
self->errormsg = "md5 hashing failed";
self->errornumber = CONN_INVALID_AUTHENTICATION;
return 0;
}
break;
case AUTH_REQ_SCM_CREDS:
self->errormsg = "Unix socket credential authentication not supported";
self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
return 0;
default:
self->errormsg = "Unknown authentication type";
self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
return 0;
}
break;
case 'K': /* Secret key (6.4 protocol) */
(void) SOCK_get_int(sock, 4); /* pid */
(void) SOCK_get_int(sock, 4); /* key */
break;
case 'Z': /* Backend is ready for new query (6.4) */
ReadyForQuery = TRUE;
break;
default:
self->errormsg = "Unexpected protocol character during authentication";
self->errornumber = CONN_INVALID_AUTHENTICATION;
return 0;
}
/*
* There were no ReadyForQuery responce before 6.4.
*/
if (before_64 && areq == AUTH_REQ_OK)
ReadyForQuery = TRUE;
} while (!ReadyForQuery);
}
CC_clear_error(self); /* clear any password error */
/*
* send an empty query in order to find out whether the specified
* database really exists on the server machine
*/
mylog("sending an empty query...\n");
res = CC_send_query(self, " ", NULL);
if (res == NULL || QR_get_status(res) != PGRES_EMPTY_QUERY)
{
mylog("got no result from the empty query. (probably database does not exist)\n");
self->errornumber = CONNECTION_NO_SUCH_DATABASE;
self->errormsg = "The database does not exist on the server\nor user authentication failed.";
if (res != NULL)
QR_Destructor(res);
return 0;
}
if (res)
QR_Destructor(res);
mylog("empty query seems to be OK.\n");
CC_set_translation(self);
/*
* Send any initial settings
*/
/*
* Since these functions allocate statements, and since the connection
* is not established yet, it would violate odbc state transition
* rules. Therefore, these functions call the corresponding local
* function instead.
*/
CC_send_settings(self);
CC_lookup_lo(self); /* a hack to get the oid of our large
* object oid type */
CC_lookup_pg_version(self); /* Get PostgreSQL version for SQLGetInfo
* use */
CC_clear_error(self); /* clear any initial command errors */
self->status = CONN_CONNECTED;
mylog("%s: returning...\n", func);
return 1;
}
char
CC_add_statement(ConnectionClass *self, StatementClass *stmt)
{
int i;
mylog("CC_add_statement: self=%u, stmt=%u\n", self, stmt);
for (i = 0; i < self->num_stmts; i++)
{
if (!self->stmts[i])
{
stmt->hdbc = self;
self->stmts[i] = stmt;
return TRUE;
}
}
/* no more room -- allocate more memory */
self->stmts = (StatementClass **) realloc(self->stmts, sizeof(StatementClass *) * (STMT_INCREMENT + self->num_stmts));
if (!self->stmts)
return FALSE;
memset(&self->stmts[self->num_stmts], 0, sizeof(StatementClass *) * STMT_INCREMENT);
stmt->hdbc = self;
self->stmts[self->num_stmts] = stmt;
self->num_stmts += STMT_INCREMENT;
return TRUE;
}
char
CC_remove_statement(ConnectionClass *self, StatementClass *stmt)
{
int i;
for (i = 0; i < self->num_stmts; i++)
{
if (self->stmts[i] == stmt && stmt->status != STMT_EXECUTING)
{
self->stmts[i] = NULL;
return TRUE;
}
}
return FALSE;
}
/*
* Create a more informative error message by concatenating the connection
* error message with its socket error message.
*/
char *
CC_create_errormsg(ConnectionClass *self)
{
SocketClass *sock = self->sock;
int pos;
static char msg[4096];
mylog("enter CC_create_errormsg\n");
msg[0] = '\0';
if (self->errormsg)
strcpy(msg, self->errormsg);
mylog("msg = '%s'\n", msg);
if (sock && sock->errormsg && sock->errormsg[0] != '\0')
{
pos = strlen(msg);
sprintf(&msg[pos], ";\n%s", sock->errormsg);
}
mylog("exit CC_create_errormsg\n");
return msg;
}
char
CC_get_error(ConnectionClass *self, int *number, char **message)
{
int rv;
mylog("enter CC_get_error\n");
/* Create a very informative errormsg if it hasn't been done yet. */
if (!self->errormsg_created)
{
self->errormsg = CC_create_errormsg(self);
self->errormsg_created = TRUE;
}
if (self->errornumber)
{
*number = self->errornumber;
*message = self->errormsg;
}
rv = (self->errornumber != 0);
self->errornumber = 0; /* clear the error */
mylog("exit CC_get_error\n");
return rv;
}
/*
* The "result_in" is only used by QR_next_tuple() to fetch another group of rows into
* the same existing QResultClass (this occurs when the tuple cache is depleted and
* needs to be re-filled).
*
* The "cursor" is used by SQLExecute to associate a statement handle as the cursor name
* (i.e., C3326857) for SQL select statements. This cursor is then used in future
* 'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements.
*/
QResultClass *
CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi)
{
QResultClass *result_in = NULL,
*res = NULL,
*retres = NULL;
char swallow,
*wq;
int id;
SocketClass *sock = self->sock;
int maxlen,
empty_reqs;
BOOL msg_truncated,
ReadyToReturn,
tuples_return = FALSE,
query_completed = FALSE,
before_64 = PG_VERSION_LT(self, 6.4);
/* ERROR_MSG_LENGTH is suffcient */
static char msgbuffer[ERROR_MSG_LENGTH + 1];
/* QR_set_command() dups this string so doesn't need static */
char cmdbuffer[ERROR_MSG_LENGTH + 1];
mylog("send_query(): conn=%u, query='%s'\n", self, query);
qlog("conn=%u, query='%s'\n", self, query);
/* Indicate that we are sending a query to the backend */
maxlen = CC_get_max_query_len(self);
if (maxlen > 0 && maxlen < (int) strlen(query) + 1)
{
self->errornumber = CONNECTION_MSG_TOO_LONG;
self->errormsg = "Query string is too long";
return NULL;
}
if ((NULL == query) || (query[0] == '\0'))
return NULL;
if (SOCK_get_errcode(sock) != 0)
{
self->errornumber = CONNECTION_COULD_NOT_SEND;
self->errormsg = "Could not send Query to backend";
CC_set_no_trans(self);
return NULL;
}
SOCK_put_char(sock, 'Q');
if (SOCK_get_errcode(sock) != 0)
{
self->errornumber = CONNECTION_COULD_NOT_SEND;
self->errormsg = "Could not send Query to backend";
CC_set_no_trans(self);
return NULL;
}
SOCK_put_string(sock, query);
SOCK_flush_output(sock);
if (SOCK_get_errcode(sock) != 0)
{
self->errornumber = CONNECTION_COULD_NOT_SEND;
self->errormsg = "Could not send Query to backend";
CC_set_no_trans(self);
return NULL;
}
mylog("send_query: done sending query\n");
ReadyToReturn = FALSE;
empty_reqs = 0;
for (wq = query; isspace((unsigned char) *wq); wq++)
;
if (*wq == '\0')
empty_reqs = 1;
while (!ReadyToReturn)
{
/* what type of message is coming now ? */
id = SOCK_get_char(sock);
if ((SOCK_get_errcode(sock) != 0) || (id == EOF))
{
self->errornumber = CONNECTION_NO_RESPONSE;
self->errormsg = "No response from the backend";
mylog("send_query: 'id' - %s\n", self->errormsg);
CC_set_no_trans(self);
ReadyToReturn = TRUE;
retres = NULL;
break;
}
mylog("send_query: got id = '%c'\n", id);
switch (id)
{
case 'A': /* Asynchronous Messages are ignored */
(void) SOCK_get_int(sock, 4); /* id of notification */
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
/* name of the relation the message comes from */
break;
case 'C': /* portal query command, no tuples
* returned */
/* read in the return message from the backend */
SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
if (SOCK_get_errcode(sock) != 0)
{
self->errornumber = CONNECTION_NO_RESPONSE;
self->errormsg = "No response from backend while receiving a portal query command";
mylog("send_query: 'C' - %s\n", self->errormsg);
CC_set_no_trans(self);
ReadyToReturn = TRUE;
retres = NULL;
}
else
{
mylog("send_query: ok - 'C' - %s\n", cmdbuffer);
if (res == NULL) /* allow for "show" style notices */
res = QR_Constructor();
mylog("send_query: setting cmdbuffer = '%s'\n", cmdbuffer);
/* Only save the first command */
if (QR_command_successful(res))
QR_set_status(res, PGRES_COMMAND_OK);
QR_set_command(res, cmdbuffer);
query_completed = TRUE;
mylog("send_query: returning res = %u\n", res);
if (!before_64)
break;
/*
* (Quotation from the original comments) since
* backend may produce more than one result for some
* commands we need to poll until clear so we send an
* empty query, and keep reading out of the pipe until
* an 'I' is received
*/
if (empty_reqs == 0)
{
SOCK_put_string(sock, "Q ");
SOCK_flush_output(sock);
empty_reqs++;
}
}
break;
case 'Z': /* Backend is ready for new query (6.4) */
if (empty_reqs == 0)
{
ReadyToReturn = TRUE;
if (res && QR_get_aborted(res))
retres = res;
else if (tuples_return)
retres = result_in;
else if (query_completed)
retres = res;
else
ReadyToReturn = FALSE;
}
break;
case 'N': /* NOTICE: */
msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
if (!res)
res = QR_Constructor();
if (QR_command_successful(res))
QR_set_status(res, PGRES_NONFATAL_ERROR);
QR_set_notice(res, cmdbuffer); /* will dup this string */
mylog("~~~ NOTICE: '%s'\n", cmdbuffer);
qlog("NOTICE from backend during send_query: '%s'\n", cmdbuffer);
while (msg_truncated)
msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
continue; /* dont return a result -- continue
* reading */
case 'I': /* The server sends an empty query */
/* There is a closing '\0' following the 'I', so we eat it */
swallow = SOCK_get_char(sock);
if (!res)
res = QR_Constructor();
if ((swallow != '\0') || SOCK_get_errcode(sock) != 0)
{
self->errornumber = CONNECTION_BACKEND_CRAZY;
self->errormsg = "Unexpected protocol character from backend (send_query - I)";
QR_set_status(res, PGRES_FATAL_ERROR);
ReadyToReturn = TRUE;
retres = res;
break;
}
else
{
/* We return the empty query */
QR_set_status(res, PGRES_EMPTY_QUERY);
}
if (empty_reqs > 0)
{
if (--empty_reqs == 0)
query_completed = TRUE;
}
break;
case 'E':
msg_truncated = SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
/* Remove a newline */
if (msgbuffer[0] != '\0' && msgbuffer[strlen(msgbuffer) - 1] == '\n')
msgbuffer[strlen(msgbuffer) - 1] = '\0';
self->errormsg = msgbuffer;
mylog("send_query: 'E' - %s\n", self->errormsg);
qlog("ERROR from backend during send_query: '%s'\n", self->errormsg);
/* We should report that an error occured. Zoltan */
if (!res)
res = QR_Constructor();
if (!strncmp(self->errormsg, "FATAL", 5))
{
self->errornumber = CONNECTION_SERVER_REPORTED_ERROR;
CC_set_no_trans(self);
}
else
self->errornumber = CONNECTION_SERVER_REPORTED_WARNING;
QR_set_status(res, PGRES_FATAL_ERROR);
QR_set_aborted(res, TRUE);
while (msg_truncated)
msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
query_completed = TRUE;
break;
case 'P': /* get the Portal name */
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
break;
case 'T': /* Tuple results start here */
result_in = qi ? qi->result_in : NULL;
if (result_in == NULL)
{
result_in = QR_Constructor();
mylog("send_query: 'T' no result_in: res = %u\n", result_in);
if (!result_in)
{
self->errornumber = CONNECTION_COULD_NOT_RECEIVE;
self->errormsg = "Could not create result info in send_query.";
ReadyToReturn = TRUE;
retres = NULL;
break;
}
if (qi)
QR_set_cache_size(result_in, qi->row_size);
if (!QR_fetch_tuples(result_in, self, qi ? qi->cursor : NULL))
{
self->errornumber = CONNECTION_COULD_NOT_RECEIVE;
self->errormsg = QR_get_message(result_in);
ReadyToReturn = TRUE;
retres = NULL;
break;
}
}
else
{ /* next fetch, so reuse an existing result */
/*
* called from QR_next_tuple and must return
* immediately.
*/
ReadyToReturn = TRUE;
if (!QR_fetch_tuples(result_in, NULL, NULL))
{
self->errornumber = CONNECTION_COULD_NOT_RECEIVE;
self->errormsg = QR_get_message(result_in);
retres = NULL;
break;
}
retres = result_in;
}
tuples_return = TRUE;
break;
case 'D': /* Copy in command began successfully */
if (!res)
res = QR_Constructor();
if (QR_command_successful(res))
QR_set_status(res, PGRES_COPY_IN);
ReadyToReturn = TRUE;
retres = res;
break;
case 'B': /* Copy out command began successfully */
if (!res)
res = QR_Constructor();
if (QR_command_successful(res))
QR_set_status(res, PGRES_COPY_OUT);
ReadyToReturn = TRUE;
retres = res;
break;
default:
self->errornumber = CONNECTION_BACKEND_CRAZY;
self->errormsg = "Unexpected protocol character from backend (send_query)";
CC_set_no_trans(self);
mylog("send_query: error - %s\n", self->errormsg);
ReadyToReturn = TRUE;
retres = NULL;
break;
}
/*
* There were no ReadyForQuery response before 6.4.
*/
if (before_64)
{
if (empty_reqs == 0 && (query_completed || tuples_return))
break;
}
}
/*
* Break before being ready to return.
*/
if (!ReadyToReturn)
{
if (res && QR_get_aborted(res))
retres = res;
else if (tuples_return)
retres = result_in;
else
retres = res;
}
/*
* set notice message to result_in.
*/
if (result_in && res && retres == result_in)
{
if (QR_command_successful(result_in))
QR_set_status(result_in, QR_get_status(res));
QR_set_notice(result_in, QR_get_notice(res));
}
/*
* Cleanup garbage results before returning.
*/
if (res && retres != res)
QR_Destructor(res);
if (result_in && retres != result_in)
{
if (qi && qi->result_in)
;
else
QR_Destructor(result_in);
}
return retres;
}
int
CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *args, int nargs)
{
char id,
c,
done;
SocketClass *sock = self->sock;
/* ERROR_MSG_LENGTH is sufficient */
static char msgbuffer[ERROR_MSG_LENGTH + 1];
int i;
mylog("send_function(): conn=%u, fnid=%d, result_is_int=%d, nargs=%d\n", self, fnid, result_is_int, nargs);
if (SOCK_get_errcode(sock) != 0)
{
self->errornumber = CONNECTION_COULD_NOT_SEND;
self->errormsg = "Could not send function to backend";
CC_set_no_trans(self);
return FALSE;
}
SOCK_put_string(sock, "F ");
if (SOCK_get_errcode(sock) != 0)
{
self->errornumber = CONNECTION_COULD_NOT_SEND;
self->errormsg = "Could not send function to backend";
CC_set_no_trans(self);
return FALSE;
}
SOCK_put_int(sock, fnid, 4);
SOCK_put_int(sock, nargs, 4);
mylog("send_function: done sending function\n");
for (i = 0; i < nargs; ++i)
{
mylog(" arg[%d]: len = %d, isint = %d, integer = %d, ptr = %u\n", i, args[i].len, args[i].isint, args[i].u.integer, args[i].u.ptr);
SOCK_put_int(sock, args[i].len, 4);
if (args[i].isint)
SOCK_put_int(sock, args[i].u.integer, 4);
else
SOCK_put_n_char(sock, (char *) args[i].u.ptr, args[i].len);
}
mylog(" done sending args\n");
SOCK_flush_output(sock);
mylog(" after flush output\n");
done = FALSE;
while (!done)
{
id = SOCK_get_char(sock);
mylog(" got id = %c\n", id);
switch (id)
{
case 'V':
done = TRUE;
break; /* ok */
case 'N':
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
mylog("send_function(V): 'N' - %s\n", msgbuffer);
/* continue reading */
break;
case 'E':
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
self->errormsg = msgbuffer;
mylog("send_function(V): 'E' - %s\n", self->errormsg);
qlog("ERROR from backend during send_function: '%s'\n", self->errormsg);
return FALSE;
case 'Z':
break;
default:
self->errornumber = CONNECTION_BACKEND_CRAZY;
self->errormsg = "Unexpected protocol character from backend (send_function, args)";
CC_set_no_trans(self);
mylog("send_function: error - %s\n", self->errormsg);
return FALSE;
}
}
id = SOCK_get_char(sock);
for (;;)
{
switch (id)
{
case 'G': /* function returned properly */
mylog(" got G!\n");
*actual_result_len = SOCK_get_int(sock, 4);
mylog(" actual_result_len = %d\n", *actual_result_len);
if (result_is_int)
*((int *) result_buf) = SOCK_get_int(sock, 4);
else
SOCK_get_n_char(sock, (char *) result_buf, *actual_result_len);
mylog(" after get result\n");
c = SOCK_get_char(sock); /* get the last '0' */
mylog(" after get 0\n");
return TRUE;
case 'E':
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
self->errormsg = msgbuffer;
mylog("send_function(G): 'E' - %s\n", self->errormsg);
qlog("ERROR from backend during send_function: '%s'\n", self->errormsg);
return FALSE;
case 'N':
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
mylog("send_function(G): 'N' - %s\n", msgbuffer);
qlog("NOTICE from backend during send_function: '%s'\n", msgbuffer);
continue; /* dont return a result -- continue
* reading */
case '0': /* empty result */
return TRUE;
default:
self->errornumber = CONNECTION_BACKEND_CRAZY;
self->errormsg = "Unexpected protocol character from backend (send_function, result)";
CC_set_no_trans(self);
mylog("send_function: error - %s\n", self->errormsg);
return FALSE;
}
}
}
char
CC_send_settings(ConnectionClass *self)
{
/* char ini_query[MAX_MESSAGE_LEN]; */
ConnInfo *ci = &(self->connInfo);
/* QResultClass *res; */
HSTMT hstmt;
StatementClass *stmt;
RETCODE result;
char status = TRUE;
char *cs,
*ptr;
static char *func = "CC_send_settings";
mylog("%s: entering...\n", func);
/*
* This function must use the local odbc API functions since the odbc state
* has not transitioned to "connected" yet.
*/
result = PGAPI_AllocStmt(self, &hstmt);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
return FALSE;
stmt = (StatementClass *) hstmt;
stmt->internal = TRUE; /* ensure no BEGIN/COMMIT/ABORT stuff */
/* Set the Datestyle to the format the driver expects it to be in */
result = PGAPI_ExecDirect(hstmt, "set DateStyle to 'ISO'", SQL_NTS);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
status = FALSE;
mylog("%s: result %d, status %d from set DateStyle\n", func, result, status);
/* Disable genetic optimizer based on global flag */
if (ci->drivers.disable_optimizer)
{
result = PGAPI_ExecDirect(hstmt, "set geqo to 'OFF'", SQL_NTS);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
status = FALSE;
mylog("%s: result %d, status %d from set geqo\n", func, result, status);
}
/* KSQO */
if (ci->drivers.ksqo)
{
result = PGAPI_ExecDirect(hstmt, "set ksqo to 'ON'", SQL_NTS);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
status = FALSE;
mylog("%s: result %d, status %d from set ksqo\n", func, result, status);
}
/* Global settings */
if (ci->drivers.conn_settings[0] != '\0')
{
cs = strdup(ci->drivers.conn_settings);
ptr = strtok(cs, ";");
while (ptr)
{
result = PGAPI_ExecDirect(hstmt, ptr, SQL_NTS);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
status = FALSE;
mylog("%s: result %d, status %d from '%s'\n", func, result, status, ptr);
ptr = strtok(NULL, ";");
}
free(cs);
}
/* Per Datasource settings */
if (ci->conn_settings[0] != '\0')
{
cs = strdup(ci->conn_settings);
ptr = strtok(cs, ";");
while (ptr)
{
result = PGAPI_ExecDirect(hstmt, ptr, SQL_NTS);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
status = FALSE;
mylog("%s: result %d, status %d from '%s'\n", func, result, status, ptr);
ptr = strtok(NULL, ";");
}
free(cs);
}
PGAPI_FreeStmt(hstmt, SQL_DROP);
return status;
}
/*
* This function is just a hack to get the oid of our Large Object oid type.
* If a real Large Object oid type is made part of Postgres, this function
* will go away and the define 'PG_TYPE_LO' will be updated.
*/
void
CC_lookup_lo(ConnectionClass *self)
{
HSTMT hstmt;
StatementClass *stmt;
RETCODE result;
static char *func = "CC_lookup_lo";
mylog("%s: entering...\n", func);
/*
* This function must use the local odbc API functions since the odbc state
* has not transitioned to "connected" yet.
*/
result = PGAPI_AllocStmt(self, &hstmt);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
return;
stmt = (StatementClass *) hstmt;
result = PGAPI_ExecDirect(hstmt, "select oid from pg_type where typname='" PG_TYPE_LO_NAME "'", SQL_NTS);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
{
PGAPI_FreeStmt(hstmt, SQL_DROP);
return;
}
result = PGAPI_Fetch(hstmt);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
{
PGAPI_FreeStmt(hstmt, SQL_DROP);
return;
}
result = PGAPI_GetData(hstmt, 1, SQL_C_SLONG, &self->lobj_type, sizeof(self->lobj_type), NULL);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
{
PGAPI_FreeStmt(hstmt, SQL_DROP);
return;
}
mylog("Got the large object oid: %d\n", self->lobj_type);
qlog(" [ Large Object oid = %d ]\n", self->lobj_type);
result = PGAPI_FreeStmt(hstmt, SQL_DROP);
}
/*
* This function initializes the version of PostgreSQL from
* connInfo.protocol that we're connected to.
* h-inoue 01-2-2001
*/
void
CC_initialize_pg_version(ConnectionClass *self)
{
strcpy(self->pg_version, self->connInfo.protocol);
if (PROTOCOL_62(&self->connInfo))
{
self->pg_version_number = (float) 6.2;
self->pg_version_major = 6;
self->pg_version_minor = 2;
}
else if (PROTOCOL_63(&self->connInfo))
{
self->pg_version_number = (float) 6.3;
self->pg_version_major = 6;
self->pg_version_minor = 3;
}
else
{
self->pg_version_number = (float) 6.4;
self->pg_version_major = 6;
self->pg_version_minor = 4;
}
}
/*
* This function gets the version of PostgreSQL that we're connected to.
* This is used to return the correct info in SQLGetInfo
* DJP - 25-1-2001
*/
void
CC_lookup_pg_version(ConnectionClass *self)
{
HSTMT hstmt;
StatementClass *stmt;
RETCODE result;
char szVersion[32];
int major,
minor;
static char *func = "CC_lookup_pg_version";
mylog("%s: entering...\n", func);
/*
* This function must use the local odbc API functions since the odbc state
* has not transitioned to "connected" yet.
*/
result = PGAPI_AllocStmt(self, &hstmt);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
return;
stmt = (StatementClass *) hstmt;
/* get the server's version if possible */
result = PGAPI_ExecDirect(hstmt, "select version()", SQL_NTS);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
{
PGAPI_FreeStmt(hstmt, SQL_DROP);
return;
}
result = PGAPI_Fetch(hstmt);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
{
PGAPI_FreeStmt(hstmt, SQL_DROP);
return;
}
result = PGAPI_GetData(hstmt, 1, SQL_C_CHAR, self->pg_version, MAX_INFO_STRING, NULL);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
{
PGAPI_FreeStmt(hstmt, SQL_DROP);
return;
}
/*
* Extract the Major and Minor numbers from the string. This assumes
* the string starts 'Postgresql X.X'
*/
strcpy(szVersion, "0.0");
if (sscanf(self->pg_version, "%*s %d.%d", &major, &minor) >= 2)
{
sprintf(szVersion, "%d.%d", major, minor);
self->pg_version_major = major;
self->pg_version_minor = minor;
}
self->pg_version_number = (float) atof(szVersion);
mylog("Got the PostgreSQL version string: '%s'\n", self->pg_version);
mylog("Extracted PostgreSQL version number: '%1.1f'\n", self->pg_version_number);
qlog(" [ PostgreSQL version string = '%s' ]\n", self->pg_version);
qlog(" [ PostgreSQL version number = '%1.1f' ]\n", self->pg_version_number);
result = PGAPI_FreeStmt(hstmt, SQL_DROP);
}
void
CC_log_error(char *func, char *desc, ConnectionClass *self)
{
#ifdef PRN_NULLCHECK
#define nullcheck(a) (a ? a : "(NULL)")
#endif
if (self)
{
qlog("CONN ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, nullcheck(self->errormsg));
mylog("CONN ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, nullcheck(self->errormsg));
qlog(" ------------------------------------------------------------\n");
qlog(" henv=%u, conn=%u, status=%u, num_stmts=%d\n", self->henv, self, self->status, self->num_stmts);
qlog(" sock=%u, stmts=%u, lobj_type=%d\n", self->sock, self->stmts, self->lobj_type);
qlog(" ---------------- Socket Info -------------------------------\n");
if (self->sock)
{
SocketClass *sock = self->sock;
qlog(" socket=%d, reverse=%d, errornumber=%d, errormsg='%s'\n", sock->socket, sock->reverse, sock->errornumber, nullcheck(sock->errormsg));
qlog(" buffer_in=%u, buffer_out=%u\n", sock->buffer_in, sock->buffer_out);
qlog(" buffer_filled_in=%d, buffer_filled_out=%d, buffer_read_in=%d\n", sock->buffer_filled_in, sock->buffer_filled_out, sock->buffer_read_in);
}
}
else
qlog("INVALID CONNECTION HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
#undef PRN_NULLCHECK
}
int
CC_get_max_query_len(const ConnectionClass *conn)
{
int value;
/* Long Queries in 7.0+ */
if (PG_VERSION_GE(conn, 7.0))
value = 0 /* MAX_STATEMENT_LEN */ ;
/* Prior to 7.0 we used 2*BLCKSZ */
else if (PG_VERSION_GE(conn, 6.5))
value = (2 * BLCKSZ);
else
/* Prior to 6.5 we used BLCKSZ */
value = BLCKSZ;
return value;
}
/* File: connection.h
*
* Description: See "connection.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __CONNECTION_H__
#define __CONNECTION_H__
#include "psqlodbc.h"
#include <stdlib.h>
#include <string.h>
typedef enum
{
CONN_NOT_CONNECTED, /* Connection has not been established */
CONN_CONNECTED, /* Connection is up and has been
* established */
CONN_DOWN, /* Connection is broken */
CONN_EXECUTING /* the connection is currently executing a
* statement */
} CONN_Status;
/* These errors have general sql error state */
#define CONNECTION_SERVER_NOT_REACHED 101
#define CONNECTION_MSG_TOO_LONG 103
#define CONNECTION_COULD_NOT_SEND 104
#define CONNECTION_NO_SUCH_DATABASE 105
#define CONNECTION_BACKEND_CRAZY 106
#define CONNECTION_NO_RESPONSE 107
#define CONNECTION_SERVER_REPORTED_ERROR 108
#define CONNECTION_COULD_NOT_RECEIVE 109
#define CONNECTION_SERVER_REPORTED_WARNING 110
#define CONNECTION_NEED_PASSWORD 112
/* These errors correspond to specific SQL states */
#define CONN_INIREAD_ERROR 201
#define CONN_OPENDB_ERROR 202
#define CONN_STMT_ALLOC_ERROR 203
#define CONN_IN_USE 204
#define CONN_UNSUPPORTED_OPTION 205
/* Used by SetConnectoption to indicate unsupported options */
#define CONN_INVALID_ARGUMENT_NO 206
/* SetConnectOption: corresponds to ODBC--"S1009" */
#define CONN_TRANSACT_IN_PROGRES 207
#define CONN_NO_MEMORY_ERROR 208
#define CONN_NOT_IMPLEMENTED_ERROR 209
#define CONN_INVALID_AUTHENTICATION 210
#define CONN_AUTH_TYPE_UNSUPPORTED 211
#define CONN_UNABLE_TO_LOAD_DLL 212
#define CONN_OPTION_VALUE_CHANGED 213
#define CONN_VALUE_OUT_OF_RANGE 214
#define CONN_TRUNCATED 215
/* Conn_status defines */
#define CONN_IN_AUTOCOMMIT 0x01
#define CONN_IN_TRANSACTION 0x02
/* AutoCommit functions */
#define CC_set_autocommit_off(x) (x->transact_status &= ~CONN_IN_AUTOCOMMIT)
#define CC_set_autocommit_on(x) (x->transact_status |= CONN_IN_AUTOCOMMIT)
#define CC_is_in_autocommit(x) (x->transact_status & CONN_IN_AUTOCOMMIT)
/* Transaction in/not functions */
#define CC_set_in_trans(x) (x->transact_status |= CONN_IN_TRANSACTION)
#define CC_set_no_trans(x) (x->transact_status &= ~CONN_IN_TRANSACTION)
#define CC_is_in_trans(x) (x->transact_status & CONN_IN_TRANSACTION)
/* Authentication types */
#define AUTH_REQ_OK 0
#define AUTH_REQ_KRB4 1
#define AUTH_REQ_KRB5 2
#define AUTH_REQ_PASSWORD 3
#define AUTH_REQ_CRYPT 4
#define AUTH_REQ_MD5 5
#define AUTH_REQ_SCM_CREDS 6
/* Startup Packet sizes */
#define SM_DATABASE 64
#define SM_USER 32
#define SM_OPTIONS 64
#define SM_UNUSED 64
#define SM_TTY 64
/* Old 6.2 protocol defines */
#define NO_AUTHENTICATION 7
#define PATH_SIZE 64
#define ARGV_SIZE 64
#define NAMEDATALEN 16
typedef unsigned int ProtocolVersion;
#define PG_PROTOCOL(major, minor) (((major) << 16) | (minor))
#define PG_PROTOCOL_LATEST PG_PROTOCOL(2, 0)
#define PG_PROTOCOL_63 PG_PROTOCOL(1, 0)
#define PG_PROTOCOL_62 PG_PROTOCOL(0, 0)
/* This startup packet is to support latest Postgres protocol (6.4, 6.3) */
typedef struct _StartupPacket
{
ProtocolVersion protoVersion;
char database[SM_DATABASE];
char user[SM_USER];
char options[SM_OPTIONS];
char unused[SM_UNUSED];
char tty[SM_TTY];
} StartupPacket;
/* This startup packet is to support pre-Postgres 6.3 protocol */
typedef struct _StartupPacket6_2
{
unsigned int authtype;
char database[PATH_SIZE];
char user[NAMEDATALEN];
char options[ARGV_SIZE];
char execfile[ARGV_SIZE];
char tty[PATH_SIZE];
} StartupPacket6_2;
/* Structure to hold all the connection attributes for a specific
connection (used for both registry and file, DSN and DRIVER)
*/
typedef struct
{
char dsn[MEDIUM_REGISTRY_LEN];
char desc[MEDIUM_REGISTRY_LEN];
char driver[MEDIUM_REGISTRY_LEN];
char server[MEDIUM_REGISTRY_LEN];
char database[MEDIUM_REGISTRY_LEN];
char username[MEDIUM_REGISTRY_LEN];
char password[MEDIUM_REGISTRY_LEN];
char conn_settings[LARGE_REGISTRY_LEN];
char protocol[SMALL_REGISTRY_LEN];
char port[SMALL_REGISTRY_LEN];
char onlyread[SMALL_REGISTRY_LEN];
char fake_oid_index[SMALL_REGISTRY_LEN];
char show_oid_column[SMALL_REGISTRY_LEN];
char row_versioning[SMALL_REGISTRY_LEN];
char show_system_tables[SMALL_REGISTRY_LEN];
char translation_dll[MEDIUM_REGISTRY_LEN];
char translation_option[SMALL_REGISTRY_LEN];
char focus_password;
char disallow_premature;
char updatable_cursors;
GLOBAL_VALUES drivers; /* moved from driver's option */
} ConnInfo;
/* Macro to determine is the connection using 6.2 protocol? */
#define PROTOCOL_62(conninfo_) (strncmp((conninfo_)->protocol, PG62, strlen(PG62)) == 0)
/* Macro to determine is the connection using 6.3 protocol? */
#define PROTOCOL_63(conninfo_) (strncmp((conninfo_)->protocol, PG63, strlen(PG63)) == 0)
/*
* Macros to compare the server's version with a specified version
* 1st parameter: pointer to a ConnectionClass object
* 2nd parameter: major version number
* 3rd parameter: minor version number
*/
#define SERVER_VERSION_GT(conn, major, minor) \
((conn)->pg_version_major > major || \
((conn)->pg_version_major == major && (conn)->pg_version_minor > minor))
#define SERVER_VERSION_GE(conn, major, minor) \
((conn)->pg_version_major > major || \
((conn)->pg_version_major == major && (conn)->pg_version_minor >= minor))
#define SERVER_VERSION_EQ(conn, major, minor) \
((conn)->pg_version_major == major && (conn)->pg_version_minor == minor)
#define SERVER_VERSION_LE(conn, major, minor) (! SERVER_VERSION_GT(conn, major, minor))
#define SERVER_VERSION_LT(conn, major, minor) (! SERVER_VERSION_GE(conn, major, minor))
/*#if ! defined(HAVE_CONFIG_H) || defined(HAVE_STRINGIZE)*/
#define STRING_AFTER_DOT(string) (strchr(#string, '.') + 1)
/*#else
#define STRING_AFTER_DOT(str) (strchr("str", '.') + 1)
#endif*/
/*
* Simplified macros to compare the server's version with a
* specified version
* Note: Never pass a variable as the second parameter.
* It must be a decimal constant of the form %d.%d .
*/
#define PG_VERSION_GT(conn, ver) \
(SERVER_VERSION_GT(conn, (int) ver, atoi(STRING_AFTER_DOT(ver))))
#define PG_VERSION_GE(conn, ver) \
(SERVER_VERSION_GE(conn, (int) ver, atoi(STRING_AFTER_DOT(ver))))
#define PG_VERSION_EQ(conn, ver) \
(SERVER_VERSION_EQ(conn, (int) ver, atoi(STRING_AFTER_DOT(ver))))
#define PG_VERSION_LE(conn, ver) (! PG_VERSION_GT(conn, ver))
#define PG_VERSION_LT(conn, ver) (! PG_VERSION_GE(conn, ver))
/* This is used to store cached table information in the connection */
struct col_info
{
QResultClass *result;
char name[MAX_TABLE_LEN + 1];
};
/* Translation DLL entry points */
#ifdef WIN32
#define DLLHANDLE HINSTANCE
#else
#define WINAPI CALLBACK
#define DLLHANDLE void *
#define HINSTANCE void *
#endif
typedef BOOL (FAR WINAPI * DataSourceToDriverProc) (UDWORD,
SWORD,
PTR,
SDWORD,
PTR,
SDWORD,
SDWORD FAR *,
UCHAR FAR *,
SWORD,
SWORD FAR *);
typedef BOOL (FAR WINAPI * DriverToDataSourceProc) (UDWORD,
SWORD,
PTR,
SDWORD,
PTR,
SDWORD,
SDWORD FAR *,
UCHAR FAR *,
SWORD,
SWORD FAR *);
/******* The Connection handle ************/
struct ConnectionClass_
{
HENV henv; /* environment this connection was created
* on */
StatementOptions stmtOptions;
char *errormsg;
int errornumber;
CONN_Status status;
ConnInfo connInfo;
StatementClass **stmts;
int num_stmts;
SocketClass *sock;
int lobj_type;
int ntables;
COL_INFO **col_info;
long translation_option;
HINSTANCE translation_handle;
DataSourceToDriverProc DataSourceToDriver;
DriverToDataSourceProc DriverToDataSource;
Int2 driver_version; /* prepared for ODBC3.0 */
char transact_status;/* Is a transaction is currently in
* progress */
char errormsg_created; /* has an informative error msg
* been created? */
char pg_version[MAX_INFO_STRING]; /* Version of PostgreSQL
* we're connected to -
* DJP 25-1-2001 */
float pg_version_number;
Int2 pg_version_major;
Int2 pg_version_minor;
char ms_jet;
#ifdef MULTIBYTE
char *client_encoding;
char *server_encoding;
#endif /* MULTIBYTE */
};
/* Accessor functions */
#define CC_get_socket(x) (x->sock)
#define CC_get_database(x) (x->connInfo.database)
#define CC_get_server(x) (x->connInfo.server)
#define CC_get_DSN(x) (x->connInfo.dsn)
#define CC_get_username(x) (x->connInfo.username)
#define CC_is_onlyread(x) (x->connInfo.onlyread[0] == '1')
/* for CC_DSN_info */
#define CONN_DONT_OVERWRITE 0
#define CONN_OVERWRITE 1
/* prototypes */
ConnectionClass *CC_Constructor(void);
char CC_Destructor(ConnectionClass *self);
int CC_cursor_count(ConnectionClass *self);
char CC_cleanup(ConnectionClass *self);
char CC_abort(ConnectionClass *self);
int CC_set_translation(ConnectionClass *self);
char CC_connect(ConnectionClass *self, char do_password);
char CC_add_statement(ConnectionClass *self, StatementClass *stmt);
char CC_remove_statement(ConnectionClass *self, StatementClass *stmt);
char CC_get_error(ConnectionClass *self, int *number, char **message);
QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi);
void CC_clear_error(ConnectionClass *self);
char *CC_create_errormsg(ConnectionClass *self);
int CC_send_function(ConnectionClass *conn, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *argv, int nargs);
char CC_send_settings(ConnectionClass *self);
void CC_lookup_lo(ConnectionClass *conn);
void CC_lookup_pg_version(ConnectionClass *conn);
void CC_initialize_pg_version(ConnectionClass *conn);
void CC_log_error(char *func, char *desc, ConnectionClass *self);
int CC_get_max_query_len(const ConnectionClass *self);
#endif
/*-------
* Module: convert.c
*
* Description: This module contains routines related to
* converting parameters and columns into requested data types.
* Parameters are converted from their SQL_C data types into
* the appropriate postgres type. Columns are converted from
* their postgres type (SQL type) into the appropriate SQL_C
* data type.
*
* Classes: n/a
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
/* Multibyte support Eiji Tokuya 2001-03-15 */
#include "convert.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#ifdef MULTIBYTE
#include "multibyte.h"
#endif
#include <time.h>
#include <math.h>
#include <stdlib.h>
#include "statement.h"
#include "qresult.h"
#include "bind.h"
#include "pgtypes.h"
#include "lobj.h"
#include "connection.h"
#include "pgapifunc.h"
#ifdef __CYGWIN__
#define TIMEZONE_GLOBAL _timezone
#elif defined(WIN32) || defined(HAVE_INT_TIMEZONE)
#define TIMEZONE_GLOBAL timezone
#endif
/*
* How to map ODBC scalar functions {fn func(args)} to Postgres.
* This is just a simple substitution. List augmented from:
* http://www.merant.com/datadirect/download/docs/odbc16/Odbcref/rappc.htm
* - thomas 2000-04-03
*/
char *mapFuncs[][2] = {
/* { "ASCII", "ascii" }, */
{"CHAR", "chr"},
{"CONCAT", "textcat"},
/* { "DIFFERENCE", "difference" }, */
/* { "INSERT", "insert" }, */
{"LCASE", "lower"},
{"LEFT", "ltrunc"},
{"LOCATE", "strpos"},
{"LENGTH", "char_length"},
/* { "LTRIM", "ltrim" }, */
{"RIGHT", "rtrunc"},
/* { "REPEAT", "repeat" }, */
/* { "REPLACE", "replace" }, */
/* { "RTRIM", "rtrim" }, */
/* { "SOUNDEX", "soundex" }, */
{"SUBSTRING", "substr"},
{"UCASE", "upper"},
/* { "ABS", "abs" }, */
/* { "ACOS", "acos" }, */
/* { "ASIN", "asin" }, */
/* { "ATAN", "atan" }, */
/* { "ATAN2", "atan2" }, */
{"CEILING", "ceil"},
/* { "COS", "cos" }, */
/* { "COT", "cot" }, */
/* { "DEGREES", "degrees" }, */
/* { "EXP", "exp" }, */
/* { "FLOOR", "floor" }, */
{"LOG", "ln"},
{"LOG10", "log"},
/* { "MOD", "mod" }, */
/* { "PI", "pi" }, */
{"POWER", "pow"},
/* { "RADIANS", "radians" }, */
{"RAND", "random"},
/* { "ROUND", "round" }, */
/* { "SIGN", "sign" }, */
/* { "SIN", "sin" }, */
/* { "SQRT", "sqrt" }, */
/* { "TAN", "tan" }, */
{"TRUNCATE", "trunc"},
{"CURRENT_DATE", "curdate"},
{"CURRENT_TIME", "curtime"},
{"CURRENT_TIMESTAMP", "odbc_timestamp"},
{"CURRENT_USER", "odbc_current_user"},
{"SESSION_USER", "odbc_session_user"},
/* { "CURDATE", "curdate" }, */
/* { "CURTIME", "curtime" }, */
/* { "DAYNAME", "dayname" }, */
/* { "DAYOFMONTH", "dayofmonth" }, */
/* { "DAYOFWEEK", "dayofweek" }, */
/* { "DAYOFYEAR", "dayofyear" }, */
/* { "HOUR", "hour" }, */
/* { "MINUTE", "minute" }, */
/* { "MONTH", "month" }, */
/* { "MONTHNAME", "monthname" }, */
/* { "NOW", "now" }, */
/* { "QUARTER", "quarter" }, */
/* { "SECOND", "second" }, */
/* { "WEEK", "week" }, */
/* { "YEAR", "year" }, */
/* { "DATABASE", "database" }, */
{"IFNULL", "coalesce"},
{"USER", "odbc_user"},
{0, 0}
};
static char *mapFunction(const char *func);
static unsigned int conv_from_octal(const unsigned char *s);
static unsigned int conv_from_hex(const unsigned char *s);
static char *conv_to_octal(unsigned char val);
/*---------
* A Guide for date/time/timestamp conversions
*
* field_type fCType Output
* ---------- ------ ----------
* PG_TYPE_DATE SQL_C_DEFAULT SQL_C_DATE
* PG_TYPE_DATE SQL_C_DATE SQL_C_DATE
* PG_TYPE_DATE SQL_C_TIMESTAMP SQL_C_TIMESTAMP (time = 0 (midnight))
* PG_TYPE_TIME SQL_C_DEFAULT SQL_C_TIME
* PG_TYPE_TIME SQL_C_TIME SQL_C_TIME
* PG_TYPE_TIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP (date = current date)
* PG_TYPE_ABSTIME SQL_C_DEFAULT SQL_C_TIMESTAMP
* PG_TYPE_ABSTIME SQL_C_DATE SQL_C_DATE (time is truncated)
* PG_TYPE_ABSTIME SQL_C_TIME SQL_C_TIME (date is truncated)
* PG_TYPE_ABSTIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP
*---------
*/
/*
* TIMESTAMP <-----> SIMPLE_TIME
* precision support since 7.2.
* time zone support is unavailable(the stuff is unreliable)
*/
static BOOL
timestamp2stime(const char *str, SIMPLE_TIME *st, BOOL *bZone, int *zone)
{
char rest[64],
*ptr;
int scnt,
i;
long timediff;
BOOL withZone = *bZone;
*bZone = FALSE;
*zone = 0;
st->fr = 0;
if ((scnt = sscanf(str, "%4d-%2d-%2d %2d:%2d:%2d%s", &st->y, &st->m, &st->d, &st->hh, &st->mm, &st->ss, rest)) < 6)
return FALSE;
else if (scnt == 6)
return TRUE;
switch (rest[0])
{
case '+':
*bZone = TRUE;
*zone = atoi(&rest[1]);
break;
case '-':
*bZone = TRUE;
*zone = -atoi(&rest[1]);
break;
case '.':
if ((ptr = strchr(rest, '+')) != NULL)
{
*bZone = TRUE;
*zone = atoi(&ptr[1]);
*ptr = '\0';
}
else if ((ptr = strchr(rest, '-')) != NULL)
{
*bZone = TRUE;
*zone = -atoi(&ptr[1]);
*ptr = '\0';
}
for (i = 1; i < 10; i++)
{
if (!isdigit((unsigned char) rest[i]))
break;
}
for (; i < 10; i++)
rest[i] = '0';
rest[i] = '\0';
st->fr = atoi(&rest[1]);
break;
default:
return TRUE;
}
if (!withZone || !*bZone || st->y < 1970)
return TRUE;
#if defined(WIN32) || defined(HAVE_INT_TIMEZONE)
if (!tzname[0] || !tzname[0][0])
{
*bZone = FALSE;
return TRUE;
}
timediff = TIMEZONE_GLOBAL + (*zone) * 3600;
if (!daylight && timediff == 0) /* the same timezone */
return TRUE;
else
{
struct tm tm,
*tm2;
time_t time0;
*bZone = FALSE;
tm.tm_year = st->y - 1900;
tm.tm_mon = st->m - 1;
tm.tm_mday = st->d;
tm.tm_hour = st->hh;
tm.tm_min = st->mm;
tm.tm_sec = st->ss;
tm.tm_isdst = -1;
time0 = mktime(&tm);
if (time0 < 0)
return TRUE;
if (tm.tm_isdst > 0)
timediff -= 3600;
if (timediff == 0) /* the same time zone */
return TRUE;
time0 -= timediff;
if (time0 >= 0 && (tm2 = localtime(&time0)) != NULL)
{
st->y = tm2->tm_year + 1900;
st->m = tm2->tm_mon + 1;
st->d = tm2->tm_mday;
st->hh = tm2->tm_hour;
st->mm = tm2->tm_min;
st->ss = tm2->tm_sec;
*bZone = TRUE;
}
}
#endif /* WIN32 */
return TRUE;
}
static BOOL
stime2timestamp(const SIMPLE_TIME *st, char *str, BOOL bZone, BOOL precision)
{
char precstr[16],
zonestr[16];
int i;
precstr[0] = '\0';
if (precision && st->fr)
{
sprintf(precstr, ".%09d", st->fr);
for (i = 9; i > 0; i--)
{
if (precstr[i] != '0')
break;
precstr[i] = '\0';
}
}
zonestr[0] = '\0';
#if defined(WIN32) || defined(HAVE_INT_TIMEZONE)
if (bZone && tzname[0] && tzname[0][0] && st->y >= 1970)
{
long zoneint;
struct tm tm;
time_t time0;
zoneint = TIMEZONE_GLOBAL;
if (daylight && st->y >= 1900)
{
tm.tm_year = st->y - 1900;
tm.tm_mon = st->m - 1;
tm.tm_mday = st->d;
tm.tm_hour = st->hh;
tm.tm_min = st->mm;
tm.tm_sec = st->ss;
tm.tm_isdst = -1;
time0 = mktime(&tm);
if (time0 >= 0 && tm.tm_isdst > 0)
zoneint -= 3600;
}
if (zoneint > 0)
sprintf(zonestr, "-%02d", (int) zoneint / 3600);
else
sprintf(zonestr, "+%02d", -(int) zoneint / 3600);
}
#endif /* WIN32 */
sprintf(str, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d%s%s", st->y, st->m, st->d, st->hh, st->mm, st->ss, precstr, zonestr);
return TRUE;
}
/* This is called by SQLFetch() */
int
copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col)
{
BindInfoClass *bic = &(stmt->bindings[col]);
return copy_and_convert_field(stmt, field_type, value, (Int2) bic->returntype, (PTR) bic->buffer,
(SDWORD) bic->buflen, (SDWORD *) bic->used);
}
/* This is called by SQLGetData() */
int
copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType,
PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue)
{
Int4 len = 0,
copy_len = 0;
SIMPLE_TIME st;
time_t t = time(NULL);
struct tm *tim;
int pcbValueOffset,
rgbValueOffset;
char *rgbValueBindRow;
const char *ptr;
int bind_row = stmt->bind_row;
int bind_size = stmt->options.bind_size;
int result = COPY_OK;
BOOL changed;
const char *neut_str = value;
char midtemp[2][32];
int mtemp_cnt = 0;
static BindInfoClass sbic;
BindInfoClass *pbic;
if (stmt->current_col >= 0)
{
pbic = &stmt->bindings[stmt->current_col];
if (pbic->data_left == -2)
pbic->data_left = (cbValueMax > 0) ? 0 : -1; /* This seems to be *
* needed for ADO ? */
if (pbic->data_left == 0)
{
if (pbic->ttlbuf != NULL)
{
free(pbic->ttlbuf);
pbic->ttlbuf = NULL;
pbic->ttlbuflen = 0;
}
pbic->data_left = -2; /* needed by ADO ? */
return COPY_NO_DATA_FOUND;
}
}
/*---------
* rgbValueOffset is *ONLY* for character and binary data.
* pcbValueOffset is for computing any pcbValue location
*---------
*/
if (bind_size > 0)
pcbValueOffset = rgbValueOffset = (bind_size * bind_row);
else
{
pcbValueOffset = bind_row * sizeof(SDWORD);
rgbValueOffset = bind_row * cbValueMax;
}
memset(&st, 0, sizeof(SIMPLE_TIME));
/* Initialize current date */
tim = localtime(&t);
st.m = tim->tm_mon + 1;
st.d = tim->tm_mday;
st.y = tim->tm_year + 1900;
mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, (value == NULL) ? "<NULL>" : value, cbValueMax);
if (!value)
{
/*
* handle a null just by returning SQL_NULL_DATA in pcbValue, and
* doing nothing to the buffer.
*/
if (pcbValue)
*(SDWORD *) ((char *) pcbValue + pcbValueOffset) = SQL_NULL_DATA;
return COPY_OK;
}
if (stmt->hdbc->DataSourceToDriver != NULL)
{
int length = strlen(value);
stmt->hdbc->DataSourceToDriver(stmt->hdbc->translation_option,
SQL_CHAR,
value, length,
value, length, NULL,
NULL, 0, NULL);
}
/*
* First convert any specific postgres types into more useable data.
*
* NOTE: Conversions from PG char/varchar of a date/time/timestamp value
* to SQL_C_DATE,SQL_C_TIME, SQL_C_TIMESTAMP not supported
*/
switch (field_type)
{
/*
* $$$ need to add parsing for date/time/timestamp strings in
* PG_TYPE_CHAR,VARCHAR $$$
*/
case PG_TYPE_DATE:
sscanf(value, "%4d-%2d-%2d", &st.y, &st.m, &st.d);
break;
case PG_TYPE_TIME:
sscanf(value, "%2d:%2d:%2d", &st.hh, &st.mm, &st.ss);
break;
case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME:
case PG_TYPE_TIMESTAMP:
st.fr = 0;
if (strnicmp(value, "invalid", 7) != 0)
{
BOOL bZone = (field_type != PG_TYPE_TIMESTAMP_NO_TMZONE && PG_VERSION_GE(SC_get_conn(stmt), 7.2));
int zone;
/*
* sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m,
* &st.d, &st.hh, &st.mm, &st.ss);
*/
bZone = FALSE; /* time zone stuff is unreliable */
timestamp2stime(value, &st, &bZone, &zone);
}
else
{
/*
* The timestamp is invalid so set something conspicuous,
* like the epoch
*/
t = 0;
tim = localtime(&t);
st.m = tim->tm_mon + 1;
st.d = tim->tm_mday;
st.y = tim->tm_year + 1900;
st.hh = tim->tm_hour;
st.mm = tim->tm_min;
st.ss = tim->tm_sec;
}
break;
case PG_TYPE_BOOL:
{ /* change T/F to 1/0 */
char *s;
s = midtemp[mtemp_cnt];
strcpy(s, (char *) value);
if (s[0] == 'f' || s[0] == 'F' || s[0] == 'n' || s[0] == 'N' || s[0] == '0')
s[0] = '0';
else
s[0] = '1';
s[1] = '\0';
neut_str = midtemp[mtemp_cnt];
mtemp_cnt++;
}
break;
/* This is for internal use by SQLStatistics() */
case PG_TYPE_INT2VECTOR:
{
int nval,
i;
const char *vp;
/* this is an array of eight integers */
short *short_array = (short *) ((char *) rgbValue + rgbValueOffset);
len = 32;
vp = value;
nval = 0;
mylog("index=(");
for (i = 0; i < 16; i++)
{
if (sscanf(vp, "%hd", &short_array[i]) != 1)
break;
mylog(" %d", short_array[i]);
nval++;
/* skip the current token */
while ((*vp != '\0') && (!isspace((unsigned char) *vp)))
vp++;
/* and skip the space to the next token */
while ((*vp != '\0') && (isspace((unsigned char) *vp)))
vp++;
if (*vp == '\0')
break;
}
mylog(") nval = %d\n", nval);
for (i = nval; i < 16; i++)
short_array[i] = 0;
#if 0
sscanf(value, "%hd %hd %hd %hd %hd %hd %hd %hd",
&short_array[0],
&short_array[1],
&short_array[2],
&short_array[3],
&short_array[4],
&short_array[5],
&short_array[6],
&short_array[7]);
#endif
/* There is no corresponding fCType for this. */
if (pcbValue)
*(SDWORD *) ((char *) pcbValue + pcbValueOffset) = len;
return COPY_OK; /* dont go any further or the data will be
* trashed */
}
/*
* This is a large object OID, which is used to store
* LONGVARBINARY objects.
*/
case PG_TYPE_LO:
return convert_lo(stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset));
default:
if (field_type == stmt->hdbc->lobj_type) /* hack until permanent
* type available */
return convert_lo(stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset));
}
/* Change default into something useable */
if (fCType == SQL_C_DEFAULT)
{
fCType = pgtype_to_ctype(stmt, field_type);
mylog("copy_and_convert, SQL_C_DEFAULT: fCType = %d\n", fCType);
}
rgbValueBindRow = (char *) rgbValue + rgbValueOffset;
if (fCType == SQL_C_CHAR)
{
/* Special character formatting as required */
/*
* These really should return error if cbValueMax is not big
* enough.
*/
switch (field_type)
{
case PG_TYPE_DATE:
len = 10;
if (cbValueMax > len)
sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d", st.y, st.m, st.d);
break;
case PG_TYPE_TIME:
len = 8;
if (cbValueMax > len)
sprintf(rgbValueBindRow, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss);
break;
case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME:
case PG_TYPE_TIMESTAMP:
len = 19;
if (cbValueMax > len)
sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
st.y, st.m, st.d, st.hh, st.mm, st.ss);
break;
case PG_TYPE_BOOL:
len = 1;
if (cbValueMax > len)
{
strcpy(rgbValueBindRow, neut_str);
mylog("PG_TYPE_BOOL: rgbValueBindRow = '%s'\n", rgbValueBindRow);
}
break;
/*
* Currently, data is SILENTLY TRUNCATED for BYTEA and
* character data types if there is not enough room in
* cbValueMax because the driver can't handle multiple
* calls to SQLGetData for these, yet. Most likely, the
* buffer passed in will be big enough to handle the
* maximum limit of postgres, anyway.
*
* LongVarBinary types are handled correctly above, observing
* truncation and all that stuff since there is
* essentially no limit on the large object used to store
* those.
*/
case PG_TYPE_BYTEA:/* convert binary data to hex strings
* (i.e, 255 = "FF") */
len = convert_pgbinary_to_char(neut_str, rgbValueBindRow, cbValueMax);
/***** THIS IS NOT PROPERLY IMPLEMENTED *****/
break;
default:
if (stmt->current_col < 0)
{
pbic = &sbic;
pbic->data_left = -1;
}
else
pbic = &stmt->bindings[stmt->current_col];
if (pbic->data_left < 0)
{
/* convert linefeeds to carriage-return/linefeed */
len = convert_linefeeds(neut_str, NULL, 0, &changed);
if (cbValueMax == 0) /* just returns length
* info */
{
result = COPY_RESULT_TRUNCATED;
break;
}
if (!pbic->ttlbuf)
pbic->ttlbuflen = 0;
if (changed || len >= cbValueMax)
{
if (len >= (int) pbic->ttlbuflen)
{
pbic->ttlbuf = realloc(pbic->ttlbuf, len + 1);
pbic->ttlbuflen = len + 1;
}
convert_linefeeds(neut_str, pbic->ttlbuf, pbic->ttlbuflen, &changed);
ptr = pbic->ttlbuf;
}
else
{
if (pbic->ttlbuf)
{
free(pbic->ttlbuf);
pbic->ttlbuf = NULL;
}
ptr = neut_str;
}
}
else
ptr = pbic->ttlbuf;
mylog("DEFAULT: len = %d, ptr = '%s'\n", len, ptr);
if (stmt->current_col >= 0)
{
if (pbic->data_left > 0)
{
ptr += strlen(ptr) - pbic->data_left;
len = pbic->data_left;
}
else
pbic->data_left = len;
}
if (cbValueMax > 0)
{
copy_len = (len >= cbValueMax) ? cbValueMax - 1 : len;
/* Copy the data */
memcpy(rgbValueBindRow, ptr, copy_len);
rgbValueBindRow[copy_len] = '\0';
/* Adjust data_left for next time */
if (stmt->current_col >= 0)
pbic->data_left -= copy_len;
}
/*
* Finally, check for truncation so that proper status can
* be returned
*/
if (cbValueMax > 0 && len >= cbValueMax)
result = COPY_RESULT_TRUNCATED;
else
{
if (pbic->ttlbuf != NULL)
{
free(pbic->ttlbuf);
pbic->ttlbuf = NULL;
}
}
mylog(" SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValueBindRow = '%s'\n", len, cbValueMax, rgbValueBindRow);
break;
}
}
else
{
/*
* for SQL_C_CHAR, it's probably ok to leave currency symbols in.
* But to convert to numeric types, it is necessary to get rid of
* those.
*/
if (field_type == PG_TYPE_MONEY)
{
if (convert_money(neut_str, midtemp[mtemp_cnt], sizeof(midtemp[0])))
{
neut_str = midtemp[mtemp_cnt];
mtemp_cnt++;
}
else
return COPY_UNSUPPORTED_TYPE;
}
switch (fCType)
{
case SQL_C_DATE:
#if (ODBCVER >= 0x0300)
case SQL_C_TYPE_DATE: /* 91 */
#endif
len = 6;
{
DATE_STRUCT *ds;
if (bind_size > 0)
ds = (DATE_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
else
ds = (DATE_STRUCT *) rgbValue + bind_row;
ds->year = st.y;
ds->month = st.m;
ds->day = st.d;
}
break;
case SQL_C_TIME:
#if (ODBCVER >= 0x0300)
case SQL_C_TYPE_TIME: /* 92 */
#endif
len = 6;
{
TIME_STRUCT *ts;
if (bind_size > 0)
ts = (TIME_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
else
ts = (TIME_STRUCT *) rgbValue + bind_row;
ts->hour = st.hh;
ts->minute = st.mm;
ts->second = st.ss;
}
break;
case SQL_C_TIMESTAMP:
#if (ODBCVER >= 0x0300)
case SQL_C_TYPE_TIMESTAMP: /* 93 */
#endif
len = 16;
{
TIMESTAMP_STRUCT *ts;
if (bind_size > 0)
ts = (TIMESTAMP_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
else
ts = (TIMESTAMP_STRUCT *) rgbValue + bind_row;
ts->year = st.y;
ts->month = st.m;
ts->day = st.d;
ts->hour = st.hh;
ts->minute = st.mm;
ts->second = st.ss;
ts->fraction = st.fr;
}
break;
case SQL_C_BIT:
len = 1;
if (bind_size > 0)
*(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str);
else
*((UCHAR *) rgbValue + bind_row) = atoi(neut_str);
/*
* mylog("SQL_C_BIT: bind_row = %d val = %d, cb = %d,
* rgb=%d\n", bind_row, atoi(neut_str), cbValueMax,
* *((UCHAR *)rgbValue));
*/
break;
case SQL_C_STINYINT:
case SQL_C_TINYINT:
len = 1;
if (bind_size > 0)
*(SCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str);
else
*((SCHAR *) rgbValue + bind_row) = atoi(neut_str);
break;
case SQL_C_UTINYINT:
len = 1;
if (bind_size > 0)
*(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str);
else
*((UCHAR *) rgbValue + bind_row) = atoi(neut_str);
break;
case SQL_C_FLOAT:
len = 4;
if (bind_size > 0)
*(SFLOAT *) ((char *) rgbValue + (bind_row * bind_size)) = (float) atof(neut_str);
else
*((SFLOAT *) rgbValue + bind_row) = (float) atof(neut_str);
break;
case SQL_C_DOUBLE:
len = 8;
if (bind_size > 0)
*(SDOUBLE *) ((char *) rgbValue + (bind_row * bind_size)) = atof(neut_str);
else
*((SDOUBLE *) rgbValue + bind_row) = atof(neut_str);
break;
case SQL_C_SSHORT:
case SQL_C_SHORT:
len = 2;
if (bind_size > 0)
*(SWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str);
else
*((SWORD *) rgbValue + bind_row) = atoi(neut_str);
break;
case SQL_C_USHORT:
len = 2;
if (bind_size > 0)
*(UWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str);
else
*((UWORD *) rgbValue + bind_row) = atoi(neut_str);
break;
case SQL_C_SLONG:
case SQL_C_LONG:
len = 4;
if (bind_size > 0)
*(SDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(neut_str);
else
*((SDWORD *) rgbValue + bind_row) = atol(neut_str);
break;
case SQL_C_ULONG:
len = 4;
if (bind_size > 0)
*(UDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(neut_str);
else
*((UDWORD *) rgbValue + bind_row) = atol(neut_str);
break;
case SQL_C_BINARY:
/* truncate if necessary */
/* convert octal escapes to bytes */
if (stmt->current_col < 0)
{
pbic = &sbic;
pbic->data_left = -1;
}
else
pbic = &stmt->bindings[stmt->current_col];
if (!pbic->ttlbuf)
pbic->ttlbuflen = 0;
if (len = strlen(neut_str), len >= (int) pbic->ttlbuflen)
{
pbic->ttlbuf = realloc(pbic->ttlbuf, len + 1);
pbic->ttlbuflen = len + 1;
}
len = convert_from_pgbinary(neut_str, pbic->ttlbuf, pbic->ttlbuflen);
ptr = pbic->ttlbuf;
if (stmt->current_col >= 0)
{
/*
* Second (or more) call to SQLGetData so move the
* pointer
*/
if (pbic->data_left > 0)
{
ptr += len - pbic->data_left;
len = pbic->data_left;
}
/* First call to SQLGetData so initialize data_left */
else
pbic->data_left = len;
}
if (cbValueMax > 0)
{
copy_len = (len > cbValueMax) ? cbValueMax : len;
/* Copy the data */
memcpy(rgbValueBindRow, ptr, copy_len);
/* Adjust data_left for next time */
if (stmt->current_col >= 0)
pbic->data_left -= copy_len;
}
/*
* Finally, check for truncation so that proper status can
* be returned
*/
if (len > cbValueMax)
result = COPY_RESULT_TRUNCATED;
if (pbic->ttlbuf)
{
free(pbic->ttlbuf);
pbic->ttlbuf = NULL;
}
mylog("SQL_C_BINARY: len = %d, copy_len = %d\n", len, copy_len);
break;
default:
return COPY_UNSUPPORTED_TYPE;
}
}
/* store the length of what was copied, if there's a place for it */
if (pcbValue)
*(SDWORD *) ((char *) pcbValue + pcbValueOffset) = len;
if (result == COPY_OK && stmt->current_col >= 0)
stmt->bindings[stmt->current_col].data_left = 0;
return result;
}
/*--------------------------------------------------------------------
* Functions/Macros to get rid of query size limit.
*
* I always used the follwoing macros to convert from
* old_statement to new_statement. Please improve it
* if you have a better way. Hiroshi 2001/05/22
*--------------------------------------------------------------------
*/
#define INIT_MIN_ALLOC 4096
static int
enlarge_statement(StatementClass *stmt, unsigned int newsize)
{
unsigned int newalsize = INIT_MIN_ALLOC;
static char *func = "enlarge_statement";
if (stmt->stmt_size_limit > 0 && stmt->stmt_size_limit < (int) newsize)
{
stmt->errormsg = "Query buffer overflow in copy_statement_with_parameters";
stmt->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", stmt);
return -1;
}
while (newalsize <= newsize)
newalsize *= 2;
if (!(stmt->stmt_with_params = realloc(stmt->stmt_with_params, newalsize)))
{
stmt->errormsg = "Query buffer allocate error in copy_statement_with_parameters";
stmt->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", stmt);
return 0;
}
return newalsize;
}
/*----------
* Enlarge stmt_with_params if necessary.
*----------
*/
#define ENLARGE_NEWSTATEMENT(newpos) \
if (newpos >= new_stsize) \
{ \
if ((new_stsize = enlarge_statement(stmt, newpos)) <= 0) \
return SQL_ERROR; \
new_statement = stmt->stmt_with_params; \
}
/*----------
* Initialize stmt_with_params, new_statement etc.
*----------
*/
#define CVT_INIT(size) \
do { \
if (stmt->stmt_with_params) \
free(stmt->stmt_with_params); \
if (stmt->stmt_size_limit > 0) \
new_stsize = stmt->stmt_size_limit; \
else \
{ \
new_stsize = INIT_MIN_ALLOC; \
while (new_stsize <= size) \
new_stsize *= 2; \
} \
new_statement = malloc(new_stsize); \
stmt->stmt_with_params = new_statement; \
npos = 0; \
new_statement[0] = '\0'; \
} while (0)
/*----------
* Terminate the stmt_with_params string with NULL.
*----------
*/
#define CVT_TERMINATE \
do { \
new_statement[npos] = '\0'; \
} while (0)
/*----------
* Append a data.
*----------
*/
#define CVT_APPEND_DATA(s, len) \
do { \
unsigned int newpos = npos + len; \
ENLARGE_NEWSTATEMENT(newpos) \
memcpy(&new_statement[npos], s, len); \
npos = newpos; \
new_statement[npos] = '\0'; \
} while (0)
/*----------
* Append a string.
*----------
*/
#define CVT_APPEND_STR(s) \
do { \
unsigned int len = strlen(s); \
CVT_APPEND_DATA(s, len); \
} while (0)
/*----------
* Append a char.
*----------
*/
#define CVT_APPEND_CHAR(c) \
do { \
ENLARGE_NEWSTATEMENT(npos + 1); \
new_statement[npos++] = c; \
} while (0)
/*----------
* Append a binary data.
* Newly reqeuired size may be overestimated currently.
*----------
*/
#define CVT_APPEND_BINARY(buf, used) \
do { \
unsigned int newlimit = npos + 5 * used; \
ENLARGE_NEWSTATEMENT(newlimit); \
npos += convert_to_pgbinary(buf, &new_statement[npos], used); \
} while (0)
/*----------
*
*----------
*/
#define CVT_SPECIAL_CHARS(buf, used) \
do { \
int cnvlen = convert_special_chars(buf, NULL, used); \
unsigned int newlimit = npos + cnvlen; \
\
ENLARGE_NEWSTATEMENT(newlimit); \
convert_special_chars(buf, &new_statement[npos], used); \
npos += cnvlen; \
} while (0)
/*----------
* Check if the statement is
* SELECT ... INTO table FROM .....
* This isn't really a strict check but ...
*----------
*/
static BOOL
into_table_from(const char *stmt)
{
if (strnicmp(stmt, "into", 4))
return FALSE;
stmt += 4;
if (!isspace((unsigned char) *stmt))
return FALSE;
while (isspace((unsigned char) *(++stmt)));
switch (*stmt)
{
case '\0':
case ',':
case '\'':
return FALSE;
case '\"': /* double quoted table name ? */
do
{
do
while (*(++stmt) != '\"' && *stmt);
while (*stmt && *(++stmt) == '\"');
while (*stmt && !isspace((unsigned char) *stmt) && *stmt != '\"')
stmt++;
}
while (*stmt == '\"');
break;
default:
while (!isspace((unsigned char) *(++stmt)));
break;
}
if (!*stmt)
return FALSE;
while (isspace((unsigned char) *(++stmt)));
if (strnicmp(stmt, "from", 4))
return FALSE;
return isspace((unsigned char) stmt[4]);
}
/*----------
* Check if the statement is
* SELECT ... FOR UPDATE .....
* This isn't really a strict check but ...
*----------
*/
static BOOL
table_for_update(const char *stmt, int *endpos)
{
const char *wstmt = stmt;
while (isspace((unsigned char) *(++wstmt)));
if (!*wstmt)
return FALSE;
if (strnicmp(wstmt, "update", 6))
return FALSE;
wstmt += 6;
*endpos = wstmt - stmt;
return !wstmt[0] || isspace((unsigned char) wstmt[0]);
}
/*
* This function inserts parameters into an SQL statements.
* It will also modify a SELECT statement for use with declare/fetch cursors.
* This function does a dynamic memory allocation to get rid of query size limit!
*/
int
copy_statement_with_parameters(StatementClass *stmt)
{
static char *func = "copy_statement_with_parameters";
unsigned int opos,
npos,
oldstmtlen;
char param_string[128],
tmp[256],
cbuf[PG_NUMERIC_MAX_PRECISION * 2]; /* seems big enough to
* handle the data in
* this function */
int param_number;
Int2 param_ctype,
param_sqltype;
char *old_statement = stmt->statement,
oldchar;
char *new_statement = stmt->stmt_with_params;
unsigned int new_stsize = 0;
SIMPLE_TIME st;
time_t t = time(NULL);
struct tm *tim;
SDWORD used;
char *buffer,
*buf;
BOOL in_quote = FALSE,
in_dquote = FALSE,
in_escape = FALSE;
Oid lobj_oid;
int lobj_fd,
retval;
BOOL check_cursor_ok = FALSE; /* check cursor
* restriction */
BOOL proc_no_param = TRUE;
unsigned int declare_pos = 0;
ConnectionClass *conn = SC_get_conn(stmt);
ConnInfo *ci = &(conn->connInfo);
BOOL prepare_dummy_cursor = FALSE,
begin_first = FALSE;
char token_save[64];
int token_len;
BOOL prev_token_end;
#ifdef DRIVER_CURSOR_IMPLEMENT
BOOL search_from_pos = FALSE;
#endif /* DRIVER_CURSOR_IMPLEMENT */
if (ci->disallow_premature)
prepare_dummy_cursor = stmt->pre_executing;
if (!old_statement)
{
SC_log_error(func, "No statement string", stmt);
return SQL_ERROR;
}
memset(&st, 0, sizeof(SIMPLE_TIME));
/* Initialize current date */
tim = localtime(&t);
st.m = tim->tm_mon + 1;
st.d = tim->tm_mday;
st.y = tim->tm_year + 1900;
#ifdef DRIVER_CURSOR_IMPLEMENT
if (stmt->statement_type != STMT_TYPE_SELECT)
{
stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
}
else if (stmt->options.cursor_type == SQL_CURSOR_FORWARD_ONLY)
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
else if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
{
if (stmt->parse_status == STMT_PARSE_NONE)
parse_statement(stmt);
if (stmt->parse_status != STMT_PARSE_COMPLETE)
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
else if (!stmt->ti || stmt->ntab != 1)
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
else
search_from_pos = TRUE;
}
#endif /* DRIVER_CURSOR_IMPLEMENT */
/* If the application hasn't set a cursor name, then generate one */
if (stmt->cursor_name[0] == '\0')
sprintf(stmt->cursor_name, "SQL_CUR%p", stmt);
oldstmtlen = strlen(old_statement);
CVT_INIT(oldstmtlen);
stmt->miscinfo = 0;
token_len = 0;
prev_token_end = TRUE;
/* For selects, prepend a declare cursor to the statement */
if (stmt->statement_type == STMT_TYPE_SELECT)
{
SC_set_pre_executable(stmt);
if (prepare_dummy_cursor || ci->drivers.use_declarefetch)
{
if (prepare_dummy_cursor)
{
if (!CC_is_in_trans(conn) && PG_VERSION_GE(conn, 7.1))
{
strcpy(new_statement, "BEGIN;");
begin_first = TRUE;
}
}
else if (ci->drivers.use_declarefetch)
SC_set_fetchcursor(stmt);
sprintf(new_statement, "%sdeclare %s cursor for ",
new_statement, stmt->cursor_name);
npos = strlen(new_statement);
check_cursor_ok = TRUE;
declare_pos = npos;
}
}
param_number = -1;
#ifdef MULTIBYTE
multibyte_init();
#endif
for (opos = 0; opos < oldstmtlen; opos++)
{
oldchar = old_statement[opos];
#ifdef MULTIBYTE
if (multibyte_char_check(oldchar) != 0)
{
CVT_APPEND_CHAR(oldchar);
continue;
}
/*
* From here we are guaranteed to handle a 1-byte character.
*/
#endif
if (in_escape) /* escape check */
{
in_escape = FALSE;
CVT_APPEND_CHAR(oldchar);
continue;
}
else if (in_quote || in_dquote) /* quote/double quote check */
{
if (oldchar == '\\')
in_escape = TRUE;
else if (oldchar == '\'' && in_quote)
in_quote = FALSE;
else if (oldchar == '\"' && in_dquote)
in_dquote = FALSE;
CVT_APPEND_CHAR(oldchar);
continue;
}
/*
* From here we are guranteed to be in neither an escape, a quote
* nor a double quote.
*/
/* Squeeze carriage-return/linefeed pairs to linefeed only */
else if (oldchar == '\r' && opos + 1 < oldstmtlen &&
old_statement[opos + 1] == '\n')
continue;
/*
* Handle literals (date, time, timestamp) and ODBC scalar
* functions
*/
else if (oldchar == '{')
{
char *esc;
char *begin = &old_statement[opos + 1];
#ifdef MULTIBYTE
char *end = multibyte_strchr(begin, '}');
#else
char *end = strchr(begin, '}');
#endif
if (!end)
continue;
/* procedure calls */
if (stmt->statement_type == STMT_TYPE_PROCCALL)
{
int lit_call_len = 4;
while (isspace((unsigned char) old_statement[++opos]));
/* '=?' to accept return values exists ? */
if (old_statement[opos] == '?')
{
param_number++;
while (isspace((unsigned char) old_statement[++opos]));
if (old_statement[opos] != '=')
{
opos--;
continue;
}
while (isspace((unsigned char) old_statement[++opos]));
}
if (strnicmp(&old_statement[opos], "call", lit_call_len) ||
!isspace((unsigned char) old_statement[opos + lit_call_len]))
{
opos--;
continue;
}
opos += lit_call_len;
CVT_APPEND_STR("SELECT ");
#ifdef MULTIBYTE
if (multibyte_strchr(&old_statement[opos], '('))
#else
if (strchr(&old_statement[opos], '('))
#endif /* MULTIBYTE */
proc_no_param = FALSE;
continue;
}
*end = '\0';
esc = convert_escape(begin);
if (esc)
CVT_APPEND_STR(esc);
else
{ /* it's not a valid literal so just copy */
*end = '}';
CVT_APPEND_CHAR(oldchar);
continue;
}
opos += end - begin + 1;
*end = '}';
continue;
}
/* End of a procedure call */
else if (oldchar == '}' && stmt->statement_type == STMT_TYPE_PROCCALL)
{
if (proc_no_param)
CVT_APPEND_STR("()");
continue;
}
/*
* Can you have parameter markers inside of quotes? I dont think
* so. All the queries I've seen expect the driver to put quotes
* if needed.
*/
else if (oldchar == '?')
; /* ok */
else
{
if (oldchar == '\'')
in_quote = TRUE;
else if (oldchar == '\\')
in_escape = TRUE;
else if (oldchar == '\"')
in_dquote = TRUE;
else
{
if (isspace((unsigned char) oldchar))
{
if (!prev_token_end)
{
prev_token_end = TRUE;
token_save[token_len] = '\0';
if (token_len == 4)
{
if (check_cursor_ok &&
into_table_from(&old_statement[opos - token_len]))
{
stmt->statement_type = STMT_TYPE_CREATE;
SC_no_pre_executable(stmt);
SC_no_fetchcursor(stmt);
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
memmove(new_statement, new_statement + declare_pos, npos - declare_pos);
npos -= declare_pos;
}
#ifdef DRIVER_CURSOR_IMPLEMENT
else if (search_from_pos && /* where's from clause */
strnicmp(token_save, "from", 4) == 0)
{
search_from_pos = FALSE;
npos -= 5;
CVT_APPEND_STR(", CTID, OID from");
}
#endif /* DRIVER_CURSOR_IMPLEMENT */
}
if (token_len == 3)
{
int endpos;
if (check_cursor_ok &&
strnicmp(token_save, "for", 3) == 0 &&
table_for_update(&old_statement[opos], &endpos))
{
SC_no_fetchcursor(stmt);
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
if (prepare_dummy_cursor)
{
npos -= 4;
opos += endpos;
}
else
{
memmove(new_statement, new_statement + declare_pos, npos - declare_pos);
npos -= declare_pos;
}
}
}
}
}
else if (prev_token_end)
{
prev_token_end = FALSE;
token_save[0] = oldchar;
token_len = 1;
}
else if (token_len + 1 < sizeof(token_save))
token_save[token_len++] = oldchar;
}
CVT_APPEND_CHAR(oldchar);
continue;
}
/*
* Its a '?' parameter alright
*/
param_number++;
if (param_number >= stmt->parameters_allocated)
{
if (stmt->pre_executing)
{
CVT_APPEND_STR("NULL");
stmt->inaccurate_result = TRUE;
continue;
}
else
{
CVT_APPEND_CHAR('?');
continue;
}
}
/* Assign correct buffers based on data at exec param or not */
if (stmt->parameters[param_number].data_at_exec)
{
used = stmt->parameters[param_number].EXEC_used ? *stmt->parameters[param_number].EXEC_used : SQL_NTS;
buffer = stmt->parameters[param_number].EXEC_buffer;
}
else
{
used = stmt->parameters[param_number].used ? *stmt->parameters[param_number].used : SQL_NTS;
buffer = stmt->parameters[param_number].buffer;
}
/* Handle NULL parameter data */
if (used == SQL_NULL_DATA)
{
CVT_APPEND_STR("NULL");
continue;
}
/*
* If no buffer, and it's not null, then what the hell is it? Just
* leave it alone then.
*/
if (!buffer)
{
if (stmt->pre_executing)
{
CVT_APPEND_STR("NULL");
stmt->inaccurate_result = TRUE;
continue;
}
else
{
CVT_APPEND_CHAR('?');
continue;
}
}
param_ctype = stmt->parameters[param_number].CType;
param_sqltype = stmt->parameters[param_number].SQLType;
mylog("copy_statement_with_params: from(fcType)=%d, to(fSqlType)=%d\n", param_ctype, param_sqltype);
/* replace DEFAULT with something we can use */
if (param_ctype == SQL_C_DEFAULT)
param_ctype = sqltype_to_default_ctype(param_sqltype);
buf = NULL;
param_string[0] = '\0';
cbuf[0] = '\0';
/* Convert input C type to a neutral format */
switch (param_ctype)
{
case SQL_C_BINARY:
case SQL_C_CHAR:
buf = buffer;
break;
case SQL_C_DOUBLE:
sprintf(param_string, "%.15g",
*((SDOUBLE *) buffer));
break;
case SQL_C_FLOAT:
sprintf(param_string, "%.6g",
*((SFLOAT *) buffer));
break;
case SQL_C_SLONG:
case SQL_C_LONG:
sprintf(param_string, "%ld",
*((SDWORD *) buffer));
break;
case SQL_C_SSHORT:
case SQL_C_SHORT:
sprintf(param_string, "%d",
*((SWORD *) buffer));
break;
case SQL_C_STINYINT:
case SQL_C_TINYINT:
sprintf(param_string, "%d",
*((SCHAR *) buffer));
break;
case SQL_C_ULONG:
sprintf(param_string, "%lu",
*((UDWORD *) buffer));
break;
case SQL_C_USHORT:
sprintf(param_string, "%u",
*((UWORD *) buffer));
break;
case SQL_C_UTINYINT:
sprintf(param_string, "%u",
*((UCHAR *) buffer));
break;
case SQL_C_BIT:
{
int i = *((UCHAR *) buffer);
sprintf(param_string, "%d", i ? 1 : 0);
break;
}
case SQL_C_DATE:
#if (ODBCVER >= 0x0300)
case SQL_C_TYPE_DATE: /* 91 */
#endif
{
DATE_STRUCT *ds = (DATE_STRUCT *) buffer;
st.m = ds->month;
st.d = ds->day;
st.y = ds->year;
break;
}
case SQL_C_TIME:
#if (ODBCVER >= 0x0300)
case SQL_C_TYPE_TIME: /* 92 */
#endif
{
TIME_STRUCT *ts = (TIME_STRUCT *) buffer;
st.hh = ts->hour;
st.mm = ts->minute;
st.ss = ts->second;
break;
}
case SQL_C_TIMESTAMP:
#if (ODBCVER >= 0x0300)
case SQL_C_TYPE_TIMESTAMP: /* 93 */
#endif
{
TIMESTAMP_STRUCT *tss = (TIMESTAMP_STRUCT *) buffer;
st.m = tss->month;
st.d = tss->day;
st.y = tss->year;
st.hh = tss->hour;
st.mm = tss->minute;
st.ss = tss->second;
st.fr = tss->fraction;
mylog("m=%d,d=%d,y=%d,hh=%d,mm=%d,ss=%d\n", st.m, st.d, st.y, st.hh, st.mm, st.ss);
break;
}
default:
/* error */
stmt->errormsg = "Unrecognized C_parameter type in copy_statement_with_parameters";
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
CVT_TERMINATE; /* just in case */
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/*
* Now that the input data is in a neutral format, convert it to
* the desired output format (sqltype)
*/
switch (param_sqltype)
{
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
CVT_APPEND_CHAR('\''); /* Open Quote */
/* it was a SQL_C_CHAR */
if (buf)
CVT_SPECIAL_CHARS(buf, used);
/* it was a numeric type */
else if (param_string[0] != '\0')
CVT_APPEND_STR(param_string);
/* it was date,time,timestamp -- use m,d,y,hh,mm,ss */
else
{
sprintf(tmp, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
st.y, st.m, st.d, st.hh, st.mm, st.ss);
CVT_APPEND_STR(tmp);
}
CVT_APPEND_CHAR('\''); /* Close Quote */
break;
case SQL_DATE:
#if (ODBCVER >= 0x0300)
case SQL_TYPE_DATE: /* 91 */
#endif
if (buf)
{ /* copy char data to time */
my_strcpy(cbuf, sizeof(cbuf), buf, used);
parse_datetime(cbuf, &st);
}
sprintf(tmp, "'%.4d-%.2d-%.2d'", st.y, st.m, st.d);
CVT_APPEND_STR(tmp);
break;
case SQL_TIME:
#if (ODBCVER >= 0x0300)
case SQL_TYPE_TIME: /* 92 */
#endif
if (buf)
{ /* copy char data to time */
my_strcpy(cbuf, sizeof(cbuf), buf, used);
parse_datetime(cbuf, &st);
}
sprintf(tmp, "'%.2d:%.2d:%.2d'", st.hh, st.mm, st.ss);
CVT_APPEND_STR(tmp);
break;
case SQL_TIMESTAMP:
#if (ODBCVER >= 0x0300)
case SQL_TYPE_TIMESTAMP: /* 93 */
#endif
if (buf)
{
my_strcpy(cbuf, sizeof(cbuf), buf, used);
parse_datetime(cbuf, &st);
}
/*
* sprintf(tmp, "'%.4d-%.2d-%.2d %.2d:%.2d:%.2d'", st.y,
* st.m, st.d, st.hh, st.mm, st.ss);
*/
tmp[0] = '\'';
/* Time zone stuff is unreliable */
stime2timestamp(&st, tmp + 1, FALSE, PG_VERSION_GE(conn, 7.2));
strcat(tmp, "'");
CVT_APPEND_STR(tmp);
break;
case SQL_BINARY:
case SQL_VARBINARY:/* non-ascii characters should be
* converted to octal */
CVT_APPEND_CHAR('\''); /* Open Quote */
mylog("SQL_VARBINARY: about to call convert_to_pgbinary, used = %d\n", used);
CVT_APPEND_BINARY(buf, used);
CVT_APPEND_CHAR('\''); /* Close Quote */
break;
case SQL_LONGVARBINARY:
if (stmt->parameters[param_number].data_at_exec)
lobj_oid = stmt->parameters[param_number].lobj_oid;
else
{
/* begin transaction if needed */
if (!CC_is_in_trans(conn))
{
QResultClass *res;
char ok;
res = CC_send_query(conn, "BEGIN", NULL);
if (!res)
{
stmt->errormsg = "Could not begin (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
ok = QR_command_successful(res);
QR_Destructor(res);
if (!ok)
{
stmt->errormsg = "Could not begin (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
CC_set_in_trans(conn);
}
/* store the oid */
lobj_oid = lo_creat(conn, INV_READ | INV_WRITE);
if (lobj_oid == 0)
{
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Couldnt create (in-line) large object.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/* store the fd */
lobj_fd = lo_open(conn, lobj_oid, INV_WRITE);
if (lobj_fd < 0)
{
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Couldnt open (in-line) large object for writing.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
retval = lo_write(conn, lobj_fd, buffer, used);
lo_close(conn, lobj_fd);
/* commit transaction if needed */
if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn))
{
QResultClass *res;
char ok;
res = CC_send_query(conn, "COMMIT", NULL);
if (!res)
{
stmt->errormsg = "Could not commit (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
ok = QR_command_successful(res);
QR_Destructor(res);
if (!ok)
{
stmt->errormsg = "Could not commit (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
CC_set_no_trans(conn);
}
}
/*
* the oid of the large object -- just put that in for the
* parameter marker -- the data has already been sent to
* the large object
*/
sprintf(param_string, "'%d'", lobj_oid);
CVT_APPEND_STR(param_string);
break;
/*
* because of no conversion operator for bool and int4,
* SQL_BIT
*/
/* must be quoted (0 or 1 is ok to use inside the quotes) */
case SQL_REAL:
if (buf)
my_strcpy(param_string, sizeof(param_string), buf, used);
sprintf(tmp, "'%s'::float4", param_string);
CVT_APPEND_STR(tmp);
break;
case SQL_FLOAT:
case SQL_DOUBLE:
if (buf)
my_strcpy(param_string, sizeof(param_string), buf, used);
sprintf(tmp, "'%s'::float8", param_string);
CVT_APPEND_STR(tmp);
break;
case SQL_NUMERIC:
if (buf)
{
cbuf[0] = '\'';
my_strcpy(cbuf + 1, sizeof(cbuf) - 12, buf, used); /* 12 = 1('\'') +
* strlen("'::numeric")
* + 1('\0') */
strcat(cbuf, "'::numeric");
}
else
sprintf(cbuf, "'%s'::numeric", param_string);
CVT_APPEND_STR(cbuf);
break;
default: /* a numeric type or SQL_BIT */
if (param_sqltype == SQL_BIT)
CVT_APPEND_CHAR('\''); /* Open Quote */
if (buf)
{
switch (used)
{
case SQL_NULL_DATA:
break;
case SQL_NTS:
CVT_APPEND_STR(buf);
break;
default:
CVT_APPEND_DATA(buf, used);
}
}
else
CVT_APPEND_STR(param_string);
if (param_sqltype == SQL_BIT)
CVT_APPEND_CHAR('\''); /* Close Quote */
break;
}
} /* end, for */
/* make sure new_statement is always null-terminated */
CVT_TERMINATE;
if (conn->DriverToDataSource != NULL)
{
int length = strlen(new_statement);
conn->DriverToDataSource(conn->translation_option,
SQL_CHAR,
new_statement, length,
new_statement, length, NULL,
NULL, 0, NULL);
}
#ifdef DRIVER_CURSOR_IMPLEMENT
if (search_from_pos)
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
#endif /* DRIVER_CURSOR_IMPLEMENT */
if (prepare_dummy_cursor && SC_is_pre_executable(stmt))
{
char fetchstr[128];
sprintf(fetchstr, ";fetch backward in %s;close %s;",
stmt->cursor_name, stmt->cursor_name);
if (begin_first && CC_is_in_autocommit(conn))
strcat(fetchstr, "COMMIT;");
CVT_APPEND_STR(fetchstr);
stmt->inaccurate_result = TRUE;
}
return SQL_SUCCESS;
}
static char *
mapFunction(const char *func)
{
int i;
for (i = 0; mapFuncs[i][0]; i++)
if (!stricmp(mapFuncs[i][0], func))
return mapFuncs[i][1];
return NULL;
}
/*
* convert_escape()
*
* This function returns a pointer to static memory!
*/
char *
convert_escape(char *value)
{
static char escape[1024];
char key[33];
/* Separate off the key, skipping leading and trailing whitespace */
while ((*value != '\0') && isspace((unsigned char) *value))
value++;
sscanf(value, "%32s", key);
while ((*value != '\0') && (!isspace((unsigned char) *value)))
value++;
while ((*value != '\0') && isspace((unsigned char) *value))
value++;
mylog("convert_escape: key='%s', val='%s'\n", key, value);
if ((strcmp(key, "d") == 0) ||
(strcmp(key, "t") == 0) ||
(strcmp(key, "oj") == 0) || /* {oj syntax support for 7.1
* servers */
(strcmp(key, "ts") == 0))
{
/* Literal; return the escape part as-is */
strncpy(escape, value, sizeof(escape) - 1);
}
else if (strcmp(key, "fn") == 0)
{
/*
* Function invocation Separate off the func name, skipping
* trailing whitespace.
*/
char *funcEnd = value;
char svchar;
char *mapFunc;
while ((*funcEnd != '\0') && (*funcEnd != '(') &&
(!isspace((unsigned char) *funcEnd)))
funcEnd++;
svchar = *funcEnd;
*funcEnd = '\0';
sscanf(value, "%32s", key);
*funcEnd = svchar;
while ((*funcEnd != '\0') && isspace((unsigned char) *funcEnd))
funcEnd++;
/*
* We expect left parenthesis here, else return fn body as-is
* since it is one of those "function constants".
*/
if (*funcEnd != '(')
{
strncpy(escape, value, sizeof(escape) - 1);
return escape;
}
mapFunc = mapFunction(key);
/*
* We could have mapFunction() return key if not in table... -
* thomas 2000-04-03
*/
if (mapFunc == NULL)
{
/* If unrecognized function name, return fn body as-is */
strncpy(escape, value, sizeof(escape) - 1);
return escape;
}
/* copy mapped name and remaining input string */
strcpy(escape, mapFunc);
strncat(escape, funcEnd, sizeof(escape) - 1 - strlen(mapFunc));
}
else
{
/* Bogus key, leave untranslated */
return NULL;
}
return escape;
}
BOOL
convert_money(const char *s, char *sout, size_t soutmax)
{
size_t i = 0,
out = 0;
for (i = 0; s[i]; i++)
{
if (s[i] == '$' || s[i] == ',' || s[i] == ')')
; /* skip these characters */
else
{
if (out + 1 >= soutmax)
return FALSE; /* sout is too short */
if (s[i] == '(')
sout[out++] = '-';
else
sout[out++] = s[i];
}
}
sout[out] = '\0';
return TRUE;
}
/*
* This function parses a character string for date/time info and fills in SIMPLE_TIME
* It does not zero out SIMPLE_TIME in case it is desired to initialize it with a value
*/
char
parse_datetime(char *buf, SIMPLE_TIME *st)
{
int y,
m,
d,
hh,
mm,
ss;
int nf;
y = m = d = hh = mm = ss = 0;
/* escape sequence ? */
if (buf[0] == '{')
{
while (*(++buf) && *buf != '\'');
if (!(*buf))
return FALSE;
buf++;
}
if (buf[4] == '-') /* year first */
nf = sscanf(buf, "%4d-%2d-%2d %2d:%2d:%2d", &y, &m, &d, &hh, &mm, &ss);
else
nf = sscanf(buf, "%2d-%2d-%4d %2d:%2d:%2d", &m, &d, &y, &hh, &mm, &ss);
if (nf == 5 || nf == 6)
{
st->y = y;
st->m = m;
st->d = d;
st->hh = hh;
st->mm = mm;
st->ss = ss;
return TRUE;
}
if (buf[4] == '-') /* year first */
nf = sscanf(buf, "%4d-%2d-%2d", &y, &m, &d);
else
nf = sscanf(buf, "%2d-%2d-%4d", &m, &d, &y);
if (nf == 3)
{
st->y = y;
st->m = m;
st->d = d;
return TRUE;
}
nf = sscanf(buf, "%2d:%2d:%2d", &hh, &mm, &ss);
if (nf == 2 || nf == 3)
{
st->hh = hh;
st->mm = mm;
st->ss = ss;
return TRUE;
}
return FALSE;
}
/* Change linefeed to carriage-return/linefeed */
int
convert_linefeeds(const char *si, char *dst, size_t max, BOOL *changed)
{
size_t i = 0,
out = 0;
if (max == 0)
max = 0xffffffff;
*changed = FALSE;
for (i = 0; si[i] && out < max - 1; i++)
{
if (si[i] == '\n')
{
/* Only add the carriage-return if needed */
if (i > 0 && si[i - 1] == '\r')
{
if (dst)
dst[out++] = si[i];
else
out++;
continue;
}
*changed = TRUE;
if (dst)
{
dst[out++] = '\r';
dst[out++] = '\n';
}
else
out += 2;
}
else
{
if (dst)
dst[out++] = si[i];
else
out++;
}
}
if (dst)
dst[out] = '\0';
return out;
}
/*
* Change carriage-return/linefeed to just linefeed
* Plus, escape any special characters.
*/
int
convert_special_chars(const char *si, char *dst, int used)
{
size_t i = 0,
out = 0,
max;
char *p = NULL;
if (used == SQL_NTS)
max = strlen(si);
else
max = used;
if (dst)
{
p = dst;
p[0] = '\0';
}
#ifdef MULTIBYTE
multibyte_init();
#endif
for (i = 0; i < max; i++)
{
#ifdef MULTIBYTE
if (multibyte_char_check(si[i]) != 0)
{
if (p)
p[out] = si[i];
out++;
continue;
}
#endif
if (si[i] == '\r' && si[i + 1] == '\n')
continue;
else if (si[i] == '\'' || si[i] == '\\')
{
if (p)
p[out++] = '\\';
else
out++;
}
if (p)
p[out++] = si[i];
else
out++;
}
if (p)
p[out] = '\0';
return out;
}
/* !!! Need to implement this function !!! */
int
convert_pgbinary_to_char(const char *value, char *rgbValue, int cbValueMax)
{
mylog("convert_pgbinary_to_char: value = '%s'\n", value);
strncpy_null(rgbValue, value, cbValueMax);
return 0;
}
static unsigned int
conv_from_octal(const unsigned char *s)
{
int i,
y = 0;
for (i = 1; i <= 3; i++)
y += (s[i] - 48) * (int) pow(8, 3 - i);
return y;
}
static unsigned int
conv_from_hex(const unsigned char *s)
{
int i,
y = 0,
val;
for (i = 1; i <= 2; i++)
{
if (s[i] >= 'a' && s[i] <= 'f')
val = s[i] - 'a' + 10;
else if (s[i] >= 'A' && s[i] <= 'F')
val = s[i] - 'A' + 10;
else
val = s[i] - '0';
y += val * (int) pow(16, 2 - i);
}
return y;
}
/* convert octal escapes to bytes */
int
convert_from_pgbinary(const unsigned char *value, unsigned char *rgbValue, int cbValueMax)
{
size_t i,
ilen = strlen(value);
int o = 0;
for (i = 0; i < ilen;)
{
if (value[i] == '\\')
{
if (value[i + 1] == '\\')
{
rgbValue[o] = value[i];
i += 2;
}
else
{
rgbValue[o] = conv_from_octal(&value[i]);
i += 4;
}
}
else
rgbValue[o] = value[i++];
mylog("convert_from_pgbinary: i=%d, rgbValue[%d] = %d, %c\n", i, o, rgbValue[o], rgbValue[o]);
o++;
}
rgbValue[o] = '\0'; /* extra protection */
return o;
}
static char *
conv_to_octal(unsigned char val)
{
int i;
static char x[6];
x[0] = '\\';
x[1] = '\\';
x[5] = '\0';
for (i = 4; i > 1; i--)
{
x[i] = (val & 7) + 48;
val >>= 3;
}
return x;
}
/* convert non-ascii bytes to octal escape sequences */
int
convert_to_pgbinary(const unsigned char *in, char *out, int len)
{
int i,
o = 0;
for (i = 0; i < len; i++)
{
mylog("convert_to_pgbinary: in[%d] = %d, %c\n", i, in[i], in[i]);
if (isalnum(in[i]) || in[i] == ' ')
out[o++] = in[i];
else
{
strcpy(&out[o], conv_to_octal(in[i]));
o += 5;
}
}
mylog("convert_to_pgbinary: returning %d, out='%.*s'\n", o, o, out);
return o;
}
void
encode(const char *in, char *out)
{
unsigned int i,
ilen = strlen(in),
o = 0;
for (i = 0; i < ilen; i++)
{
if (in[i] == '+')
{
sprintf(&out[o], "%%2B");
o += 3;
}
else if (isspace((unsigned char) in[i]))
out[o++] = '+';
else if (!isalnum((unsigned char) in[i]))
{
sprintf(&out[o], "%%%02x", (unsigned char) in[i]);
o += 3;
}
else
out[o++] = in[i];
}
out[o++] = '\0';
}
void
decode(const char *in, char *out)
{
unsigned int i,
ilen = strlen(in),
o = 0;
for (i = 0; i < ilen; i++)
{
if (in[i] == '+')
out[o++] = ' ';
else if (in[i] == '%')
{
sprintf(&out[o++], "%c", conv_from_hex(&in[i]));
i += 2;
}
else
out[o++] = in[i];
}
out[o++] = '\0';
}
static const char *hextbl = "0123456789ABCDEF";
static int
pg_bin2hex(UCHAR *src, UCHAR *dst, int length)
{
UCHAR chr,
*src_wk,
*dst_wk;
BOOL backwards;
int i;
backwards = FALSE;
if (dst < src)
{
if (dst + length > src + 1)
return -1;
}
else if (dst < src + length)
backwards = TRUE;
if (backwards)
{
for (i = 0, src_wk = src + length - 1, dst_wk = dst + 2 * length - 1; i < length; i++, src_wk--)
{
chr = *src_wk;
*dst_wk-- = hextbl[chr % 16];
*dst_wk-- = hextbl[chr >> 4];
}
}
else
{
for (i = 0, src_wk = src, dst_wk = dst; i < length; i++, src_wk++)
{
chr = *src_wk;
*dst_wk++ = hextbl[chr >> 4];
*dst_wk++ = hextbl[chr % 16];
}
}
dst[2 * length] = '\0';
return length;
}
/*-------
* 1. get oid (from 'value')
* 2. open the large object
* 3. read from the large object (handle multiple GetData)
* 4. close when read less than requested? -OR-
* lseek/read each time
* handle case where application receives truncated and
* decides not to continue reading.
*
* CURRENTLY, ONLY LONGVARBINARY is handled, since that is the only
* data type currently mapped to a PG_TYPE_LO. But, if any other types
* are desired to map to a large object (PG_TYPE_LO), then that would
* need to be handled here. For example, LONGVARCHAR could possibly be
* mapped to PG_TYPE_LO someday, instead of PG_TYPE_TEXT as it is now.
*-------
*/
int
convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue,
SDWORD cbValueMax, SDWORD *pcbValue)
{
Oid oid;
int retval,
result,
left = -1;
BindInfoClass *bindInfo = NULL;
ConnectionClass *conn = SC_get_conn(stmt);
ConnInfo *ci = &(conn->connInfo);
int factor = (fCType == SQL_C_CHAR ? 2 : 1);
/* If using SQLGetData, then current_col will be set */
if (stmt->current_col >= 0)
{
bindInfo = &stmt->bindings[stmt->current_col];
left = bindInfo->data_left;
}
/*
* if this is the first call for this column, open the large object
* for reading
*/
if (!bindInfo || bindInfo->data_left == -1)
{
/* begin transaction if needed */
if (!CC_is_in_trans(conn))
{
QResultClass *res;
char ok;
res = CC_send_query(conn, "BEGIN", NULL);
if (!res)
{
stmt->errormsg = "Could not begin (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
return COPY_GENERAL_ERROR;
}
ok = QR_command_successful(res);
QR_Destructor(res);
if (!ok)
{
stmt->errormsg = "Could not begin (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
return COPY_GENERAL_ERROR;
}
CC_set_in_trans(conn);
}
oid = atoi(value);
stmt->lobj_fd = lo_open(conn, oid, INV_READ);
if (stmt->lobj_fd < 0)
{
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Couldnt open large object for reading.";
return COPY_GENERAL_ERROR;
}
/* Get the size */
retval = lo_lseek(conn, stmt->lobj_fd, 0L, SEEK_END);
if (retval >= 0)
{
left = lo_tell(conn, stmt->lobj_fd);
if (bindInfo)
bindInfo->data_left = left;
/* return to beginning */
lo_lseek(conn, stmt->lobj_fd, 0L, SEEK_SET);
}
}
mylog("lo data left = %d\n", left);
if (left == 0)
return COPY_NO_DATA_FOUND;
if (stmt->lobj_fd < 0)
{
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Large object FD undefined for multiple read.";
return COPY_GENERAL_ERROR;
}
retval = lo_read(conn, stmt->lobj_fd, (char *) rgbValue, factor > 1 ? (cbValueMax - 1) / factor : cbValueMax);
if (retval < 0)
{
lo_close(conn, stmt->lobj_fd);
/* commit transaction if needed */
if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn))
{
QResultClass *res;
char ok;
res = CC_send_query(conn, "COMMIT", NULL);
if (!res)
{
stmt->errormsg = "Could not commit (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
return COPY_GENERAL_ERROR;
}
ok = QR_command_successful(res);
QR_Destructor(res);
if (!ok)
{
stmt->errormsg = "Could not commit (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
return COPY_GENERAL_ERROR;
}
CC_set_no_trans(conn);
}
stmt->lobj_fd = -1;
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Error reading from large object.";
return COPY_GENERAL_ERROR;
}
if (factor > 1)
pg_bin2hex((char *) rgbValue, (char *) rgbValue, retval);
if (retval < left)
result = COPY_RESULT_TRUNCATED;
else
result = COPY_OK;
if (pcbValue)
*pcbValue = left < 0 ? SQL_NO_TOTAL : left * factor;
if (bindInfo && bindInfo->data_left > 0)
bindInfo->data_left -= retval;
if (!bindInfo || bindInfo->data_left == 0)
{
lo_close(conn, stmt->lobj_fd);
/* commit transaction if needed */
if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn))
{
QResultClass *res;
char ok;
res = CC_send_query(conn, "COMMIT", NULL);
if (!res)
{
stmt->errormsg = "Could not commit (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
return COPY_GENERAL_ERROR;
}
ok = QR_command_successful(res);
QR_Destructor(res);
if (!ok)
{
stmt->errormsg = "Could not commit (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
return COPY_GENERAL_ERROR;
}
CC_set_no_trans(conn);
}
stmt->lobj_fd = -1; /* prevent further reading */
}
return result;
}
/* File: convert.h
*
* Description: See "convert.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __CONVERT_H__
#define __CONVERT_H__
#include "psqlodbc.h"
/* copy_and_convert results */
#define COPY_OK 0
#define COPY_UNSUPPORTED_TYPE 1
#define COPY_UNSUPPORTED_CONVERSION 2
#define COPY_RESULT_TRUNCATED 3
#define COPY_GENERAL_ERROR 4
#define COPY_NO_DATA_FOUND 5
typedef struct
{
int m;
int d;
int y;
int hh;
int mm;
int ss;
int fr;
} SIMPLE_TIME;
int copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col);
int copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType,
PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue);
int copy_statement_with_parameters(StatementClass *stmt);
char *convert_escape(char *value);
BOOL convert_money(const char *s, char *sout, size_t soutmax);
char parse_datetime(char *buf, SIMPLE_TIME *st);
int convert_linefeeds(const char *s, char *dst, size_t max, BOOL *changed);
int convert_special_chars(const char *si, char *dst, int used);
int convert_pgbinary_to_char(const char *value, char *rgbValue, int cbValueMax);
int convert_from_pgbinary(const unsigned char *value, unsigned char *rgbValue, int cbValueMax);
int convert_to_pgbinary(const unsigned char *in, char *out, int len);
void encode(const char *in, char *out);
void decode(const char *in, char *out);
int convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue,
SDWORD cbValueMax, SDWORD *pcbValue);
#endif
/*-------
* Module: dlg_specific.c
*
* Description: This module contains any specific code for handling
* dialog boxes such as driver/datasource options. Both the
* ConfigDSN() and the SQLDriverConnect() functions use
* functions in this module. If you were to add a new option
* to any dialog box, you would most likely only have to change
* things in here rather than in 2 separate places as before.
*
* Classes: none
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
/* Multibyte support Eiji Tokuya 2001-03-15 */
#include "dlg_specific.h"
#include "convert.h"
#ifdef MULTIBYTE
#include "multibyte.h"
#endif
#include "pgapifunc.h"
#ifndef BOOL
#define BOOL int
#endif
#ifndef FALSE
#define FALSE (BOOL)0
#endif
#ifndef TRUE
#define TRUE (BOOL)1
#endif
extern GLOBAL_VALUES globals;
#ifdef WIN32
static int driver_optionsDraw(HWND, const ConnInfo *, int src, BOOL enable);
static int driver_options_update(HWND hdlg, ConnInfo *ci, BOOL);
static void updateCommons(const ConnInfo *ci);
#endif
#ifdef WIN32
void
SetDlgStuff(HWND hdlg, const ConnInfo *ci)
{
/*
* If driver attribute NOT present, then set the datasource name and
* description
*/
if (ci->driver[0] == '\0')
{
SetDlgItemText(hdlg, IDC_DSNAME, ci->dsn);
SetDlgItemText(hdlg, IDC_DESC, ci->desc);
}
SetDlgItemText(hdlg, IDC_DATABASE, ci->database);
SetDlgItemText(hdlg, IDC_SERVER, ci->server);
SetDlgItemText(hdlg, IDC_USER, ci->username);
SetDlgItemText(hdlg, IDC_PASSWORD, ci->password);
SetDlgItemText(hdlg, IDC_PORT, ci->port);
}
void
GetDlgStuff(HWND hdlg, ConnInfo *ci)
{
GetDlgItemText(hdlg, IDC_DESC, ci->desc, sizeof(ci->desc));
GetDlgItemText(hdlg, IDC_DATABASE, ci->database, sizeof(ci->database));
GetDlgItemText(hdlg, IDC_SERVER, ci->server, sizeof(ci->server));
GetDlgItemText(hdlg, IDC_USER, ci->username, sizeof(ci->username));
GetDlgItemText(hdlg, IDC_PASSWORD, ci->password, sizeof(ci->password));
GetDlgItemText(hdlg, IDC_PORT, ci->port, sizeof(ci->port));
}
static int
driver_optionsDraw(HWND hdlg, const ConnInfo *ci, int src, BOOL enable)
{
const GLOBAL_VALUES *comval;
static BOOL defset = FALSE;
static GLOBAL_VALUES defval;
switch (src)
{
case 0: /* driver common */
comval = &globals;
break;
case 1: /* dsn specific */
comval = &(ci->drivers);
break;
case 2: /* default */
if (!defset)
{
defval.commlog = DEFAULT_COMMLOG;
defval.disable_optimizer = DEFAULT_OPTIMIZER;
defval.ksqo = DEFAULT_KSQO;
defval.unique_index = DEFAULT_UNIQUEINDEX;
defval.onlyread = DEFAULT_READONLY;
defval.use_declarefetch = DEFAULT_USEDECLAREFETCH;
defval.parse = DEFAULT_PARSE;
defval.cancel_as_freestmt = DEFAULT_CANCELASFREESTMT;
defval.debug = DEFAULT_DEBUG;
/* Unknown Sizes */
defval.unknown_sizes = DEFAULT_UNKNOWNSIZES;
defval.text_as_longvarchar = DEFAULT_TEXTASLONGVARCHAR;
defval.unknowns_as_longvarchar = DEFAULT_UNKNOWNSASLONGVARCHAR;
defval.bools_as_char = DEFAULT_BOOLSASCHAR;
}
defset = TRUE;
comval = &defval;
break;
}
CheckDlgButton(hdlg, DRV_COMMLOG, comval->commlog);
CheckDlgButton(hdlg, DRV_OPTIMIZER, comval->disable_optimizer);
CheckDlgButton(hdlg, DRV_KSQO, comval->ksqo);
CheckDlgButton(hdlg, DRV_UNIQUEINDEX, comval->unique_index);
EnableWindow(GetDlgItem(hdlg, DRV_UNIQUEINDEX), enable);
CheckDlgButton(hdlg, DRV_READONLY, comval->onlyread);
EnableWindow(GetDlgItem(hdlg, DRV_READONLY), enable);
CheckDlgButton(hdlg, DRV_USEDECLAREFETCH, comval->use_declarefetch);
/* Unknown Sizes clear */
CheckDlgButton(hdlg, DRV_UNKNOWN_DONTKNOW, 0);
CheckDlgButton(hdlg, DRV_UNKNOWN_LONGEST, 0);
CheckDlgButton(hdlg, DRV_UNKNOWN_MAX, 0);
/* Unknown (Default) Data Type sizes */
switch (comval->unknown_sizes)
{
case UNKNOWNS_AS_DONTKNOW:
CheckDlgButton(hdlg, DRV_UNKNOWN_DONTKNOW, 1);
break;
case UNKNOWNS_AS_LONGEST:
CheckDlgButton(hdlg, DRV_UNKNOWN_LONGEST, 1);
break;
case UNKNOWNS_AS_MAX:
default:
CheckDlgButton(hdlg, DRV_UNKNOWN_MAX, 1);
break;
}
CheckDlgButton(hdlg, DRV_TEXT_LONGVARCHAR, comval->text_as_longvarchar);
CheckDlgButton(hdlg, DRV_UNKNOWNS_LONGVARCHAR, comval->unknowns_as_longvarchar);
CheckDlgButton(hdlg, DRV_BOOLS_CHAR, comval->bools_as_char);
CheckDlgButton(hdlg, DRV_PARSE, comval->parse);
CheckDlgButton(hdlg, DRV_CANCELASFREESTMT, comval->cancel_as_freestmt);
CheckDlgButton(hdlg, DRV_DEBUG, comval->debug);
SetDlgItemInt(hdlg, DRV_CACHE_SIZE, comval->fetch_max, FALSE);
SetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, comval->max_varchar_size, FALSE);
SetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, comval->max_longvarchar_size, TRUE);
SetDlgItemText(hdlg, DRV_EXTRASYSTABLEPREFIXES, comval->extra_systable_prefixes);
/* Driver Connection Settings */
SetDlgItemText(hdlg, DRV_CONNSETTINGS, comval->conn_settings);
EnableWindow(GetDlgItem(hdlg, DRV_CONNSETTINGS), enable);
return 0;
}
static int
driver_options_update(HWND hdlg, ConnInfo *ci, BOOL updateProfile)
{
GLOBAL_VALUES *comval;
if (ci)
comval = &(ci->drivers);
else
comval = &globals;
comval->commlog = IsDlgButtonChecked(hdlg, DRV_COMMLOG);
comval->disable_optimizer = IsDlgButtonChecked(hdlg, DRV_OPTIMIZER);
comval->ksqo = IsDlgButtonChecked(hdlg, DRV_KSQO);
if (!ci)
{
comval->unique_index = IsDlgButtonChecked(hdlg, DRV_UNIQUEINDEX);
comval->onlyread = IsDlgButtonChecked(hdlg, DRV_READONLY);
}
comval->use_declarefetch = IsDlgButtonChecked(hdlg, DRV_USEDECLAREFETCH);
/* Unknown (Default) Data Type sizes */
if (IsDlgButtonChecked(hdlg, DRV_UNKNOWN_MAX))
comval->unknown_sizes = UNKNOWNS_AS_MAX;
else if (IsDlgButtonChecked(hdlg, DRV_UNKNOWN_DONTKNOW))
comval->unknown_sizes = UNKNOWNS_AS_DONTKNOW;
else if (IsDlgButtonChecked(hdlg, DRV_UNKNOWN_LONGEST))
comval->unknown_sizes = UNKNOWNS_AS_LONGEST;
else
comval->unknown_sizes = UNKNOWNS_AS_MAX;
comval->text_as_longvarchar = IsDlgButtonChecked(hdlg, DRV_TEXT_LONGVARCHAR);
comval->unknowns_as_longvarchar = IsDlgButtonChecked(hdlg, DRV_UNKNOWNS_LONGVARCHAR);
comval->bools_as_char = IsDlgButtonChecked(hdlg, DRV_BOOLS_CHAR);
comval->parse = IsDlgButtonChecked(hdlg, DRV_PARSE);
comval->cancel_as_freestmt = IsDlgButtonChecked(hdlg, DRV_CANCELASFREESTMT);
comval->debug = IsDlgButtonChecked(hdlg, DRV_DEBUG);
comval->fetch_max = GetDlgItemInt(hdlg, DRV_CACHE_SIZE, NULL, FALSE);
comval->max_varchar_size = GetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, NULL, FALSE);
comval->max_longvarchar_size = GetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, NULL, TRUE); /* allows for
* SQL_NO_TOTAL */
GetDlgItemText(hdlg, DRV_EXTRASYSTABLEPREFIXES, comval->extra_systable_prefixes, sizeof(comval->extra_systable_prefixes));
/* Driver Connection Settings */
if (!ci)
GetDlgItemText(hdlg, DRV_CONNSETTINGS, comval->conn_settings, sizeof(comval->conn_settings));
if (updateProfile)
updateCommons(ci);
/* fall through */
return 0;
}
int CALLBACK
driver_optionsProc(HWND hdlg,
WORD wMsg,
WPARAM wParam,
LPARAM lParam)
{
ConnInfo *ci;
switch (wMsg)
{
case WM_INITDIALOG:
SetWindowLong(hdlg, DWL_USER, lParam); /* save for OK etc */
ci = (ConnInfo *) lParam;
CheckDlgButton(hdlg, DRV_OR_DSN, 0);
if (ci && ci->dsn && ci->dsn[0])
SetWindowText(hdlg, "Advanced Options (per DSN)");
else
{
SetWindowText(hdlg, "Advanced Options (Connection)");
ShowWindow(GetDlgItem(hdlg, DRV_OR_DSN), SW_HIDE);
}
driver_optionsDraw(hdlg, ci, 1, FALSE);
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
driver_options_update(hdlg, IsDlgButtonChecked(hdlg, DRV_OR_DSN) ? NULL : ci,
ci && ci->dsn && ci->dsn[0]);
case IDCANCEL:
EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
return TRUE;
case IDDEFAULTS:
if (IsDlgButtonChecked(hdlg, DRV_OR_DSN))
driver_optionsDraw(hdlg, NULL, 2, TRUE);
else
{
ConnInfo *ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
driver_optionsDraw(hdlg, ci, 0, FALSE);
}
break;
case DRV_OR_DSN:
if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
{
mylog("DRV_OR_DSN clicked\n");
if (IsDlgButtonChecked(hdlg, DRV_OR_DSN))
{
SetWindowText(hdlg, "Advanced Options (Common)");
driver_optionsDraw(hdlg, NULL, 0, TRUE);
}
else
{
ConnInfo *ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
SetWindowText(hdlg, "Advanced Options (per DSN)");
driver_optionsDraw(hdlg, ci, ci ? 1 : 0, ci == NULL);
}
}
break;
}
}
return FALSE;
}
int CALLBACK
ds_optionsProc(HWND hdlg,
WORD wMsg,
WPARAM wParam,
LPARAM lParam)
{
ConnInfo *ci;
char buf[128];
switch (wMsg)
{
case WM_INITDIALOG:
ci = (ConnInfo *) lParam;
SetWindowLong(hdlg, DWL_USER, lParam); /* save for OK */
/* Change window caption */
if (ci->driver[0])
SetWindowText(hdlg, "Advanced Options (Connection)");
else
{
sprintf(buf, "Advanced Options (%s)", ci->dsn);
SetWindowText(hdlg, buf);
}
/* Readonly */
CheckDlgButton(hdlg, DS_READONLY, atoi(ci->onlyread));
/* Protocol */
if (strncmp(ci->protocol, PG62, strlen(PG62)) == 0)
CheckDlgButton(hdlg, DS_PG62, 1);
else if (strncmp(ci->protocol, PG63, strlen(PG63)) == 0)
CheckDlgButton(hdlg, DS_PG63, 1);
else
/* latest */
CheckDlgButton(hdlg, DS_PG64, 1);
CheckDlgButton(hdlg, DS_SHOWOIDCOLUMN, atoi(ci->show_oid_column));
CheckDlgButton(hdlg, DS_FAKEOIDINDEX, atoi(ci->fake_oid_index));
CheckDlgButton(hdlg, DS_ROWVERSIONING, atoi(ci->row_versioning));
CheckDlgButton(hdlg, DS_SHOWSYSTEMTABLES, atoi(ci->show_system_tables));
CheckDlgButton(hdlg, DS_DISALLOWPREMATURE, ci->disallow_premature);
EnableWindow(GetDlgItem(hdlg, DS_FAKEOIDINDEX), atoi(ci->show_oid_column));
/* Datasource Connection Settings */
SetDlgItemText(hdlg, DS_CONNSETTINGS, ci->conn_settings);
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case DS_SHOWOIDCOLUMN:
mylog("WM_COMMAND: DS_SHOWOIDCOLUMN\n");
EnableWindow(GetDlgItem(hdlg, DS_FAKEOIDINDEX), IsDlgButtonChecked(hdlg, DS_SHOWOIDCOLUMN));
return TRUE;
case IDOK:
ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
mylog("IDOK: got ci = %u\n", ci);
/* Readonly */
sprintf(ci->onlyread, "%d", IsDlgButtonChecked(hdlg, DS_READONLY));
/* Protocol */
if (IsDlgButtonChecked(hdlg, DS_PG62))
strcpy(ci->protocol, PG62);
else if (IsDlgButtonChecked(hdlg, DS_PG63))
strcpy(ci->protocol, PG63);
else
/* latest */
strcpy(ci->protocol, PG64);
sprintf(ci->show_system_tables, "%d", IsDlgButtonChecked(hdlg, DS_SHOWSYSTEMTABLES));
sprintf(ci->row_versioning, "%d", IsDlgButtonChecked(hdlg, DS_ROWVERSIONING));
ci->disallow_premature = IsDlgButtonChecked(hdlg, DS_DISALLOWPREMATURE);
/* OID Options */
sprintf(ci->fake_oid_index, "%d", IsDlgButtonChecked(hdlg, DS_FAKEOIDINDEX));
sprintf(ci->show_oid_column, "%d", IsDlgButtonChecked(hdlg, DS_SHOWOIDCOLUMN));
/* Datasource Connection Settings */
GetDlgItemText(hdlg, DS_CONNSETTINGS, ci->conn_settings, sizeof(ci->conn_settings));
/* fall through */
case IDCANCEL:
EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
return TRUE;
}
}
return FALSE;
}
/*
* This function writes any global parameters (that can be manipulated)
* to the ODBCINST.INI portion of the registry
*/
static void
updateCommons(const ConnInfo *ci)
{
const char *sectionName;
const char *fileName;
const GLOBAL_VALUES *comval;
char tmp[128];
if (ci)
if (ci->dsn && ci->dsn[0])
{
mylog("DSN=%s updating\n", ci->dsn);
comval = &(ci->drivers);
sectionName = ci->dsn;
fileName = ODBC_INI;
}
else
{
mylog("ci but dsn==NULL\n");
return;
}
else
{
mylog("drivers updating\n");
comval = &globals;
sectionName = DBMS_NAME;
fileName = ODBCINST_INI;
}
sprintf(tmp, "%d", comval->fetch_max);
SQLWritePrivateProfileString(sectionName,
INI_FETCH, tmp, fileName);
sprintf(tmp, "%d", comval->commlog);
SQLWritePrivateProfileString(sectionName,
INI_COMMLOG, tmp, fileName);
sprintf(tmp, "%d", comval->debug);
SQLWritePrivateProfileString(sectionName,
INI_DEBUG, tmp, fileName);
sprintf(tmp, "%d", comval->disable_optimizer);
SQLWritePrivateProfileString(sectionName,
INI_OPTIMIZER, tmp, fileName);
sprintf(tmp, "%d", comval->ksqo);
SQLWritePrivateProfileString(sectionName,
INI_KSQO, tmp, fileName);
/*
* Never update the onlyread, unique_index from this module.
*/
if (!ci)
{
sprintf(tmp, "%d", comval->unique_index);
SQLWritePrivateProfileString(sectionName, INI_UNIQUEINDEX, tmp,
fileName);
sprintf(tmp, "%d", comval->onlyread);
SQLWritePrivateProfileString(sectionName, INI_READONLY, tmp,
fileName);
}
sprintf(tmp, "%d", comval->use_declarefetch);
SQLWritePrivateProfileString(sectionName,
INI_USEDECLAREFETCH, tmp, fileName);
sprintf(tmp, "%d", comval->unknown_sizes);
SQLWritePrivateProfileString(sectionName,
INI_UNKNOWNSIZES, tmp, fileName);
sprintf(tmp, "%d", comval->text_as_longvarchar);
SQLWritePrivateProfileString(sectionName,
INI_TEXTASLONGVARCHAR, tmp, fileName);
sprintf(tmp, "%d", comval->unknowns_as_longvarchar);
SQLWritePrivateProfileString(sectionName,
INI_UNKNOWNSASLONGVARCHAR, tmp, fileName);
sprintf(tmp, "%d", comval->bools_as_char);
SQLWritePrivateProfileString(sectionName,
INI_BOOLSASCHAR, tmp, fileName);
sprintf(tmp, "%d", comval->parse);
SQLWritePrivateProfileString(sectionName,
INI_PARSE, tmp, fileName);
sprintf(tmp, "%d", comval->cancel_as_freestmt);
SQLWritePrivateProfileString(sectionName,
INI_CANCELASFREESTMT, tmp, fileName);
sprintf(tmp, "%d", comval->max_varchar_size);
SQLWritePrivateProfileString(sectionName,
INI_MAXVARCHARSIZE, tmp, fileName);
sprintf(tmp, "%d", comval->max_longvarchar_size);
SQLWritePrivateProfileString(sectionName,
INI_MAXLONGVARCHARSIZE, tmp, fileName);
SQLWritePrivateProfileString(sectionName,
INI_EXTRASYSTABLEPREFIXES, comval->extra_systable_prefixes, fileName);
/*
* Never update the conn_setting from this module
* SQLWritePrivateProfileString(sectionName, INI_CONNSETTINGS,
* comval->conn_settings, fileName);
*/
}
#endif /* WIN32 */
void
makeConnectString(char *connect_string, const ConnInfo *ci, UWORD len)
{
char got_dsn = (ci->dsn[0] != '\0');
char encoded_conn_settings[LARGE_REGISTRY_LEN];
UWORD hlen;
/*BOOL abbrev = (len <= 400);*/
BOOL abbrev = (len < 1024);
/* fundamental info */
sprintf(connect_string, "%s=%s;DATABASE=%s;SERVER=%s;PORT=%s;UID=%s;PWD=%s",
got_dsn ? "DSN" : "DRIVER",
got_dsn ? ci->dsn : ci->driver,
ci->database,
ci->server,
ci->port,
ci->username,
ci->password);
encode(ci->conn_settings, encoded_conn_settings);
/* extra info */
hlen = strlen(connect_string);
if (!abbrev)
sprintf(&connect_string[hlen],
";READONLY=%s;PROTOCOL=%s;FAKEOIDINDEX=%s;SHOWOIDCOLUMN=%s;ROWVERSIONING=%s;SHOWSYSTEMTABLES=%s;CONNSETTINGS=%s;FETCH=%d;SOCKET=%d;UNKNOWNSIZES=%d;MAXVARCHARSIZE=%d;MAXLONGVARCHARSIZE=%d;DEBUG=%d;COMMLOG=%d;OPTIMIZER=%d;KSQO=%d;USEDECLAREFETCH=%d;TEXTASLONGVARCHAR=%d;UNKNOWNSASLONGVARCHAR=%d;BOOLSASCHAR=%d;PARSE=%d;CANCELASFREESTMT=%d;EXTRASYSTABLEPREFIXES=%s",
ci->onlyread,
ci->protocol,
ci->fake_oid_index,
ci->show_oid_column,
ci->row_versioning,
ci->show_system_tables,
encoded_conn_settings,
ci->drivers.fetch_max,
ci->drivers.socket_buffersize,
ci->drivers.unknown_sizes,
ci->drivers.max_varchar_size,
ci->drivers.max_longvarchar_size,
ci->drivers.debug,
ci->drivers.commlog,
ci->drivers.disable_optimizer,
ci->drivers.ksqo,
ci->drivers.use_declarefetch,
ci->drivers.text_as_longvarchar,
ci->drivers.unknowns_as_longvarchar,
ci->drivers.bools_as_char,
ci->drivers.parse,
ci->drivers.cancel_as_freestmt,
ci->drivers.extra_systable_prefixes);
/* Abbrebiation is needed ? */
if (abbrev || strlen(connect_string) >= len)
sprintf(&connect_string[hlen],
";A0=%s;A1=%s;A2=%s;A3=%s;A4=%s;A5=%s;A6=%s;A7=%d;A8=%d;A9=%d;B0=%d;B1=%d;B2=%d;B3=%d;B4=%d;B5=%d;B6=%d;B7=%d;B8=%d;B9=%d;C0=%d;C1=%d;C2=%s",
ci->onlyread,
ci->protocol,
ci->fake_oid_index,
ci->show_oid_column,
ci->row_versioning,
ci->show_system_tables,
encoded_conn_settings,
ci->drivers.fetch_max,
ci->drivers.socket_buffersize,
ci->drivers.unknown_sizes,
ci->drivers.max_varchar_size,
ci->drivers.max_longvarchar_size,
ci->drivers.debug,
ci->drivers.commlog,
ci->drivers.disable_optimizer,
ci->drivers.ksqo,
ci->drivers.use_declarefetch,
ci->drivers.text_as_longvarchar,
ci->drivers.unknowns_as_longvarchar,
ci->drivers.bools_as_char,
ci->drivers.parse,
ci->drivers.cancel_as_freestmt,
ci->drivers.extra_systable_prefixes);
}
void
copyAttributes(ConnInfo *ci, const char *attribute, const char *value)
{
if (stricmp(attribute, "DSN") == 0)
strcpy(ci->dsn, value);
else if (stricmp(attribute, "driver") == 0)
strcpy(ci->driver, value);
else if (stricmp(attribute, INI_DATABASE) == 0)
strcpy(ci->database, value);
else if (stricmp(attribute, INI_SERVER) == 0 || stricmp(attribute, "server") == 0)
strcpy(ci->server, value);
else if (stricmp(attribute, INI_USER) == 0 || stricmp(attribute, "uid") == 0)
strcpy(ci->username, value);
else if (stricmp(attribute, INI_PASSWORD) == 0 || stricmp(attribute, "pwd") == 0)
strcpy(ci->password, value);
else if (stricmp(attribute, INI_PORT) == 0)
strcpy(ci->port, value);
else if (stricmp(attribute, INI_READONLY) == 0 || stricmp(attribute, "A0") == 0)
strcpy(ci->onlyread, value);
else if (stricmp(attribute, INI_PROTOCOL) == 0 || stricmp(attribute, "A1") == 0)
strcpy(ci->protocol, value);
else if (stricmp(attribute, INI_SHOWOIDCOLUMN) == 0 || stricmp(attribute, "A3") == 0)
strcpy(ci->show_oid_column, value);
else if (stricmp(attribute, INI_FAKEOIDINDEX) == 0 || stricmp(attribute, "A2") == 0)
strcpy(ci->fake_oid_index, value);
else if (stricmp(attribute, INI_ROWVERSIONING) == 0 || stricmp(attribute, "A4") == 0)
strcpy(ci->row_versioning, value);
else if (stricmp(attribute, INI_SHOWSYSTEMTABLES) == 0 || stricmp(attribute, "A5") == 0)
strcpy(ci->show_system_tables, value);
else if (stricmp(attribute, INI_CONNSETTINGS) == 0 || stricmp(attribute, "A6") == 0)
{
decode(value, ci->conn_settings);
/* strcpy(ci->conn_settings, value); */
}
else if (stricmp(attribute, INI_DISALLOWPREMATURE) == 0 || stricmp(attribute, "C3") == 0)
ci->disallow_premature = atoi(value);
else if (stricmp(attribute, INI_UPDATABLECURSORS) == 0 || stricmp(attribute, "C4") == 0)
ci->updatable_cursors = atoi(value);
mylog("copyAttributes: DSN='%s',server='%s',dbase='%s',user='%s',passwd='%s',port='%s',onlyread='%s',protocol='%s',conn_settings='%s',disallow_premature=%d)\n", ci->dsn, ci->server, ci->database, ci->username, ci->password, ci->port, ci->onlyread, ci->protocol, ci->conn_settings, ci->disallow_premature);
}
void
copyCommonAttributes(ConnInfo *ci, const char *attribute, const char *value)
{
if (stricmp(attribute, INI_FETCH) == 0 || stricmp(attribute, "A7") == 0)
ci->drivers.fetch_max = atoi(value);
else if (stricmp(attribute, INI_SOCKET) == 0 || stricmp(attribute, "A8") == 0)
ci->drivers.socket_buffersize = atoi(value);
else if (stricmp(attribute, INI_DEBUG) == 0 || stricmp(attribute, "B2") == 0)
ci->drivers.debug = atoi(value);
else if (stricmp(attribute, INI_COMMLOG) == 0 || stricmp(attribute, "B3") == 0)
ci->drivers.commlog = atoi(value);
else if (stricmp(attribute, INI_OPTIMIZER) == 0 || stricmp(attribute, "B4") == 0)
ci->drivers.disable_optimizer = atoi(value);
else if (stricmp(attribute, INI_KSQO) == 0 || stricmp(attribute, "B5") == 0)
ci->drivers.ksqo = atoi(value);
/*
* else if (stricmp(attribute, INI_UNIQUEINDEX) == 0 ||
* stricmp(attribute, "UIX") == 0) ci->drivers.unique_index =
* atoi(value);
*/
else if (stricmp(attribute, INI_UNKNOWNSIZES) == 0 || stricmp(attribute, "A9") == 0)
ci->drivers.unknown_sizes = atoi(value);
else if (stricmp(attribute, INI_LIE) == 0)
ci->drivers.lie = atoi(value);
else if (stricmp(attribute, INI_PARSE) == 0 || stricmp(attribute, "C0") == 0)
ci->drivers.parse = atoi(value);
else if (stricmp(attribute, INI_CANCELASFREESTMT) == 0 || stricmp(attribute, "C1") == 0)
ci->drivers.cancel_as_freestmt = atoi(value);
else if (stricmp(attribute, INI_USEDECLAREFETCH) == 0 || stricmp(attribute, "B6") == 0)
ci->drivers.use_declarefetch = atoi(value);
else if (stricmp(attribute, INI_MAXVARCHARSIZE) == 0 || stricmp(attribute, "B0") == 0)
ci->drivers.max_varchar_size = atoi(value);
else if (stricmp(attribute, INI_MAXLONGVARCHARSIZE) == 0 || stricmp(attribute, "B1") == 0)
ci->drivers.max_longvarchar_size = atoi(value);
else if (stricmp(attribute, INI_TEXTASLONGVARCHAR) == 0 || stricmp(attribute, "B7") == 0)
ci->drivers.text_as_longvarchar = atoi(value);
else if (stricmp(attribute, INI_UNKNOWNSASLONGVARCHAR) == 0 || stricmp(attribute, "B8") == 0)
ci->drivers.unknowns_as_longvarchar = atoi(value);
else if (stricmp(attribute, INI_BOOLSASCHAR) == 0 || stricmp(attribute, "B9") == 0)
ci->drivers.bools_as_char = atoi(value);
else if (stricmp(attribute, INI_EXTRASYSTABLEPREFIXES) == 0 || stricmp(attribute, "C2") == 0)
strcpy(ci->drivers.extra_systable_prefixes, value);
mylog("CopyCommonAttributes: A7=%d;A8=%d;A9=%d;B0=%d;B1=%d;B2=%d;B3=%d;B4=%d;B5=%d;B6=%d;B7=%d;B8=%d;B9=%d;C0=%d;C1=%d;C2=%s",
ci->drivers.fetch_max,
ci->drivers.socket_buffersize,
ci->drivers.unknown_sizes,
ci->drivers.max_varchar_size,
ci->drivers.max_longvarchar_size,
ci->drivers.debug,
ci->drivers.commlog,
ci->drivers.disable_optimizer,
ci->drivers.ksqo,
ci->drivers.use_declarefetch,
ci->drivers.text_as_longvarchar,
ci->drivers.unknowns_as_longvarchar,
ci->drivers.bools_as_char,
ci->drivers.parse,
ci->drivers.cancel_as_freestmt,
ci->drivers.extra_systable_prefixes);
}
void
getDSNdefaults(ConnInfo *ci)
{
if (ci->port[0] == '\0')
strcpy(ci->port, DEFAULT_PORT);
if (ci->onlyread[0] == '\0')
sprintf(ci->onlyread, "%d", globals.onlyread);
if (ci->protocol[0] == '\0')
strcpy(ci->protocol, globals.protocol);
if (ci->fake_oid_index[0] == '\0')
sprintf(ci->fake_oid_index, "%d", DEFAULT_FAKEOIDINDEX);
if (ci->show_oid_column[0] == '\0')
sprintf(ci->show_oid_column, "%d", DEFAULT_SHOWOIDCOLUMN);
if (ci->show_system_tables[0] == '\0')
sprintf(ci->show_system_tables, "%d", DEFAULT_SHOWSYSTEMTABLES);
if (ci->row_versioning[0] == '\0')
sprintf(ci->row_versioning, "%d", DEFAULT_ROWVERSIONING);
}
void
getDSNinfo(ConnInfo *ci, char overwrite)
{
char *DSN = ci->dsn;
char encoded_conn_settings[LARGE_REGISTRY_LEN],
temp[SMALL_REGISTRY_LEN];
/*
* If a driver keyword was present, then dont use a DSN and return.
* If DSN is null and no driver, then use the default datasource.
*/
memcpy(&ci->drivers, &globals, sizeof(globals));
if (DSN[0] == '\0')
{
if (ci->driver[0] != '\0')
return;
else
strcpy(DSN, INI_DSN);
}
/* brute-force chop off trailing blanks... */
while (*(DSN + strlen(DSN) - 1) == ' ')
*(DSN + strlen(DSN) - 1) = '\0';
/* Proceed with getting info for the given DSN. */
if (ci->desc[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_KDESC, "", ci->desc, sizeof(ci->desc), ODBC_INI);
if (ci->server[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_SERVER, "", ci->server, sizeof(ci->server), ODBC_INI);
if (ci->database[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_DATABASE, "", ci->database, sizeof(ci->database), ODBC_INI);
if (ci->username[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_USER, "", ci->username, sizeof(ci->username), ODBC_INI);
if (ci->password[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_PASSWORD, "", ci->password, sizeof(ci->password), ODBC_INI);
if (ci->port[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_PORT, "", ci->port, sizeof(ci->port), ODBC_INI);
if (ci->onlyread[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_READONLY, "", ci->onlyread, sizeof(ci->onlyread), ODBC_INI);
if (ci->show_oid_column[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_SHOWOIDCOLUMN, "", ci->show_oid_column, sizeof(ci->show_oid_column), ODBC_INI);
if (ci->fake_oid_index[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_FAKEOIDINDEX, "", ci->fake_oid_index, sizeof(ci->fake_oid_index), ODBC_INI);
if (ci->row_versioning[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_ROWVERSIONING, "", ci->row_versioning, sizeof(ci->row_versioning), ODBC_INI);
if (ci->show_system_tables[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_SHOWSYSTEMTABLES, "", ci->show_system_tables, sizeof(ci->show_system_tables), ODBC_INI);
if (ci->protocol[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_PROTOCOL, "", ci->protocol, sizeof(ci->protocol), ODBC_INI);
if (ci->conn_settings[0] == '\0' || overwrite)
{
SQLGetPrivateProfileString(DSN, INI_CONNSETTINGS, "", encoded_conn_settings, sizeof(encoded_conn_settings), ODBC_INI);
decode(encoded_conn_settings, ci->conn_settings);
}
if (ci->translation_dll[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_TRANSLATIONDLL, "", ci->translation_dll, sizeof(ci->translation_dll), ODBC_INI);
if (ci->translation_option[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_TRANSLATIONOPTION, "", ci->translation_option, sizeof(ci->translation_option), ODBC_INI);
if (ci->disallow_premature == 0 || overwrite)
{
SQLGetPrivateProfileString(DSN, INI_DISALLOWPREMATURE, "", temp, sizeof(temp), ODBC_INI);
ci->disallow_premature = atoi(temp);
}
if (ci->updatable_cursors == 0 || overwrite)
{
SQLGetPrivateProfileString(DSN, INI_UPDATABLECURSORS, "", temp, sizeof(temp), ODBC_INI);
ci->updatable_cursors = atoi(temp);
}
/* Allow override of odbcinst.ini parameters here */
getCommonDefaults(DSN, ODBC_INI, ci);
qlog("DSN info: DSN='%s',server='%s',port='%s',dbase='%s',user='%s',passwd='%s'\n",
DSN,
ci->server,
ci->port,
ci->database,
ci->username,
ci->password);
qlog(" onlyread='%s',protocol='%s',showoid='%s',fakeoidindex='%s',showsystable='%s'\n",
ci->onlyread,
ci->protocol,
ci->show_oid_column,
ci->fake_oid_index,
ci->show_system_tables);
#ifdef MULTIBYTE
check_client_encoding(ci->conn_settings);
qlog(" conn_settings='%s',conn_encoding='%s'\n",
ci->conn_settings,
check_client_encoding(ci->conn_settings));
#else
qlog(" conn_settings='%s'\n",
ci->conn_settings);
#endif
qlog(" translation_dll='%s',translation_option='%s'\n",
ci->translation_dll,
ci->translation_option);
}
/* This is for datasource based options only */
void
writeDSNinfo(const ConnInfo *ci)
{
const char *DSN = ci->dsn;
char encoded_conn_settings[LARGE_REGISTRY_LEN],
temp[SMALL_REGISTRY_LEN];
encode(ci->conn_settings, encoded_conn_settings);
SQLWritePrivateProfileString(DSN,
INI_KDESC,
ci->desc,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_DATABASE,
ci->database,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_SERVER,
ci->server,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_PORT,
ci->port,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_USER,
ci->username,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_PASSWORD,
ci->password,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_READONLY,
ci->onlyread,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_SHOWOIDCOLUMN,
ci->show_oid_column,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_FAKEOIDINDEX,
ci->fake_oid_index,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_ROWVERSIONING,
ci->row_versioning,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_SHOWSYSTEMTABLES,
ci->show_system_tables,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_PROTOCOL,
ci->protocol,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_CONNSETTINGS,
encoded_conn_settings,
ODBC_INI);
sprintf(temp, "%d", ci->disallow_premature);
SQLWritePrivateProfileString(DSN,
INI_DISALLOWPREMATURE,
temp,
ODBC_INI);
sprintf(temp, "%d", ci->updatable_cursors);
SQLWritePrivateProfileString(DSN,
INI_UPDATABLECURSORS,
temp,
ODBC_INI);
}
/*
* This function reads the ODBCINST.INI portion of
* the registry and gets any driver defaults.
*/
void
getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
{
char temp[256];
GLOBAL_VALUES *comval;
if (ci)
comval = &(ci->drivers);
else
comval = &globals;
/* Fetch Count is stored in driver section */
SQLGetPrivateProfileString(section, INI_FETCH, "",
temp, sizeof(temp), filename);
if (temp[0])
{
comval->fetch_max = atoi(temp);
/* sanity check if using cursors */
if (comval->fetch_max <= 0)
comval->fetch_max = FETCH_MAX;
}
else if (!ci)
comval->fetch_max = FETCH_MAX;
/* Socket Buffersize is stored in driver section */
SQLGetPrivateProfileString(section, INI_SOCKET, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->socket_buffersize = atoi(temp);
else if (!ci)
comval->socket_buffersize = SOCK_BUFFER_SIZE;
/* Debug is stored in the driver section */
SQLGetPrivateProfileString(section, INI_DEBUG, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->debug = atoi(temp);
else if (!ci)
comval->debug = DEFAULT_DEBUG;
/* CommLog is stored in the driver section */
SQLGetPrivateProfileString(section, INI_COMMLOG, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->commlog = atoi(temp);
else if (!ci)
comval->commlog = DEFAULT_COMMLOG;
if (!ci)
logs_on_off(0, 0, 0);
/* Optimizer is stored in the driver section only */
SQLGetPrivateProfileString(section, INI_OPTIMIZER, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->disable_optimizer = atoi(temp);
else if (!ci)
comval->disable_optimizer = DEFAULT_OPTIMIZER;
/* KSQO is stored in the driver section only */
SQLGetPrivateProfileString(section, INI_KSQO, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->ksqo = atoi(temp);
else if (!ci)
comval->ksqo = DEFAULT_KSQO;
/* Recognize Unique Index is stored in the driver section only */
SQLGetPrivateProfileString(section, INI_UNIQUEINDEX, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->unique_index = atoi(temp);
else if (!ci)
comval->unique_index = DEFAULT_UNIQUEINDEX;
/* Unknown Sizes is stored in the driver section only */
SQLGetPrivateProfileString(section, INI_UNKNOWNSIZES, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->unknown_sizes = atoi(temp);
else if (!ci)
comval->unknown_sizes = DEFAULT_UNKNOWNSIZES;
/* Lie about supported functions? */
SQLGetPrivateProfileString(section, INI_LIE, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->lie = atoi(temp);
else if (!ci)
comval->lie = DEFAULT_LIE;
/* Parse statements */
SQLGetPrivateProfileString(section, INI_PARSE, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->parse = atoi(temp);
else if (!ci)
comval->parse = DEFAULT_PARSE;
/* SQLCancel calls SQLFreeStmt in Driver Manager */
SQLGetPrivateProfileString(section, INI_CANCELASFREESTMT, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->cancel_as_freestmt = atoi(temp);
else if (!ci)
comval->cancel_as_freestmt = DEFAULT_CANCELASFREESTMT;
/* UseDeclareFetch is stored in the driver section only */
SQLGetPrivateProfileString(section, INI_USEDECLAREFETCH, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->use_declarefetch = atoi(temp);
else if (!ci)
comval->use_declarefetch = DEFAULT_USEDECLAREFETCH;
/* Max Varchar Size */
SQLGetPrivateProfileString(section, INI_MAXVARCHARSIZE, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->max_varchar_size = atoi(temp);
else if (!ci)
comval->max_varchar_size = MAX_VARCHAR_SIZE;
/* Max TextField Size */
SQLGetPrivateProfileString(section, INI_MAXLONGVARCHARSIZE, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->max_longvarchar_size = atoi(temp);
else if (!ci)
comval->max_longvarchar_size = TEXT_FIELD_SIZE;
/* Text As LongVarchar */
SQLGetPrivateProfileString(section, INI_TEXTASLONGVARCHAR, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->text_as_longvarchar = atoi(temp);
else if (!ci)
comval->text_as_longvarchar = DEFAULT_TEXTASLONGVARCHAR;
/* Unknowns As LongVarchar */
SQLGetPrivateProfileString(section, INI_UNKNOWNSASLONGVARCHAR, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->unknowns_as_longvarchar = atoi(temp);
else if (!ci)
comval->unknowns_as_longvarchar = DEFAULT_UNKNOWNSASLONGVARCHAR;
/* Bools As Char */
SQLGetPrivateProfileString(section, INI_BOOLSASCHAR, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->bools_as_char = atoi(temp);
else if (!ci)
comval->bools_as_char = DEFAULT_BOOLSASCHAR;
/* Extra Systable prefixes */
/*
* Use @@@ to distinguish between blank extra prefixes and no key
* entry
*/
SQLGetPrivateProfileString(section, INI_EXTRASYSTABLEPREFIXES, "@@@",
temp, sizeof(temp), filename);
if (strcmp(temp, "@@@"))
strcpy(comval->extra_systable_prefixes, temp);
else if (!ci)
strcpy(comval->extra_systable_prefixes, DEFAULT_EXTRASYSTABLEPREFIXES);
mylog("globals.extra_systable_prefixes = '%s'\n", comval->extra_systable_prefixes);
/* Dont allow override of an override! */
if (!ci)
{
/*
* ConnSettings is stored in the driver section and per datasource
* for override
*/
SQLGetPrivateProfileString(section, INI_CONNSETTINGS, "",
comval->conn_settings, sizeof(comval->conn_settings), filename);
/* Default state for future DSN's Readonly attribute */
SQLGetPrivateProfileString(section, INI_READONLY, "",
temp, sizeof(temp), filename);
if (temp[0])
comval->onlyread = atoi(temp);
else
comval->onlyread = DEFAULT_READONLY;
/*
* Default state for future DSN's protocol attribute This isn't a
* real driver option YET. This is more intended for
* customization from the install.
*/
SQLGetPrivateProfileString(section, INI_PROTOCOL, "@@@",
temp, sizeof(temp), filename);
if (strcmp(temp, "@@@"))
strcpy(comval->protocol, temp);
else
strcpy(comval->protocol, DEFAULT_PROTOCOL);
}
}
/* File: dlg_specific.h
*
* Description: See "dlg_specific.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __DLG_SPECIFIC_H__
#define __DLG_SPECIFIC_H__
#include "psqlodbc.h"
#include "connection.h"
#ifdef WIN32
#include <windowsx.h>
#include "resource.h"
#endif
/* Unknown data type sizes */
#define UNKNOWNS_AS_MAX 0
#define UNKNOWNS_AS_DONTKNOW 1
#define UNKNOWNS_AS_LONGEST 2
/* ODBC initialization files */
#ifndef WIN32
#define ODBC_INI ".odbc.ini"
#define ODBCINST_INI "odbcinst.ini"
#else
#define ODBC_INI "ODBC.INI"
#define ODBCINST_INI "ODBCINST.INI"
#endif
#define INI_DSN DBMS_NAME /* Name of default
* Datasource in ini
* file (not used?) */
#define INI_KDESC "Description" /* Data source
* description */
#define INI_SERVER "Servername" /* Name of Server
* running the Postgres
* service */
#define INI_PORT "Port" /* Port on which the
* Postmaster is listening */
#define INI_DATABASE "Database" /* Database Name */
#define INI_USER "Username" /* Default User Name */
#define INI_PASSWORD "Password" /* Default Password */
#define INI_DEBUG "Debug" /* Debug flag */
#define INI_FETCH "Fetch" /* Fetch Max Count */
#define INI_SOCKET "Socket" /* Socket buffer size */
#define INI_READONLY "ReadOnly" /* Database is read only */
#define INI_COMMLOG "CommLog" /* Communication to
* backend logging */
#define INI_PROTOCOL "Protocol" /* What protocol (6.2) */
#define INI_OPTIMIZER "Optimizer" /* Use backend genetic
* optimizer */
#define INI_KSQO "Ksqo" /* Keyset query
* optimization */
#define INI_CONNSETTINGS "ConnSettings" /* Anything to send to
* backend on successful
* connection */
#define INI_UNIQUEINDEX "UniqueIndex" /* Recognize unique
* indexes */
#define INI_UNKNOWNSIZES "UnknownSizes" /* How to handle unknown
* result set sizes */
#define INI_CANCELASFREESTMT "CancelAsFreeStmt"
#define INI_USEDECLAREFETCH "UseDeclareFetch" /* Use Declare/Fetch
* cursors */
/* More ini stuff */
#define INI_TEXTASLONGVARCHAR "TextAsLongVarchar"
#define INI_UNKNOWNSASLONGVARCHAR "UnknownsAsLongVarchar"
#define INI_BOOLSASCHAR "BoolsAsChar"
#define INI_MAXVARCHARSIZE "MaxVarcharSize"
#define INI_MAXLONGVARCHARSIZE "MaxLongVarcharSize"
#define INI_FAKEOIDINDEX "FakeOidIndex"
#define INI_SHOWOIDCOLUMN "ShowOidColumn"
#define INI_ROWVERSIONING "RowVersioning"
#define INI_SHOWSYSTEMTABLES "ShowSystemTables"
#define INI_LIE "Lie"
#define INI_PARSE "Parse"
#define INI_EXTRASYSTABLEPREFIXES "ExtraSysTablePrefixes"
#define INI_TRANSLATIONNAME "TranslationName"
#define INI_TRANSLATIONDLL "TranslationDLL"
#define INI_TRANSLATIONOPTION "TranslationOption"
#define INI_DISALLOWPREMATURE "DisallowPremature"
#define INI_UPDATABLECURSORS "UpdatableCursors"
/* Connection Defaults */
#define DEFAULT_PORT "5432"
#define DEFAULT_READONLY 0
#define DEFAULT_PROTOCOL "6.4" /* the latest protocol is
* the default */
#define DEFAULT_USEDECLAREFETCH 0
#define DEFAULT_TEXTASLONGVARCHAR 1
#define DEFAULT_UNKNOWNSASLONGVARCHAR 0
#define DEFAULT_BOOLSASCHAR 1
#define DEFAULT_OPTIMIZER 1 /* disable */
#define DEFAULT_KSQO 1 /* on */
#define DEFAULT_UNIQUEINDEX 1 /* dont recognize */
#define DEFAULT_COMMLOG 0 /* dont log */
#define DEFAULT_DEBUG 0
#define DEFAULT_UNKNOWNSIZES UNKNOWNS_AS_MAX
#define DEFAULT_FAKEOIDINDEX 0
#define DEFAULT_SHOWOIDCOLUMN 0
#define DEFAULT_ROWVERSIONING 0
#define DEFAULT_SHOWSYSTEMTABLES 0 /* dont show system tables */
#define DEFAULT_LIE 0
#define DEFAULT_PARSE 0
#define DEFAULT_CANCELASFREESTMT 0
#define DEFAULT_EXTRASYSTABLEPREFIXES "dd_;"
/* prototypes */
void getCommonDefaults(const char *section, const char *filename, ConnInfo *ci);
#ifdef WIN32
void SetDlgStuff(HWND hdlg, const ConnInfo *ci);
void GetDlgStuff(HWND hdlg, ConnInfo *ci);
int CALLBACK driver_optionsProc(HWND hdlg,
WORD wMsg,
WPARAM wParam,
LPARAM lParam);
int CALLBACK ds_optionsProc(HWND hdlg,
WORD wMsg,
WPARAM wParam,
LPARAM lParam);
#endif /* WIN32 */
void updateGlobals(void);
void writeDSNinfo(const ConnInfo *ci);
void getDSNdefaults(ConnInfo *ci);
void getDSNinfo(ConnInfo *ci, char overwrite);
void makeConnectString(char *connect_string, const ConnInfo *ci, UWORD);
void copyAttributes(ConnInfo *ci, const char *attribute, const char *value);
void copyCommonAttributes(ConnInfo *ci, const char *attribute, const char *value);
#endif
/*-------
Module: drvconn.c
*
* Description: This module contains only routines related to
* implementing SQLDriverConnect.
*
* Classes: n/a
*
* API functions: SQLDriverConnect
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "psqlodbc.h"
#include <stdio.h>
#include <stdlib.h>
#include "connection.h"
#ifndef WIN32
#include <sys/types.h>
#include <sys/socket.h>
#define NEAR
#else
#include <winsock.h>
#endif
#include <string.h>
#ifdef WIN32
#include <windowsx.h>
#include "resource.h"
#endif
#include "pgapifunc.h"
#ifndef TRUE
#define TRUE (BOOL)1
#endif
#ifndef FALSE
#define FALSE (BOOL)0
#endif
#include "dlg_specific.h"
/* prototypes */
void dconn_get_connect_attributes(const UCHAR FAR * connect_string, ConnInfo *ci);
static void dconn_get_common_attributes(const UCHAR FAR * connect_string, ConnInfo *ci);
#ifdef WIN32
BOOL FAR PASCAL dconn_FDriverConnectProc(HWND hdlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
RETCODE dconn_DoDialog(HWND hwnd, ConnInfo *ci);
extern HINSTANCE NEAR s_hModule; /* Saved module handle. */
#endif
RETCODE SQL_API
PGAPI_DriverConnect(
HDBC hdbc,
HWND hwnd,
UCHAR FAR * szConnStrIn,
SWORD cbConnStrIn,
UCHAR FAR * szConnStrOut,
SWORD cbConnStrOutMax,
SWORD FAR * pcbConnStrOut,
UWORD fDriverCompletion)
{
static char *func = "PGAPI_DriverConnect";
ConnectionClass *conn = (ConnectionClass *) hdbc;
ConnInfo *ci;
#ifdef WIN32
RETCODE dialog_result;
#endif
RETCODE result;
char connStrIn[MAX_CONNECT_STRING];
char connStrOut[MAX_CONNECT_STRING];
int retval;
char password_required = FALSE;
int len = 0;
SWORD lenStrout;
mylog("%s: entering...\n", func);
if (!conn)
{
CC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
make_string(szConnStrIn, cbConnStrIn, connStrIn);
mylog("**** PGAPI_DriverConnect: fDriverCompletion=%d, connStrIn='%s'\n", fDriverCompletion, connStrIn);
qlog("conn=%u, PGAPI_DriverConnect( in)='%s', fDriverCompletion=%d\n", conn, connStrIn, fDriverCompletion);
ci = &(conn->connInfo);
/* Parse the connect string and fill in conninfo for this hdbc. */
dconn_get_connect_attributes(connStrIn, ci);
/*
* If the ConnInfo in the hdbc is missing anything, this function will
* fill them in from the registry (assuming of course there is a DSN
* given -- if not, it does nothing!)
*/
getDSNinfo(ci, CONN_DONT_OVERWRITE);
dconn_get_common_attributes(connStrIn, ci);
logs_on_off(1, ci->drivers.debug, ci->drivers.commlog);
/* Fill in any default parameters if they are not there. */
getDSNdefaults(ci);
/* initialize pg_version */
CC_initialize_pg_version(conn);
#ifdef WIN32
dialog:
#endif
ci->focus_password = password_required;
switch (fDriverCompletion)
{
#ifdef WIN32
case SQL_DRIVER_PROMPT:
dialog_result = dconn_DoDialog(hwnd, ci);
if (dialog_result != SQL_SUCCESS)
return dialog_result;
break;
case SQL_DRIVER_COMPLETE_REQUIRED:
/* Fall through */
case SQL_DRIVER_COMPLETE:
/* Password is not a required parameter. */
if (ci->username[0] == '\0' ||
ci->server[0] == '\0' ||
ci->database[0] == '\0' ||
ci->port[0] == '\0' ||
password_required)
{
dialog_result = dconn_DoDialog(hwnd, ci);
if (dialog_result != SQL_SUCCESS)
return dialog_result;
}
break;
#else
case SQL_DRIVER_PROMPT:
case SQL_DRIVER_COMPLETE:
case SQL_DRIVER_COMPLETE_REQUIRED:
#endif
case SQL_DRIVER_NOPROMPT:
break;
}
/*
* Password is not a required parameter unless authentication asks for
* it. For now, I think it's better to just let the application ask
* over and over until a password is entered (the user can always hit
* Cancel to get out)
*/
if (ci->username[0] == '\0' ||
ci->server[0] == '\0' ||
ci->database[0] == '\0' ||
ci->port[0] == '\0')
{
/* (password_required && ci->password[0] == '\0')) */
return SQL_NO_DATA_FOUND;
}
/* do the actual connect */
retval = CC_connect(conn, password_required);
if (retval < 0)
{ /* need a password */
if (fDriverCompletion == SQL_DRIVER_NOPROMPT)
{
CC_log_error(func, "Need password but Driver_NoPrompt", conn);
return SQL_ERROR; /* need a password but not allowed to
* prompt so error */
}
else
{
#ifdef WIN32
password_required = TRUE;
goto dialog;
#else
return SQL_ERROR; /* until a better solution is found. */
#endif
}
}
else if (retval == 0)
{
/* error msg filled in above */
CC_log_error(func, "Error from CC_Connect", conn);
return SQL_ERROR;
}
/*
* Create the Output Connection String
*/
result = SQL_SUCCESS;
lenStrout = cbConnStrOutMax;
if (conn->ms_jet && lenStrout > 255)
lenStrout = 255;
makeConnectString(connStrOut, ci, lenStrout);
len = strlen(connStrOut);
if (szConnStrOut)
{
/*
* Return the completed string to the caller. The correct method
* is to only construct the connect string if a dialog was put up,
* otherwise, it should just copy the connection input string to
* the output. However, it seems ok to just always construct an
* output string. There are possible bad side effects on working
* applications (Access) by implementing the correct behavior,
* anyway.
*/
strncpy_null(szConnStrOut, connStrOut, cbConnStrOutMax);
if (len >= cbConnStrOutMax)
{
int clen;
for (clen = strlen(szConnStrOut) - 1; clen >= 0 && szConnStrOut[clen] != ';'; clen--)
szConnStrOut[clen] = '\0';
result = SQL_SUCCESS_WITH_INFO;
conn->errornumber = CONN_TRUNCATED;
conn->errormsg = "The buffer was too small for the ConnStrOut.";
}
}
if (pcbConnStrOut)
*pcbConnStrOut = len;
mylog("szConnStrOut = '%s' len=%d,%d\n", szConnStrOut, len, cbConnStrOutMax);
qlog("conn=%u, PGAPI_DriverConnect(out)='%s'\n", conn, szConnStrOut);
mylog("PGAPI_DRiverConnect: returning %d\n", result);
return result;
}
#ifdef WIN32
RETCODE
dconn_DoDialog(HWND hwnd, ConnInfo *ci)
{
int dialog_result;
mylog("dconn_DoDialog: ci = %u\n", ci);
if (hwnd)
{
dialog_result = DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_CONFIG),
hwnd, dconn_FDriverConnectProc, (LPARAM) ci);
if (!dialog_result || (dialog_result == -1))
return SQL_NO_DATA_FOUND;
else
return SQL_SUCCESS;
}
return SQL_ERROR;
}
BOOL FAR PASCAL
dconn_FDriverConnectProc(
HWND hdlg,
UINT wMsg,
WPARAM wParam,
LPARAM lParam)
{
ConnInfo *ci;
switch (wMsg)
{
case WM_INITDIALOG:
ci = (ConnInfo *) lParam;
/* Change the caption for the setup dialog */
SetWindowText(hdlg, "PostgreSQL Connection");
SetWindowText(GetDlgItem(hdlg, IDC_DATASOURCE), "Connection");
/* Hide the DSN and description fields */
ShowWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), SW_HIDE);
ShowWindow(GetDlgItem(hdlg, IDC_DSNAME), SW_HIDE);
ShowWindow(GetDlgItem(hdlg, IDC_DESCTEXT), SW_HIDE);
ShowWindow(GetDlgItem(hdlg, IDC_DESC), SW_HIDE);
SetWindowLong(hdlg, DWL_USER, lParam); /* Save the ConnInfo for
* the "OK" */
SetDlgStuff(hdlg, ci);
if (ci->database[0] == '\0')
; /* default focus */
else if (ci->server[0] == '\0')
SetFocus(GetDlgItem(hdlg, IDC_SERVER));
else if (ci->port[0] == '\0')
SetFocus(GetDlgItem(hdlg, IDC_PORT));
else if (ci->username[0] == '\0')
SetFocus(GetDlgItem(hdlg, IDC_USER));
else if (ci->focus_password)
SetFocus(GetDlgItem(hdlg, IDC_PASSWORD));
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
GetDlgStuff(hdlg, ci);
case IDCANCEL:
EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
return TRUE;
case IDC_DRIVER:
ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DRV),
hdlg, driver_optionsProc, (LPARAM) ci);
break;
case IDC_DATASOURCE:
ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DS),
hdlg, ds_optionsProc, (LPARAM) ci);
break;
}
}
return FALSE;
}
#endif /* WIN32 */
void
dconn_get_connect_attributes(const UCHAR FAR * connect_string, ConnInfo *ci)
{
char *our_connect_string;
char *pair,
*attribute,
*value,
*equals;
char *strtok_arg;
memset(ci, 0, sizeof(ConnInfo));
#ifdef DRIVER_CURSOR_IMPLEMENT
ci->updatable_cursors = 1;
#endif /* DRIVER_CURSOR_IMPLEMENT */
our_connect_string = strdup(connect_string);
strtok_arg = our_connect_string;
mylog("our_connect_string = '%s'\n", our_connect_string);
while (1)
{
pair = strtok(strtok_arg, ";");
if (strtok_arg)
strtok_arg = 0;
if (!pair)
break;
equals = strchr(pair, '=');
if (!equals)
continue;
*equals = '\0';
attribute = pair; /* ex. DSN */
value = equals + 1; /* ex. 'CEO co1' */
mylog("attribute = '%s', value = '%s'\n", attribute, value);
if (!attribute || !value)
continue;
/* Copy the appropriate value to the conninfo */
copyAttributes(ci, attribute, value);
}
free(our_connect_string);
}
static void
dconn_get_common_attributes(const UCHAR FAR * connect_string, ConnInfo *ci)
{
char *our_connect_string;
char *pair,
*attribute,
*value,
*equals;
char *strtok_arg;
our_connect_string = strdup(connect_string);
strtok_arg = our_connect_string;
mylog("our_connect_string = '%s'\n", our_connect_string);
while (1)
{
pair = strtok(strtok_arg, ";");
if (strtok_arg)
strtok_arg = 0;
if (!pair)
break;
equals = strchr(pair, '=');
if (!equals)
continue;
*equals = '\0';
attribute = pair; /* ex. DSN */
value = equals + 1; /* ex. 'CEO co1' */
mylog("attribute = '%s', value = '%s'\n", attribute, value);
if (!attribute || !value)
continue;
/* Copy the appropriate value to the conninfo */
copyCommonAttributes(ci, attribute, value);
}
free(our_connect_string);
}
/*-------
* Module: environ.c
*
* Description: This module contains routines related to
* the environment, such as storing connection handles,
* and returning errors.
*
* Classes: EnvironmentClass (Functions prefix: "EN_")
*
* API functions: SQLAllocEnv, SQLFreeEnv, SQLError
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "environ.h"
#include "connection.h"
#include "dlg_specific.h"
#include "statement.h"
#include <stdlib.h>
#include <string.h>
#include "pgapifunc.h"
extern GLOBAL_VALUES globals;
/* The one instance of the handles */
ConnectionClass *conns[MAX_CONNECTIONS];
RETCODE SQL_API
PGAPI_AllocEnv(HENV FAR * phenv)
{
static char *func = "PGAPI_AllocEnv";
mylog("**** in PGAPI_AllocEnv ** \n");
/*
* Hack for systems on which none of the constructor-making techniques
* in psqlodbc.c work: if globals appears not to have been
* initialized, then cause it to be initialized. Since this should be
* the first function called in this shared library, doing it here
* should work.
*/
if (globals.socket_buffersize <= 0)
getCommonDefaults(DBMS_NAME, ODBCINST_INI, NULL);
*phenv = (HENV) EN_Constructor();
if (!*phenv)
{
*phenv = SQL_NULL_HENV;
EN_log_error(func, "Error allocating environment", NULL);
return SQL_ERROR;
}
mylog("** exit PGAPI_AllocEnv: phenv = %u **\n", *phenv);
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_FreeEnv(HENV henv)
{
static char *func = "PGAPI_FreeEnv";
EnvironmentClass *env = (EnvironmentClass *) henv;
mylog("**** in PGAPI_FreeEnv: env = %u ** \n", env);
if (env && EN_Destructor(env))
{
mylog(" ok\n");
return SQL_SUCCESS;
}
mylog(" error\n");
EN_log_error(func, "Error freeing environment", env);
return SQL_ERROR;
}
/* Returns the next SQL error information. */
RETCODE SQL_API
PGAPI_Error(
HENV henv,
HDBC hdbc,
HSTMT hstmt,
UCHAR FAR * szSqlState,
SDWORD FAR * pfNativeError,
UCHAR FAR * szErrorMsg,
SWORD cbErrorMsgMax,
SWORD FAR * pcbErrorMsg)
{
char *msg;
int status;
BOOL once_again = FALSE;
SWORD msglen;
mylog("**** PGAPI_Error: henv=%u, hdbc=%u, hstmt=%u <%d>\n", henv, hdbc, hstmt, cbErrorMsgMax);
if (cbErrorMsgMax < 0)
return SQL_ERROR;
if (SQL_NULL_HSTMT != hstmt)
{
/* CC: return an error of a hstmt */
StatementClass *stmt = (StatementClass *) hstmt;
if (SC_get_error(stmt, &status, &msg))
{
mylog("SC_get_error: status = %d, msg = #%s#\n", status, msg);
if (NULL == msg)
{
if (NULL != szSqlState)
strcpy(szSqlState, "00000");
if (NULL != pcbErrorMsg)
*pcbErrorMsg = 0;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
szErrorMsg[0] = '\0';
return SQL_NO_DATA_FOUND;
}
msglen = (SWORD) strlen(msg);
if (NULL != pcbErrorMsg)
{
*pcbErrorMsg = msglen;
if (cbErrorMsgMax == 0)
once_again = TRUE;
else if (msglen >= cbErrorMsgMax)
{
once_again = TRUE;
*pcbErrorMsg = cbErrorMsgMax - 1;
}
}
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
strncpy_null(szErrorMsg, msg, cbErrorMsgMax);
if (NULL != pfNativeError)
*pfNativeError = status;
if (NULL != szSqlState)
switch (status)
{
/* now determine the SQLSTATE to be returned */
case STMT_ROW_VERSION_CHANGED:
strcpy(szSqlState, "01001");
/* data truncated */
break;
case STMT_TRUNCATED:
strcpy(szSqlState, "01004");
/* data truncated */
break;
case STMT_INFO_ONLY:
strcpy(szSqlState, "00000");
/* just information that is returned, no error */
break;
case STMT_BAD_ERROR:
strcpy(szSqlState, "08S01");
/* communication link failure */
break;
case STMT_CREATE_TABLE_ERROR:
strcpy(szSqlState, "S0001");
/* table already exists */
break;
case STMT_STATUS_ERROR:
case STMT_SEQUENCE_ERROR:
strcpy(szSqlState, "S1010");
/* Function sequence error */
break;
case STMT_NO_MEMORY_ERROR:
strcpy(szSqlState, "S1001");
/* memory allocation failure */
break;
case STMT_COLNUM_ERROR:
strcpy(szSqlState, "S1002");
/* invalid column number */
break;
case STMT_NO_STMTSTRING:
strcpy(szSqlState, "S1001");
/* having no stmtstring is also a malloc problem */
break;
case STMT_ERROR_TAKEN_FROM_BACKEND:
strcpy(szSqlState, "S1000");
/* general error */
break;
case STMT_INTERNAL_ERROR:
strcpy(szSqlState, "S1000");
/* general error */
break;
case STMT_ROW_OUT_OF_RANGE:
strcpy(szSqlState, "S1107");
break;
case STMT_OPERATION_CANCELLED:
strcpy(szSqlState, "S1008");
break;
case STMT_NOT_IMPLEMENTED_ERROR:
strcpy(szSqlState, "S1C00"); /* == 'driver not
* capable' */
break;
case STMT_OPTION_OUT_OF_RANGE_ERROR:
strcpy(szSqlState, "S1092");
break;
case STMT_BAD_PARAMETER_NUMBER_ERROR:
strcpy(szSqlState, "S1093");
break;
case STMT_INVALID_COLUMN_NUMBER_ERROR:
strcpy(szSqlState, "S1002");
break;
case STMT_RESTRICTED_DATA_TYPE_ERROR:
strcpy(szSqlState, "07006");
break;
case STMT_INVALID_CURSOR_STATE_ERROR:
strcpy(szSqlState, "24000");
break;
case STMT_OPTION_VALUE_CHANGED:
strcpy(szSqlState, "01S02");
break;
case STMT_POS_BEFORE_RECORDSET:
strcpy(szSqlState, "01S06");
break;
case STMT_INVALID_CURSOR_NAME:
strcpy(szSqlState, "34000");
break;
case STMT_NO_CURSOR_NAME:
strcpy(szSqlState, "S1015");
break;
case STMT_INVALID_ARGUMENT_NO:
strcpy(szSqlState, "S1009");
/* invalid argument value */
break;
case STMT_INVALID_CURSOR_POSITION:
strcpy(szSqlState, "S1109");
break;
case STMT_VALUE_OUT_OF_RANGE:
strcpy(szSqlState, "22003");
break;
case STMT_OPERATION_INVALID:
strcpy(szSqlState, "S1011");
break;
case STMT_INVALID_OPTION_IDENTIFIER:
strcpy(szSqlState, "HY092");
break;
case STMT_EXEC_ERROR:
default:
strcpy(szSqlState, "S1000");
/* also a general error */
break;
}
mylog(" szSqlState = '%s', szError='%s'\n", szSqlState, szErrorMsg);
}
else
{
if (NULL != szSqlState)
strcpy(szSqlState, "00000");
if (NULL != pcbErrorMsg)
*pcbErrorMsg = 0;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
szErrorMsg[0] = '\0';
mylog(" returning NO_DATA_FOUND\n");
return SQL_NO_DATA_FOUND;
}
if (once_again)
{
int outlen;
stmt->errornumber = status;
if (cbErrorMsgMax > 0)
outlen = *pcbErrorMsg;
else
outlen = 0;
if (!stmt->errormsg_malloced || !stmt->errormsg)
{
stmt->errormsg = malloc(msglen - outlen + 1);
stmt->errormsg_malloced = TRUE;
}
memmove(stmt->errormsg, msg + outlen, msglen - outlen + 1);
}
else if (stmt->errormsg_malloced)
SC_clear_error(stmt);
if (cbErrorMsgMax == 0)
return SQL_SUCCESS_WITH_INFO;
else
return SQL_SUCCESS;
}
else if (SQL_NULL_HDBC != hdbc)
{
ConnectionClass *conn = (ConnectionClass *) hdbc;
mylog("calling CC_get_error\n");
if (CC_get_error(conn, &status, &msg))
{
mylog("CC_get_error: status = %d, msg = #%s#\n", status, msg);
if (NULL == msg)
{
if (NULL != szSqlState)
strcpy(szSqlState, "00000");
if (NULL != pcbErrorMsg)
*pcbErrorMsg = 0;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
szErrorMsg[0] = '\0';
return SQL_NO_DATA_FOUND;
}
msglen = strlen(msg);
if (NULL != pcbErrorMsg)
{
*pcbErrorMsg = msglen;
if (cbErrorMsgMax == 0)
once_again = TRUE;
else if (msglen >= cbErrorMsgMax)
*pcbErrorMsg = cbErrorMsgMax - 1;
}
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
strncpy_null(szErrorMsg, msg, cbErrorMsgMax);
if (NULL != pfNativeError)
*pfNativeError = status;
if (NULL != szSqlState)
switch (status)
{
case STMT_OPTION_VALUE_CHANGED:
case CONN_OPTION_VALUE_CHANGED:
strcpy(szSqlState, "01S02");
break;
case STMT_TRUNCATED:
case CONN_TRUNCATED:
strcpy(szSqlState, "01004");
/* data truncated */
break;
case CONN_INIREAD_ERROR:
strcpy(szSqlState, "IM002");
/* data source not found */
break;
case CONN_OPENDB_ERROR:
strcpy(szSqlState, "08001");
/* unable to connect to data source */
break;
case CONN_INVALID_AUTHENTICATION:
case CONN_AUTH_TYPE_UNSUPPORTED:
strcpy(szSqlState, "28000");
break;
case CONN_STMT_ALLOC_ERROR:
strcpy(szSqlState, "S1001");
/* memory allocation failure */
break;
case CONN_IN_USE:
strcpy(szSqlState, "S1000");
/* general error */
break;
case CONN_UNSUPPORTED_OPTION:
strcpy(szSqlState, "IM001");
/* driver does not support this function */
case CONN_INVALID_ARGUMENT_NO:
strcpy(szSqlState, "S1009");
/* invalid argument value */
break;
case CONN_TRANSACT_IN_PROGRES:
strcpy(szSqlState, "S1010");
/*
* when the user tries to switch commit mode in a
* transaction
*/
/* -> function sequence error */
break;
case CONN_NO_MEMORY_ERROR:
strcpy(szSqlState, "S1001");
break;
case CONN_NOT_IMPLEMENTED_ERROR:
case STMT_NOT_IMPLEMENTED_ERROR:
strcpy(szSqlState, "S1C00");
break;
case CONN_VALUE_OUT_OF_RANGE:
case STMT_VALUE_OUT_OF_RANGE:
strcpy(szSqlState, "22003");
break;
default:
strcpy(szSqlState, "S1000");
/* general error */
break;
}
}
else
{
mylog("CC_Get_error returned nothing.\n");
if (NULL != szSqlState)
strcpy(szSqlState, "00000");
if (NULL != pcbErrorMsg)
*pcbErrorMsg = 0;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
szErrorMsg[0] = '\0';
return SQL_NO_DATA_FOUND;
}
if (once_again)
{
conn->errornumber = status;
return SQL_SUCCESS_WITH_INFO;
}
else
return SQL_SUCCESS;
}
else if (SQL_NULL_HENV != henv)
{
EnvironmentClass *env = (EnvironmentClass *) henv;
if (EN_get_error(env, &status, &msg))
{
mylog("EN_get_error: status = %d, msg = #%s#\n", status, msg);
if (NULL == msg)
{
if (NULL != szSqlState)
strcpy(szSqlState, "00000");
if (NULL != pcbErrorMsg)
*pcbErrorMsg = 0;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
szErrorMsg[0] = '\0';
return SQL_NO_DATA_FOUND;
}
if (NULL != pcbErrorMsg)
*pcbErrorMsg = (SWORD) strlen(msg);
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
strncpy_null(szErrorMsg, msg, cbErrorMsgMax);
if (NULL != pfNativeError)
*pfNativeError = status;
if (szSqlState)
{
switch (status)
{
case ENV_ALLOC_ERROR:
/* memory allocation failure */
strcpy(szSqlState, "S1001");
break;
default:
strcpy(szSqlState, "S1000");
/* general error */
break;
}
}
}
else
{
if (NULL != szSqlState)
strcpy(szSqlState, "00000");
if (NULL != pcbErrorMsg)
*pcbErrorMsg = 0;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
szErrorMsg[0] = '\0';
return SQL_NO_DATA_FOUND;
}
return SQL_SUCCESS;
}
if (NULL != szSqlState)
strcpy(szSqlState, "00000");
if (NULL != pcbErrorMsg)
*pcbErrorMsg = 0;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
szErrorMsg[0] = '\0';
return SQL_NO_DATA_FOUND;
}
/*
* EnvironmentClass implementation
*/
EnvironmentClass *
EN_Constructor(void)
{
EnvironmentClass *rv;
rv = (EnvironmentClass *) malloc(sizeof(EnvironmentClass));
if (rv)
{
rv->errormsg = 0;
rv->errornumber = 0;
}
return rv;
}
char
EN_Destructor(EnvironmentClass *self)
{
int lf;
char rv = 1;
mylog("in EN_Destructor, self=%u\n", self);
/*
* the error messages are static strings distributed throughout the
* source--they should not be freed
*/
/* Free any connections belonging to this environment */
for (lf = 0; lf < MAX_CONNECTIONS; lf++)
{
if (conns[lf] && conns[lf]->henv == self)
rv = rv && CC_Destructor(conns[lf]);
}
free(self);
mylog("exit EN_Destructor: rv = %d\n", rv);
#ifdef _MEMORY_DEBUG_
debug_memory_inouecheck();
#endif /* _MEMORY_DEBUG_ */
return rv;
}
char
EN_get_error(EnvironmentClass *self, int *number, char **message)
{
if (self && self->errormsg && self->errornumber)
{
*message = self->errormsg;
*number = self->errornumber;
self->errormsg = 0;
self->errornumber = 0;
return 1;
}
else
return 0;
}
char
EN_add_connection(EnvironmentClass *self, ConnectionClass *conn)
{
int i;
mylog("EN_add_connection: self = %u, conn = %u\n", self, conn);
for (i = 0; i < MAX_CONNECTIONS; i++)
{
if (!conns[i])
{
conn->henv = self;
conns[i] = conn;
mylog(" added at i =%d, conn->henv = %u, conns[i]->henv = %u\n", i, conn->henv, conns[i]->henv);
return TRUE;
}
}
return FALSE;
}
char
EN_remove_connection(EnvironmentClass *self, ConnectionClass *conn)
{
int i;
for (i = 0; i < MAX_CONNECTIONS; i++)
if (conns[i] == conn && conns[i]->status != CONN_EXECUTING)
{
conns[i] = NULL;
return TRUE;
}
return FALSE;
}
void
EN_log_error(char *func, char *desc, EnvironmentClass *self)
{
if (self)
qlog("ENVIRON ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, self->errormsg);
else
qlog("INVALID ENVIRON HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
}
/* File: environ.h
*
* Description: See "environ.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __ENVIRON_H__
#define __ENVIRON_H__
#include "psqlodbc.h"
#define ENV_ALLOC_ERROR 1
/********** Environment Handle *************/
struct EnvironmentClass_
{
char *errormsg;
int errornumber;
};
/* Environment prototypes */
EnvironmentClass *EN_Constructor(void);
char EN_Destructor(EnvironmentClass *self);
char EN_get_error(EnvironmentClass *self, int *number, char **message);
char EN_add_connection(EnvironmentClass *self, ConnectionClass *conn);
char EN_remove_connection(EnvironmentClass *self, ConnectionClass *conn);
void EN_log_error(char *func, char *desc, EnvironmentClass *self);
#endif
/*-------
* Module: execute.c
*
* Description: This module contains routines related to
* preparing and executing an SQL statement.
*
* Classes: n/a
*
* API functions: SQLPrepare, SQLExecute, SQLExecDirect, SQLTransact,
* SQLCancel, SQLNativeSql, SQLParamData, SQLPutData
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "psqlodbc.h"
#include <stdio.h>
#include <string.h>
#include "connection.h"
#include "statement.h"
#include "qresult.h"
#include "convert.h"
#include "bind.h"
#include "pgtypes.h"
#include "lobj.h"
#include "pgapifunc.h"
/*extern GLOBAL_VALUES globals;*/
/* Perform a Prepare on the SQL statement */
RETCODE SQL_API
PGAPI_Prepare(HSTMT hstmt,
UCHAR FAR * szSqlStr,
SDWORD cbSqlStr)
{
static char *func = "PGAPI_Prepare";
StatementClass *self = (StatementClass *) hstmt;
mylog("%s: entering...\n", func);
if (!self)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
/*
* According to the ODBC specs it is valid to call SQLPrepare mulitple
* times. In that case, the bound SQL statement is replaced by the new
* one
*/
switch (self->status)
{
case STMT_PREMATURE:
mylog("**** PGAPI_Prepare: STMT_PREMATURE, recycle\n");
SC_recycle_statement(self); /* recycle the statement, but do
* not remove parameter bindings */
break;
case STMT_FINISHED:
mylog("**** PGAPI_Prepare: STMT_FINISHED, recycle\n");
SC_recycle_statement(self); /* recycle the statement, but do
* not remove parameter bindings */
break;
case STMT_ALLOCATED:
mylog("**** PGAPI_Prepare: STMT_ALLOCATED, copy\n");
self->status = STMT_READY;
break;
case STMT_READY:
mylog("**** PGAPI_Prepare: STMT_READY, change SQL\n");
break;
case STMT_EXECUTING:
mylog("**** PGAPI_Prepare: STMT_EXECUTING, error!\n");
self->errornumber = STMT_SEQUENCE_ERROR;
self->errormsg = "PGAPI_Prepare(): The handle does not point to a statement that is ready to be executed";
SC_log_error(func, "", self);
return SQL_ERROR;
default:
self->errornumber = STMT_INTERNAL_ERROR;
self->errormsg = "An Internal Error has occured -- Unknown statement status.";
SC_log_error(func, "", self);
return SQL_ERROR;
}
if (self->statement)
free(self->statement);
self->statement = make_string(szSqlStr, cbSqlStr, NULL);
if (!self->statement)
{
self->errornumber = STMT_NO_MEMORY_ERROR;
self->errormsg = "No memory available to store statement";
SC_log_error(func, "", self);
return SQL_ERROR;
}
self->prepare = TRUE;
self->statement_type = statement_type(self->statement);
/* Check if connection is onlyread (only selects are allowed) */
if (CC_is_onlyread(self->hdbc) && STMT_UPDATE(self))
{
self->errornumber = STMT_EXEC_ERROR;
self->errormsg = "Connection is readonly, only select statements are allowed.";
SC_log_error(func, "", self);
return SQL_ERROR;
}
return SQL_SUCCESS;
}
/* Performs the equivalent of SQLPrepare, followed by SQLExecute. */
RETCODE SQL_API
PGAPI_ExecDirect(
HSTMT hstmt,
UCHAR FAR * szSqlStr,
SDWORD cbSqlStr)
{
StatementClass *stmt = (StatementClass *) hstmt;
RETCODE result;
static char *func = "PGAPI_ExecDirect";
mylog("%s: entering...\n", func);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
if (stmt->statement)
free(stmt->statement);
/*
* keep a copy of the un-parametrized statement, in case they try to
* execute this statement again
*/
stmt->statement = make_string(szSqlStr, cbSqlStr, NULL);
if (!stmt->statement)
{
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "No memory available to store statement";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
mylog("**** %s: hstmt=%u, statement='%s'\n", func, hstmt, stmt->statement);
stmt->prepare = FALSE;
/*
* If an SQLPrepare was performed prior to this, but was left in the
* premature state because an error occurred prior to SQLExecute then
* set the statement to finished so it can be recycled.
*/
if (stmt->status == STMT_PREMATURE)
stmt->status = STMT_FINISHED;
stmt->statement_type = statement_type(stmt->statement);
/* Check if connection is onlyread (only selects are allowed) */
if (CC_is_onlyread(stmt->hdbc) && STMT_UPDATE(stmt))
{
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Connection is readonly, only select statements are allowed.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
mylog("%s: calling PGAPI_Execute...\n", func);
result = PGAPI_Execute(hstmt);
mylog("%s: returned %hd from PGAPI_Execute\n", func, result);
return result;
}
/* Execute a prepared SQL statement */
RETCODE SQL_API
PGAPI_Execute(
HSTMT hstmt)
{
static char *func = "PGAPI_Execute";
StatementClass *stmt = (StatementClass *) hstmt;
ConnectionClass *conn;
int i,
retval;
mylog("%s: entering...\n", func);
if (!stmt)
{
SC_log_error(func, "", NULL);
mylog("%s: NULL statement so return SQL_INVALID_HANDLE\n", func);
return SQL_INVALID_HANDLE;
}
/*
* If the statement is premature, it means we already executed it from
* an SQLPrepare/SQLDescribeCol type of scenario. So just return
* success.
*/
if (stmt->prepare && stmt->status == STMT_PREMATURE)
{
if (stmt->inaccurate_result)
SC_recycle_statement(stmt);
else
{
stmt->status = STMT_FINISHED;
if (stmt->errormsg == NULL)
{
mylog("%s: premature statement but return SQL_SUCCESS\n", func);
return SQL_SUCCESS;
}
else
{
SC_log_error(func, "", stmt);
mylog("%s: premature statement so return SQL_ERROR\n", func);
return SQL_ERROR;
}
}
}
mylog("%s: clear errors...\n", func);
SC_clear_error(stmt);
conn = SC_get_conn(stmt);
if (conn->status == CONN_EXECUTING)
{
stmt->errormsg = "Connection is already in use.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
mylog("%s: problem with connection\n", func);
return SQL_ERROR;
}
if (!stmt->statement)
{
stmt->errornumber = STMT_NO_STMTSTRING;
stmt->errormsg = "This handle does not have a SQL statement stored in it";
SC_log_error(func, "", stmt);
mylog("%s: problem with handle\n", func);
return SQL_ERROR;
}
/*
* If SQLExecute is being called again, recycle the statement. Note
* this should have been done by the application in a call to
* SQLFreeStmt(SQL_CLOSE) or SQLCancel.
*/
if (stmt->status == STMT_FINISHED)
{
mylog("%s: recycling statement (should have been done by app)...\n", func);
SC_recycle_statement(stmt);
}
/* Check if the statement is in the correct state */
if ((stmt->prepare && stmt->status != STMT_READY) ||
(stmt->status != STMT_ALLOCATED && stmt->status != STMT_READY))
{
stmt->errornumber = STMT_STATUS_ERROR;
stmt->errormsg = "The handle does not point to a statement that is ready to be executed";
SC_log_error(func, "", stmt);
mylog("%s: problem with statement\n", func);
return SQL_ERROR;
}
/*
* Check if statement has any data-at-execute parameters when it is
* not in SC_pre_execute.
*/
if (!stmt->pre_executing)
{
/*
* The bound parameters could have possibly changed since the last
* execute of this statement? Therefore check for params and
* re-copy.
*/
stmt->data_at_exec = -1;
for (i = 0; i < stmt->parameters_allocated; i++)
{
Int4 *pcVal = stmt->parameters[i].used;
if (pcVal && (*pcVal == SQL_DATA_AT_EXEC || *pcVal <= SQL_LEN_DATA_AT_EXEC_OFFSET))
stmt->parameters[i].data_at_exec = TRUE;
else
stmt->parameters[i].data_at_exec = FALSE;
/* Check for data at execution parameters */
if (stmt->parameters[i].data_at_exec == TRUE)
{
if (stmt->data_at_exec < 0)
stmt->data_at_exec = 1;
else
stmt->data_at_exec++;
}
}
/*
* If there are some data at execution parameters, return need
* data
*/
/*
* SQLParamData and SQLPutData will be used to send params and
* execute the statement.
*/
if (stmt->data_at_exec > 0)
return SQL_NEED_DATA;
}
mylog("%s: copying statement params: trans_status=%d, len=%d, stmt='%s'\n", func, conn->transact_status, strlen(stmt->statement), stmt->statement);
/* Create the statement with parameters substituted. */
retval = copy_statement_with_parameters(stmt);
if (retval != SQL_SUCCESS)
/* error msg passed from above */
return retval;
mylog(" stmt_with_params = '%s'\n", stmt->stmt_with_params);
/*
* Get the field info for the prepared query using dummy backward
* fetch.
*/
if (stmt->inaccurate_result && conn->connInfo.disallow_premature)
{
if (SC_is_pre_executable(stmt))
{
BOOL in_trans = CC_is_in_trans(conn);
BOOL issued_begin = FALSE,
begin_included = FALSE;
QResultClass *res;
if (strnicmp(stmt->stmt_with_params, "BEGIN;", 6) == 0)
begin_included = TRUE;
else if (!in_trans)
{
res = CC_send_query(conn, "BEGIN", NULL);
if (res && !QR_aborted(res))
issued_begin = TRUE;
if (res)
QR_Destructor(res);
if (!issued_begin)
{
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Handle prepare error";
return SQL_ERROR;
}
}
/* we are now in a transaction */
CC_set_in_trans(conn);
stmt->result = res = CC_send_query(conn, stmt->stmt_with_params, NULL);
if (!res || QR_aborted(res))
{
CC_abort(conn);
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Handle prepare error";
return SQL_ERROR;
}
else
{
if (CC_is_in_autocommit(conn))
{
if (issued_begin)
{
res = CC_send_query(conn, "COMMIT", NULL);
CC_set_no_trans(conn);
if (res)
QR_Destructor(res);
}
else if (!in_trans && begin_included)
CC_set_no_trans(conn);
}
stmt->status = STMT_FINISHED;
return SQL_SUCCESS;
}
}
else
return SQL_SUCCESS;
}
return SC_execute(stmt);
}
RETCODE SQL_API
PGAPI_Transact(
HENV henv,
HDBC hdbc,
UWORD fType)
{
static char *func = "PGAPI_Transact";
extern ConnectionClass *conns[];
ConnectionClass *conn;
QResultClass *res;
char ok,
*stmt_string;
int lf;
mylog("entering %s: hdbc=%u, henv=%u\n", func, hdbc, henv);
if (hdbc == SQL_NULL_HDBC && henv == SQL_NULL_HENV)
{
CC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
/*
* If hdbc is null and henv is valid, it means transact all
* connections on that henv.
*/
if (hdbc == SQL_NULL_HDBC && henv != SQL_NULL_HENV)
{
for (lf = 0; lf < MAX_CONNECTIONS; lf++)
{
conn = conns[lf];
if (conn && conn->henv == henv)
if (PGAPI_Transact(henv, (HDBC) conn, fType) != SQL_SUCCESS)
return SQL_ERROR;
}
return SQL_SUCCESS;
}
conn = (ConnectionClass *) hdbc;
if (fType == SQL_COMMIT)
stmt_string = "COMMIT";
else if (fType == SQL_ROLLBACK)
stmt_string = "ROLLBACK";
else
{
conn->errornumber = CONN_INVALID_ARGUMENT_NO;
conn->errormsg = "PGAPI_Transact can only be called with SQL_COMMIT or SQL_ROLLBACK as parameter";
CC_log_error(func, "", conn);
return SQL_ERROR;
}
/* If manual commit and in transaction, then proceed. */
if (!CC_is_in_autocommit(conn) && CC_is_in_trans(conn))
{
mylog("PGAPI_Transact: sending on conn %d '%s'\n", conn, stmt_string);
res = CC_send_query(conn, stmt_string, NULL);
CC_set_no_trans(conn);
if (!res)
{
/* error msg will be in the connection */
CC_log_error(func, "", conn);
return SQL_ERROR;
}
ok = QR_command_successful(res);
QR_Destructor(res);
if (!ok)
{
CC_log_error(func, "", conn);
return SQL_ERROR;
}
}
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_Cancel(
HSTMT hstmt) /* Statement to cancel. */
{
static char *func = "PGAPI_Cancel";
StatementClass *stmt = (StatementClass *) hstmt;
RETCODE result;
ConnInfo *ci;
#ifdef WIN32
HMODULE hmodule;
FARPROC addr;
#endif
mylog("%s: entering...\n", func);
/* Check if this can handle canceling in the middle of a SQLPutData? */
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
ci = &(SC_get_conn(stmt)->connInfo);
/*
* Not in the middle of SQLParamData/SQLPutData so cancel like a
* close.
*/
if (stmt->data_at_exec < 0)
{
/*
* MAJOR HACK for Windows to reset the driver manager's cursor
* state: Because of what seems like a bug in the Odbc driver
* manager, SQLCancel does not act like a SQLFreeStmt(CLOSE), as
* many applications depend on this behavior. So, this brute
* force method calls the driver manager's function on behalf of
* the application.
*/
#ifdef WIN32
if (ci->drivers.cancel_as_freestmt)
{
hmodule = GetModuleHandle("ODBC32");
addr = GetProcAddress(hmodule, "SQLFreeStmt");
result = addr((char *) (stmt->phstmt) - 96, SQL_CLOSE);
}
else
result = PGAPI_FreeStmt(hstmt, SQL_CLOSE);
#else
result = PGAPI_FreeStmt(hstmt, SQL_CLOSE);
#endif
mylog("PGAPI_Cancel: PGAPI_FreeStmt returned %d\n", result);
SC_clear_error(hstmt);
return SQL_SUCCESS;
}
/* In the middle of SQLParamData/SQLPutData, so cancel that. */
/*
* Note, any previous data-at-exec buffers will be freed in the
* recycle
*/
/* if they call SQLExecDirect or SQLExecute again. */
stmt->data_at_exec = -1;
stmt->current_exec_param = -1;
stmt->put_data = FALSE;
return SQL_SUCCESS;
}
/*
* Returns the SQL string as modified by the driver.
* Currently, just copy the input string without modification
* observing buffer limits and truncation.
*/
RETCODE SQL_API
PGAPI_NativeSql(
HDBC hdbc,
UCHAR FAR * szSqlStrIn,
SDWORD cbSqlStrIn,
UCHAR FAR * szSqlStr,
SDWORD cbSqlStrMax,
SDWORD FAR * pcbSqlStr)
{
static char *func = "PGAPI_NativeSql";
int len = 0;
char *ptr;
ConnectionClass *conn = (ConnectionClass *) hdbc;
RETCODE result;
mylog("%s: entering...cbSqlStrIn=%d\n", func, cbSqlStrIn);
ptr = (cbSqlStrIn == 0) ? "" : make_string(szSqlStrIn, cbSqlStrIn, NULL);
if (!ptr)
{
conn->errornumber = CONN_NO_MEMORY_ERROR;
conn->errormsg = "No memory available to store native sql string";
CC_log_error(func, "", conn);
return SQL_ERROR;
}
result = SQL_SUCCESS;
len = strlen(ptr);
if (szSqlStr)
{
strncpy_null(szSqlStr, ptr, cbSqlStrMax);
if (len >= cbSqlStrMax)
{
result = SQL_SUCCESS_WITH_INFO;
conn->errornumber = STMT_TRUNCATED;
conn->errormsg = "The buffer was too small for the NativeSQL.";
}
}
if (pcbSqlStr)
*pcbSqlStr = len;
if (cbSqlStrIn)
free(ptr);
return result;
}
/*
* Supplies parameter data at execution time.
* Used in conjuction with SQLPutData.
*/
RETCODE SQL_API
PGAPI_ParamData(
HSTMT hstmt,
PTR FAR * prgbValue)
{
static char *func = "PGAPI_ParamData";
StatementClass *stmt = (StatementClass *) hstmt;
int i,
retval;
ConnInfo *ci;
mylog("%s: entering...\n", func);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
ci = &(SC_get_conn(stmt)->connInfo);
mylog("%s: data_at_exec=%d, params_alloc=%d\n", func, stmt->data_at_exec, stmt->parameters_allocated);
if (stmt->data_at_exec < 0)
{
stmt->errornumber = STMT_SEQUENCE_ERROR;
stmt->errormsg = "No execution-time parameters for this statement";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->data_at_exec > stmt->parameters_allocated)
{
stmt->errornumber = STMT_SEQUENCE_ERROR;
stmt->errormsg = "Too many execution-time parameters were present";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/* close the large object */
if (stmt->lobj_fd >= 0)
{
lo_close(stmt->hdbc, stmt->lobj_fd);
/* commit transaction if needed */
if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(stmt->hdbc))
{
QResultClass *res;
char ok;
res = CC_send_query(stmt->hdbc, "COMMIT", NULL);
if (!res)
{
stmt->errormsg = "Could not commit (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
ok = QR_command_successful(res);
CC_set_no_trans(stmt->hdbc);
QR_Destructor(res);
if (!ok)
{
stmt->errormsg = "Could not commit (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
}
stmt->lobj_fd = -1;
}
/* Done, now copy the params and then execute the statement */
if (stmt->data_at_exec == 0)
{
retval = copy_statement_with_parameters(stmt);
if (retval != SQL_SUCCESS)
return retval;
stmt->current_exec_param = -1;
return SC_execute(stmt);
}
/*
* Set beginning param; if first time SQLParamData is called , start
* at 0. Otherwise, start at the last parameter + 1.
*/
i = stmt->current_exec_param >= 0 ? stmt->current_exec_param + 1 : 0;
/* At least 1 data at execution parameter, so Fill in the token value */
for (; i < stmt->parameters_allocated; i++)
{
if (stmt->parameters[i].data_at_exec == TRUE)
{
stmt->data_at_exec--;
stmt->current_exec_param = i;
stmt->put_data = FALSE;
*prgbValue = stmt->parameters[i].buffer; /* token */
break;
}
}
return SQL_NEED_DATA;
}
/*
* Supplies parameter data at execution time.
* Used in conjunction with SQLParamData.
*/
RETCODE SQL_API
PGAPI_PutData(
HSTMT hstmt,
PTR rgbValue,
SDWORD cbValue)
{
static char *func = "PGAPI_PutData";
StatementClass *stmt = (StatementClass *) hstmt;
int old_pos,
retval;
ParameterInfoClass *current_param;
char *buffer;
mylog("%s: entering...\n", func);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
if (stmt->current_exec_param < 0)
{
stmt->errornumber = STMT_SEQUENCE_ERROR;
stmt->errormsg = "Previous call was not SQLPutData or SQLParamData";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
current_param = &(stmt->parameters[stmt->current_exec_param]);
if (!stmt->put_data)
{ /* first call */
mylog("PGAPI_PutData: (1) cbValue = %d\n", cbValue);
stmt->put_data = TRUE;
current_param->EXEC_used = (SDWORD *) malloc(sizeof(SDWORD));
if (!current_param->EXEC_used)
{
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Out of memory in PGAPI_PutData (1)";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
*current_param->EXEC_used = cbValue;
if (cbValue == SQL_NULL_DATA)
return SQL_SUCCESS;
/* Handle Long Var Binary with Large Objects */
if (current_param->SQLType == SQL_LONGVARBINARY)
{
/* begin transaction if needed */
if (!CC_is_in_trans(stmt->hdbc))
{
QResultClass *res;
char ok;
res = CC_send_query(stmt->hdbc, "BEGIN", NULL);
if (!res)
{
stmt->errormsg = "Could not begin (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
ok = QR_command_successful(res);
QR_Destructor(res);
if (!ok)
{
stmt->errormsg = "Could not begin (in-line) a transaction";
stmt->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
CC_set_in_trans(stmt->hdbc);
}
/* store the oid */
current_param->lobj_oid = lo_creat(stmt->hdbc, INV_READ | INV_WRITE);
if (current_param->lobj_oid == 0)
{
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Couldnt create large object.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/*
* major hack -- to allow convert to see somethings there have
* to modify convert to handle this better
*/
current_param->EXEC_buffer = (char *) &current_param->lobj_oid;
/* store the fd */
stmt->lobj_fd = lo_open(stmt->hdbc, current_param->lobj_oid, INV_WRITE);
if (stmt->lobj_fd < 0)
{
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Couldnt open large object for writing.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
retval = lo_write(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValue);
mylog("lo_write: cbValue=%d, wrote %d bytes\n", cbValue, retval);
}
else
{
/* for handling fields */
if (cbValue == SQL_NTS)
{
current_param->EXEC_buffer = strdup(rgbValue);
if (!current_param->EXEC_buffer)
{
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Out of memory in PGAPI_PutData (2)";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
}
else
{
Int2 ctype = current_param->CType;
if (ctype == SQL_C_DEFAULT)
ctype = sqltype_to_default_ctype(current_param->SQLType);
if (ctype == SQL_C_CHAR || ctype == SQL_C_BINARY)
{
current_param->EXEC_buffer = malloc(cbValue + 1);
if (!current_param->EXEC_buffer)
{
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Out of memory in PGAPI_PutData (2)";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
memcpy(current_param->EXEC_buffer, rgbValue, cbValue);
current_param->EXEC_buffer[cbValue] = '\0';
}
else
{
Int4 used = ctype_length(ctype);
current_param->EXEC_buffer = malloc(used);
if (!current_param->EXEC_buffer)
{
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Out of memory in PGAPI_PutData (2)";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
memcpy(current_param->EXEC_buffer, rgbValue, used);
}
}
}
}
else
{
/* calling SQLPutData more than once */
mylog("PGAPI_PutData: (>1) cbValue = %d\n", cbValue);
if (current_param->SQLType == SQL_LONGVARBINARY)
{
/* the large object fd is in EXEC_buffer */
retval = lo_write(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValue);
mylog("lo_write(2): cbValue = %d, wrote %d bytes\n", cbValue, retval);
*current_param->EXEC_used += cbValue;
}
else
{
buffer = current_param->EXEC_buffer;
if (cbValue == SQL_NTS)
{
buffer = realloc(buffer, strlen(buffer) + strlen(rgbValue) + 1);
if (!buffer)
{
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Out of memory in PGAPI_PutData (3)";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
strcat(buffer, rgbValue);
mylog(" cbValue = SQL_NTS: strlen(buffer) = %d\n", strlen(buffer));
*current_param->EXEC_used = cbValue;
/* reassign buffer incase realloc moved it */
current_param->EXEC_buffer = buffer;
}
else if (cbValue > 0)
{
old_pos = *current_param->EXEC_used;
*current_param->EXEC_used += cbValue;
mylog(" cbValue = %d, old_pos = %d, *used = %d\n", cbValue, old_pos, *current_param->EXEC_used);
/* dont lose the old pointer in case out of memory */
buffer = realloc(current_param->EXEC_buffer, *current_param->EXEC_used + 1);
if (!buffer)
{
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Out of memory in PGAPI_PutData (3)";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
memcpy(&buffer[old_pos], rgbValue, cbValue);
buffer[*current_param->EXEC_used] = '\0';
/* reassign buffer incase realloc moved it */
current_param->EXEC_buffer = buffer;
}
else
{
SC_log_error(func, "bad cbValue", stmt);
return SQL_ERROR;
}
}
}
return SQL_SUCCESS;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
#ifndef _IODBC_H
#define _IODBC_H
#if !defined(WIN32) && !defined(WIN32_SYSTEM)
#define _UNIX_
#include <stdlib.h>
#include <sys/types.h>
#define MEM_ALLOC(size) (malloc((size_t)(size)))
#define MEM_FREE(ptr) \
do { \
if(ptr) \
free(ptr); \
} while (0)
#define STRCPY(t, s) (strcpy((char*)(t), (char*)(s)))
#define STRNCPY(t,s,n) (strncpy((char*)(t), (char*)(s), (size_t)(n)))
#define STRCAT(t, s) (strcat((char*)(t), (char*)(s)))
#define STRNCAT(t,s,n) (strncat((char*)(t), (char*)(s), (size_t)(n)))
#define STREQ(a, b) (strcmp((char*)(a), (char*)(b)) == 0)
#define STRLEN(str) ((str)? strlen((char*)(str)):0)
#define EXPORT
#define CALLBACK
#define FAR
typedef signed short SSHOR;
typedef short WORD;
typedef long DWORD;
typedef WORD WPARAM;
typedef DWORD LPARAM;
typedef void *HWND;
typedef int BOOL;
#endif /* _UNIX_ */
#if defined(WIN32) || defined(WIN32_SYSTEM)
#include <windows.h>
#include <windowsx.h>
#ifdef _MSVC_
#define MEM_ALLOC(size) (fmalloc((size_t)(size)))
#define MEM_FREE(ptr) ((ptr)? ffree((PTR)(ptr)):0))
#define STRCPY(t, s) (fstrcpy((char FAR*)(t), (char FAR*)(s)))
#define STRNCPY(t,s,n) (fstrncpy((char FAR*)(t), (char FAR*)(s), (size_t)(n)))
#define STRLEN(str) ((str)? fstrlen((char FAR*)(str)):0)
#define STREQ(a, b) (fstrcmp((char FAR*)(a), (char FAR*)(b) == 0)
#endif
#ifdef _BORLAND_
#define MEM_ALLOC(size) (farmalloc((unsigned long)(size))
#define MEM_FREE(ptr) ((ptr)? farfree((void far*)(ptr)):0)
#define STRCPY(t, s) (_fstrcpy((char FAR*)(t), (char FAR*)(s)))
#define STRNCPY(t,s,n) (_fstrncpy((char FAR*)(t), (char FAR*)(s), (size_t)(n)))
#define STRLEN(str) ((str)? _fstrlen((char FAR*)(str)):0)
#define STREQ(a, b) (_fstrcmp((char FAR*)(a), (char FAR*)(b) == 0)
#endif
#endif /* WIN32 */
#define SYSERR (-1)
#ifndef NULL
#define NULL ((void FAR*)0UL)
#endif
#endif
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library 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.
This library 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 this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
/*--------
* Module: lobj.c
*
* Description: This module contains routines related to manipulating
* large objects.
*
* Classes: none
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*--------
*/
#include "lobj.h"
#include "connection.h"
Oid
lo_creat(ConnectionClass *conn, int mode)
{
LO_ARG argv[1];
int retval,
result_len;
argv[0].isint = 1;
argv[0].len = 4;
argv[0].u.integer = mode;
if (!CC_send_function(conn, LO_CREAT, &retval, &result_len, 1, argv, 1))
return 0; /* invalid oid */
else
return retval;
}
int
lo_open(ConnectionClass *conn, int lobjId, int mode)
{
int fd;
int result_len;
LO_ARG argv[2];
argv[0].isint = 1;
argv[0].len = 4;
argv[0].u.integer = lobjId;
argv[1].isint = 1;
argv[1].len = 4;
argv[1].u.integer = mode;
if (!CC_send_function(conn, LO_OPEN, &fd, &result_len, 1, argv, 2))
return -1;
if (fd >= 0 && lo_lseek(conn, fd, 0L, SEEK_SET) < 0)
return -1;
return fd;
}
int
lo_close(ConnectionClass *conn, int fd)
{
LO_ARG argv[1];
int retval,
result_len;
argv[0].isint = 1;
argv[0].len = 4;
argv[0].u.integer = fd;
if (!CC_send_function(conn, LO_CLOSE, &retval, &result_len, 1, argv, 1))
return -1;
else
return retval;
}
int
lo_read(ConnectionClass *conn, int fd, char *buf, int len)
{
LO_ARG argv[2];
int result_len;
argv[0].isint = 1;
argv[0].len = 4;
argv[0].u.integer = fd;
argv[1].isint = 1;
argv[1].len = 4;
argv[1].u.integer = len;
if (!CC_send_function(conn, LO_READ, (int *) buf, &result_len, 0, argv, 2))
return -1;
else
return result_len;
}
int
lo_write(ConnectionClass *conn, int fd, char *buf, int len)
{
LO_ARG argv[2];
int retval,
result_len;
if (len <= 0)
return 0;
argv[0].isint = 1;
argv[0].len = 4;
argv[0].u.integer = fd;
argv[1].isint = 0;
argv[1].len = len;
argv[1].u.ptr = (char *) buf;
if (!CC_send_function(conn, LO_WRITE, &retval, &result_len, 1, argv, 2))
return -1;
else
return retval;
}
int
lo_lseek(ConnectionClass *conn, int fd, int offset, int whence)
{
LO_ARG argv[3];
int retval,
result_len;
argv[0].isint = 1;
argv[0].len = 4;
argv[0].u.integer = fd;
argv[1].isint = 1;
argv[1].len = 4;
argv[1].u.integer = offset;
argv[2].isint = 1;
argv[2].len = 4;
argv[2].u.integer = whence;
if (!CC_send_function(conn, LO_LSEEK, &retval, &result_len, 1, argv, 3))
return -1;
else
return retval;
}
int
lo_tell(ConnectionClass *conn, int fd)
{
LO_ARG argv[1];
int retval,
result_len;
argv[0].isint = 1;
argv[0].len = 4;
argv[0].u.integer = fd;
if (!CC_send_function(conn, LO_TELL, &retval, &result_len, 1, argv, 1))
return -1;
else
return retval;
}
int
lo_unlink(ConnectionClass *conn, Oid lobjId)
{
LO_ARG argv[1];
int retval,
result_len;
argv[0].isint = 1;
argv[0].len = 4;
argv[0].u.integer = lobjId;
if (!CC_send_function(conn, LO_UNLINK, &retval, &result_len, 1, argv, 1))
return -1;
else
return retval;
}
/* File: lobj.h
*
* Description: See "lobj.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __LOBJ_H__
#define __LOBJ_H__
#include "psqlodbc.h"
struct lo_arg
{
int isint;
int len;
union
{
int integer;
char *ptr;
} u;
};
#define LO_CREAT 957
#define LO_OPEN 952
#define LO_CLOSE 953
#define LO_READ 954
#define LO_WRITE 955
#define LO_LSEEK 956
#define LO_TELL 958
#define LO_UNLINK 964
#define INV_WRITE 0x00020000
#define INV_READ 0x00040000
Oid lo_creat(ConnectionClass *conn, int mode);
int lo_open(ConnectionClass *conn, int lobjId, int mode);
int lo_close(ConnectionClass *conn, int fd);
int lo_read(ConnectionClass *conn, int fd, char *buf, int len);
int lo_write(ConnectionClass *conn, int fd, char *buf, int len);
int lo_lseek(ConnectionClass *conn, int fd, int offset, int len);
int lo_tell(ConnectionClass *conn, int fd);
int lo_unlink(ConnectionClass *conn, Oid lobjId);
#endif
/*
* md5.c
*
* Implements the MD5 Message-Digest Algorithm as specified in
* RFC 1321. This implementation is a simple one, in that it
* needs every input byte to be buffered before doing any
* calculations. I do not expect this file to be used for
* general purpose MD5'ing of large amounts of data, only for
* generating hashed passwords from limited input.
*
* Sverre H. Huseby <sverrehu@online.no>
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/odbc/windev/Attic/md5.c,v 1.1 2002/01/11 02:50:01 inoue Exp $
*/
/*
* NOTE:
*
* There are two copies of this file, one in backend/libpq and another
* in interfaces/odbc. They should be identical. This is done so ODBC
* can be compiled stand-alone.
*/
#ifndef MD5_ODBC
#include "postgres.h"
#include "libpq/crypt.h"
#else
#include "md5.h"
#endif
#ifdef FRONTEND
#undef palloc
#define palloc malloc
#undef pfree
#define pfree free
#endif
/*
* PRIVATE FUNCTIONS
*/
/*
* The returned array is allocated using malloc. the caller should free it
* when it is no longer needed.
*/
static uint8 *
createPaddedCopyWithLength(uint8 *b, uint32 *l)
{
uint8 *ret;
uint32 q;
uint32 len,
newLen448;
uint32 len_high,
len_low; /* 64-bit value split into 32-bit sections */
len = ((b == NULL) ? 0 : *l);
newLen448 = len + 64 - (len % 64) - 8;
if (newLen448 <= len)
newLen448 += 64;
*l = newLen448 + 8;
if ((ret = (uint8 *) malloc(sizeof(uint8) * *l)) == NULL)
return NULL;
if (b != NULL)
memcpy(ret, b, sizeof(uint8) * len);
/* pad */
ret[len] = 0x80;
for (q = len + 1; q < newLen448; q++)
ret[q] = 0x00;
/* append length as a 64 bit bitcount */
len_low = len;
/* split into two 32-bit values */
/* we only look at the bottom 32-bits */
len_high = len >> 29;
len_low <<= 3;
q = newLen448;
ret[q++] = (len_low & 0xff);
len_low >>= 8;
ret[q++] = (len_low & 0xff);
len_low >>= 8;
ret[q++] = (len_low & 0xff);
len_low >>= 8;
ret[q++] = (len_low & 0xff);
ret[q++] = (len_high & 0xff);
len_high >>= 8;
ret[q++] = (len_high & 0xff);
len_high >>= 8;
ret[q++] = (len_high & 0xff);
len_high >>= 8;
ret[q] = (len_high & 0xff);
return ret;
}
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define ROT_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
static void
doTheRounds(uint32 X[16], uint32 state[4])
{
uint32 a,
b,
c,
d;
a = state[0];
b = state[1];
c = state[2];
d = state[3];
/* round 1 */
a = b + ROT_LEFT((a + F(b, c, d) + X[0] + 0xd76aa478), 7); /* 1 */
d = a + ROT_LEFT((d + F(a, b, c) + X[1] + 0xe8c7b756), 12); /* 2 */
c = d + ROT_LEFT((c + F(d, a, b) + X[2] + 0x242070db), 17); /* 3 */
b = c + ROT_LEFT((b + F(c, d, a) + X[3] + 0xc1bdceee), 22); /* 4 */
a = b + ROT_LEFT((a + F(b, c, d) + X[4] + 0xf57c0faf), 7); /* 5 */
d = a + ROT_LEFT((d + F(a, b, c) + X[5] + 0x4787c62a), 12); /* 6 */
c = d + ROT_LEFT((c + F(d, a, b) + X[6] + 0xa8304613), 17); /* 7 */
b = c + ROT_LEFT((b + F(c, d, a) + X[7] + 0xfd469501), 22); /* 8 */
a = b + ROT_LEFT((a + F(b, c, d) + X[8] + 0x698098d8), 7); /* 9 */
d = a + ROT_LEFT((d + F(a, b, c) + X[9] + 0x8b44f7af), 12); /* 10 */
c = d + ROT_LEFT((c + F(d, a, b) + X[10] + 0xffff5bb1), 17); /* 11 */
b = c + ROT_LEFT((b + F(c, d, a) + X[11] + 0x895cd7be), 22); /* 12 */
a = b + ROT_LEFT((a + F(b, c, d) + X[12] + 0x6b901122), 7); /* 13 */
d = a + ROT_LEFT((d + F(a, b, c) + X[13] + 0xfd987193), 12); /* 14 */
c = d + ROT_LEFT((c + F(d, a, b) + X[14] + 0xa679438e), 17); /* 15 */
b = c + ROT_LEFT((b + F(c, d, a) + X[15] + 0x49b40821), 22); /* 16 */
/* round 2 */
a = b + ROT_LEFT((a + G(b, c, d) + X[1] + 0xf61e2562), 5); /* 17 */
d = a + ROT_LEFT((d + G(a, b, c) + X[6] + 0xc040b340), 9); /* 18 */
c = d + ROT_LEFT((c + G(d, a, b) + X[11] + 0x265e5a51), 14); /* 19 */
b = c + ROT_LEFT((b + G(c, d, a) + X[0] + 0xe9b6c7aa), 20); /* 20 */
a = b + ROT_LEFT((a + G(b, c, d) + X[5] + 0xd62f105d), 5); /* 21 */
d = a + ROT_LEFT((d + G(a, b, c) + X[10] + 0x02441453), 9); /* 22 */
c = d + ROT_LEFT((c + G(d, a, b) + X[15] + 0xd8a1e681), 14); /* 23 */
b = c + ROT_LEFT((b + G(c, d, a) + X[4] + 0xe7d3fbc8), 20); /* 24 */
a = b + ROT_LEFT((a + G(b, c, d) + X[9] + 0x21e1cde6), 5); /* 25 */
d = a + ROT_LEFT((d + G(a, b, c) + X[14] + 0xc33707d6), 9); /* 26 */
c = d + ROT_LEFT((c + G(d, a, b) + X[3] + 0xf4d50d87), 14); /* 27 */
b = c + ROT_LEFT((b + G(c, d, a) + X[8] + 0x455a14ed), 20); /* 28 */
a = b + ROT_LEFT((a + G(b, c, d) + X[13] + 0xa9e3e905), 5); /* 29 */
d = a + ROT_LEFT((d + G(a, b, c) + X[2] + 0xfcefa3f8), 9); /* 30 */
c = d + ROT_LEFT((c + G(d, a, b) + X[7] + 0x676f02d9), 14); /* 31 */
b = c + ROT_LEFT((b + G(c, d, a) + X[12] + 0x8d2a4c8a), 20); /* 32 */
/* round 3 */
a = b + ROT_LEFT((a + H(b, c, d) + X[5] + 0xfffa3942), 4); /* 33 */
d = a + ROT_LEFT((d + H(a, b, c) + X[8] + 0x8771f681), 11); /* 34 */
c = d + ROT_LEFT((c + H(d, a, b) + X[11] + 0x6d9d6122), 16); /* 35 */
b = c + ROT_LEFT((b + H(c, d, a) + X[14] + 0xfde5380c), 23); /* 36 */
a = b + ROT_LEFT((a + H(b, c, d) + X[1] + 0xa4beea44), 4); /* 37 */
d = a + ROT_LEFT((d + H(a, b, c) + X[4] + 0x4bdecfa9), 11); /* 38 */
c = d + ROT_LEFT((c + H(d, a, b) + X[7] + 0xf6bb4b60), 16); /* 39 */
b = c + ROT_LEFT((b + H(c, d, a) + X[10] + 0xbebfbc70), 23); /* 40 */
a = b + ROT_LEFT((a + H(b, c, d) + X[13] + 0x289b7ec6), 4); /* 41 */
d = a + ROT_LEFT((d + H(a, b, c) + X[0] + 0xeaa127fa), 11); /* 42 */
c = d + ROT_LEFT((c + H(d, a, b) + X[3] + 0xd4ef3085), 16); /* 43 */
b = c + ROT_LEFT((b + H(c, d, a) + X[6] + 0x04881d05), 23); /* 44 */
a = b + ROT_LEFT((a + H(b, c, d) + X[9] + 0xd9d4d039), 4); /* 45 */
d = a + ROT_LEFT((d + H(a, b, c) + X[12] + 0xe6db99e5), 11); /* 46 */
c = d + ROT_LEFT((c + H(d, a, b) + X[15] + 0x1fa27cf8), 16); /* 47 */
b = c + ROT_LEFT((b + H(c, d, a) + X[2] + 0xc4ac5665), 23); /* 48 */
/* round 4 */
a = b + ROT_LEFT((a + I(b, c, d) + X[0] + 0xf4292244), 6); /* 49 */
d = a + ROT_LEFT((d + I(a, b, c) + X[7] + 0x432aff97), 10); /* 50 */
c = d + ROT_LEFT((c + I(d, a, b) + X[14] + 0xab9423a7), 15); /* 51 */
b = c + ROT_LEFT((b + I(c, d, a) + X[5] + 0xfc93a039), 21); /* 52 */
a = b + ROT_LEFT((a + I(b, c, d) + X[12] + 0x655b59c3), 6); /* 53 */
d = a + ROT_LEFT((d + I(a, b, c) + X[3] + 0x8f0ccc92), 10); /* 54 */
c = d + ROT_LEFT((c + I(d, a, b) + X[10] + 0xffeff47d), 15); /* 55 */
b = c + ROT_LEFT((b + I(c, d, a) + X[1] + 0x85845dd1), 21); /* 56 */
a = b + ROT_LEFT((a + I(b, c, d) + X[8] + 0x6fa87e4f), 6); /* 57 */
d = a + ROT_LEFT((d + I(a, b, c) + X[15] + 0xfe2ce6e0), 10); /* 58 */
c = d + ROT_LEFT((c + I(d, a, b) + X[6] + 0xa3014314), 15); /* 59 */
b = c + ROT_LEFT((b + I(c, d, a) + X[13] + 0x4e0811a1), 21); /* 60 */
a = b + ROT_LEFT((a + I(b, c, d) + X[4] + 0xf7537e82), 6); /* 61 */
d = a + ROT_LEFT((d + I(a, b, c) + X[11] + 0xbd3af235), 10); /* 62 */
c = d + ROT_LEFT((c + I(d, a, b) + X[2] + 0x2ad7d2bb), 15); /* 63 */
b = c + ROT_LEFT((b + I(c, d, a) + X[9] + 0xeb86d391), 21); /* 64 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
static int
calculateDigestFromBuffer(uint8 *b, uint32 len, uint8 sum[16])
{
register uint32 i,
j,
k,
newI;
uint32 l;
uint8 *input;
register uint32 *wbp;
uint32 workBuff[16],
state[4];
l = len;
state[0] = 0x67452301;
state[1] = 0xEFCDAB89;
state[2] = 0x98BADCFE;
state[3] = 0x10325476;
if ((input = createPaddedCopyWithLength(b, &l)) == NULL)
return 0;
for (i = 0;;)
{
if ((newI = i + 16 * 4) > l)
break;
k = i + 3;
for (j = 0; j < 16; j++)
{
wbp = (workBuff + j);
*wbp = input[k--];
*wbp <<= 8;
*wbp |= input[k--];
*wbp <<= 8;
*wbp |= input[k--];
*wbp <<= 8;
*wbp |= input[k];
k += 7;
}
doTheRounds(workBuff, state);
i = newI;
}
free(input);
j = 0;
for (i = 0; i < 4; i++)
{
k = state[i];
sum[j++] = (k & 0xff);
k >>= 8;
sum[j++] = (k & 0xff);
k >>= 8;
sum[j++] = (k & 0xff);
k >>= 8;
sum[j++] = (k & 0xff);
}
return 1;
}
static void
bytesToHex(uint8 b[16], char *s)
{
static char *hex = "0123456789abcdef";
int q,
w;
for (q = 0, w = 0; q < 16; q++)
{
s[w++] = hex[(b[q] >> 4) & 0x0F];
s[w++] = hex[b[q] & 0x0F];
}
s[w] = '\0';
}
/*
* PUBLIC FUNCTIONS
*/
/*
* md5_hash
*
* Calculates the MD5 sum of the bytes in a buffer.
*
* SYNOPSIS #include "crypt.h"
* int md5_hash(const void *buff, size_t len, char *hexsum)
*
* INPUT buff the buffer containing the bytes that you want
* the MD5 sum of.
* len number of bytes in the buffer.
*
* OUTPUT hexsum the MD5 sum as a '\0'-terminated string of
* hexadecimal digits. an MD5 sum is 16 bytes long.
* each byte is represented by two heaxadecimal
* characters. you thus need to provide an array
* of 33 characters, including the trailing '\0'.
*
* RETURNS 0 on failure (out of memory for internal buffers) or
* non-zero on success.
*
* STANDARDS MD5 is described in RFC 1321.
*
* AUTHOR Sverre H. Huseby <sverrehu@online.no>
*
*/
bool
md5_hash(const void *buff, size_t len, char *hexsum)
{
uint8 sum[16];
if (!calculateDigestFromBuffer((uint8 *) buff, len, sum))
return false;
bytesToHex(sum, hexsum);
return true;
}
/*
* Computes MD5 checksum of "passwd" (a null-terminated string) followed
* by "salt" (which need not be null-terminated).
*
* Output format is "md5" followed by a 32-hex-digit MD5 checksum.
* Hence, the output buffer "buf" must be at least 36 bytes long.
*
* Returns TRUE if okay, FALSE on error (out of memory).
*/
bool
EncryptMD5(const char *passwd, const char *salt, size_t salt_len,
char *buf)
{
size_t passwd_len = strlen(passwd);
char *crypt_buf = palloc(passwd_len + salt_len);
bool ret;
/*
* Place salt at the end because it may be known by users trying to
* crack the MD5 output.
*/
strcpy(crypt_buf, passwd);
memcpy(crypt_buf + passwd_len, salt, salt_len);
strcpy(buf, "md5");
ret = md5_hash(crypt_buf, passwd_len + salt_len, buf + 3);
pfree(crypt_buf);
return ret;
}
/* File: md5.h
*
* Description: See "md5.h"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __MD5_H__
#define __MD5_H__
#include "psqlodbc.h"
#include <stdlib.h>
#include <string.h>
#define MD5_PASSWD_LEN 35
/* From c.h */
#ifndef __BEOS__
#ifndef __cplusplus
#ifndef bool
typedef char bool;
#endif
#ifndef true
#define true ((bool) 1)
#endif
#ifndef false
#define false ((bool) 0)
#endif
#endif /* not C++ */
#endif /* __BEOS__ */
/* Also defined in include/c.h */
#ifndef HAVE_UINT8
typedef unsigned char uint8; /* == 8 bits */
typedef unsigned short uint16; /* == 16 bits */
typedef unsigned int uint32; /* == 32 bits */
#endif /* not HAVE_UINT8 */
extern bool md5_hash(const void *buff, size_t len, char *hexsum);
extern bool EncryptMD5(const char *passwd, const char *salt,
size_t salt_len, char *buf);
#endif
/*-------
* Module: misc.c
*
* Description: This module contains miscellaneous routines
* such as for debugging/logging and string functions.
*
* Classes: n/a
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "psqlodbc.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#ifndef WIN32
#if HAVE_PWD_H
#include <pwd.h>
#endif
#include <sys/types.h>
#include <unistd.h>
#else
#include <process.h> /* Byron: is this where Windows keeps def.
* of getpid ? */
#endif
extern GLOBAL_VALUES globals;
void generate_filename(const char *, const char *, char *);
void
generate_filename(const char *dirname, const char *prefix, char *filename)
{
int pid = 0;
#ifndef WIN32
struct passwd *ptr = 0;
ptr = getpwuid(getuid());
#endif
pid = getpid();
if (dirname == 0 || filename == 0)
return;
strcpy(filename, dirname);
strcat(filename, DIRSEPARATOR);
if (prefix != 0)
strcat(filename, prefix);
#ifndef WIN32
strcat(filename, ptr->pw_name);
#endif
sprintf(filename, "%s%u%s", filename, pid, ".log");
return;
}
static int mylog_on = 0,
qlog_on = 0;
void
logs_on_off(int cnopen, int mylog_onoff, int qlog_onoff)
{
static int mylog_on_count = 0,
mylog_off_count = 0,
qlog_on_count = 0,
qlog_off_count = 0;
if (mylog_onoff)
mylog_on_count += cnopen;
else
mylog_off_count += cnopen;
if (mylog_on_count > 0)
mylog_on = 1;
else if (mylog_off_count > 0)
mylog_on = 0;
else
mylog_on = globals.debug;
if (qlog_onoff)
qlog_on_count += cnopen;
else
qlog_off_count += cnopen;
if (qlog_on_count > 0)
qlog_on = 1;
else if (qlog_off_count > 0)
qlog_on = 0;
else
qlog_on = globals.commlog;
}
#ifdef MY_LOG
void
mylog(char *fmt,...)
{
va_list args;
char filebuf[80];
static FILE *LOGFP = NULL;
if (mylog_on)
{
va_start(args, fmt);
if (!LOGFP)
{
generate_filename(MYLOGDIR, MYLOGFILE, filebuf);
LOGFP = fopen(filebuf, PG_BINARY_W);
setbuf(LOGFP, NULL);
}
if (LOGFP)
vfprintf(LOGFP, fmt, args);
va_end(args);
}
}
#endif
#ifdef Q_LOG
void
qlog(char *fmt,...)
{
va_list args;
char filebuf[80];
static FILE *LOGFP = NULL;
if (qlog_on)
{
va_start(args, fmt);
if (!LOGFP)
{
generate_filename(QLOGDIR, QLOGFILE, filebuf);
LOGFP = fopen(filebuf, PG_BINARY_W);
setbuf(LOGFP, NULL);
}
if (LOGFP)
vfprintf(LOGFP, fmt, args);
va_end(args);
}
}
#endif
/*
* returns STRCPY_FAIL, STRCPY_TRUNCATED, or #bytes copied
* (not including null term)
*/
int
my_strcpy(char *dst, int dst_len, const char *src, int src_len)
{
if (dst_len <= 0)
return STRCPY_FAIL;
if (src_len == SQL_NULL_DATA)
{
dst[0] = '\0';
return STRCPY_NULL;
}
else if (src_len == SQL_NTS)
src_len = strlen(src);
if (src_len <= 0)
return STRCPY_FAIL;
else
{
if (src_len < dst_len)
{
memcpy(dst, src, src_len);
dst[src_len] = '\0';
}
else
{
memcpy(dst, src, dst_len - 1);
dst[dst_len - 1] = '\0'; /* truncated */
return STRCPY_TRUNCATED;
}
}
return strlen(dst);
}
/*
* strncpy copies up to len characters, and doesn't terminate
* the destination string if src has len characters or more.
* instead, I want it to copy up to len-1 characters and always
* terminate the destination string.
*/
char *
strncpy_null(char *dst, const char *src, int len)
{
int i;
if (NULL != dst)
{
/* Just in case, check for special lengths */
if (len == SQL_NULL_DATA)
{
dst[0] = '\0';
return NULL;
}
else if (len == SQL_NTS)
len = strlen(src) + 1;
for (i = 0; src[i] && i < len - 1; i++)
dst[i] = src[i];
if (len > 0)
dst[i] = '\0';
}
return dst;
}
/*------
* Create a null terminated string (handling the SQL_NTS thing):
* 1. If buf is supplied, place the string in there
* (assumes enough space) and return buf.
* 2. If buf is not supplied, malloc space and return this string
*------
*/
char *
make_string(const char *s, int len, char *buf)
{
int length;
char *str;
if (s && (len > 0 || (len == SQL_NTS && strlen(s) > 0)))
{
length = (len > 0) ? len : strlen(s);
if (buf)
{
strncpy_null(buf, s, length + 1);
return buf;
}
str = malloc(length + 1);
if (!str)
return NULL;
strncpy_null(str, s, length + 1);
return str;
}
return NULL;
}
/*
* Concatenate a single formatted argument to a given buffer handling the SQL_NTS thing.
* "fmt" must contain somewhere in it the single form '%.*s'.
* This is heavily used in creating queries for info routines (SQLTables, SQLColumns).
* This routine could be modified to use vsprintf() to handle multiple arguments.
*/
char *
my_strcat(char *buf, const char *fmt, const char *s, int len)
{
if (s && (len > 0 || (len == SQL_NTS && strlen(s) > 0)))
{
int length = (len > 0) ? len : strlen(s);
int pos = strlen(buf);
sprintf(&buf[pos], fmt, length, s);
return buf;
}
return NULL;
}
void
remove_newlines(char *string)
{
unsigned int i;
for (i = 0; i < strlen(string); i++)
{
if ((string[i] == '\n') ||
(string[i] == '\r'))
string[i] = ' ';
}
}
char *
trim(char *s)
{
int i;
for (i = strlen(s) - 1; i >= 0; i--)
{
if (s[i] == ' ')
s[i] = '\0';
else
break;
}
return s;
}
/* File: misc.h
*
* Description: See "misc.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __MISC_H__
#define __MISC_H__
#include "psqlodbc.h"
#include <stdio.h>
/* Uncomment MY_LOG define to compile in the mylog() statements.
Then, debug logging will occur if 'Debug' is set to 1 in the ODBCINST.INI
portion of the registry. You may have to manually add this key.
This logfile is intended for development use, not for an end user!
*/
#define MY_LOG
/* Uncomment Q_LOG to compile in the qlog() statements (Communications log, i.e. CommLog).
This logfile contains serious log statements that are intended for an
end user to be able to read and understand. It is controlled by the
'CommLog' flag in the ODBCINST.INI portion of the registry (see above),
which is manipulated on the setup/connection dialog boxes.
*/
#define Q_LOG
#ifdef MY_LOG
#define MYLOGFILE "mylog_"
#ifndef WIN32
#define MYLOGDIR "/tmp"
#else
#define MYLOGDIR "c:"
#endif
extern void mylog(char *fmt,...);
#else
#ifndef WIN32
#define mylog(args...) /* GNU convention for variable arguments */
#else
#define mylog /* mylog */
#endif
#endif
#ifdef Q_LOG
#define QLOGFILE "psqlodbc_"
#ifndef WIN32
#define QLOGDIR "/tmp"
#else
#define QLOGDIR "c:"
#endif
extern void qlog(char *fmt,...);
#else
#ifndef WIN32
#define qlog(args...) /* GNU convention for variable arguments */
#else
#define qlog /* qlog */
#endif
#endif
#ifndef WIN32
#define DIRSEPARATOR "/"
#else
#define DIRSEPARATOR "\\"
#endif
#ifdef WIN32
#define PG_BINARY O_BINARY
#define PG_BINARY_R "rb"
#define PG_BINARY_W "wb"
#else
#define PG_BINARY 0
#define PG_BINARY_R "r"
#define PG_BINARY_W "w"
#endif
void remove_newlines(char *string);
char *strncpy_null(char *dst, const char *src, int len);
char *trim(char *string);
char *make_string(const char *s, int len, char *buf);
char *my_strcat(char *buf, const char *fmt, const char *s, int len);
/* defines for return value of my_strcpy */
#define STRCPY_SUCCESS 1
#define STRCPY_FAIL 0
#define STRCPY_TRUNCATED (-1)
#define STRCPY_NULL (-2)
int my_strcpy(char *dst, int dst_len, const char *src, int src_len);
#endif
/*--------
* Module : multibyte.c
*
* Description: Mlutibyte related additional function.
*
* Create 2001-03-03 Eiji Tokuya
*--------
*/
#include "multibyte.h"
#include <string.h>
int multibyte_client_encoding; /* Multibyte Client Encoding. */
int multibyte_status; /* Multibyte Odds and ends character. */
unsigned char *
multibyte_strchr(unsigned char *s, unsigned char c)
{
int mb_st = 0,
i = 0;
while (!(mb_st == 0 && (s[i] == c || s[i] == 0)))
{
if (s[i] == 0)
return (0);
switch (multibyte_client_encoding)
{
case SJIS:
{
if (mb_st < 2 && s[i] > 0x80 && !(s[i] > 0x9f && s[i] < 0xe0))
mb_st = 2;
else if (mb_st == 2)
mb_st = 1;
else
mb_st = 0;
}
break;
/* Chinese Big5 Support. */
case BIG5:
{
if (mb_st < 2 && s[i] > 0xA0)
mb_st = 2;
else if (mb_st == 2)
mb_st = 1;
else
mb_st = 0;
}
break;
default:
mb_st = 0;
}
i++;
}
#ifdef _DEBUG
qlog("i = %d\n", i);
#endif
return (s + i);
}
void
multibyte_init(void)
{
multibyte_status = 0;
}
unsigned char *
check_client_encoding(unsigned char *str)
{
if (strstr(str, "%27SJIS%27") ||
strstr(str, "%27Shift_JIS%27") ||
strstr(str, "'SJIS'") ||
strstr(str, "'sjis'") ||
strstr(str, "'Shift_JIS'"))
{
multibyte_client_encoding = SJIS;
return ("SJIS");
}
if (strstr(str, "%27BIG5%27") ||
strstr(str, "%27Big5%27") ||
strstr(str, "'BIG5'") ||
strstr(str, "'big5'") ||
strstr(str, "'Big5'"))
{
multibyte_client_encoding = BIG5;
return ("BIG5");
}
return ("OTHER");
}
/*--------
* Multibyte Status Function.
* Input char
* Output 0 : 1 Byte Character.
* 1 : MultibyteCharacter Last Byte.
* N : MultibyteCharacter Fast or Middle Byte.
*--------
*/
int
multibyte_char_check(unsigned char s)
{
switch (multibyte_client_encoding)
{
/* Japanese Shift-JIS(CP932) Support. */
case SJIS:
{
if (multibyte_status < 2 && s > 0x80 && !(s > 0x9f && s < 0xE0))
multibyte_status = 2;
else if (multibyte_status == 2)
multibyte_status = 1;
else
multibyte_status = 0;
}
break;
/* Chinese Big5(CP950) Support. */
case BIG5:
{
if (multibyte_status < 2 && s > 0xA0)
multibyte_status = 2;
else if (multibyte_status == 2)
multibyte_status = 1;
else
multibyte_status = 0;
}
break;
default:
multibyte_status = 0;
}
#ifdef _DEBUG
qlog("multibyte_client_encoding = %d s = 0x%02X multibyte_stat = %d\n", multibyte_client_encoding, s, multibyte_status);
#endif
return (multibyte_status);
}
/*
*
* Multibyte library header ( psqlODBC Only )
*
*/
#include "psqlodbc.h"
/* PostgreSQL client encoding */
#define SQL_ASCII 0 /* SQL/ASCII */
#define EUC_JP 1 /* EUC for Japanese */
#define EUC_CN 2 /* EUC for Chinese */
#define EUC_KR 3 /* EUC for Korean */
#define EUC_TW 4 /* EUC for Taiwan */
#define UNICODE 5 /* Unicode UTF-8 */
#define MULE_INTERNAL 6 /* Mule internal code */
#define LATIN1 7 /* ISO-8859 Latin 1 */
#define LATIN2 8 /* ISO-8859 Latin 2 */
#define LATIN3 9 /* ISO-8859 Latin 3 */
#define LATIN4 10 /* ISO-8859 Latin 4 */
#define LATIN5 11 /* ISO-8859 Latin 5 */
#define LATIN6 12 /* ISO-8859 Latin 6 */
#define LATIN7 13 /* ISO-8859 Latin 7 */
#define LATIN8 14 /* ISO-8859 Latin 8 */
#define LATIN9 15 /* ISO-8859 Latin 9 */
#define KOI8 16 /* KOI8-R/U */
#define WIN 17 /* windows-1251 */
#define ALT 18 /* Alternativny Variant (MS-DOS CP866) */
#define SJIS 32 /* Shift JIS */
#define BIG5 33 /* Big5 */
#define WIN1250 34 /* windows-1250 */
extern int multibyte_client_encoding; /* Multibyte client encoding. */
extern int multibyte_status; /* Multibyte charcter status. */
void multibyte_init(void);
unsigned char *check_client_encoding(unsigned char *str);
int multibyte_char_check(unsigned char s);
unsigned char *multibyte_strchr(unsigned char *s, unsigned char c);
/********************************************************************
PSQLODBC.DLL - A library to talk to the PostgreSQL DBMS using ODBC.
Copyright (C) 1998; Insight Distribution Systems
The code contained in this library is based on code written by
Christian Czezatke and Dan McGuirk, (C) 1996.
This library 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.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTIBILITY 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 this library (see "license.txt"); if not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
02139, USA.
How to contact the author:
email: byronn@insightdist.com (Byron Nikolaidis)
***********************************************************************/
/*-------
* Module: odbcapi.c
*
* Description: This module contains routines related to
* preparing and executing an SQL statement.
*
* Classes: n/a
*
* API functions: SQLAllocConnect, SQLAllocEnv, SQLAllocStmt,
SQLBindCol, SQLCancel, SQLColumns, SQLConnect,
SQLDataSources, SQLDescribeCol, SQLDisconnect,
SQLError, SQLExecDirect, SQLExecute, SQLFetch,
SQLFreeConnect, SQLFreeEnv, SQLFreeStmt,
SQLGetConnectOption, SQLGetCursorName, SQLGetData,
SQLGetFunctions, SQLGetInfo, SQLGetStmtOption,
SQLGetTypeInfo, SQLNumResultCols, SQLParamData,
SQLPrepare, SQLPutData, SQLRowCount,
SQLSetConnectOption, SQLSetCursorName, SQLSetParam,
SQLSetStmtOption, SQLSpecialColumns, SQLStatistics,
SQLTables, SQLTransact, SQLColAttributes,
SQLColumnPrivileges, SQLDescribeParam, SQLExtendedFetch,
SQLForeignKeys, SQLMoreResults, SQLNativeSql,
SQLNumParams, SQLParamOptions, SQLPrimaryKeys,
SQLProcedureColumns, SQLProcedures, SQLSetPos,
SQLTablePrivileges, SQLBindParameter
*-------
*/
#include "psqlodbc.h"
#include <stdio.h>
#include <string.h>
#include "pgapifunc.h"
#include "connection.h"
#include "statement.h"
RETCODE SQL_API
SQLAllocConnect(HENV EnvironmentHandle,
HDBC FAR * ConnectionHandle)
{
mylog("[SQLAllocConnect]");
return PGAPI_AllocConnect(EnvironmentHandle, ConnectionHandle);
}
RETCODE SQL_API
SQLAllocEnv(HENV FAR * EnvironmentHandle)
{
mylog("[SQLAllocEnv]");
return PGAPI_AllocEnv(EnvironmentHandle);
}
RETCODE SQL_API
SQLAllocStmt(HDBC ConnectionHandle,
HSTMT *StatementHandle)
{
mylog("[SQLAllocStmt]");
return PGAPI_AllocStmt(ConnectionHandle, StatementHandle);
}
RETCODE SQL_API
SQLBindCol(HSTMT StatementHandle,
SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
PTR TargetValue, SQLINTEGER BufferLength,
SQLINTEGER *StrLen_or_Ind)
{
mylog("[SQLBindCol]");
return PGAPI_BindCol(StatementHandle, ColumnNumber,
TargetType, TargetValue, BufferLength, StrLen_or_Ind);
}
RETCODE SQL_API
SQLCancel(HSTMT StatementHandle)
{
mylog("[SQLCancel]");
return PGAPI_Cancel(StatementHandle);
}
RETCODE SQL_API
SQLColumns(HSTMT StatementHandle,
SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
SQLCHAR *TableName, SQLSMALLINT NameLength3,
SQLCHAR *ColumnName, SQLSMALLINT NameLength4)
{
mylog("[SQLColumns]");
return PGAPI_Columns(StatementHandle, CatalogName, NameLength1,
SchemaName, NameLength2, TableName, NameLength3,
ColumnName, NameLength4);
}
RETCODE SQL_API
SQLConnect(HDBC ConnectionHandle,
SQLCHAR *ServerName, SQLSMALLINT NameLength1,
SQLCHAR *UserName, SQLSMALLINT NameLength2,
SQLCHAR *Authentication, SQLSMALLINT NameLength3)
{
mylog("[SQLConnect]");
return PGAPI_Connect(ConnectionHandle, ServerName, NameLength1,
UserName, NameLength2, Authentication, NameLength3);
}
RETCODE SQL_API
SQLDriverConnect(HDBC hdbc,
HWND hwnd,
UCHAR FAR * szConnStrIn,
SWORD cbConnStrIn,
UCHAR FAR * szConnStrOut,
SWORD cbConnStrOutMax,
SWORD FAR * pcbConnStrOut,
UWORD fDriverCompletion)
{
mylog("[SQLDriverConnect]");
return PGAPI_DriverConnect(hdbc, hwnd, szConnStrIn, cbConnStrIn,
szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion);
}
RETCODE SQL_API
SQLBrowseConnect(
HDBC hdbc,
SQLCHAR *szConnStrIn,
SQLSMALLINT cbConnStrIn,
SQLCHAR *szConnStrOut,
SQLSMALLINT cbConnStrOutMax,
SQLSMALLINT *pcbConnStrOut)
{
mylog("[SQLBrowseConnect]");
return PGAPI_BrowseConnect(hdbc, szConnStrIn, cbConnStrIn,
szConnStrOut, cbConnStrOutMax, pcbConnStrOut);
}
RETCODE SQL_API
SQLDataSources(HENV EnvironmentHandle,
SQLUSMALLINT Direction, SQLCHAR *ServerName,
SQLSMALLINT BufferLength1, SQLSMALLINT *NameLength1,
SQLCHAR *Description, SQLSMALLINT BufferLength2,
SQLSMALLINT *NameLength2)
{
mylog("[SQLDataSources]");
/*
* return PGAPI_DataSources(EnvironmentHandle, Direction, ServerName,
* BufferLength1, NameLength1, Description, BufferLength2,
* NameLength2);
*/
return SQL_ERROR;
}
RETCODE SQL_API
SQLDescribeCol(HSTMT StatementHandle,
SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName,
SQLSMALLINT BufferLength, SQLSMALLINT *NameLength,
SQLSMALLINT *DataType, SQLUINTEGER *ColumnSize,
SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable)
{
mylog("[SQLDescribeCol]");
return PGAPI_DescribeCol(StatementHandle, ColumnNumber,
ColumnName, BufferLength, NameLength,
DataType, ColumnSize, DecimalDigits, Nullable);
}
RETCODE SQL_API
SQLDisconnect(HDBC ConnectionHandle)
{
mylog("[SQLDisconnect]");
return PGAPI_Disconnect(ConnectionHandle);
}
RETCODE SQL_API
SQLError(HENV EnvironmentHandle,
HDBC ConnectionHandle, HSTMT StatementHandle,
SQLCHAR *Sqlstate, SQLINTEGER *NativeError,
SQLCHAR *MessageText, SQLSMALLINT BufferLength,
SQLSMALLINT *TextLength)
{
mylog("[SQLError]");
return PGAPI_Error(EnvironmentHandle, ConnectionHandle, StatementHandle,
Sqlstate, NativeError, MessageText, BufferLength, TextLength);
}
RETCODE SQL_API
SQLExecDirect(HSTMT StatementHandle,
SQLCHAR *StatementText, SQLINTEGER TextLength)
{
mylog("[SQLExecDirect]");
return PGAPI_ExecDirect(StatementHandle, StatementText, TextLength);
}
RETCODE SQL_API
SQLExecute(HSTMT StatementHandle)
{
mylog("[SQLExecute]");
return PGAPI_Execute(StatementHandle);
}
RETCODE SQL_API
SQLFetch(HSTMT StatementHandle)
{
static char *func = "SQLFetch";
#if (ODBCVER >= 0x0300)
StatementClass *stmt = (StatementClass *) StatementHandle;
ConnectionClass *conn = SC_get_conn(stmt);
if (conn->driver_version >= 0x0300)
{
SQLUSMALLINT *rowStatusArray = stmt->options.rowStatusArray;
SQLINTEGER *pcRow = stmt->options.rowsFetched;
mylog("[[%s]]", func);
return PGAPI_ExtendedFetch(StatementHandle, SQL_FETCH_NEXT, 0,
pcRow, rowStatusArray);
}
#endif
mylog("[%s]", func);
return PGAPI_Fetch(StatementHandle);
}
RETCODE SQL_API
SQLFreeConnect(HDBC ConnectionHandle)
{
mylog("[SQLFreeStmt]");
return PGAPI_FreeConnect(ConnectionHandle);
}
RETCODE SQL_API
SQLFreeEnv(HENV EnvironmentHandle)
{
mylog("[SQLFreeEnv]");
return PGAPI_FreeEnv(EnvironmentHandle);
}
RETCODE SQL_API
SQLFreeStmt(HSTMT StatementHandle,
SQLUSMALLINT Option)
{
mylog("[SQLFreeStmt]");
return PGAPI_FreeStmt(StatementHandle, Option);
}
RETCODE SQL_API
SQLGetConnectOption(HDBC ConnectionHandle,
SQLUSMALLINT Option, PTR Value)
{
mylog("[SQLGetConnectOption]");
return PGAPI_GetConnectOption(ConnectionHandle, Option, Value);
}
RETCODE SQL_API
SQLGetCursorName(HSTMT StatementHandle,
SQLCHAR *CursorName, SQLSMALLINT BufferLength,
SQLSMALLINT *NameLength)
{
mylog("[SQLGetCursorName]");
return PGAPI_GetCursorName(StatementHandle, CursorName, BufferLength,
NameLength);
}
RETCODE SQL_API
SQLGetData(HSTMT StatementHandle,
SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
PTR TargetValue, SQLINTEGER BufferLength,
SQLINTEGER *StrLen_or_Ind)
{
mylog("[SQLGetData]");
return PGAPI_GetData(StatementHandle, ColumnNumber, TargetType,
TargetValue, BufferLength, StrLen_or_Ind);
}
RETCODE SQL_API
SQLGetFunctions(HDBC ConnectionHandle,
SQLUSMALLINT FunctionId, SQLUSMALLINT *Supported)
{
mylog("[SQLGetFunctions]");
#if (ODBCVER >= 0x0300)
if (FunctionId == SQL_API_ODBC3_ALL_FUNCTIONS)
return PGAPI_GetFunctions30(ConnectionHandle, FunctionId, Supported);
#endif
return PGAPI_GetFunctions(ConnectionHandle, FunctionId, Supported);
}
RETCODE SQL_API
SQLGetInfo(HDBC ConnectionHandle,
SQLUSMALLINT InfoType, PTR InfoValue,
SQLSMALLINT BufferLength, SQLSMALLINT *StringLength)
{
#if (ODBCVER >= 0x0300)
RETCODE ret;
mylog("[SQLGetInfo(30)]");
if ((ret = PGAPI_GetInfo(ConnectionHandle, InfoType, InfoValue,
BufferLength, StringLength)) == SQL_ERROR)
{
if (((ConnectionClass *) ConnectionHandle)->driver_version >= 0x0300)
return PGAPI_GetInfo30(ConnectionHandle, InfoType, InfoValue,
BufferLength, StringLength);
}
return ret;
#else
mylog("[SQLGetInfo]");
return PGAPI_GetInfo(ConnectionHandle, InfoType, InfoValue,
BufferLength, StringLength);
#endif
}
RETCODE SQL_API
SQLGetStmtOption(HSTMT StatementHandle,
SQLUSMALLINT Option, PTR Value)
{
mylog("[SQLGetStmtOption]");
return PGAPI_GetStmtOption(StatementHandle, Option, Value);
}
RETCODE SQL_API
SQLGetTypeInfo(HSTMT StatementHandle,
SQLSMALLINT DataType)
{
mylog("[SQLGetTypeInfo]");
return PGAPI_GetTypeInfo(StatementHandle, DataType);
}
RETCODE SQL_API
SQLNumResultCols(HSTMT StatementHandle,
SQLSMALLINT *ColumnCount)
{
mylog("[SQLNumResultCols]");
return PGAPI_NumResultCols(StatementHandle, ColumnCount);
}
RETCODE SQL_API
SQLParamData(HSTMT StatementHandle,
PTR *Value)
{
mylog("[SQLParamData]");
return PGAPI_ParamData(StatementHandle, Value);
}
RETCODE SQL_API
SQLPrepare(HSTMT StatementHandle,
SQLCHAR *StatementText, SQLINTEGER TextLength)
{
mylog("[SQLPrepare]");
return PGAPI_Prepare(StatementHandle, StatementText, TextLength);
}
RETCODE SQL_API
SQLPutData(HSTMT StatementHandle,
PTR Data, SQLINTEGER StrLen_or_Ind)
{
mylog("[SQLPutData]");
return PGAPI_PutData(StatementHandle, Data, StrLen_or_Ind);
}
RETCODE SQL_API
SQLRowCount(HSTMT StatementHandle,
SQLINTEGER *RowCount)
{
mylog("[SQLRowCount]");
return PGAPI_RowCount(StatementHandle, RowCount);
}
RETCODE SQL_API
SQLSetConnectOption(HDBC ConnectionHandle,
SQLUSMALLINT Option, SQLUINTEGER Value)
{
mylog("[SQLSetConnectionOption]");
return PGAPI_SetConnectOption(ConnectionHandle, Option, Value);
}
RETCODE SQL_API
SQLSetCursorName(HSTMT StatementHandle,
SQLCHAR *CursorName, SQLSMALLINT NameLength)
{
mylog("[SQLSetCursorName]");
return PGAPI_SetCursorName(StatementHandle, CursorName, NameLength);
}
RETCODE SQL_API
SQLSetParam(HSTMT StatementHandle,
SQLUSMALLINT ParameterNumber, SQLSMALLINT ValueType,
SQLSMALLINT ParameterType, SQLUINTEGER LengthPrecision,
SQLSMALLINT ParameterScale, PTR ParameterValue,
SQLINTEGER *StrLen_or_Ind)
{
mylog("[SQLSetParam]");
/*
* return PGAPI_SetParam(StatementHandle, ParameterNumber, ValueType,
* ParameterType, LengthPrecision, ParameterScale, ParameterValue,
* StrLen_or_Ind);
*/
return SQL_ERROR;
}
RETCODE SQL_API
SQLSetStmtOption(HSTMT StatementHandle,
SQLUSMALLINT Option, SQLUINTEGER Value)
{
mylog("[SQLSetStmtOption]");
return PGAPI_SetStmtOption(StatementHandle, Option, Value);
}
RETCODE SQL_API
SQLSpecialColumns(HSTMT StatementHandle,
SQLUSMALLINT IdentifierType, SQLCHAR *CatalogName,
SQLSMALLINT NameLength1, SQLCHAR *SchemaName,
SQLSMALLINT NameLength2, SQLCHAR *TableName,
SQLSMALLINT NameLength3, SQLUSMALLINT Scope,
SQLUSMALLINT Nullable)
{
mylog("[SQLSpecialColumns]");
return PGAPI_SpecialColumns(StatementHandle, IdentifierType, CatalogName,
NameLength1, SchemaName, NameLength2, TableName, NameLength3,
Scope, Nullable);
}
RETCODE SQL_API
SQLStatistics(HSTMT StatementHandle,
SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
SQLCHAR *TableName, SQLSMALLINT NameLength3,
SQLUSMALLINT Unique, SQLUSMALLINT Reserved)
{
mylog("[SQLStatistics]");
return PGAPI_Statistics(StatementHandle, CatalogName, NameLength1,
SchemaName, NameLength2, TableName, NameLength3, Unique,
Reserved);
}
RETCODE SQL_API
SQLTables(HSTMT StatementHandle,
SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
SQLCHAR *TableName, SQLSMALLINT NameLength3,
SQLCHAR *TableType, SQLSMALLINT NameLength4)
{
mylog("[SQLTables]");
return PGAPI_Tables(StatementHandle, CatalogName, NameLength1,
SchemaName, NameLength2, TableName, NameLength3,
TableType, NameLength4);
}
RETCODE SQL_API
SQLTransact(HENV EnvironmentHandle,
HDBC ConnectionHandle, SQLUSMALLINT CompletionType)
{
mylog("[SQLTransact]");
return PGAPI_Transact(EnvironmentHandle, ConnectionHandle, CompletionType);
}
RETCODE SQL_API
SQLColAttributes(
HSTMT hstmt,
SQLUSMALLINT icol,
SQLUSMALLINT fDescType,
PTR rgbDesc,
SQLSMALLINT cbDescMax,
SQLSMALLINT *pcbDesc,
SQLINTEGER *pfDesc)
{
mylog("[SQLColAttributes]");
return PGAPI_ColAttributes(hstmt, icol, fDescType, rgbDesc,
cbDescMax, pcbDesc, pfDesc);
}
RETCODE SQL_API
SQLColumnPrivileges(
HSTMT hstmt,
SQLCHAR *szCatalogName,
SQLSMALLINT cbCatalogName,
SQLCHAR *szSchemaName,
SQLSMALLINT cbSchemaName,
SQLCHAR *szTableName,
SQLSMALLINT cbTableName,
SQLCHAR *szColumnName,
SQLSMALLINT cbColumnName)
{
mylog("[SQLColumnPrivileges]");
return PGAPI_ColumnPrivileges(hstmt, szCatalogName, cbCatalogName,
szSchemaName, cbSchemaName, szTableName, cbTableName,
szColumnName, cbColumnName);
}
RETCODE SQL_API
SQLDescribeParam(
HSTMT hstmt,
SQLUSMALLINT ipar,
SQLSMALLINT *pfSqlType,
SQLUINTEGER *pcbParamDef,
SQLSMALLINT *pibScale,
SQLSMALLINT *pfNullable)
{
mylog("[SQLDescribeParam]");
return PGAPI_DescribeParam(hstmt, ipar, pfSqlType, pcbParamDef,
pibScale, pfNullable);
}
RETCODE SQL_API
SQLExtendedFetch(
HSTMT hstmt,
SQLUSMALLINT fFetchType,
SQLINTEGER irow,
SQLUINTEGER *pcrow,
SQLUSMALLINT *rgfRowStatus)
{
mylog("[SQLExtendedFetch]");
return PGAPI_ExtendedFetch(hstmt, fFetchType, irow, pcrow, rgfRowStatus);
}
RETCODE SQL_API
SQLForeignKeys(
HSTMT hstmt,
SQLCHAR *szPkCatalogName,
SQLSMALLINT cbPkCatalogName,
SQLCHAR *szPkSchemaName,
SQLSMALLINT cbPkSchemaName,
SQLCHAR *szPkTableName,
SQLSMALLINT cbPkTableName,
SQLCHAR *szFkCatalogName,
SQLSMALLINT cbFkCatalogName,
SQLCHAR *szFkSchemaName,
SQLSMALLINT cbFkSchemaName,
SQLCHAR *szFkTableName,
SQLSMALLINT cbFkTableName)
{
mylog("[SQLForeignKeys]");
return PGAPI_ForeignKeys(hstmt, szPkCatalogName, cbPkCatalogName,
szPkSchemaName, cbPkSchemaName, szPkTableName,
cbPkTableName, szFkCatalogName, cbFkCatalogName,
szFkSchemaName, cbFkSchemaName, szFkTableName, cbFkTableName);
}
RETCODE SQL_API
SQLMoreResults(HSTMT hstmt)
{
mylog("[SQLMoreResults]");
return PGAPI_MoreResults(hstmt);
}
RETCODE SQL_API
SQLNativeSql(
HDBC hdbc,
SQLCHAR *szSqlStrIn,
SQLINTEGER cbSqlStrIn,
SQLCHAR *szSqlStr,
SQLINTEGER cbSqlStrMax,
SQLINTEGER *pcbSqlStr)
{
mylog("[SQLNativeSql]");
return PGAPI_NativeSql(hdbc, szSqlStrIn, cbSqlStrIn, szSqlStr,
cbSqlStrMax, pcbSqlStr);
}
RETCODE SQL_API
SQLNumParams(
HSTMT hstmt,
SQLSMALLINT *pcpar)
{
mylog("[SQLNumParams]");
return PGAPI_NumParams(hstmt, pcpar);
}
RETCODE SQL_API
SQLParamOptions(
HSTMT hstmt,
SQLUINTEGER crow,
SQLUINTEGER *pirow)
{
mylog("[SQLParamOptions]");
return PGAPI_ParamOptions(hstmt, crow, pirow);
}
RETCODE SQL_API
SQLPrimaryKeys(
HSTMT hstmt,
SQLCHAR *szCatalogName,
SQLSMALLINT cbCatalogName,
SQLCHAR *szSchemaName,
SQLSMALLINT cbSchemaName,
SQLCHAR *szTableName,
SQLSMALLINT cbTableName)
{
mylog("[SQLPrimaryKeys]");
return PGAPI_PrimaryKeys(hstmt, szCatalogName, cbCatalogName,
szSchemaName, cbSchemaName, szTableName, cbTableName);
}
RETCODE SQL_API
SQLProcedureColumns(
HSTMT hstmt,
SQLCHAR *szCatalogName,
SQLSMALLINT cbCatalogName,
SQLCHAR *szSchemaName,
SQLSMALLINT cbSchemaName,
SQLCHAR *szProcName,
SQLSMALLINT cbProcName,
SQLCHAR *szColumnName,
SQLSMALLINT cbColumnName)
{
mylog("[SQLProcedureColumns]");
return PGAPI_ProcedureColumns(hstmt, szCatalogName, cbCatalogName,
szSchemaName, cbSchemaName, szProcName, cbProcName,
szColumnName, cbColumnName);
}
RETCODE SQL_API
SQLProcedures(
HSTMT hstmt,
SQLCHAR *szCatalogName,
SQLSMALLINT cbCatalogName,
SQLCHAR *szSchemaName,
SQLSMALLINT cbSchemaName,
SQLCHAR *szProcName,
SQLSMALLINT cbProcName)
{
mylog("[SQLProcedures]");
return PGAPI_Procedures(hstmt, szCatalogName, cbCatalogName,
szSchemaName, cbSchemaName, szProcName, cbProcName);
}
RETCODE SQL_API
SQLSetPos(
HSTMT hstmt,
SQLUSMALLINT irow,
SQLUSMALLINT fOption,
SQLUSMALLINT fLock)
{
mylog("[SQLSetPos]");
return PGAPI_SetPos(hstmt, irow, fOption, fLock);
}
RETCODE SQL_API
SQLTablePrivileges(
HSTMT hstmt,
SQLCHAR *szCatalogName,
SQLSMALLINT cbCatalogName,
SQLCHAR *szSchemaName,
SQLSMALLINT cbSchemaName,
SQLCHAR *szTableName,
SQLSMALLINT cbTableName)
{
mylog("[SQLTablePrivileges]");
return PGAPI_TablePrivileges(hstmt, szCatalogName, cbCatalogName,
szSchemaName, cbSchemaName, szTableName, cbTableName);
}
RETCODE SQL_API
SQLBindParameter(
HSTMT hstmt,
SQLUSMALLINT ipar,
SQLSMALLINT fParamType,
SQLSMALLINT fCType,
SQLSMALLINT fSqlType,
SQLUINTEGER cbColDef,
SQLSMALLINT ibScale,
PTR rgbValue,
SQLINTEGER cbValueMax,
SQLINTEGER *pcbValue)
{
mylog("[SQLBindParameter]");
return PGAPI_BindParameter(hstmt, ipar, fParamType, fCType,
fSqlType, cbColDef, ibScale, rgbValue, cbValueMax,
pcbValue);
}
/*-------
* Module: odbcapi30.c
*
* Description: This module contains routines related to ODBC 3.0
* most of their implementations are temporary
* and must be rewritten properly.
* 2001/07/23 inoue
*
* Classes: n/a
*
* API functions: SQLAllocHandle, SQLBindParam, SQLCloseCursor,
SQLColAttribute, SQLCopyDesc, SQLEndTran,
SQLFetchScroll, SQLFreeHandle, SQLGetDescField,
SQLGetDescRec, SQLGetDiagField, SQLGetDiagRec,
SQLGetEnvAttr, SQLGetConnectAttr, SQLGetStmtAttr,
SQLSetConnectAttr, SQLSetDescField, SQLSetDescRec,
SQLSetEnvAttr, SQLSetStmtAttr, SQLBulkOperations
*-------
*/
#ifndef ODBCVER
#define ODBCVER 0x0300
#endif
#include "psqlodbc.h"
#include <stdio.h>
#include <string.h>
#include "environ.h"
#include "connection.h"
#include "statement.h"
#include "pgapifunc.h"
/* SQLAllocConnect/SQLAllocEnv/SQLAllocStmt -> SQLAllocHandle */
RETCODE SQL_API
SQLAllocHandle(SQLSMALLINT HandleType,
SQLHANDLE InputHandle, SQLHANDLE * OutputHandle)
{
mylog("[[SQLAllocHandle]]");
switch (HandleType)
{
case SQL_HANDLE_ENV:
return PGAPI_AllocEnv(OutputHandle);
case SQL_HANDLE_DBC:
return PGAPI_AllocConnect(InputHandle, OutputHandle);
case SQL_HANDLE_STMT:
return PGAPI_AllocStmt(InputHandle, OutputHandle);
default:
break;
}
return SQL_ERROR;
}
/* SQLBindParameter/SQLSetParam -> SQLBindParam */
RETCODE SQL_API
SQLBindParam(HSTMT StatementHandle,
SQLUSMALLINT ParameterNumber, SQLSMALLINT ValueType,
SQLSMALLINT ParameterType, SQLUINTEGER LengthPrecision,
SQLSMALLINT ParameterScale, PTR ParameterValue,
SQLINTEGER *StrLen_or_Ind)
{
int BufferLength = 512; /* Is it OK ? */
mylog("[[SQLBindParam]]");
return PGAPI_BindParameter(StatementHandle, ParameterNumber, SQL_PARAM_INPUT, ValueType, ParameterType, LengthPrecision, ParameterScale, ParameterValue, BufferLength, StrLen_or_Ind);
}
/* New function */
RETCODE SQL_API
SQLCloseCursor(HSTMT StatementHandle)
{
mylog("[[SQLCloseCursor]]");
return PGAPI_FreeStmt(StatementHandle, SQL_CLOSE);
}
/* SQLColAttributes -> SQLColAttribute */
RETCODE SQL_API
SQLColAttribute(HSTMT StatementHandle,
SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier,
PTR CharacterAttribute, SQLSMALLINT BufferLength,
SQLSMALLINT *StringLength, PTR NumericAttribute)
{
mylog("[[SQLColAttribute]]");
return PGAPI_ColAttributes(StatementHandle, ColumnNumber,
FieldIdentifier, CharacterAttribute, BufferLength,
StringLength, NumericAttribute);
}
/* new function */
RETCODE SQL_API
SQLCopyDesc(SQLHDESC SourceDescHandle,
SQLHDESC TargetDescHandle)
{
mylog("[[SQLCopyDesc]]\n");
return SQL_ERROR;
}
/* SQLTransact -> SQLEndTran */
RETCODE SQL_API
SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle,
SQLSMALLINT CompletionType)
{
mylog("[[SQLEndTran]]");
switch (HandleType)
{
case SQL_HANDLE_ENV:
return PGAPI_Transact(Handle, SQL_NULL_HDBC, CompletionType);
case SQL_HANDLE_DBC:
return PGAPI_Transact(SQL_NULL_HENV, Handle, CompletionType);
default:
break;
}
return SQL_ERROR; /* SQLSTATE HY092 ("Invalid
* attribute/option identifier") */
}
/* SQLExtendedFetch -> SQLFetchScroll */
RETCODE SQL_API
SQLFetchScroll(HSTMT StatementHandle,
SQLSMALLINT FetchOrientation, SQLINTEGER FetchOffset)
{
static char *func = "SQLFetchScroll";
StatementClass *stmt = (StatementClass *) StatementHandle;
RETCODE ret;
SQLUSMALLINT *rowStatusArray = stmt->options.rowStatusArray;
SQLINTEGER *pcRow = stmt->options.rowsFetched;
mylog("[[%s]] %d,%d\n", func, FetchOrientation, FetchOffset);
if (FetchOrientation == SQL_FETCH_BOOKMARK)
{
if (stmt->options.bookmark_ptr)
FetchOffset += *((Int4 *) stmt->options.bookmark_ptr);
else
{
stmt->errornumber = STMT_SEQUENCE_ERROR;
stmt->errormsg = "Bookmark isn't specifed yet";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
}
ret = PGAPI_ExtendedFetch(StatementHandle, FetchOrientation, FetchOffset,
pcRow, rowStatusArray);
if (ret != SQL_SUCCESS)
mylog("%s return = %d\n", func, ret);
return ret;
}
/* SQLFree(Connect/Env/Stmt) -> SQLFreeHandle */
RETCODE SQL_API
SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle)
{
mylog("[[SQLFreeHandle]]");
switch (HandleType)
{
case SQL_HANDLE_ENV:
return PGAPI_FreeEnv(Handle);
case SQL_HANDLE_DBC:
return PGAPI_FreeConnect(Handle);
case SQL_HANDLE_STMT:
return PGAPI_FreeStmt(Handle, SQL_DROP);
default:
break;
}
return SQL_ERROR;
}
/* new function */
RETCODE SQL_API
SQLGetDescField(SQLHDESC DescriptorHandle,
SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier,
PTR Value, SQLINTEGER BufferLength,
SQLINTEGER *StringLength)
{
mylog("[[SQLGetDescField]]\n");
return SQL_ERROR;
}
/* new function */
RETCODE SQL_API
SQLGetDescRec(SQLHDESC DescriptorHandle,
SQLSMALLINT RecNumber, SQLCHAR *Name,
SQLSMALLINT BufferLength, SQLSMALLINT *StringLength,
SQLSMALLINT *Type, SQLSMALLINT *SubType,
SQLINTEGER *Length, SQLSMALLINT *Precision,
SQLSMALLINT *Scale, SQLSMALLINT *Nullable)
{
mylog("[[SQLGetDescRec]]\n");
return SQL_ERROR;
}
/* new function */
RETCODE SQL_API
SQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle,
SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier,
PTR DiagInfo, SQLSMALLINT BufferLength,
SQLSMALLINT *StringLength)
{
mylog("[[SQLGetDiagField]]\n");
return SQL_ERROR;
}
/* SQLError -> SQLDiagRec */
RETCODE SQL_API
SQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle,
SQLSMALLINT RecNumber, SQLCHAR *Sqlstate,
SQLINTEGER *NativeError, SQLCHAR *MessageText,
SQLSMALLINT BufferLength, SQLSMALLINT *TextLength)
{
RETCODE ret;
mylog("[[SQLGetDiagRec]]\n");
switch (HandleType)
{
case SQL_HANDLE_ENV:
ret = PGAPI_Error(Handle, NULL, NULL, Sqlstate, NativeError,
MessageText, BufferLength, TextLength);
break;
case SQL_HANDLE_DBC:
ret = PGAPI_Error(NULL, Handle, NULL, Sqlstate, NativeError,
MessageText, BufferLength, TextLength);
break;
case SQL_HANDLE_STMT:
ret = PGAPI_Error(NULL, NULL, Handle, Sqlstate, NativeError,
MessageText, BufferLength, TextLength);
break;
default:
ret = SQL_ERROR;
}
if (ret == SQL_SUCCESS_WITH_INFO &&
BufferLength == 0 &&
*TextLength)
{
SQLSMALLINT BufferLength = *TextLength + 4;
SQLCHAR *MessageText = malloc(BufferLength);
ret = SQLGetDiagRec(HandleType, Handle, RecNumber, Sqlstate,
NativeError, MessageText, BufferLength,
TextLength);
free(MessageText);
}
return ret;
}
/* new function */
RETCODE SQL_API
SQLGetEnvAttr(HENV EnvironmentHandle,
SQLINTEGER Attribute, PTR Value,
SQLINTEGER BufferLength, SQLINTEGER *StringLength)
{
EnvironmentClass *env = (EnvironmentClass *) EnvironmentHandle;
mylog("[[SQLGetEnvAttr]] %d\n", Attribute);
switch (Attribute)
{
case SQL_ATTR_CONNECTION_POOLING:
*((unsigned int *) Value) = SQL_CP_OFF;
break;
case SQL_ATTR_CP_MATCH:
*((unsigned int *) Value) = SQL_CP_RELAXED_MATCH;
break;
case SQL_ATTR_ODBC_VERSION:
*((unsigned int *) Value) = SQL_OV_ODBC3;
break;
case SQL_ATTR_OUTPUT_NTS:
*((unsigned int *) Value) = SQL_TRUE;
break;
default:
env->errornumber = CONN_INVALID_ARGUMENT_NO;
return SQL_ERROR;
}
return SQL_SUCCESS;
}
/* SQLGetConnectOption -> SQLGetconnectAttr */
RETCODE SQL_API
SQLGetConnectAttr(HDBC ConnectionHandle,
SQLINTEGER Attribute, PTR Value,
SQLINTEGER BufferLength, SQLINTEGER *StringLength)
{
ConnectionClass *conn = (ConnectionClass *) ConnectionHandle;
mylog("[[SQLGetConnectAttr]] %d\n", Attribute);
switch (Attribute)
{
case SQL_ATTR_ASYNC_ENABLE:
case SQL_ATTR_AUTO_IPD:
case SQL_ATTR_CONNECTION_DEAD:
case SQL_ATTR_CONNECTION_TIMEOUT:
case SQL_ATTR_METADATA_ID:
conn->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
conn->errormsg = "Unsupported connection option (Set)";
return SQL_ERROR;
}
return PGAPI_GetConnectOption(ConnectionHandle, (UWORD) Attribute, Value);
}
/* SQLGetStmtOption -> SQLGetStmtAttr */
RETCODE SQL_API
SQLGetStmtAttr(HSTMT StatementHandle,
SQLINTEGER Attribute, PTR Value,
SQLINTEGER BufferLength, SQLINTEGER *StringLength)
{
static char *func = "SQLGetStmtAttr";
StatementClass *stmt = (StatementClass *) StatementHandle;
RETCODE ret = SQL_SUCCESS;
int len = 0;
mylog("[[%s]] %d\n", func, Attribute);
switch (Attribute)
{
case SQL_ATTR_FETCH_BOOKMARK_PTR: /* 16 */
Value = stmt->options.bookmark_ptr;
len = 4;
break;
case SQL_ATTR_ROW_STATUS_PTR: /* 25 */
Value = stmt->options.rowStatusArray;
len = 4;
break;
case SQL_ATTR_ROWS_FETCHED_PTR: /* 26 */
Value = stmt->options.rowsFetched;
len = 4;
break;
case SQL_ATTR_ROW_ARRAY_SIZE: /* 27 */
*((SQLUINTEGER *) Value) = stmt->options.rowset_size;
len = 4;
break;
case SQL_ATTR_APP_ROW_DESC: /* 10010 */
*((HSTMT *) Value) = StatementHandle; /* this is useless */
len = 4;
break;
case SQL_ATTR_APP_PARAM_DESC: /* 10011 */
*((HSTMT *) Value) = StatementHandle; /* this is useless */
len = 4;
break;
case SQL_ATTR_IMP_ROW_DESC: /* 10012 */
*((HSTMT *) Value) = StatementHandle; /* this is useless */
len = 4;
break;
case SQL_ATTR_IMP_PARAM_DESC: /* 10013 */
*((HSTMT *) Value) = StatementHandle; /* this is useless */
len = 4;
break;
case SQL_ATTR_AUTO_IPD: /* 10001 */
/* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */
case SQL_ATTR_PARAMSET_SIZE: /* 22 */
case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */
case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */
case SQL_ATTR_CURSOR_SCROLLABLE: /* -1 */
case SQL_ATTR_CURSOR_SENSITIVITY: /* -2 */
case SQL_ATTR_ENABLE_AUTO_IPD: /* 15 */
case SQL_ATTR_METADATA_ID: /* 10014 */
/*
* case SQL_ATTR_PREDICATE_PTR: case
* SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR:
*/
case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */
case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */
case SQL_ATTR_PARAM_OPERATION_PTR: /* 19 */
case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */
case SQL_ATTR_ROW_OPERATION_PTR: /* 24 */
stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
stmt->errormsg = "Unsupported statement option (Get)";
SC_log_error(func, "", stmt);
return SQL_ERROR;
default:
len = 4;
ret = PGAPI_GetStmtOption(StatementHandle, (UWORD) Attribute, Value);
}
if (ret == SQL_SUCCESS && StringLength)
*StringLength = len;
return ret;
}
/* SQLSetConnectOption -> SQLSetConnectAttr */
RETCODE SQL_API
SQLSetConnectAttr(HDBC ConnectionHandle,
SQLINTEGER Attribute, PTR Value,
SQLINTEGER StringLength)
{
ConnectionClass *conn = (ConnectionClass *) ConnectionHandle;
mylog("[[SQLSetConnectAttr]] %d\n", Attribute);
switch (Attribute)
{
case SQL_ATTR_ASYNC_ENABLE:
case SQL_ATTR_AUTO_IPD:
case SQL_ATTR_CONNECTION_DEAD:
case SQL_ATTR_CONNECTION_TIMEOUT:
case SQL_ATTR_METADATA_ID:
conn->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
conn->errormsg = "Unsupported connection option (Set)";
return SQL_ERROR;
}
return PGAPI_SetConnectOption(ConnectionHandle, (UWORD) Attribute, (UDWORD) Value);
}
/* new function */
RETCODE SQL_API
SQLSetDescField(SQLHDESC DescriptorHandle,
SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier,
PTR Value, SQLINTEGER BufferLength)
{
mylog("[[SQLSetDescField]]\n");
return SQL_ERROR;
}
/* new fucntion */
RETCODE SQL_API
SQLSetDescRec(SQLHDESC DescriptorHandle,
SQLSMALLINT RecNumber, SQLSMALLINT Type,
SQLSMALLINT SubType, SQLINTEGER Length,
SQLSMALLINT Precision, SQLSMALLINT Scale,
PTR Data, SQLINTEGER *StringLength,
SQLINTEGER *Indicator)
{
mylog("[[SQLsetDescRec]]\n");
return SQL_ERROR;
}
/* new function */
RETCODE SQL_API
SQLSetEnvAttr(HENV EnvironmentHandle,
SQLINTEGER Attribute, PTR Value,
SQLINTEGER StringLength)
{
EnvironmentClass *env = (EnvironmentClass *) EnvironmentHandle;
mylog("[[SQLSetEnvAttr]] att=%d,%u\n", Attribute, Value);
switch (Attribute)
{
case SQL_ATTR_CONNECTION_POOLING:
if ((SQLUINTEGER) Value == SQL_CP_OFF)
return SQL_SUCCESS;
break;
case SQL_ATTR_CP_MATCH:
/* *((unsigned int *) Value) = SQL_CP_RELAXED_MATCH; */
return SQL_SUCCESS;
case SQL_ATTR_ODBC_VERSION:
if ((SQLUINTEGER) Value == SQL_OV_ODBC2)
return SQL_SUCCESS;
break;
case SQL_ATTR_OUTPUT_NTS:
if ((SQLUINTEGER) Value == SQL_TRUE)
return SQL_SUCCESS;
break;
default:
env->errornumber = CONN_INVALID_ARGUMENT_NO;
return SQL_ERROR;
}
env->errornumber = CONN_OPTION_VALUE_CHANGED;
env->errormsg = "SetEnv changed to ";
return SQL_SUCCESS_WITH_INFO;
}
/* SQLSet(Param/Scroll/Stmt)Option -> SQLSetStmtAttr */
RETCODE SQL_API
SQLSetStmtAttr(HSTMT StatementHandle,
SQLINTEGER Attribute, PTR Value,
SQLINTEGER StringLength)
{
static char *func = "SQLSetStmtAttr";
StatementClass *stmt = (StatementClass *) StatementHandle;
UDWORD rowcount;
mylog("[[%s]] %d,%u\n", func, Attribute, Value);
switch (Attribute)
{
case SQL_ATTR_PARAMSET_SIZE: /* 22 */
return PGAPI_ParamOptions(StatementHandle, (UWORD) Value, &rowcount);
case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */
case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */
case SQL_ATTR_CURSOR_SCROLLABLE: /* -1 */
case SQL_ATTR_CURSOR_SENSITIVITY: /* -2 */
case SQL_ATTR_ENABLE_AUTO_IPD: /* 15 */
case SQL_ATTR_APP_ROW_DESC: /* 10010 */
case SQL_ATTR_APP_PARAM_DESC: /* 10011 */
case SQL_ATTR_AUTO_IPD: /* 10001 */
/* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */
case SQL_ATTR_IMP_ROW_DESC: /* 10012 */
case SQL_ATTR_IMP_PARAM_DESC: /* 10013 */
case SQL_ATTR_METADATA_ID: /* 10014 */
/*
* case SQL_ATTR_PREDICATE_PTR: case
* SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR:
*/
case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */
case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */
case SQL_ATTR_PARAM_OPERATION_PTR: /* 19 */
case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */
case SQL_ATTR_ROW_OPERATION_PTR: /* 24 */
stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
stmt->errormsg = "Unsupported statement option (Set)";
SC_log_error(func, "", stmt);
return SQL_ERROR;
case SQL_ATTR_FETCH_BOOKMARK_PTR: /* 16 */
stmt->options.bookmark_ptr = Value;
break;
case SQL_ATTR_ROW_STATUS_PTR: /* 25 */
stmt->options.rowStatusArray = (SQLUSMALLINT *) Value;
break;
case SQL_ATTR_ROWS_FETCHED_PTR: /* 26 */
stmt->options.rowsFetched = (SQLUINTEGER *) Value;
break;
case SQL_ATTR_ROW_ARRAY_SIZE: /* 27 */
stmt->options.rowset_size = (SQLUINTEGER) Value;
break;
default:
return PGAPI_SetStmtOption(StatementHandle, (UWORD) Attribute, (UDWORD) Value);
}
return SQL_SUCCESS;
}
#define SQL_FUNC_ESET(pfExists, uwAPI) \
(*(((UWORD*) (pfExists)) + ((uwAPI) >> 4)) \
|= (1 << ((uwAPI) & 0x000F)) \
)
RETCODE SQL_API
PGAPI_GetFunctions30(HDBC hdbc, UWORD fFunction, UWORD FAR * pfExists)
{
if (fFunction != SQL_API_ODBC3_ALL_FUNCTIONS)
return SQL_ERROR;
memset(pfExists, 0, sizeof(UWORD) * SQL_API_ODBC3_ALL_FUNCTIONS_SIZE);
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLALLOCCONNECT); 1 deprecated */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLALLOCENV); 2 deprecated */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLALLOCSTMT); 3 deprecated */
/*
* for (i = SQL_API_SQLBINDCOL; i <= 23; i++) SQL_FUNC_ESET(pfExists,
* i);
*/
SQL_FUNC_ESET(pfExists, SQL_API_SQLBINDCOL); /* 4 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLCANCEL); /* 5 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLCOLATTRIBUTE); /* 6 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLCONNECT); /* 7 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLDESCRIBECOL); /* 8 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLDISCONNECT); /* 9 */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLERROR); 10 deprecated */
SQL_FUNC_ESET(pfExists, SQL_API_SQLEXECDIRECT); /* 11 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLEXECUTE); /* 12 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLFETCH); /* 13 */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLFREECONNECT); 14 deprecated */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLFREEENV); 15 deprecated */
SQL_FUNC_ESET(pfExists, SQL_API_SQLFREESTMT); /* 16 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETCURSORNAME); /* 17 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLNUMRESULTCOLS); /* 18 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLPREPARE); /* 19 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLROWCOUNT); /* 20 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLSETCURSORNAME); /* 21 */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLSETPARAM); 22 deprecated */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLTRANSACT); 23 deprecated */
/*
* for (i = 40; i < SQL_API_SQLEXTENDEDFETCH; i++)
* SQL_FUNC_ESET(pfExists, i);
*/
SQL_FUNC_ESET(pfExists, SQL_API_SQLCOLUMNS); /* 40 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLDRIVERCONNECT); /* 41 */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLGETCONNECTOPTION); 42 deprecated */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDATA); /* 43 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETFUNCTIONS); /* 44 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETINFO); /* 45 */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLGETSTMTOPTION); 46 deprecated */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETTYPEINFO); /* 47 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLPARAMDATA); /* 48 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLPUTDATA); /* 49 */
/*
* SQL_FUNC_ESET(pfExists, SQL_API_SQLSETCONNECTIONOPTION); 50
* deprecated
*/
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLSETSTMTOPTION); 51 deprecated */
SQL_FUNC_ESET(pfExists, SQL_API_SQLSPECIALCOLUMNS); /* 52 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLSTATISTICS); /* 53 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLTABLES); /* 54 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLBROWSECONNECT); /* 55 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLCOLUMNPRIVILEGES); /* 56 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLDATASOURCES); /* 57 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLDESCRIBEPARAM); /* 58 */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLEXTENDEDFETCH); 59 deprecated */
/*
* for (++i; i < SQL_API_SQLBINDPARAMETER; i++)
* SQL_FUNC_ESET(pfExists, i);
*/
SQL_FUNC_ESET(pfExists, SQL_API_SQLFOREIGNKEYS); /* 60 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLMORERESULTS); /* 61 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLNATIVESQL); /* 62 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLNUMPARAMS); /* 63 */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLPARAMOPTIONS); 64 deprecated */
SQL_FUNC_ESET(pfExists, SQL_API_SQLPRIMARYKEYS); /* 65 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLPROCEDURECOLUMNS); /* 66 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLPROCEDURES); /* 67 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLSETPOS); /* 68 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLSETSCROLLOPTIONS); /* 69 deprecated */
SQL_FUNC_ESET(pfExists, SQL_API_SQLTABLEPRIVILEGES); /* 70 */
/* SQL_FUNC_ESET(pfExists, SQL_API_SQLDRIVERS); */ /* 71 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLBINDPARAMETER); /* 72 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLALLOCHANDLE); /* 1001 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLBINDPARAM); /* 1002 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLCLOSECURSOR); /* 1003 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLCOPYDESC); /* 1004 not implemented
* yet */
SQL_FUNC_ESET(pfExists, SQL_API_SQLENDTRAN); /* 1005 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLFREEHANDLE); /* 1006 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETCONNECTATTR); /* 1007 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDESCFIELD); /* 1008 not implemented
* yet */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDESCREC); /* 1009 not implemented
* yet */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDIAGFIELD); /* 1010 not implemented
* yet */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDIAGREC); /* 1011 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETENVATTR); /* 1012 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLGETSTMTATTR); /* 1014 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLSETCONNECTATTR); /* 1016 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLSETDESCFIELD); /* 1017 not implemeted
* yet */
SQL_FUNC_ESET(pfExists, SQL_API_SQLSETDESCREC); /* 1018 not implemented
* yet */
SQL_FUNC_ESET(pfExists, SQL_API_SQLSETENVATTR); /* 1019 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLSETSTMTATTR); /* 1020 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLFETCHSCROLL); /* 1021 */
SQL_FUNC_ESET(pfExists, SQL_API_SQLBULKOPERATIONS); /* 24 not implemented
* yet */
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue,
SWORD cbInfoValueMax, SWORD FAR * pcbInfoValue)
{
static char *func = "PGAPI_GetInfo30";
ConnectionClass *conn = (ConnectionClass *) hdbc;
char *p = NULL;
int len = 0,
value = 0;
RETCODE result;
switch (fInfoType)
{
case SQL_DYNAMIC_CURSOR_ATTRIBUTES1:
len = 4;
value = 0;
break;
case SQL_DYNAMIC_CURSOR_ATTRIBUTES2:
len = 4;
value = 0;
break;
case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1:
len = 4;
value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE |
SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK;
break;
case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2:
len = 4;
value = 0;
break;
case SQL_KEYSET_CURSOR_ATTRIBUTES1:
len = 4;
value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE
| SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK
| SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION
| SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE
| SQL_CA1_POS_REFRESH
| SQL_CA1_BULK_ADD
| SQL_CA1_BULK_UPDATE_BY_BOOKMARK
| SQL_CA1_BULK_DELETE_BY_BOOKMARK
| SQL_CA1_BULK_FETCH_BY_BOOKMARK
;
break;
case SQL_KEYSET_CURSOR_ATTRIBUTES2:
len = 4;
value = SQL_CA2_OPT_ROWVER_CONCURRENCY |
SQL_CA2_SENSITIVITY_ADDITIONS |
SQL_CA2_SENSITIVITY_DELETIONS |
SQL_CA2_SENSITIVITY_UPDATES;
break;
case SQL_STATIC_CURSOR_ATTRIBUTES1:
len = 4;
value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE |
SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK |
SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION |
SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE |
SQL_CA1_POS_REFRESH;
break;
case SQL_STATIC_CURSOR_ATTRIBUTES2:
len = 4;
value = SQL_CA2_OPT_ROWVER_CONCURRENCY |
SQL_CA2_SENSITIVITY_ADDITIONS |
SQL_CA2_SENSITIVITY_DELETIONS |
SQL_CA2_SENSITIVITY_UPDATES;
break;
default:
/* unrecognized key */
conn->errormsg = "Unrecognized key passed to SQLGetInfo.";
conn->errornumber = CONN_NOT_IMPLEMENTED_ERROR;
CC_log_error(func, "", conn);
return SQL_ERROR;
}
result = SQL_SUCCESS;
if (p)
{
/* char/binary data */
len = strlen(p);
if (rgbInfoValue)
{
strncpy_null((char *) rgbInfoValue, p, (size_t) cbInfoValueMax);
if (len >= cbInfoValueMax)
{
result = SQL_SUCCESS_WITH_INFO;
conn->errornumber = STMT_TRUNCATED;
conn->errormsg = "The buffer was too small for tthe InfoValue.";
}
}
}
else
{
/* numeric data */
if (rgbInfoValue)
{
if (len == 2)
*((WORD *) rgbInfoValue) = (WORD) value;
else if (len == 4)
*((DWORD *) rgbInfoValue) = (DWORD) value;
}
}
if (pcbInfoValue)
*pcbInfoValue = len;
return result;
}
/*--------
* Module: options.c
*
* Description: This module contains routines for getting/setting
* connection and statement options.
*
* Classes: n/a
*
* API functions: SQLSetConnectOption, SQLSetStmtOption, SQLGetConnectOption,
* SQLGetStmtOption
*
* Comments: See "notice.txt" for copyright and license information.
*--------
*/
#include "psqlodbc.h"
#include <string.h>
#include "environ.h"
#include "connection.h"
#include "statement.h"
#include "qresult.h"
#include "pgapifunc.h"
RETCODE set_statement_option(ConnectionClass *conn,
StatementClass *stmt,
UWORD fOption,
UDWORD vParam);
RETCODE
set_statement_option(ConnectionClass *conn,
StatementClass *stmt,
UWORD fOption,
UDWORD vParam)
{
static char *func = "set_statement_option";
char changed = FALSE;
ConnInfo *ci = NULL;
if (conn)
ci = &(conn->connInfo);
else if (stmt)
ci = &(SC_get_conn(stmt)->connInfo);
switch (fOption)
{
case SQL_ASYNC_ENABLE: /* ignored */
break;
case SQL_BIND_TYPE:
/* now support multi-column and multi-row binding */
if (conn)
conn->stmtOptions.bind_size = vParam;
if (stmt)
stmt->options.bind_size = vParam;
break;
case SQL_CONCURRENCY:
/*
* positioned update isn't supported so cursor concurrency is
* read-only
*/
mylog("SetStmtOption(): SQL_CONCURRENCY = %d\n", vParam);
if (ci->drivers.lie || vParam == SQL_CONCUR_READ_ONLY || vParam == SQL_CONCUR_ROWVER)
{
if (conn)
conn->stmtOptions.scroll_concurrency = vParam;
if (stmt)
stmt->options.scroll_concurrency = vParam;
}
else
{
if (conn)
conn->stmtOptions.scroll_concurrency = SQL_CONCUR_ROWVER;
if (stmt)
stmt->options.scroll_concurrency = SQL_CONCUR_ROWVER;
changed = TRUE;
}
break;
case SQL_CURSOR_TYPE:
/*
* if declare/fetch, then type can only be forward. otherwise,
* it can only be forward or static.
*/
mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam);
if (ci->drivers.lie)
{
if (conn)
conn->stmtOptions.cursor_type = vParam;
if (stmt)
stmt->options.cursor_type = vParam;
}
else
{
if (ci->drivers.use_declarefetch)
{
if (conn)
conn->stmtOptions.cursor_type = SQL_CURSOR_FORWARD_ONLY;
if (stmt)
stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
if (vParam != SQL_CURSOR_FORWARD_ONLY)
changed = TRUE;
}
else
{
if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC)
{
if (conn)
conn->stmtOptions.cursor_type = vParam; /* valid type */
if (stmt)
stmt->options.cursor_type = vParam; /* valid type */
}
else
{
if (conn)
conn->stmtOptions.cursor_type = SQL_CURSOR_STATIC;
if (stmt)
stmt->options.cursor_type = SQL_CURSOR_STATIC;
changed = TRUE;
}
}
}
break;
case SQL_KEYSET_SIZE: /* ignored, but saved and returned */
mylog("SetStmtOption(): SQL_KEYSET_SIZE, vParam = %d\n", vParam);
if (conn)
conn->stmtOptions.keyset_size = vParam;
if (stmt)
stmt->options.keyset_size = vParam;
break;
/*-------
* if (ci->drivers.lie)
* stmt->keyset_size = vParam;
* else
* {
* stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
* stmt->errormsg = "Driver does not support keyset size option";
* SC_log_error(func, "", stmt);
* return SQL_ERROR;
* }
*-------
*/
case SQL_MAX_LENGTH: /* ignored, but saved */
mylog("SetStmtOption(): SQL_MAX_LENGTH, vParam = %d\n", vParam);
if (conn)
conn->stmtOptions.maxLength = vParam;
if (stmt)
stmt->options.maxLength = vParam;
break;
case SQL_MAX_ROWS: /* ignored, but saved */
mylog("SetStmtOption(): SQL_MAX_ROWS, vParam = %d\n", vParam);
if (conn)
conn->stmtOptions.maxRows = vParam;
if (stmt)
stmt->options.maxRows = vParam;
break;
case SQL_NOSCAN: /* ignored */
mylog("SetStmtOption: SQL_NOSCAN, vParam = %d\n", vParam);
break;
case SQL_QUERY_TIMEOUT: /* ignored */
mylog("SetStmtOption: SQL_QUERY_TIMEOUT, vParam = %d\n", vParam);
/* "0" returned in SQLGetStmtOption */
break;
case SQL_RETRIEVE_DATA:
mylog("SetStmtOption(): SQL_RETRIEVE_DATA, vParam = %d\n", vParam);
if (conn)
conn->stmtOptions.retrieve_data = vParam;
if (stmt)
stmt->options.retrieve_data = vParam;
break;
case SQL_ROWSET_SIZE:
mylog("SetStmtOption(): SQL_ROWSET_SIZE, vParam = %d\n", vParam);
/*
* Save old rowset size for SQLExtendedFetch purposes If the
* rowset_size is being changed since the last call to fetch
* rows.
*/
if (stmt && stmt->save_rowset_size <= 0 && stmt->last_fetch_count > 0)
stmt->save_rowset_size = stmt->options.rowset_size;
if (vParam < 1)
{
vParam = 1;
changed = TRUE;
}
if (conn)
conn->stmtOptions.rowset_size = vParam;
if (stmt)
stmt->options.rowset_size = vParam;
break;
case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */
if (stmt)
{
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Simulated positioned update/delete not supported. Use the cursor library.";
SC_log_error(func, "", stmt);
}
if (conn)
{
conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
conn->errormsg = "Simulated positioned update/delete not supported. Use the cursor library.";
CC_log_error(func, "", conn);
}
return SQL_ERROR;
case SQL_USE_BOOKMARKS:
if (stmt)
stmt->options.use_bookmarks = vParam;
if (conn)
conn->stmtOptions.use_bookmarks = vParam;
break;
default:
{
char option[64];
if (stmt)
{
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Unknown statement option (Set)";
sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
SC_log_error(func, option, stmt);
}
if (conn)
{
conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
conn->errormsg = "Unknown statement option (Set)";
sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
CC_log_error(func, option, conn);
}
return SQL_ERROR;
}
}
if (changed)
{
if (stmt)
{
stmt->errormsg = "Requested value changed.";
stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
}
if (conn)
{
conn->errormsg = "Requested value changed.";
conn->errornumber = STMT_OPTION_VALUE_CHANGED;
}
return SQL_SUCCESS_WITH_INFO;
}
else
return SQL_SUCCESS;
}
/* Implements only SQL_AUTOCOMMIT */
RETCODE SQL_API
PGAPI_SetConnectOption(
HDBC hdbc,
UWORD fOption,
UDWORD vParam)
{
static char *func = "PGAPI_SetConnectOption";
ConnectionClass *conn = (ConnectionClass *) hdbc;
char changed = FALSE;
RETCODE retval;
int i;
mylog("%s: entering fOption = %d vParam = %d\n", func, fOption, vParam);
if (!conn)
{
CC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
switch (fOption)
{
/*
* Statement Options (apply to all stmts on the connection and
* become defaults for new stmts)
*/
case SQL_ASYNC_ENABLE:
case SQL_BIND_TYPE:
case SQL_CONCURRENCY:
case SQL_CURSOR_TYPE:
case SQL_KEYSET_SIZE:
case SQL_MAX_LENGTH:
case SQL_MAX_ROWS:
case SQL_NOSCAN:
case SQL_QUERY_TIMEOUT:
case SQL_RETRIEVE_DATA:
case SQL_ROWSET_SIZE:
case SQL_SIMULATE_CURSOR:
case SQL_USE_BOOKMARKS:
/* Affect all current Statements */
for (i = 0; i < conn->num_stmts; i++)
{
if (conn->stmts[i])
set_statement_option(NULL, conn->stmts[i], fOption, vParam);
}
/*
* Become the default for all future statements on this
* connection
*/
retval = set_statement_option(conn, NULL, fOption, vParam);
if (retval == SQL_SUCCESS_WITH_INFO)
changed = TRUE;
else if (retval == SQL_ERROR)
return SQL_ERROR;
break;
/*
* Connection Options
*/
case SQL_ACCESS_MODE: /* ignored */
break;
case SQL_AUTOCOMMIT:
if (CC_is_in_trans(conn))
{
conn->errormsg = "Cannot switch commit mode while a transaction is in progress";
conn->errornumber = CONN_TRANSACT_IN_PROGRES;
CC_log_error(func, "", conn);
return SQL_ERROR;
}
mylog("PGAPI_SetConnectOption: AUTOCOMMIT: transact_status=%d, vparam=%d\n", conn->transact_status, vParam);
switch (vParam)
{
case SQL_AUTOCOMMIT_OFF:
CC_set_autocommit_off(conn);
break;
case SQL_AUTOCOMMIT_ON:
CC_set_autocommit_on(conn);
break;
default:
conn->errormsg = "Illegal parameter value for SQL_AUTOCOMMIT";
conn->errornumber = CONN_INVALID_ARGUMENT_NO;
CC_log_error(func, "", conn);
return SQL_ERROR;
}
break;
case SQL_CURRENT_QUALIFIER: /* ignored */
break;
case SQL_LOGIN_TIMEOUT: /* ignored */
break;
case SQL_PACKET_SIZE: /* ignored */
break;
case SQL_QUIET_MODE: /* ignored */
break;
case SQL_TXN_ISOLATION: /* ignored */
break;
/* These options should be handled by driver manager */
case SQL_ODBC_CURSORS:
case SQL_OPT_TRACE:
case SQL_OPT_TRACEFILE:
case SQL_TRANSLATE_DLL:
case SQL_TRANSLATE_OPTION:
CC_log_error(func, "This connect option (Set) is only used by the Driver Manager", conn);
break;
default:
{
char option[64];
conn->errormsg = "Unknown connect option (Set)";
conn->errornumber = CONN_UNSUPPORTED_OPTION;
sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
if (fOption == 30002 && vParam)
{
if (strcmp((char *) vParam, "Microsoft Jet") == 0)
{
conn->errornumber = 0;
conn->ms_jet = 1;
return SQL_SUCCESS;
}
}
CC_log_error(func, option, conn);
return SQL_ERROR;
}
}
if (changed)
{
conn->errornumber = CONN_OPTION_VALUE_CHANGED;
conn->errormsg = "Requested value changed.";
return SQL_SUCCESS_WITH_INFO;
}
else
return SQL_SUCCESS;
}
/* This function just can tell you whether you are in Autcommit mode or not */
RETCODE SQL_API
PGAPI_GetConnectOption(
HDBC hdbc,
UWORD fOption,
PTR pvParam)
{
static char *func = "PGAPI_GetConnectOption";
ConnectionClass *conn = (ConnectionClass *) hdbc;
ConnInfo *ci = &(conn->connInfo);
mylog("%s: entering...\n", func);
if (!conn)
{
CC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
switch (fOption)
{
case SQL_ACCESS_MODE: /* NOT SUPPORTED */
*((UDWORD *) pvParam) = SQL_MODE_READ_WRITE;
break;
case SQL_AUTOCOMMIT:
*((UDWORD *) pvParam) = (UDWORD) (CC_is_in_autocommit(conn) ?
SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
break;
case SQL_CURRENT_QUALIFIER: /* don't use qualifiers */
if (pvParam)
strcpy(pvParam, "");
break;
case SQL_LOGIN_TIMEOUT: /* NOT SUPPORTED */
*((UDWORD *) pvParam) = 0;
break;
case SQL_PACKET_SIZE: /* NOT SUPPORTED */
*((UDWORD *) pvParam) = ci->drivers.socket_buffersize;
break;
case SQL_QUIET_MODE: /* NOT SUPPORTED */
*((UDWORD *) pvParam) = (UDWORD) NULL;
break;
case SQL_TXN_ISOLATION: /* NOT SUPPORTED */
*((UDWORD *) pvParam) = SQL_TXN_SERIALIZABLE;
break;
/* These options should be handled by driver manager */
case SQL_ODBC_CURSORS:
case SQL_OPT_TRACE:
case SQL_OPT_TRACEFILE:
case SQL_TRANSLATE_DLL:
case SQL_TRANSLATE_OPTION:
CC_log_error(func, "This connect option (Get) is only used by the Driver Manager", conn);
break;
default:
{
char option[64];
conn->errormsg = "Unknown connect option (Get)";
conn->errornumber = CONN_UNSUPPORTED_OPTION;
sprintf(option, "fOption=%d", fOption);
CC_log_error(func, option, conn);
return SQL_ERROR;
break;
}
}
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_SetStmtOption(
HSTMT hstmt,
UWORD fOption,
UDWORD vParam)
{
static char *func = "PGAPI_SetStmtOption";
StatementClass *stmt = (StatementClass *) hstmt;
mylog("%s: entering...\n", func);
/*
* Though we could fake Access out by just returning SQL_SUCCESS all
* the time, but it tries to set a huge value for SQL_MAX_LENGTH and
* expects the driver to reduce it to the real value.
*/
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
return set_statement_option(NULL, stmt, fOption, vParam);
}
RETCODE SQL_API
PGAPI_GetStmtOption(
HSTMT hstmt,
UWORD fOption,
PTR pvParam)
{
static char *func = "PGAPI_GetStmtOption";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
ConnInfo *ci = &(SC_get_conn(stmt)->connInfo);
mylog("%s: entering...\n", func);
/*
* thought we could fake Access out by just returning SQL_SUCCESS all
* the time, but it tries to set a huge value for SQL_MAX_LENGTH and
* expects the driver to reduce it to the real value
*/
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
switch (fOption)
{
case SQL_GET_BOOKMARK:
case SQL_ROW_NUMBER:
res = stmt->result;
if (stmt->manual_result || !ci->drivers.use_declarefetch)
{
/* make sure we're positioned on a valid row */
if ((stmt->currTuple < 0) ||
(stmt->currTuple >= QR_get_num_tuples(res)))
{
stmt->errormsg = "Not positioned on a valid row.";
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
}
else
{
if (stmt->currTuple == -1 || !res || !res->tupleField)
{
stmt->errormsg = "Not positioned on a valid row.";
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
}
if (fOption == SQL_GET_BOOKMARK && stmt->options.use_bookmarks == SQL_UB_OFF)
{
stmt->errormsg = "Operation invalid because use bookmarks not enabled.";
stmt->errornumber = STMT_OPERATION_INVALID;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
*((UDWORD *) pvParam) = SC_get_bookmark(stmt);
break;
case SQL_ASYNC_ENABLE: /* NOT SUPPORTED */
*((SDWORD *) pvParam) = SQL_ASYNC_ENABLE_OFF;
break;
case SQL_BIND_TYPE:
*((SDWORD *) pvParam) = stmt->options.bind_size;
break;
case SQL_CONCURRENCY: /* NOT REALLY SUPPORTED */
mylog("GetStmtOption(): SQL_CONCURRENCY\n");
*((SDWORD *) pvParam) = stmt->options.scroll_concurrency;
break;
case SQL_CURSOR_TYPE: /* PARTIAL SUPPORT */
mylog("GetStmtOption(): SQL_CURSOR_TYPE\n");
*((SDWORD *) pvParam) = stmt->options.cursor_type;
break;
case SQL_KEYSET_SIZE: /* NOT SUPPORTED, but saved */
mylog("GetStmtOption(): SQL_KEYSET_SIZE\n");
*((SDWORD *) pvParam) = stmt->options.keyset_size;
break;
case SQL_MAX_LENGTH: /* NOT SUPPORTED, but saved */
*((SDWORD *) pvParam) = stmt->options.maxLength;
break;
case SQL_MAX_ROWS: /* NOT SUPPORTED, but saved */
*((SDWORD *) pvParam) = stmt->options.maxRows;
mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->options.maxRows);
break;
case SQL_NOSCAN: /* NOT SUPPORTED */
*((SDWORD *) pvParam) = SQL_NOSCAN_ON;
break;
case SQL_QUERY_TIMEOUT: /* NOT SUPPORTED */
*((SDWORD *) pvParam) = 0;
break;
case SQL_RETRIEVE_DATA:
*((SDWORD *) pvParam) = stmt->options.retrieve_data;
break;
case SQL_ROWSET_SIZE:
*((SDWORD *) pvParam) = stmt->options.rowset_size;
break;
case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */
*((SDWORD *) pvParam) = SQL_SC_NON_UNIQUE;
break;
case SQL_USE_BOOKMARKS:
*((SDWORD *) pvParam) = stmt->options.use_bookmarks;
break;
default:
{
char option[64];
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Unknown statement option (Get)";
sprintf(option, "fOption=%d", fOption);
SC_log_error(func, option, stmt);
return SQL_ERROR;
}
}
return SQL_SUCCESS;
}
/*--------
* Module: parse.c
*
* Description: This module contains routines related to parsing SQL
* statements. This can be useful for two reasons:
*
* 1. So the query does not actually have to be executed
* to return data about it
*
* 2. To be able to return information about precision,
* nullability, aliases, etc. in the functions
* SQLDescribeCol and SQLColAttributes. Currently,
* Postgres doesn't return any information about
* these things in a query.
*
* Classes: none
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*--------
*/
/* Multibyte support Eiji Tokuya 2001-03-15 */
#include "psqlodbc.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "statement.h"
#include "connection.h"
#include "qresult.h"
#include "pgtypes.h"
#include "pgapifunc.h"
#ifdef MULTIBYTE
#include "multibyte.h"
#endif
#define FLD_INCR 32
#define TAB_INCR 8
#define COL_INCR 16
char *getNextToken(char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric);
void getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k);
char searchColInfo(COL_INFO *col_info, FIELD_INFO *fi);
char *
getNextToken(char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric)
{
int i = 0;
int out = 0;
char qc,
in_escape = FALSE;
if (smax <= 1)
return NULL;
smax--;
/* skip leading delimiters */
while (isspace((unsigned char) s[i]) || s[i] == ',')
{
/* mylog("skipping '%c'\n", s[i]); */
i++;
}
if (s[i] == '\0')
{
token[0] = '\0';
return NULL;
}
if (quote)
*quote = FALSE;
if (dquote)
*dquote = FALSE;
if (numeric)
*numeric = FALSE;
/* get the next token */
while (!isspace((unsigned char) s[i]) && s[i] != ',' &&
s[i] != '\0' && out != smax)
{
#ifdef MULTIBYTE
if (multibyte_char_check(s[i]) != 0)
{
token[out++] = s[i++];
continue;
}
#endif
/* Handle quoted stuff */
if (out == 0 && (s[i] == '\"' || s[i] == '\''))
{
qc = s[i];
if (qc == '\"')
{
if (dquote)
*dquote = TRUE;
}
if (qc == '\'')
{
if (quote)
*quote = TRUE;
}
i++; /* dont return the quote */
while (s[i] != '\0' && out != smax)
{
#ifdef MULTIBYTE
if (multibyte_char_check(s[i]) != 0)
{
token[out++] = s[i++];
continue;
}
#endif
if (s[i] == qc && !in_escape)
break;
if (s[i] == '\\' && !in_escape)
in_escape = TRUE;
else
{
in_escape = FALSE;
token[out++] = s[i];
}
i++;
}
if (s[i] == qc)
i++;
break;
}
/* Check for numeric literals */
if (out == 0 && isdigit((unsigned char) s[i]))
{
if (numeric)
*numeric = TRUE;
token[out++] = s[i++];
while (isalnum((unsigned char) s[i]) || s[i] == '.')
token[out++] = s[i++];
break;
}
if (ispunct((unsigned char) s[i]) && s[i] != '_')
{
mylog("got ispunct: s[%d] = '%c'\n", i, s[i]);
if (out == 0)
{
token[out++] = s[i++];
break;
}
else
break;
}
if (out != smax)
token[out++] = s[i];
i++;
}
/* mylog("done -- s[%d] = '%c'\n", i, s[i]); */
token[out] = '\0';
/* find the delimiter */
while (isspace((unsigned char) s[i]))
i++;
/* return the most priority delimiter */
if (s[i] == ',')
{
if (delim)
*delim = s[i];
}
else if (s[i] == '\0')
{
if (delim)
*delim = '\0';
}
else
{
if (delim)
*delim = ' ';
}
/* skip trailing blanks */
while (isspace((unsigned char) s[i]))
i++;
return &s[i];
}
#if 0
QR_set_num_fields(stmt->result, 14);
QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
QR_set_field_info(stmt->result, 3, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
QR_set_field_info(stmt->result, 4, "DATA_TYPE", PG_TYPE_INT2, 2);
QR_set_field_info(stmt->result, 5, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
QR_set_field_info(stmt->result, 6, "PRECISION", PG_TYPE_INT4, 4);
QR_set_field_info(stmt->result, 7, "LENGTH", PG_TYPE_INT4, 4);
QR_set_field_info(stmt->result, 8, "SCALE", PG_TYPE_INT2, 2);
QR_set_field_info(stmt->result, 9, "RADIX", PG_TYPE_INT2, 2);
QR_set_field_info(stmt->result, 10, "NULLABLE", PG_TYPE_INT2, 2);
QR_set_field_info(stmt->result, 11, "REMARKS", PG_TYPE_TEXT, 254);
/* User defined fields */
QR_set_field_info(stmt->result, 12, "DISPLAY_SIZE", PG_TYPE_INT4, 4);
QR_set_field_info(stmt->result, 13, "FIELD_TYPE", PG_TYPE_INT4, 4);
#endif
void
getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k)
{
char *str;
if (fi->name[0] == '\0')
strcpy(fi->name, QR_get_value_manual(col_info->result, k, 3));
fi->type = atoi(QR_get_value_manual(col_info->result, k, 13));
fi->precision = atoi(QR_get_value_manual(col_info->result, k, 6));
fi->length = atoi(QR_get_value_manual(col_info->result, k, 7));
if (str = QR_get_value_manual(col_info->result, k, 8), str)
fi->scale = atoi(str);
else
fi->scale = -1;
fi->nullable = atoi(QR_get_value_manual(col_info->result, k, 10));
fi->display_size = atoi(QR_get_value_manual(col_info->result, k, 12));
}
char
searchColInfo(COL_INFO *col_info, FIELD_INFO *fi)
{
int k,
cmp;
char *col;
for (k = 0; k < QR_get_num_tuples(col_info->result); k++)
{
col = QR_get_value_manual(col_info->result, k, 3);
if (fi->dquote)
cmp = strcmp(col, fi->name);
else
cmp = stricmp(col, fi->name);
if (!cmp)
{
if (!fi->dquote)
strcpy(fi->name, col);
getColInfo(col_info, fi, k);
mylog("PARSE: searchColInfo: \n");
return TRUE;
}
}
return FALSE;
}
char
parse_statement(StatementClass *stmt)
{
static char *func = "parse_statement";
char token[256];
char delim,
quote,
dquote,
numeric,
unquoted;
char *ptr,
*pptr = NULL;
char in_select = FALSE,
in_distinct = FALSE,
in_on = FALSE,
in_from = FALSE,
from_found = FALSE,
in_where = FALSE,
in_table = FALSE;
char in_field = FALSE,
in_expr = FALSE,
in_func = FALSE,
in_dot = FALSE,
in_as = FALSE;
int j,
i,
k = 0,
n,
first_where = 0,
blevel = 0;
FIELD_INFO **fi;
TABLE_INFO **ti;
char parse;
ConnectionClass *conn = stmt->hdbc;
HSTMT hcol_stmt;
StatementClass *col_stmt;
RETCODE result;
mylog("%s: entering...\n", func);
ptr = stmt->statement;
fi = stmt->fi;
ti = stmt->ti;
stmt->nfld = 0;
stmt->ntab = 0;
#ifdef MULTIBYTE
multibyte_init();
#endif
while (pptr = ptr, (ptr = getNextToken(pptr, token, sizeof(token), &delim, &quote, &dquote, &numeric)) != NULL)
{
unquoted = !(quote || dquote);
mylog("unquoted=%d, quote=%d, dquote=%d, numeric=%d, delim='%c', token='%s', ptr='%s'\n", unquoted, quote, dquote, numeric, delim, token, ptr);
if (in_select && unquoted && blevel == 0)
{
if (!stricmp(token, "distinct"))
{
in_distinct = TRUE;
mylog("DISTINCT\n");
continue;
}
if (!stricmp(token, "into"))
{
in_select = FALSE;
mylog("INTO\n");
stmt->statement_type = STMT_TYPE_CREATE;
stmt->parse_status = STMT_PARSE_FATAL;
return FALSE;
}
if (!stricmp(token, "from"))
{
in_select = FALSE;
in_from = TRUE;
if (!from_found &&
(!strnicmp(pptr, "from", 4)))
{
mylog("First ");
from_found = TRUE;
}
mylog("FROM\n");
continue;
}
}
if (unquoted && blevel == 0)
{
if ((!stricmp(token, "where") ||
!stricmp(token, "union") ||
!stricmp(token, "intersect") ||
!stricmp(token, "except") ||
!stricmp(token, "order") ||
!stricmp(token, "group") ||
!stricmp(token, "having")))
{
in_select = FALSE;
in_from = FALSE;
in_where = TRUE;
if (!first_where &&
(!stricmp(token, "where")))
first_where = ptr - stmt->statement;
mylog("WHERE...\n");
break;
}
}
if (in_select && (in_expr || in_func))
{
/* just eat the expression */
mylog("in_expr=%d or func=%d\n", in_expr, in_func);
if (unquoted)
{
if (token[0] == '(')
{
blevel++;
mylog("blevel++ = %d\n", blevel);
}
else if (token[0] == ')')
{
blevel--;
mylog("blevel-- = %d\n", blevel);
}
}
if (blevel == 0)
{
if (delim == ',')
{
mylog("**** Got comma in_expr/func\n");
in_func = FALSE;
in_expr = FALSE;
in_field = FALSE;
}
else if (unquoted && !stricmp(token, "as"))
{
mylog("got AS in_expr\n");
in_func = FALSE;
in_expr = FALSE;
in_as = TRUE;
in_field = TRUE;
}
}
continue;
}
if (unquoted && !stricmp(token, "select"))
{
in_select = TRUE;
mylog("SELECT\n");
continue;
}
if (in_select)
{
if (in_distinct)
{
mylog("in distinct\n");
if (unquoted && !stricmp(token, "on"))
{
in_on = TRUE;
mylog("got on\n");
continue;
}
if (in_on)
{
in_distinct = FALSE;
in_on = FALSE;
continue; /* just skip the unique on field */
}
mylog("done distinct\n");
in_distinct = FALSE;
}
if (!in_field)
{
if (!token[0])
continue;
if (!(stmt->nfld % FLD_INCR))
{
mylog("reallocing at nfld=%d\n", stmt->nfld);
fi = (FIELD_INFO **) realloc(fi, (stmt->nfld + FLD_INCR) * sizeof(FIELD_INFO *));
if (!fi)
{
stmt->parse_status = STMT_PARSE_FATAL;
return FALSE;
}
stmt->fi = fi;
}
fi[stmt->nfld] = (FIELD_INFO *) malloc(sizeof(FIELD_INFO));
if (fi[stmt->nfld] == NULL)
{
stmt->parse_status = STMT_PARSE_FATAL;
return FALSE;
}
/* Initialize the field info */
memset(fi[stmt->nfld], 0, sizeof(FIELD_INFO));
/* double quotes are for qualifiers */
if (dquote)
fi[stmt->nfld]->dquote = TRUE;
if (quote)
{
fi[stmt->nfld]->quote = TRUE;
fi[stmt->nfld]->precision = strlen(token);
}
else if (numeric)
{
mylog("**** got numeric: nfld = %d\n", stmt->nfld);
fi[stmt->nfld]->numeric = TRUE;
}
else if (token[0] == '(')
{ /* expression */
mylog("got EXPRESSION\n");
fi[stmt->nfld++]->expr = TRUE;
in_expr = TRUE;
blevel = 1;
continue;
}
else
{
strcpy(fi[stmt->nfld]->name, token);
fi[stmt->nfld]->dot[0] = '\0';
}
mylog("got field='%s', dot='%s'\n", fi[stmt->nfld]->name, fi[stmt->nfld]->dot);
if (delim == ',')
mylog("comma (1)\n");
else
in_field = TRUE;
stmt->nfld++;
continue;
}
/*
* We are in a field now
*/
if (in_dot)
{
stmt->nfld--;
strcpy(fi[stmt->nfld]->dot, fi[stmt->nfld]->name);
strcpy(fi[stmt->nfld]->name, token);
stmt->nfld++;
in_dot = FALSE;
if (delim == ',')
{
mylog("in_dot: got comma\n");
in_field = FALSE;
}
continue;
}
if (in_as)
{
stmt->nfld--;
strcpy(fi[stmt->nfld]->alias, token);
mylog("alias for field '%s' is '%s'\n", fi[stmt->nfld]->name, fi[stmt->nfld]->alias);
in_as = FALSE;
in_field = FALSE;
stmt->nfld++;
if (delim == ',')
mylog("comma(2)\n");
continue;
}
/* Function */
if (token[0] == '(')
{
in_func = TRUE;
blevel = 1;
fi[stmt->nfld - 1]->func = TRUE;
/*
* name will have the function name -- maybe useful some
* day
*/
mylog("**** got function = '%s'\n", fi[stmt->nfld - 1]->name);
continue;
}
if (token[0] == '.')
{
in_dot = TRUE;
mylog("got dot\n");
continue;
}
if (!stricmp(token, "as"))
{
in_as = TRUE;
mylog("got AS\n");
continue;
}
/* otherwise, it's probably an expression */
in_expr = TRUE;
fi[stmt->nfld - 1]->expr = TRUE;
fi[stmt->nfld - 1]->name[0] = '\0';
fi[stmt->nfld - 1]->precision = 0;
mylog("*** setting expression\n");
}
if (in_from)
{
if (!in_table)
{
if (!token[0])
continue;
if (!(stmt->ntab % TAB_INCR))
{
ti = (TABLE_INFO **) realloc(ti, (stmt->ntab + TAB_INCR) * sizeof(TABLE_INFO *));
if (!ti)
{
stmt->parse_status = STMT_PARSE_FATAL;
return FALSE;
}
stmt->ti = ti;
}
ti[stmt->ntab] = (TABLE_INFO *) malloc(sizeof(TABLE_INFO));
if (ti[stmt->ntab] == NULL)
{
stmt->parse_status = STMT_PARSE_FATAL;
return FALSE;
}
ti[stmt->ntab]->alias[0] = '\0';
strcpy(ti[stmt->ntab]->name, token);
if (!dquote)
{
char *ptr;
/* lower case table name */
for (ptr = ti[stmt->ntab]->name; *ptr; ptr++)
{
#ifdef MULTIBYTE
if ((unsigned char) *ptr >= 0x80)
ptr++;
else
#endif /* MULTIBYTE */
*ptr = tolower((unsigned char) *ptr);
}
}
mylog("got table = '%s'\n", ti[stmt->ntab]->name);
if (delim == ',')
mylog("more than 1 tables\n");
else
in_table = TRUE;
stmt->ntab++;
continue;
}
strcpy(ti[stmt->ntab - 1]->alias, token);
mylog("alias for table '%s' is '%s'\n", ti[stmt->ntab - 1]->name, ti[stmt->ntab - 1]->alias);
in_table = FALSE;
if (delim == ',')
mylog("more than 1 tables\n");
}
}
/*
* Resolve any possible field names with tables
*/
parse = TRUE;
/* Resolve field names with tables */
for (i = 0; i < stmt->nfld; i++)
{
if (fi[i]->func || fi[i]->expr || fi[i]->numeric)
{
fi[i]->ti = NULL;
fi[i]->type = -1;
parse = FALSE;
continue;
}
else if (fi[i]->quote)
{ /* handle as text */
fi[i]->ti = NULL;
/*
* fi[i]->type = PG_TYPE_TEXT; fi[i]->precision = 0; the
* following may be better
*/
fi[i]->type = PG_TYPE_UNKNOWN;
if (fi[i]->precision == 0)
{
fi[i]->type = PG_TYPE_VARCHAR;
fi[i]->precision = 254;
}
fi[i]->length = fi[i]->precision;
continue;
}
/* it's a dot, resolve to table or alias */
else if (fi[i]->dot[0])
{
for (k = 0; k < stmt->ntab; k++)
{
if (!stricmp(ti[k]->name, fi[i]->dot))
{
fi[i]->ti = ti[k];
break;
}
else if (!stricmp(ti[k]->alias, fi[i]->dot))
{
fi[i]->ti = ti[k];
break;
}
}
}
else if (stmt->ntab == 1)
fi[i]->ti = ti[0];
}
mylog("--------------------------------------------\n");
mylog("nfld=%d, ntab=%d\n", stmt->nfld, stmt->ntab);
for (i = 0; i < stmt->nfld; i++)
{
mylog("Field %d: expr=%d, func=%d, quote=%d, dquote=%d, numeric=%d, name='%s', alias='%s', dot='%s'\n", i, fi[i]->expr, fi[i]->func, fi[i]->quote, fi[i]->dquote, fi[i]->numeric, fi[i]->name, fi[i]->alias, fi[i]->dot);
if (fi[i]->ti)
mylog(" ----> table_name='%s', table_alias='%s'\n", fi[i]->ti->name, fi[i]->ti->alias);
}
for (i = 0; i < stmt->ntab; i++)
mylog("Table %d: name='%s', alias='%s'\n", i, ti[i]->name, ti[i]->alias);
/*
* Now save the SQLColumns Info for the parse tables
*/
/* Call SQLColumns for each table and store the result */
for (i = 0; i < stmt->ntab; i++)
{
/* See if already got it */
char found = FALSE;
for (k = 0; k < conn->ntables; k++)
{
if (!stricmp(conn->col_info[k]->name, ti[i]->name))
{
mylog("FOUND col_info table='%s'\n", ti[i]->name);
found = TRUE;
break;
}
}
if (!found)
{
mylog("PARSE: Getting PG_Columns for table[%d]='%s'\n", i, ti[i]->name);
result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt);
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
{
stmt->errormsg = "PGAPI_AllocStmt failed in parse_statement for columns.";
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->parse_status = STMT_PARSE_FATAL;
return FALSE;
}
col_stmt = (StatementClass *) hcol_stmt;
col_stmt->internal = TRUE;
result = PGAPI_Columns(hcol_stmt, "", 0, "", 0,
ti[i]->name, (SWORD) strlen(ti[i]->name), "", 0);
mylog(" Past PG_Columns\n");
if (result == SQL_SUCCESS)
{
mylog(" Success\n");
if (!(conn->ntables % COL_INCR))
{
mylog("PARSE: Allocing col_info at ntables=%d\n", conn->ntables);
conn->col_info = (COL_INFO **) realloc(conn->col_info, (conn->ntables + COL_INCR) * sizeof(COL_INFO *));
if (!conn->col_info)
{
stmt->parse_status = STMT_PARSE_FATAL;
return FALSE;
}
}
mylog("PARSE: malloc at conn->col_info[%d]\n", conn->ntables);
conn->col_info[conn->ntables] = (COL_INFO *) malloc(sizeof(COL_INFO));
if (!conn->col_info[conn->ntables])
{
stmt->parse_status = STMT_PARSE_FATAL;
return FALSE;
}
/*
* Store the table name and the SQLColumns result
* structure
*/
strcpy(conn->col_info[conn->ntables]->name, ti[i]->name);
conn->col_info[conn->ntables]->result = col_stmt->result;
/*
* The connection will now free the result structures, so
* make sure that the statement doesn't free it
*/
col_stmt->result = NULL;
conn->ntables++;
PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
mylog("Created col_info table='%s', ntables=%d\n", ti[i]->name, conn->ntables);
}
else
{
PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
break;
}
}
/* Associate a table from the statement with a SQLColumn info */
ti[i]->col_info = conn->col_info[k];
mylog("associate col_info: i=%d, k=%d\n", i, k);
}
mylog("Done PG_Columns\n");
/*
* Now resolve the fields to point to column info
*/
for (i = 0; i < stmt->nfld;)
{
/* Dont worry about functions or quotes */
if (fi[i]->func || fi[i]->quote || fi[i]->numeric)
{
i++;
continue;
}
/* Stars get expanded to all fields in the table */
else if (fi[i]->name[0] == '*')
{
char do_all_tables;
int total_cols,
old_alloc,
new_size,
cols;
int increased_cols;
mylog("expanding field %d\n", i);
total_cols = 0;
if (fi[i]->ti) /* The star represents only the qualified
* table */
total_cols = QR_get_num_tuples(fi[i]->ti->col_info->result);
else
{ /* The star represents all tables */
/* Calculate the total number of columns after expansion */
for (k = 0; k < stmt->ntab; k++)
total_cols += QR_get_num_tuples(ti[k]->col_info->result);
}
increased_cols = total_cols - 1;
/* Allocate some more field pointers if necessary */
old_alloc = ((stmt->nfld - 1) / FLD_INCR + 1) * FLD_INCR;
new_size = stmt->nfld + increased_cols;
mylog("k=%d, increased_cols=%d, old_alloc=%d, new_size=%d\n", k, increased_cols, old_alloc, new_size);
if (new_size > old_alloc)
{
int new_alloc = ((new_size / FLD_INCR) + 1) * FLD_INCR;
mylog("need more cols: new_alloc = %d\n", new_alloc);
fi = (FIELD_INFO **) realloc(fi, new_alloc * sizeof(FIELD_INFO *));
if (!fi)
{
stmt->parse_status = STMT_PARSE_FATAL;
return FALSE;
}
stmt->fi = fi;
}
/*
* copy any other fields (if there are any) up past the
* expansion
*/
for (j = stmt->nfld - 1; j > i; j--)
{
mylog("copying field %d to %d\n", j, increased_cols + j);
fi[increased_cols + j] = fi[j];
}
mylog("done copying fields\n");
/* Set the new number of fields */
stmt->nfld += increased_cols;
mylog("stmt->nfld now at %d\n", stmt->nfld);
/* copy the new field info */
do_all_tables = (fi[i]->ti ? FALSE : TRUE);
for (k = 0; k < (do_all_tables ? stmt->ntab : 1); k++)
{
TABLE_INFO *the_ti = do_all_tables ? ti[k] : fi[i]->ti;
cols = QR_get_num_tuples(the_ti->col_info->result);
for (n = 0; n < cols; n++)
{
mylog("creating field info: n=%d\n", n);
/* skip malloc (already did it for the Star) */
if (k > 0 || n > 0)
{
mylog("allocating field info at %d\n", n + i);
fi[n + i] = (FIELD_INFO *) malloc(sizeof(FIELD_INFO));
if (fi[n + i] == NULL)
{
stmt->parse_status = STMT_PARSE_FATAL;
return FALSE;
}
}
/* Initialize the new space (or the * field) */
memset(fi[n + i], 0, sizeof(FIELD_INFO));
fi[n + i]->ti = the_ti;
mylog("about to copy at %d\n", n + i);
getColInfo(the_ti->col_info, fi[n + i], n);
mylog("done copying\n");
}
i += cols;
mylog("i now at %d\n", i);
}
}
/*
* We either know which table the field was in because it was
* qualified with a table name or alias -OR- there was only 1
* table.
*/
else if (fi[i]->ti)
{
if (!searchColInfo(fi[i]->ti->col_info, fi[i]))
parse = FALSE;
i++;
}
/* Don't know the table -- search all tables in "from" list */
else
{
parse = FALSE;
for (k = 0; k < stmt->ntab; k++)
{
if (searchColInfo(ti[k]->col_info, fi[i]))
{
fi[i]->ti = ti[k]; /* now know the table */
parse = TRUE;
break;
}
}
i++;
}
}
if (!parse)
stmt->parse_status = STMT_PARSE_INCOMPLETE;
else
stmt->parse_status = STMT_PARSE_COMPLETE;
mylog("done parse_statement: parse=%d, parse_status=%d\n", parse, stmt->parse_status);
return parse;
}
/*-------
* Module: pgapifunc.h
*
*-------
*/
#ifndef _PG_API_FUNC_H__
#define _PG_API_FUNC_H__
#include "psqlodbc.h"
#include <stdio.h>
#include <string.h>
RETCODE SQL_API PGAPI_AllocConnect(HENV EnvironmentHandle,
HDBC FAR * ConnectionHandle);
RETCODE SQL_API PGAPI_AllocEnv(HENV FAR * EnvironmentHandle);
RETCODE SQL_API PGAPI_AllocStmt(HDBC ConnectionHandle,
HSTMT *StatementHandle);
RETCODE SQL_API PGAPI_BindCol(HSTMT StatementHandle,
SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
PTR TargetValue, SQLINTEGER BufferLength,
SQLINTEGER *StrLen_or_Ind);
RETCODE SQL_API PGAPI_Cancel(HSTMT StatementHandle);
RETCODE SQL_API PGAPI_Columns(HSTMT StatementHandle,
SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
SQLCHAR *TableName, SQLSMALLINT NameLength3,
SQLCHAR *ColumnName, SQLSMALLINT NameLength4);
RETCODE SQL_API PGAPI_Connect(HDBC ConnectionHandle,
SQLCHAR *ServerName, SQLSMALLINT NameLength1,
SQLCHAR *UserName, SQLSMALLINT NameLength2,
SQLCHAR *Authentication, SQLSMALLINT NameLength3);
RETCODE SQL_API PGAPI_DriverConnect(HDBC hdbc, HWND hwnd,
UCHAR FAR * szConnStrIn, SWORD cbConnStrIn,
UCHAR FAR * szConnStrOut, SWORD cbConnStrOutMax,
SWORD FAR * pcbConnStrOut, UWORD fDriverCompletion);
RETCODE SQL_API PGAPI_BrowseConnect(HDBC hdbc,
SQLCHAR *szConnStrIn, SQLSMALLINT cbConnStrIn,
SQLCHAR *szConnStrOut, SQLSMALLINT cbConnStrOutMax,
SQLSMALLINT *pcbConnStrOut);
RETCODE SQL_API PGAPI_DataSources(HENV EnvironmentHandle,
SQLUSMALLINT Direction, SQLCHAR *ServerName,
SQLSMALLINT BufferLength1, SQLSMALLINT *NameLength1,
SQLCHAR *Description, SQLSMALLINT BufferLength2,
SQLSMALLINT *NameLength2);
RETCODE SQL_API PGAPI_DescribeCol(HSTMT StatementHandle,
SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName,
SQLSMALLINT BufferLength, SQLSMALLINT *NameLength,
SQLSMALLINT *DataType, SQLUINTEGER *ColumnSize,
SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable);
RETCODE SQL_API PGAPI_Disconnect(HDBC ConnectionHandle);
RETCODE SQL_API PGAPI_Error(HENV EnvironmentHandle,
HDBC ConnectionHandle, HSTMT StatementHandle,
SQLCHAR *Sqlstate, SQLINTEGER *NativeError,
SQLCHAR *MessageText, SQLSMALLINT BufferLength,
SQLSMALLINT *TextLength);
RETCODE SQL_API PGAPI_ExecDirect(HSTMT StatementHandle,
SQLCHAR *StatementText, SQLINTEGER TextLength);
RETCODE SQL_API PGAPI_Execute(HSTMT StatementHandle);
RETCODE SQL_API PGAPI_Fetch(HSTMT StatementHandle);
RETCODE SQL_API PGAPI_FreeConnect(HDBC ConnectionHandle);
RETCODE SQL_API PGAPI_FreeEnv(HENV EnvironmentHandle);
RETCODE SQL_API PGAPI_FreeStmt(HSTMT StatementHandle,
SQLUSMALLINT Option);
RETCODE SQL_API PGAPI_GetConnectOption(HDBC ConnectionHandle,
SQLUSMALLINT Option, PTR Value);
RETCODE SQL_API PGAPI_GetCursorName(HSTMT StatementHandle,
SQLCHAR *CursorName, SQLSMALLINT BufferLength,
SQLSMALLINT *NameLength);
RETCODE SQL_API PGAPI_GetData(HSTMT StatementHandle,
SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
PTR TargetValue, SQLINTEGER BufferLength,
SQLINTEGER *StrLen_or_Ind);
RETCODE SQL_API PGAPI_GetFunctions(HDBC ConnectionHandle,
SQLUSMALLINT FunctionId, SQLUSMALLINT *Supported);
RETCODE SQL_API PGAPI_GetFunctions30(HDBC ConnectionHandle,
SQLUSMALLINT FunctionId, SQLUSMALLINT *Supported);
RETCODE SQL_API PGAPI_GetInfo(HDBC ConnectionHandle,
SQLUSMALLINT InfoType, PTR InfoValue,
SQLSMALLINT BufferLength, SQLSMALLINT *StringLength);
RETCODE SQL_API PGAPI_GetInfo30(HDBC ConnectionHandle,
SQLUSMALLINT InfoType, PTR InfoValue,
SQLSMALLINT BufferLength, SQLSMALLINT *StringLength);
RETCODE SQL_API PGAPI_GetStmtOption(HSTMT StatementHandle,
SQLUSMALLINT Option, PTR Value);
RETCODE SQL_API PGAPI_GetTypeInfo(HSTMT StatementHandle,
SQLSMALLINT DataType);
RETCODE SQL_API PGAPI_NumResultCols(HSTMT StatementHandle,
SQLSMALLINT *ColumnCount);
RETCODE SQL_API PGAPI_ParamData(HSTMT StatementHandle,
PTR *Value);
RETCODE SQL_API PGAPI_Prepare(HSTMT StatementHandle,
SQLCHAR *StatementText, SQLINTEGER TextLength);
RETCODE SQL_API PGAPI_PutData(HSTMT StatementHandle,
PTR Data, SQLINTEGER StrLen_or_Ind);
RETCODE SQL_API PGAPI_RowCount(HSTMT StatementHandle,
SQLINTEGER *RowCount);
RETCODE SQL_API PGAPI_SetConnectOption(HDBC ConnectionHandle,
SQLUSMALLINT Option, SQLUINTEGER Value);
RETCODE SQL_API PGAPI_SetCursorName(HSTMT StatementHandle,
SQLCHAR *CursorName, SQLSMALLINT NameLength);
RETCODE SQL_API PGAPI_SetParam(HSTMT StatementHandle,
SQLUSMALLINT ParameterNumber, SQLSMALLINT ValueType,
SQLSMALLINT ParameterType, SQLUINTEGER LengthPrecision,
SQLSMALLINT ParameterScale, PTR ParameterValue,
SQLINTEGER *StrLen_or_Ind);
RETCODE SQL_API PGAPI_SetStmtOption(HSTMT StatementHandle,
SQLUSMALLINT Option, SQLUINTEGER Value);
RETCODE SQL_API PGAPI_SpecialColumns(HSTMT StatementHandle,
SQLUSMALLINT IdentifierType, SQLCHAR *CatalogName,
SQLSMALLINT NameLength1, SQLCHAR *SchemaName,
SQLSMALLINT NameLength2, SQLCHAR *TableName,
SQLSMALLINT NameLength3, SQLUSMALLINT Scope,
SQLUSMALLINT Nullable);
RETCODE SQL_API PGAPI_Statistics(HSTMT StatementHandle,
SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
SQLCHAR *TableName, SQLSMALLINT NameLength3,
SQLUSMALLINT Unique, SQLUSMALLINT Reserved);
RETCODE SQL_API PGAPI_Tables(HSTMT StatementHandle,
SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
SQLCHAR *TableName, SQLSMALLINT NameLength3,
SQLCHAR *TableType, SQLSMALLINT NameLength4);
RETCODE SQL_API PGAPI_Transact(HENV EnvironmentHandle,
HDBC ConnectionHandle, SQLUSMALLINT CompletionType);
RETCODE SQL_API PGAPI_ColAttributes(
HSTMT hstmt,
SQLUSMALLINT icol,
SQLUSMALLINT fDescType,
PTR rgbDesc,
SQLSMALLINT cbDescMax,
SQLSMALLINT *pcbDesc,
SQLINTEGER *pfDesc);
RETCODE SQL_API PGAPI_ColumnPrivileges(
HSTMT hstmt,
SQLCHAR *szCatalogName,
SQLSMALLINT cbCatalogName,
SQLCHAR *szSchemaName,
SQLSMALLINT cbSchemaName,
SQLCHAR *szTableName,
SQLSMALLINT cbTableName,
SQLCHAR *szColumnName,
SQLSMALLINT cbColumnName);
RETCODE SQL_API PGAPI_DescribeParam(
HSTMT hstmt,
SQLUSMALLINT ipar,
SQLSMALLINT *pfSqlType,
SQLUINTEGER *pcbParamDef,
SQLSMALLINT *pibScale,
SQLSMALLINT *pfNullable);
RETCODE SQL_API PGAPI_ExtendedFetch(
HSTMT hstmt,
SQLUSMALLINT fFetchType,
SQLINTEGER irow,
SQLUINTEGER *pcrow,
SQLUSMALLINT *rgfRowStatus);
RETCODE SQL_API PGAPI_ForeignKeys(
HSTMT hstmt,
SQLCHAR *szPkCatalogName,
SQLSMALLINT cbPkCatalogName,
SQLCHAR *szPkSchemaName,
SQLSMALLINT cbPkSchemaName,
SQLCHAR *szPkTableName,
SQLSMALLINT cbPkTableName,
SQLCHAR *szFkCatalogName,
SQLSMALLINT cbFkCatalogName,
SQLCHAR *szFkSchemaName,
SQLSMALLINT cbFkSchemaName,
SQLCHAR *szFkTableName,
SQLSMALLINT cbFkTableName);
RETCODE SQL_API PGAPI_MoreResults(
HSTMT hstmt);
RETCODE SQL_API PGAPI_NativeSql(
HDBC hdbc,
SQLCHAR *szSqlStrIn,
SQLINTEGER cbSqlStrIn,
SQLCHAR *szSqlStr,
SQLINTEGER cbSqlStrMax,
SQLINTEGER *pcbSqlStr);
RETCODE SQL_API PGAPI_NumParams(
HSTMT hstmt,
SQLSMALLINT *pcpar);
RETCODE SQL_API PGAPI_ParamOptions(
HSTMT hstmt,
SQLUINTEGER crow,
SQLUINTEGER *pirow);
RETCODE SQL_API PGAPI_PrimaryKeys(
HSTMT hstmt,
SQLCHAR *szCatalogName,
SQLSMALLINT cbCatalogName,
SQLCHAR *szSchemaName,
SQLSMALLINT cbSchemaName,
SQLCHAR *szTableName,
SQLSMALLINT cbTableName);
RETCODE SQL_API PGAPI_ProcedureColumns(
HSTMT hstmt,
SQLCHAR *szCatalogName,
SQLSMALLINT cbCatalogName,
SQLCHAR *szSchemaName,
SQLSMALLINT cbSchemaName,
SQLCHAR *szProcName,
SQLSMALLINT cbProcName,
SQLCHAR *szColumnName,
SQLSMALLINT cbColumnName);
RETCODE SQL_API PGAPI_Procedures(
HSTMT hstmt,
SQLCHAR *szCatalogName,
SQLSMALLINT cbCatalogName,
SQLCHAR *szSchemaName,
SQLSMALLINT cbSchemaName,
SQLCHAR *szProcName,
SQLSMALLINT cbProcName);
RETCODE SQL_API PGAPI_SetPos(
HSTMT hstmt,
SQLUSMALLINT irow,
SQLUSMALLINT fOption,
SQLUSMALLINT fLock);
RETCODE SQL_API PGAPI_TablePrivileges(
HSTMT hstmt,
SQLCHAR *szCatalogName,
SQLSMALLINT cbCatalogName,
SQLCHAR *szSchemaName,
SQLSMALLINT cbSchemaName,
SQLCHAR *szTableName,
SQLSMALLINT cbTableName);
RETCODE SQL_API PGAPI_BindParameter(
HSTMT hstmt,
SQLUSMALLINT ipar,
SQLSMALLINT fParamType,
SQLSMALLINT fCType,
SQLSMALLINT fSqlType,
SQLUINTEGER cbColDef,
SQLSMALLINT ibScale,
PTR rgbValue,
SQLINTEGER cbValueMax,
SQLINTEGER *pcbValue);
RETCODE SQL_API PGAPI_SetScrollOptions(
HSTMT hstmt,
UWORD fConcurrency,
SDWORD crowKeyset,
UWORD crowRowset);
#endif /* define_PG_API_FUNC_H__ */
/*--------
* Module: pgtypes.c
*
* Description: This module contains routines for getting information
* about the supported Postgres data types. Only the
* function pgtype_to_sqltype() returns an unknown condition.
* All other functions return a suitable default so that
* even data types that are not directly supported can be
* used (it is handled as char data).
*
* Classes: n/a
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*--------
*/
#include "pgtypes.h"
#include "dlg_specific.h"
#include "statement.h"
#include "connection.h"
#include "qresult.h"
Int4 getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as);
/*
* these are the types we support. all of the pgtype_ functions should
* return values for each one of these.
* Even types not directly supported are handled as character types
* so all types should work (points, etc.)
*/
/*
* ALL THESE TYPES ARE NO LONGER REPORTED in SQLGetTypeInfo. Instead, all
* the SQL TYPES are reported and mapped to a corresponding Postgres Type
*/
/*
Int4 pgtypes_defined[] = {
PG_TYPE_CHAR,
PG_TYPE_CHAR2,
PG_TYPE_CHAR4,
PG_TYPE_CHAR8,
PG_TYPE_CHAR16,
PG_TYPE_NAME,
PG_TYPE_VARCHAR,
PG_TYPE_BPCHAR,
PG_TYPE_DATE,
PG_TYPE_TIME,
PG_TYPE_DATETIME,
PG_TYPE_ABSTIME,
PG_TYPE_TIMESTAMP,
PG_TYPE_TEXT,
PG_TYPE_INT2,
PG_TYPE_INT4,
PG_TYPE_FLOAT4,
PG_TYPE_FLOAT8,
PG_TYPE_OID,
PG_TYPE_MONEY,
PG_TYPE_BOOL,
PG_TYPE_BYTEA,
PG_TYPE_LO,
0 };
*/
/* These are NOW the SQL Types reported in SQLGetTypeInfo. */
Int2 sqlTypes[] = {
SQL_BIGINT,
/* SQL_BINARY, -- Commented out because VarBinary is more correct. */
SQL_BIT,
SQL_CHAR,
SQL_DATE,
SQL_DECIMAL,
SQL_DOUBLE,
SQL_FLOAT,
SQL_INTEGER,
SQL_LONGVARBINARY,
SQL_LONGVARCHAR,
SQL_NUMERIC,
SQL_REAL,
SQL_SMALLINT,
SQL_TIME,
SQL_TIMESTAMP,
SQL_TINYINT,
SQL_VARBINARY,
SQL_VARCHAR,
0
};
Int4
sqltype_to_pgtype(StatementClass *stmt, SWORD fSqlType)
{
Int4 pgType;
ConnInfo *ci = &(SC_get_conn(stmt)->connInfo);
switch (fSqlType)
{
case SQL_BINARY:
pgType = PG_TYPE_BYTEA;
break;
case SQL_CHAR:
pgType = PG_TYPE_BPCHAR;
break;
case SQL_BIT:
pgType = ci->drivers.bools_as_char ? PG_TYPE_CHAR : PG_TYPE_BOOL;
break;
case SQL_DATE:
pgType = PG_TYPE_DATE;
break;
case SQL_DOUBLE:
case SQL_FLOAT:
pgType = PG_TYPE_FLOAT8;
break;
case SQL_DECIMAL:
case SQL_NUMERIC:
pgType = PG_TYPE_NUMERIC;
break;
case SQL_BIGINT:
pgType = PG_TYPE_INT8;
break;
case SQL_INTEGER:
pgType = PG_TYPE_INT4;
break;
case SQL_LONGVARBINARY:
pgType = PG_TYPE_LO;
break;
case SQL_LONGVARCHAR:
pgType = ci->drivers.text_as_longvarchar ? PG_TYPE_TEXT : PG_TYPE_VARCHAR;
break;
case SQL_REAL:
pgType = PG_TYPE_FLOAT4;
break;
case SQL_SMALLINT:
case SQL_TINYINT:
pgType = PG_TYPE_INT2;
break;
case SQL_TIME:
pgType = PG_TYPE_TIME;
break;
case SQL_TIMESTAMP:
pgType = PG_TYPE_DATETIME;
break;
case SQL_VARBINARY:
pgType = PG_TYPE_BYTEA;
break;
case SQL_VARCHAR:
pgType = PG_TYPE_VARCHAR;
break;
default:
pgType = 0; /* ??? */
break;
}
return pgType;
}
/*
* There are two ways of calling this function:
*
* 1. When going through the supported PG types (SQLGetTypeInfo)
*
* 2. When taking any type id (SQLColumns, SQLGetData)
*
* The first type will always work because all the types defined are returned here.
* The second type will return a default based on global parameter when it does not
* know. This allows for supporting
* types that are unknown. All other pg routines in here return a suitable default.
*/
Int2
pgtype_to_sqltype(StatementClass *stmt, Int4 type)
{
ConnInfo *ci = &(SC_get_conn(stmt)->connInfo);
switch (type)
{
case PG_TYPE_CHAR:
case PG_TYPE_CHAR2:
case PG_TYPE_CHAR4:
case PG_TYPE_CHAR8:
case PG_TYPE_NAME:
return SQL_CHAR;
case PG_TYPE_BPCHAR:
return SQL_CHAR;
case PG_TYPE_VARCHAR:
return SQL_VARCHAR;
case PG_TYPE_TEXT:
return ci->drivers.text_as_longvarchar ? SQL_LONGVARCHAR : SQL_VARCHAR;
case PG_TYPE_BYTEA:
return SQL_VARBINARY;
case PG_TYPE_LO:
return SQL_LONGVARBINARY;
case PG_TYPE_INT2:
return SQL_SMALLINT;
case PG_TYPE_OID:
case PG_TYPE_XID:
case PG_TYPE_INT4:
return SQL_INTEGER;
/* Change this to SQL_BIGINT for ODBC v3 bjm 2001-01-23 */
case PG_TYPE_INT8:
return SQL_CHAR;
case PG_TYPE_NUMERIC:
return SQL_NUMERIC;
case PG_TYPE_FLOAT4:
return SQL_REAL;
case PG_TYPE_FLOAT8:
return SQL_FLOAT;
case PG_TYPE_DATE:
return SQL_DATE;
case PG_TYPE_TIME:
return SQL_TIME;
case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME:
case PG_TYPE_TIMESTAMP:
return SQL_TIMESTAMP;
case PG_TYPE_MONEY:
return SQL_FLOAT;
case PG_TYPE_BOOL:
return ci->drivers.bools_as_char ? SQL_CHAR : SQL_BIT;
default:
/*
* first, check to see if 'type' is in list. If not, look up
* with query. Add oid, name to list. If it's already in
* list, just return.
*/
/* hack until permanent type is available */
if (type == stmt->hdbc->lobj_type)
return SQL_LONGVARBINARY;
return ci->drivers.unknowns_as_longvarchar ? SQL_LONGVARCHAR : SQL_VARCHAR;
}
}
Int2
pgtype_to_ctype(StatementClass *stmt, Int4 type)
{
ConnInfo *ci = &(SC_get_conn(stmt)->connInfo);
switch (type)
{
case PG_TYPE_INT8:
return SQL_C_CHAR;
case PG_TYPE_NUMERIC:
return SQL_C_CHAR;
case PG_TYPE_INT2:
return SQL_C_SSHORT;
case PG_TYPE_OID:
case PG_TYPE_XID:
case PG_TYPE_INT4:
return SQL_C_SLONG;
case PG_TYPE_FLOAT4:
return SQL_C_FLOAT;
case PG_TYPE_FLOAT8:
return SQL_C_DOUBLE;
case PG_TYPE_DATE:
return SQL_C_DATE;
case PG_TYPE_TIME:
return SQL_C_TIME;
case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME:
case PG_TYPE_TIMESTAMP:
return SQL_C_TIMESTAMP;
case PG_TYPE_MONEY:
return SQL_C_FLOAT;
case PG_TYPE_BOOL:
return ci->drivers.bools_as_char ? SQL_C_CHAR : SQL_C_BIT;
case PG_TYPE_BYTEA:
return SQL_C_BINARY;
case PG_TYPE_LO:
return SQL_C_BINARY;
default:
/* hack until permanent type is available */
if (type == stmt->hdbc->lobj_type)
return SQL_C_BINARY;
return SQL_C_CHAR;
}
}
char *
pgtype_to_name(StatementClass *stmt, Int4 type)
{
switch (type)
{
case PG_TYPE_CHAR:
return "char";
case PG_TYPE_CHAR2:
return "char2";
case PG_TYPE_CHAR4:
return "char4";
case PG_TYPE_CHAR8:
return "char8";
case PG_TYPE_INT8:
return "int8";
case PG_TYPE_NUMERIC:
return "numeric";
case PG_TYPE_VARCHAR:
return "varchar";
case PG_TYPE_BPCHAR:
return "char";
case PG_TYPE_TEXT:
return "text";
case PG_TYPE_NAME:
return "name";
case PG_TYPE_INT2:
return "int2";
case PG_TYPE_OID:
return "oid";
case PG_TYPE_INT4:
return "int4";
case PG_TYPE_FLOAT4:
return "float4";
case PG_TYPE_FLOAT8:
return "float8";
case PG_TYPE_DATE:
return "date";
case PG_TYPE_TIME:
return "time";
case PG_TYPE_ABSTIME:
return "abstime";
case PG_TYPE_DATETIME:
return "datetime";
case PG_TYPE_TIMESTAMP:
return "timestamp";
case PG_TYPE_MONEY:
return "money";
case PG_TYPE_BOOL:
return "bool";
case PG_TYPE_BYTEA:
return "bytea";
case PG_TYPE_LO:
return PG_TYPE_LO_NAME;
default:
/* hack until permanent type is available */
if (type == stmt->hdbc->lobj_type)
return PG_TYPE_LO_NAME;
/*
* "unknown" can actually be used in alter table because it is
* a real PG type!
*/
return "unknown";
}
}
static Int2
getNumericScale(StatementClass *stmt, Int4 type, int col)
{
Int4 atttypmod;
QResultClass *result;
ColumnInfoClass *flds;
mylog("getNumericScale: type=%d, col=%d\n", type, col);
if (col < 0)
return PG_NUMERIC_MAX_SCALE;
result = SC_get_Result(stmt);
/*
* Manual Result Sets -- use assigned column width (i.e., from
* set_tuplefield_string)
*/
if (stmt->manual_result)
{
flds = result->fields;
if (flds)
return flds->adtsize[col];
else
return PG_NUMERIC_MAX_SCALE;
}
atttypmod = QR_get_atttypmod(result, col);
if (atttypmod > -1)
return (atttypmod & 0xffff);
else
return (QR_get_display_size(result, col) ?
QR_get_display_size(result, col) :
PG_NUMERIC_MAX_SCALE);
}
static Int4
getNumericPrecision(StatementClass *stmt, Int4 type, int col)
{
Int4 atttypmod;
QResultClass *result;
ColumnInfoClass *flds;
mylog("getNumericPrecision: type=%d, col=%d\n", type, col);
if (col < 0)
return PG_NUMERIC_MAX_PRECISION;
result = SC_get_Result(stmt);
/*
* Manual Result Sets -- use assigned column width (i.e., from
* set_tuplefield_string)
*/
if (stmt->manual_result)
{
flds = result->fields;
if (flds)
return flds->adtsize[col];
else
return PG_NUMERIC_MAX_PRECISION;
}
atttypmod = QR_get_atttypmod(result, col);
if (atttypmod > -1)
return (atttypmod >> 16) & 0xffff;
else
return (QR_get_display_size(result, col) >= 0 ?
QR_get_display_size(result, col) :
PG_NUMERIC_MAX_PRECISION);
}
Int4
getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
{
int p = -1,
maxsize;
QResultClass *result;
ColumnInfoClass *flds;
ConnInfo *ci = &(SC_get_conn(stmt)->connInfo);
mylog("getCharPrecision: type=%d, col=%d, unknown = %d\n", type, col, handle_unknown_size_as);
/* Assign Maximum size based on parameters */
switch (type)
{
case PG_TYPE_TEXT:
if (ci->drivers.text_as_longvarchar)
maxsize = ci->drivers.max_longvarchar_size;
else
maxsize = ci->drivers.max_varchar_size;
break;
case PG_TYPE_VARCHAR:
case PG_TYPE_BPCHAR:
maxsize = ci->drivers.max_varchar_size;
break;
default:
if (ci->drivers.unknowns_as_longvarchar)
maxsize = ci->drivers.max_longvarchar_size;
else
maxsize = ci->drivers.max_varchar_size;
break;
}
/*
* Static Precision (i.e., the Maximum Precision of the datatype) This
* has nothing to do with a result set.
*/
if (maxsize == TEXT_FIELD_SIZE + 1) /* magic length for testing */
{
if (PG_VERSION_GE(SC_get_conn(stmt), 7.1))
maxsize = 0;
else
maxsize = TEXT_FIELD_SIZE;
}
if (col < 0)
return maxsize;
result = SC_get_Result(stmt);
/*
* Manual Result Sets -- use assigned column width (i.e., from
* set_tuplefield_string)
*/
if (stmt->manual_result)
{
flds = result->fields;
if (flds)
return flds->adtsize[col];
else
return maxsize;
}
/* Size is unknown -- handle according to parameter */
if (QR_get_atttypmod(result, col) > -1)
return QR_get_atttypmod(result, col);
if (type == PG_TYPE_BPCHAR || handle_unknown_size_as == UNKNOWNS_AS_LONGEST)
{
p = QR_get_display_size(result, col);
mylog("getCharPrecision: LONGEST: p = %d\n", p);
}
if (p < 0 && handle_unknown_size_as == UNKNOWNS_AS_MAX)
return maxsize;
else
return p;
}
static Int2
getTimestampScale(StatementClass *stmt, Int4 type, int col)
{
ConnectionClass *conn = SC_get_conn(stmt);
Int4 atttypmod;
QResultClass *result;
ColumnInfoClass *flds;
mylog("getTimestampScale: type=%d, col=%d\n", type, col);
if (col < 0)
return 0;
if (PG_VERSION_LT(conn, 7.2))
return 0;
result = SC_get_Result(stmt);
/*
* Manual Result Sets -- use assigned column width (i.e., from
* set_tuplefield_string)
*/
atttypmod = 0;
if (stmt->manual_result)
{
flds = result->fields;
if (flds)
atttypmod = flds->atttypmod[col];
mylog("atttypmod1=%d\n", atttypmod);
}
else
atttypmod = QR_get_atttypmod(result, col);
mylog("atttypmod2=%d\n", atttypmod);
return (atttypmod > -1 ? atttypmod : 0);
}
static Int4
getTimestampPrecision(StatementClass *stmt, Int4 type, int col)
{
Int4 fixed,
scale;
mylog("getTimestampPrecision: type=%d, col=%d\n", type, col);
switch (type)
{
case PG_TYPE_TIME:
fixed = 8;
break;
case PG_TYPE_TIME_WITH_TMZONE:
fixed = 11;
break;
case PG_TYPE_TIMESTAMP_NO_TMZONE:
fixed = 19;
break;
default:
fixed = 22;
break;
}
scale = getTimestampScale(stmt, type, col);
return (scale > 0) ? fixed + 1 + scale : fixed;
}
/*
* For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, PG_TYPE_NUMERIC, SQLColumns will
* override this length with the atttypmod length from pg_attribute .
*
* If col >= 0, then will attempt to get the info from the result set.
* This is used for functions SQLDescribeCol and SQLColAttributes.
*/
Int4
pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
{
switch (type)
{
case PG_TYPE_CHAR:
return 1;
case PG_TYPE_CHAR2:
return 2;
case PG_TYPE_CHAR4:
return 4;
case PG_TYPE_CHAR8:
return 8;
case PG_TYPE_NAME:
return NAME_FIELD_SIZE;
case PG_TYPE_INT2:
return 5;
case PG_TYPE_OID:
case PG_TYPE_XID:
case PG_TYPE_INT4:
return 10;
case PG_TYPE_INT8:
return 19; /* signed */
case PG_TYPE_NUMERIC:
return getNumericPrecision(stmt, type, col);
case PG_TYPE_FLOAT4:
case PG_TYPE_MONEY:
return 7;
case PG_TYPE_FLOAT8:
return 15;
case PG_TYPE_DATE:
return 10;
case PG_TYPE_TIME:
return 8;
case PG_TYPE_ABSTIME:
case PG_TYPE_TIMESTAMP:
return 22;
case PG_TYPE_DATETIME:
/* return 22; */
return getTimestampPrecision(stmt, type, col);
case PG_TYPE_BOOL:
return 1;
case PG_TYPE_LO:
return SQL_NO_TOTAL;
default:
if (type == stmt->hdbc->lobj_type) /* hack until permanent
* type is available */
return SQL_NO_TOTAL;
/* Handle Character types and unknown types */
return getCharPrecision(stmt, type, col, handle_unknown_size_as);
}
}
Int4
pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
{
switch (type)
{
case PG_TYPE_INT2:
return 6;
case PG_TYPE_OID:
case PG_TYPE_XID:
return 10;
case PG_TYPE_INT4:
return 11;
case PG_TYPE_INT8:
return 20; /* signed: 19 digits + sign */
case PG_TYPE_NUMERIC:
return getNumericPrecision(stmt, type, col) + 2;
case PG_TYPE_MONEY:
return 15; /* ($9,999,999.99) */
case PG_TYPE_FLOAT4:
return 13;
case PG_TYPE_FLOAT8:
return 22;
/* Character types use regular precision */
default:
return pgtype_precision(stmt, type, col, handle_unknown_size_as);
}
}
/*
* For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, SQLColumns will
* override this length with the atttypmod length from pg_attribute
*/
Int4
pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
{
switch (type)
{
case PG_TYPE_INT2:
return 2;
case PG_TYPE_OID:
case PG_TYPE_XID:
case PG_TYPE_INT4:
return 4;
case PG_TYPE_INT8:
return 20; /* signed: 19 digits + sign */
case PG_TYPE_NUMERIC:
return getNumericPrecision(stmt, type, col) + 2;
case PG_TYPE_FLOAT4:
case PG_TYPE_MONEY:
return 4;
case PG_TYPE_FLOAT8:
return 8;
case PG_TYPE_DATE:
case PG_TYPE_TIME:
return 6; /* sizeof(DATE(TIME)_STRUCT) */
case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME:
case PG_TYPE_TIMESTAMP:
return 16; /* sizeof(TIMESTAMP_STRUCT) */
/* Character types (and NUMERIC) use the default precision */
case PG_TYPE_VARCHAR:
case PG_TYPE_BPCHAR:
#ifdef MULTIBYTE
/* after 7.2 */
if (PG_VERSION_GE(SC_get_conn(stmt), 7.2))
return 3 * pgtype_precision(stmt, type, col, handle_unknown_size_as);
else
#else
/* CR -> CR/LF */
return 2 * pgtype_precision(stmt, type, col, handle_unknown_size_as);
#endif /* MULTIBYTE */
default:
return pgtype_precision(stmt, type, col, handle_unknown_size_as);
}
}
Int2
pgtype_scale(StatementClass *stmt, Int4 type, int col)
{
switch (type)
{
case PG_TYPE_INT2:
case PG_TYPE_OID:
case PG_TYPE_XID:
case PG_TYPE_INT4:
case PG_TYPE_INT8:
case PG_TYPE_FLOAT4:
case PG_TYPE_FLOAT8:
case PG_TYPE_MONEY:
case PG_TYPE_BOOL:
/*
* Number of digits to the right of the decimal point in
* "yyyy-mm=dd hh:mm:ss[.f...]"
*/
case PG_TYPE_ABSTIME:
case PG_TYPE_TIMESTAMP:
return 0;
case PG_TYPE_DATETIME:
/* return 0; */
return getTimestampScale(stmt, type, col);
case PG_TYPE_NUMERIC:
return getNumericScale(stmt, type, col);
default:
return -1;
}
}
Int2
pgtype_radix(StatementClass *stmt, Int4 type)
{
switch (type)
{
case PG_TYPE_INT2:
case PG_TYPE_OID:
case PG_TYPE_INT4:
case PG_TYPE_INT8:
case PG_TYPE_NUMERIC:
case PG_TYPE_FLOAT4:
case PG_TYPE_MONEY:
case PG_TYPE_FLOAT8:
return 10;
default:
return -1;
}
}
Int2
pgtype_nullable(StatementClass *stmt, Int4 type)
{
return SQL_NULLABLE; /* everything should be nullable */
}
Int2
pgtype_auto_increment(StatementClass *stmt, Int4 type)
{
switch (type)
{
case PG_TYPE_INT2:
case PG_TYPE_OID:
case PG_TYPE_XID:
case PG_TYPE_INT4:
case PG_TYPE_FLOAT4:
case PG_TYPE_MONEY:
case PG_TYPE_BOOL:
case PG_TYPE_FLOAT8:
case PG_TYPE_INT8:
case PG_TYPE_NUMERIC:
case PG_TYPE_DATE:
case PG_TYPE_TIME:
case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME:
case PG_TYPE_TIMESTAMP:
return FALSE;
default:
return -1;
}
}
Int2
pgtype_case_sensitive(StatementClass *stmt, Int4 type)
{
switch (type)
{
case PG_TYPE_CHAR:
case PG_TYPE_CHAR2:
case PG_TYPE_CHAR4:
case PG_TYPE_CHAR8:
case PG_TYPE_VARCHAR:
case PG_TYPE_BPCHAR:
case PG_TYPE_TEXT:
case PG_TYPE_NAME:
return TRUE;
default:
return FALSE;
}
}
Int2
pgtype_money(StatementClass *stmt, Int4 type)
{
switch (type)
{
case PG_TYPE_MONEY:
return TRUE;
default:
return FALSE;
}
}
Int2
pgtype_searchable(StatementClass *stmt, Int4 type)
{
switch (type)
{
case PG_TYPE_CHAR:
case PG_TYPE_CHAR2:
case PG_TYPE_CHAR4:
case PG_TYPE_CHAR8:
case PG_TYPE_VARCHAR:
case PG_TYPE_BPCHAR:
case PG_TYPE_TEXT:
case PG_TYPE_NAME:
return SQL_SEARCHABLE;
default:
return SQL_ALL_EXCEPT_LIKE;
}
}
Int2
pgtype_unsigned(StatementClass *stmt, Int4 type)
{
switch (type)
{
case PG_TYPE_OID:
case PG_TYPE_XID:
return TRUE;
case PG_TYPE_INT2:
case PG_TYPE_INT4:
case PG_TYPE_INT8:
case PG_TYPE_NUMERIC:
case PG_TYPE_FLOAT4:
case PG_TYPE_FLOAT8:
case PG_TYPE_MONEY:
return FALSE;
default:
return -1;
}
}
char *
pgtype_literal_prefix(StatementClass *stmt, Int4 type)
{
switch (type)
{
case PG_TYPE_INT2:
case PG_TYPE_OID:
case PG_TYPE_XID:
case PG_TYPE_INT4:
case PG_TYPE_INT8:
case PG_TYPE_NUMERIC:
case PG_TYPE_FLOAT4:
case PG_TYPE_FLOAT8:
case PG_TYPE_MONEY:
return NULL;
default:
return "'";
}
}
char *
pgtype_literal_suffix(StatementClass *stmt, Int4 type)
{
switch (type)
{
case PG_TYPE_INT2:
case PG_TYPE_OID:
case PG_TYPE_XID:
case PG_TYPE_INT4:
case PG_TYPE_INT8:
case PG_TYPE_NUMERIC:
case PG_TYPE_FLOAT4:
case PG_TYPE_FLOAT8:
case PG_TYPE_MONEY:
return NULL;
default:
return "'";
}
}
char *
pgtype_create_params(StatementClass *stmt, Int4 type)
{
switch (type)
{
case PG_TYPE_CHAR:
case PG_TYPE_VARCHAR:
return "max. length";
default:
return NULL;
}
}
Int2
sqltype_to_default_ctype(Int2 sqltype)
{
/*
* from the table on page 623 of ODBC 2.0 Programmer's Reference
* (Appendix D)
*/
switch (sqltype)
{
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
case SQL_DECIMAL:
case SQL_NUMERIC:
case SQL_BIGINT:
return SQL_C_CHAR;
case SQL_BIT:
return SQL_C_BIT;
case SQL_TINYINT:
return SQL_C_STINYINT;
case SQL_SMALLINT:
return SQL_C_SSHORT;
case SQL_INTEGER:
return SQL_C_SLONG;
case SQL_REAL:
return SQL_C_FLOAT;
case SQL_FLOAT:
case SQL_DOUBLE:
return SQL_C_DOUBLE;
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
return SQL_C_BINARY;
case SQL_DATE:
return SQL_C_DATE;
case SQL_TIME:
return SQL_C_TIME;
case SQL_TIMESTAMP:
return SQL_C_TIMESTAMP;
default:
/* should never happen */
return SQL_C_CHAR;
}
}
Int4
ctype_length(Int2 ctype)
{
switch (ctype)
{
case SQL_C_SSHORT:
case SQL_C_SHORT:
return sizeof(SWORD);
case SQL_C_USHORT:
return sizeof(UWORD);
case SQL_C_SLONG:
case SQL_C_LONG:
return sizeof(SDWORD);
case SQL_C_ULONG:
return sizeof(UDWORD);
case SQL_C_FLOAT:
return sizeof(SFLOAT);
case SQL_C_DOUBLE:
return sizeof(SDOUBLE);
case SQL_C_BIT:
return sizeof(UCHAR);
case SQL_C_STINYINT:
case SQL_C_TINYINT:
return sizeof(SCHAR);
case SQL_C_UTINYINT:
return sizeof(UCHAR);
case SQL_C_DATE:
return sizeof(DATE_STRUCT);
case SQL_C_TIME:
return sizeof(TIME_STRUCT);
case SQL_C_TIMESTAMP:
return sizeof(TIMESTAMP_STRUCT);
case SQL_C_BINARY:
case SQL_C_CHAR:
return 0;
default: /* should never happen */
return 0;
}
}
/* File: pgtypes.h
*
* Description: See "pgtypes.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __PGTYPES_H__
#define __PGTYPES_H__
#include "psqlodbc.h"
/* the type numbers are defined by the OID's of the types' rows */
/* in table pg_type */
#if 0
#define PG_TYPE_LO ???? /* waiting for permanent type */
#endif
#define PG_TYPE_BOOL 16
#define PG_TYPE_BYTEA 17
#define PG_TYPE_CHAR 18
#define PG_TYPE_NAME 19
#define PG_TYPE_INT8 20
#define PG_TYPE_INT2 21
#define PG_TYPE_INT2VECTOR 22
#define PG_TYPE_INT4 23
#define PG_TYPE_REGPROC 24
#define PG_TYPE_TEXT 25
#define PG_TYPE_OID 26
#define PG_TYPE_TID 27
#define PG_TYPE_XID 28
#define PG_TYPE_CID 29
#define PG_TYPE_OIDVECTOR 30
#define PG_TYPE_SET 32
#define PG_TYPE_CHAR2 409
#define PG_TYPE_CHAR4 410
#define PG_TYPE_CHAR8 411
#define PG_TYPE_POINT 600
#define PG_TYPE_LSEG 601
#define PG_TYPE_PATH 602
#define PG_TYPE_BOX 603
#define PG_TYPE_POLYGON 604
#define PG_TYPE_FILENAME 605
#define PG_TYPE_FLOAT4 700
#define PG_TYPE_FLOAT8 701
#define PG_TYPE_ABSTIME 702
#define PG_TYPE_RELTIME 703
#define PG_TYPE_TINTERVAL 704
#define PG_TYPE_UNKNOWN 705
#define PG_TYPE_MONEY 790
#define PG_TYPE_OIDINT2 810
#define PG_TYPE_OIDINT4 910
#define PG_TYPE_OIDNAME 911
#define PG_TYPE_BPCHAR 1042
#define PG_TYPE_VARCHAR 1043
#define PG_TYPE_DATE 1082
#define PG_TYPE_TIME 1083
#define PG_TYPE_TIMESTAMP_NO_TMZONE 1114 /* since 7.2 */
#define PG_TYPE_DATETIME 1184
#define PG_TYPE_TIME_WITH_TMZONE 1266 /* since 7.1 */
#define PG_TYPE_TIMESTAMP 1296 /* deprecated since 7.0 */
#define PG_TYPE_NUMERIC 1700
/* extern Int4 pgtypes_defined[]; */
extern Int2 sqlTypes[];
/* Defines for pgtype_precision */
#define PG_STATIC (-1)
Int4 sqltype_to_pgtype(StatementClass *stmt, Int2 fSqlType);
Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type);
Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type);
char *pgtype_to_name(StatementClass *stmt, Int4 type);
/* These functions can use static numbers or result sets(col parameter) */
Int4 pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as);
Int4 pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as);
Int4 pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as);
Int2 pgtype_scale(StatementClass *stmt, Int4 type, int col);
Int2 pgtype_radix(StatementClass *stmt, Int4 type);
Int2 pgtype_nullable(StatementClass *stmt, Int4 type);
Int2 pgtype_auto_increment(StatementClass *stmt, Int4 type);
Int2 pgtype_case_sensitive(StatementClass *stmt, Int4 type);
Int2 pgtype_money(StatementClass *stmt, Int4 type);
Int2 pgtype_searchable(StatementClass *stmt, Int4 type);
Int2 pgtype_unsigned(StatementClass *stmt, Int4 type);
char *pgtype_literal_prefix(StatementClass *stmt, Int4 type);
char *pgtype_literal_suffix(StatementClass *stmt, Int4 type);
char *pgtype_create_params(StatementClass *stmt, Int4 type);
Int2 sqltype_to_default_ctype(Int2 sqltype);
Int4 ctype_length(Int2 ctype);
#endif
/*--------
* Module: psqlodbc.c
*
* Description: This module contains the main entry point (DllMain)
* for the library. It also contains functions to get
* and set global variables for the driver in the registry.
*
* Classes: n/a
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*--------
*/
#include "psqlodbc.h"
#include "dlg_specific.h"
#ifdef WIN32
#include <winsock.h>
#endif
GLOBAL_VALUES globals;
RETCODE SQL_API SQLDummyOrdinal(void);
#ifdef WIN32
HINSTANCE NEAR s_hModule; /* Saved module handle. */
/* This is where the Driver Manager attaches to this Driver */
BOOL WINAPI
DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
WORD wVersionRequested;
WSADATA wsaData;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
s_hModule = hInst; /* Save for dialog boxes */
/* Load the WinSock Library */
wVersionRequested = MAKEWORD(1, 1);
if (WSAStartup(wVersionRequested, &wsaData))
return FALSE;
/* Verify that this is the minimum version of WinSock */
if (LOBYTE(wsaData.wVersion) != 1 ||
HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return FALSE;
}
getCommonDefaults(DBMS_NAME, ODBCINST_INI, NULL);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_PROCESS_DETACH:
WSACleanup();
return TRUE;
case DLL_THREAD_DETACH:
break;
default:
break;
}
return TRUE;
UNREFERENCED_PARAMETER(lpReserved);
}
#else /* not WIN32 */
#ifndef TRUE
#define TRUE (BOOL)1
#endif
#ifndef FALSE
#define FALSE (BOOL)0
#endif
#ifdef __GNUC__
/* This function is called at library initialization time. */
static BOOL
__attribute__((constructor))
init(void)
{
getCommonDefaults(DBMS_NAME, ODBCINST_INI, NULL);
return TRUE;
}
#else /* not __GNUC__ */
/*
* These two functions do shared library initialziation on UNIX, well at least
* on Linux. I don't know about other systems.
*/
BOOL
_init(void)
{
getCommonDefaults(DBMS_NAME, ODBCINST_INI, NULL);
return TRUE;
}
BOOL
_fini(void)
{
return TRUE;
}
#endif /* not __GNUC__ */
#endif /* not WIN32 */
/*
* This function is used to cause the Driver Manager to
* call functions by number rather than name, which is faster.
* The ordinal value of this function must be 199 to have the
* Driver Manager do this. Also, the ordinal values of the
* functions must match the value of fFunction in SQLGetFunctions()
*/
RETCODE SQL_API
SQLDummyOrdinal(void)
{
return SQL_SUCCESS;
}
/* File: psqlodbc.h
*
* Description: This file contains defines and declarations that are related to
* the entire driver.
*
* Comments: See "notice.txt" for copyright and license information.
*
* $Id: psqlodbc.h,v 1.1 2002/01/11 02:50:01 inoue Exp $
*
*/
#ifndef __PSQLODBC_H__
#define __PSQLODBC_H__
#ifndef WIN32
#include "pg_config.h"
#else
#include <windows.h>
#endif
#include <stdio.h> /* for FILE* pointers: see GLOBAL_VALUES */
/* Must come before sql.h */
#ifndef ODBCVER
#define ODBCVER 0x0250
#endif /* ODBCVER_REP */
#if defined(WIN32) || defined(WITH_UNIXODBC) || defined(WITH_IODBC)
#include <sql.h>
#include <sqlext.h>
#else
#include "iodbc.h"
#include "isql.h"
#include "isqlext.h"
#endif
#if defined(WIN32)
#include <odbcinst.h>
#elif defined(WITH_UNIXODBC)
#include <odbcinst.h>
#elif defined(WITH_IODBC)
#include <iodbcinst.h>
#else
#include "gpps.h"
#endif
#ifndef WIN32
#define Int4 long int
#define UInt4 unsigned int
#define Int2 short
#define UInt2 unsigned short
#if !defined(WITH_UNIXODBC) && !defined(WITH_IODBC)
typedef float SFLOAT;
typedef double SDOUBLE;
#endif
#ifndef CALLBACK
#define CALLBACK
#endif
#else
#define Int4 int
#define UInt4 unsigned int
#define Int2 short
#define UInt2 unsigned short
#endif
typedef UInt4 Oid;
#ifndef WIN32
#define stricmp strcasecmp
#define strnicmp strncasecmp
#endif
/* Driver stuff */
#define DRIVERNAME "PostgreSQL ODBC"
#if (ODBCVER >= 0x0300)
#define DRIVER_ODBC_VER "03.00"
#define DBMS_NAME "PostgreSQL30"
#else
#define DRIVER_ODBC_VER "02.50"
#define DBMS_NAME "PostgreSQL"
#endif /* ODBCVER */
#define POSTGRESDRIVERVERSION "07.01.0009"
#ifdef WIN32
#if (ODBCVER >= 0x0300)
#define DRIVER_FILE_NAME "PSQLODBC30.DLL"
#else
#define DRIVER_FILE_NAME "PSQLODBC.DLL"
#endif /* ODBCVER */
#else
#define DRIVER_FILE_NAME "libpsqlodbc.so"
#endif /* WIN32 */
/* Limits */
#ifdef WIN32
#define BLCKSZ 4096
#endif
#define MAX_MESSAGE_LEN 65536 /* This puts a limit on
* query size but I don't */
/* see an easy way round this - DJP 24-1-2001 */
#define MAX_CONNECT_STRING 4096
#define ERROR_MSG_LENGTH 4096
#define FETCH_MAX 100 /* default number of rows to cache
* for declare/fetch */
#define TUPLE_MALLOC_INC 100
#define SOCK_BUFFER_SIZE 4096 /* default socket buffer
* size */
#define MAX_CONNECTIONS 128 /* conns per environment
* (arbitrary) */
#define MAX_FIELDS 512
#define BYTELEN 8
#define VARHDRSZ sizeof(Int4)
#define MAX_TABLE_LEN 32
#define MAX_COLUMN_LEN 32
#define MAX_CURSOR_LEN 32
/* Registry length limits */
#define LARGE_REGISTRY_LEN 4096 /* used for special cases */
#define MEDIUM_REGISTRY_LEN 256 /* normal size for
* user,database,etc. */
#define SMALL_REGISTRY_LEN 10 /* for 1/0 settings */
/* These prefixes denote system tables */
#define POSTGRES_SYS_PREFIX "pg_"
#define KEYS_TABLE "dd_fkey"
/* Info limits */
#define MAX_INFO_STRING 128
#define MAX_KEYPARTS 20
#define MAX_KEYLEN 512 /* max key of the form
* "date+outlet+invoice" */
#define MAX_ROW_SIZE 0 /* Unlimited rowsize with the
* Tuple Toaster */
#define MAX_STATEMENT_LEN 0 /* Unlimited statement size with
* 7.0 */
/* Previously, numerous query strings were defined of length MAX_STATEMENT_LEN */
/* Now that's 0, lets use this instead. DJP 24-1-2001 */
#define STD_STATEMENT_LEN MAX_MESSAGE_LEN
#define PG62 "6.2" /* "Protocol" key setting
* to force Postgres 6.2 */
#define PG63 "6.3" /* "Protocol" key setting
* to force postgres 6.3 */
#define PG64 "6.4"
typedef struct ConnectionClass_ ConnectionClass;
typedef struct StatementClass_ StatementClass;
typedef struct QResultClass_ QResultClass;
typedef struct SocketClass_ SocketClass;
typedef struct BindInfoClass_ BindInfoClass;
typedef struct ParameterInfoClass_ ParameterInfoClass;
typedef struct ColumnInfoClass_ ColumnInfoClass;
typedef struct TupleListClass_ TupleListClass;
typedef struct EnvironmentClass_ EnvironmentClass;
typedef struct TupleNode_ TupleNode;
typedef struct TupleField_ TupleField;
typedef struct col_info COL_INFO;
typedef struct lo_arg LO_ARG;
typedef struct GlobalValues_
{
int fetch_max;
int socket_buffersize;
int unknown_sizes;
int max_varchar_size;
int max_longvarchar_size;
char debug;
char commlog;
char disable_optimizer;
char ksqo;
char unique_index;
char onlyread; /* readonly is reserved on Digital C++
* compiler */
char use_declarefetch;
char text_as_longvarchar;
char unknowns_as_longvarchar;
char bools_as_char;
char lie;
char parse;
char cancel_as_freestmt;
char extra_systable_prefixes[MEDIUM_REGISTRY_LEN];
char conn_settings[LARGE_REGISTRY_LEN];
char protocol[SMALL_REGISTRY_LEN];
} GLOBAL_VALUES;
typedef struct StatementOptions_
{
int maxRows;
int maxLength;
int rowset_size;
int keyset_size;
int cursor_type;
int scroll_concurrency;
int retrieve_data;
int bind_size; /* size of each structure if using Row
* Binding */
int use_bookmarks;
UInt4 *rowsFetched;
UInt2 *rowStatusArray;
void *bookmark_ptr;
UInt2 *row_operation_ptr;
UInt4 *row_offset_ptr;
UInt4 paramset_size;
UInt4 param_bind_type;
UInt4 *param_processed_ptr;
UInt2 *param_status_ptr;
UInt2 *param_operation_ptr;
UInt4 *param_offset_ptr;
} StatementOptions;
/* Used to pass extra query info to send_query */
typedef struct QueryInfo_
{
int row_size;
QResultClass *result_in;
char *cursor;
} QueryInfo;
void logs_on_off(int cnopen, int, int);
#define PG_TYPE_LO (-999) /* hack until permanent
* type available */
#define PG_TYPE_LO_NAME "lo"
#define OID_ATTNUM (-2) /* the attnum in pg_index
* of the oid */
/* sizes */
#define TEXT_FIELD_SIZE 8190 /* size of text fields
* (not including null
* term) */
#define NAME_FIELD_SIZE 32 /* size of name fields */
#define MAX_VARCHAR_SIZE 254 /* maximum size of a varchar (not
* including null term) */
#define PG_NUMERIC_MAX_PRECISION 1000
#define PG_NUMERIC_MAX_SCALE 1000
#define INFO_INQUIRY_LEN 8192 /* this seems sufficiently big for
* queries used in info.c inoue
* 2001/05/17 */
#include "misc.h"
#ifdef _MEMORY_DEBUG_
void *debug_alloc(size_t);
void *debug_realloc(void *, size_t);
char *debug_strdup(const char *);
void debug_free(void *);
void debug_memory_check(void);
#define malloc debug_alloc
#define realloc debug_realloc
#define strdup debug_strdup
#define free debug_free
#endif /* _MEMORY_DEBUG_ */
#endif
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
#ifdef MULTIBYTE
DLG_CONFIG DIALOG DISCARDABLE 65, 43, 299, 113
STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
WS_SYSMENU
CAPTION "PostgreSQL Driver Setup"
FONT 10, "Terminal"
BEGIN
RTEXT "&Data Source:",IDC_DSNAMETEXT,3,9,49,8,NOT WS_GROUP
EDITTEXT IDC_DSNAME,59,9,72,12,ES_AUTOHSCROLL | WS_GROUP
RTEXT "Des&cription:",IDC_DESCTEXT,135,10,49,8,NOT WS_GROUP
EDITTEXT IDC_DESC,185,10,110,25,ES_AUTOHSCROLL
RTEXT "Data&base:",IDC_STATIC,15,24,37,8,NOT WS_GROUP
EDITTEXT IDC_DATABASE,59,24,72,12,ES_AUTOHSCROLL
RTEXT "&Server:",IDC_STATIC,23,38,29,8,NOT WS_GROUP
EDITTEXT IDC_SERVER,59,38,72,12,ES_AUTOHSCROLL
RTEXT "&Port:",IDC_STATIC,161,38,21,8
EDITTEXT IDC_PORT,185,38,37,12,ES_AUTOHSCROLL
RTEXT "&User Name:",IDC_STATIC,11,53,41,8
EDITTEXT IDC_USER,59,53,72,12,ES_AUTOHSCROLL
RTEXT "Pass&word:",IDC_STATIC,145,53,37,8
EDITTEXT IDC_PASSWORD,185,53,72,12,ES_PASSWORD | ES_AUTOHSCROLL
DEFPUSHBUTTON "OK",IDOK,27,88,40,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,81,88,40,14
GROUPBOX "Options (Advanced):",IDC_OPTIONS,141,72,140,35,
BS_CENTER
PUSHBUTTON "Driver",IDC_DRIVER,149,89,50,14
PUSHBUTTON "DataSource",IDC_DATASOURCE,221,88,50,14
CTEXT "Please supply any missing information needed to connect.",
DRV_MSG_LABEL,25,4,238,10
END
DLG_OPTIONS_DRV DIALOG DISCARDABLE 0, 0, 306, 226
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Advanced Options (Driver)"
FONT 10, "Terminal"
BEGIN
CONTROL "Disable Genetic &Optimizer",DRV_OPTIMIZER,"Button",
BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,13,11,116,10
CONTROL "Comm&Log (C:\\psqlodbc.log)",DRV_COMMLOG,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,164,11,120,10
CONTROL "&KSQO (Keyset Query Optimization)",DRV_KSQO,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,13,23,144,10
CONTROL "&ReadOnly (Default)",DRV_READONLY,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,164,24,88,10
CONTROL "Recognize Unique &Indexes",DRV_UNIQUEINDEX,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,13,35,112,10
CONTROL "P&arse Statements",DRV_PARSE,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,164,37,80,10
CONTROL "&Use Declare/Fetch",DRV_USEDECLAREFETCH,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,13,47,84,10
CONTROL "Cancel as FreeStmt (Exp)",DRV_CANCELASFREESTMT,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,164,50,112,10
CONTROL "Mylog(Debug ouput)",DRV_DEBUG,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,164,63,112,10
GROUPBOX "Unknown Sizes",IDC_STATIC,13,76,175,24
CONTROL "Maximum",DRV_UNKNOWN_MAX,"Button",BS_AUTORADIOBUTTON |
WS_GROUP | WS_TABSTOP,21,84,44,10
CONTROL "Don't Know",DRV_UNKNOWN_DONTKNOW,"Button",
BS_AUTORADIOBUTTON | WS_TABSTOP,72,84,56,10
CONTROL "Longest",DRV_UNKNOWN_LONGEST,"Button",
BS_AUTORADIOBUTTON | WS_TABSTOP,135,84,44,10
GROUPBOX "Data Type Options",IDC_STATIC,13,104,282,23
CONTROL "Text as LongVarChar",DRV_TEXT_LONGVARCHAR,"Button",
BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,15,115,92,10
CONTROL "Unknowns as LongVarChar",DRV_UNKNOWNS_LONGVARCHAR,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,115,108,10
CONTROL "Bools as Char",DRV_BOOLS_CHAR,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,225,115,68,10
LTEXT "&Cache Size:",IDC_STATIC,15,133,45,8
EDITTEXT DRV_CACHE_SIZE,61,129,35,12,ES_AUTOHSCROLL
LTEXT "Max &Varchar:",IDC_STATIC,99,133,49,8
EDITTEXT DRV_VARCHAR_SIZE,149,129,35,12,ES_AUTOHSCROLL
LTEXT "Max Lon&gVarChar:",IDC_STATIC,192,133,65,8
EDITTEXT DRV_LONGVARCHAR_SIZE,259,129,35,12,ES_AUTOHSCROLL
LTEXT "SysTable &Prefixes:",IDC_STATIC,23,144,36,20
EDITTEXT DRV_EXTRASYSTABLEPREFIXES,61,153,75,12,ES_AUTOHSCROLL
LTEXT "Connect &Settings:",IDC_STATIC,22,165,35,20
EDITTEXT DRV_CONNSETTINGS,61,165,225,25,ES_MULTILINE |
ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
DEFPUSHBUTTON "OK",IDOK,59,201,50,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,124,201,50,14
PUSHBUTTON "Defaults",IDDEFAULTS,189,201,50,15
CONTROL "Default",DRV_OR_DSN,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT |
BS_NOTIFY | WS_TABSTOP,247,205,40,10
END
DLG_OPTIONS_DS DIALOG DISCARDABLE 0, 0, 267, 161
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Advanced Options (DataSource)"
FONT 10, "Terminal"
BEGIN
CONTROL "&ReadOnly",DS_READONLY,"Button",BS_AUTOCHECKBOX |
WS_GROUP | WS_TABSTOP,45,13,48,10
CONTROL "Row &Versioning",DS_ROWVERSIONING,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,149,13,72,10
CONTROL "Show System &Tables",DS_SHOWSYSTEMTABLES,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,45,28,88,10
CONTROL "Disallow &Premature",DS_DISALLOWPREMATURE,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,149,28,86,10
GROUPBOX "Protocol",IDC_STATIC,43,44,180,25
CONTROL "7.X,6.4+",DS_PG64,"Button",BS_AUTORADIOBUTTON |
WS_GROUP,53,54,47,10
CONTROL "6.3",DS_PG63,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,
131,54,26,10
CONTROL "6.2",DS_PG62,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,
187,54,26,10
GROUPBOX "OID Options",IDC_STATIC,43,74,180,25
CONTROL "Show &Column",DS_SHOWOIDCOLUMN,"Button",BS_AUTOCHECKBOX |
WS_GROUP | WS_TABSTOP,53,85,59,10
CONTROL "Fake &Index",DS_FAKEOIDINDEX,"Button",BS_AUTOCHECKBOX |
WS_GROUP | WS_TABSTOP,161,85,55,10
LTEXT "Connect &Settings:",IDC_STATIC,10,105,35,25
EDITTEXT DS_CONNSETTINGS,50,105,200,20,ES_MULTILINE |
ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
DEFPUSHBUTTON "OK",IDOK,71,135,50,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,146,135,50,14
END
#else
DLG_CONFIG DIALOG DISCARDABLE 65, 43, 292, 116
STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
WS_SYSMENU
CAPTION "PostgreSQL Driver Setup"
FONT 8, "MS Sans Serif"
BEGIN
RTEXT "&Data Source:",IDC_DSNAMETEXT,5,10,50,12,NOT WS_GROUP
EDITTEXT IDC_DSNAME,57,10,72,12,ES_AUTOHSCROLL | WS_GROUP
RTEXT "Des&cription:",IDC_DESCTEXT,135,10,39,12,NOT WS_GROUP
EDITTEXT IDC_DESC,175,10,108,12,ES_AUTOHSCROLL
RTEXT "Data&base:",IDC_STATIC,17,25,38,12,NOT WS_GROUP
EDITTEXT IDC_DATABASE,57,25,72,12,ES_AUTOHSCROLL
RTEXT "&Server:",IDC_STATIC,27,40,29,12,NOT WS_GROUP
EDITTEXT IDC_SERVER,57,40,72,12,ES_AUTOHSCROLL
RTEXT "&Port:",IDC_STATIC,153,40,22,12
EDITTEXT IDC_PORT,175,40,37,12,ES_AUTOHSCROLL
RTEXT "&User Name:",IDC_STATIC,17,55,39,12
EDITTEXT IDC_USER,57,55,72,12,ES_AUTOHSCROLL
RTEXT "Pass&word:",IDC_STATIC,141,55,34,12
EDITTEXT IDC_PASSWORD,175,55,72,12,ES_PASSWORD | ES_AUTOHSCROLL
DEFPUSHBUTTON "OK",IDOK,25,90,40,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,80,90,40,14
GROUPBOX "Options (Advanced):",IDC_OPTIONS,140,74,140,35,
BS_CENTER
PUSHBUTTON "Driver",IDC_DRIVER,160,90,50,14
PUSHBUTTON "DataSource",IDC_DATASOURCE,220,90,50,14
CTEXT "Please supply any missing information needed to connect.",
DRV_MSG_LABEL,36,5,220,15
END
DLG_OPTIONS_DRV DIALOG DISCARDABLE 0, 0, 287, 241
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Advanced Options (Driver)"
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "Disable Genetic &Optimizer",DRV_OPTIMIZER,"Button",
BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,15,5,97,10
CONTROL "Comm&Log (C:\\psqlodbc.log)",DRV_COMMLOG,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,140,5,113,10
CONTROL "&KSQO (Keyset Query Optimization)",DRV_KSQO,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,15,20,124,10
CONTROL "&ReadOnly (Default)",DRV_READONLY,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,140,20,80,10
CONTROL "Recognize Unique &Indexes",DRV_UNIQUEINDEX,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,15,35,101,10
CONTROL "P&arse Statements",DRV_PARSE,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,140,35,80,10
CONTROL "&Use Declare/Fetch",DRV_USEDECLAREFETCH,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,15,50,80,10
CONTROL "Cancel as FreeStmt (Exp)",DRV_CANCELASFREESTMT,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,140,50,105,10
CONTROL "Mylog(Debug ouput)",DRV_DEBUG,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,140,65,112,10
GROUPBOX "Unknown Sizes",IDC_STATIC,10,80,175,25
CONTROL "Maximum",DRV_UNKNOWN_MAX,"Button",BS_AUTORADIOBUTTON |
WS_GROUP | WS_TABSTOP,15,91,45,10
CONTROL "Don't Know",DRV_UNKNOWN_DONTKNOW,"Button",
BS_AUTORADIOBUTTON | WS_TABSTOP,70,91,53,10
CONTROL "Longest",DRV_UNKNOWN_LONGEST,"Button",
BS_AUTORADIOBUTTON | WS_TABSTOP,130,91,50,10
GROUPBOX "Data Type Options",IDC_STATIC,10,110,270,25
CONTROL "Text as LongVarChar",DRV_TEXT_LONGVARCHAR,"Button",
BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,15,120,80,10
CONTROL "Unknowns as LongVarChar",DRV_UNKNOWNS_LONGVARCHAR,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,105,120,100,10
CONTROL "Bools as Char",DRV_BOOLS_CHAR,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,215,120,60,10
LTEXT "&Cache Size:",IDC_STATIC,10,145,40,10
EDITTEXT DRV_CACHE_SIZE,50,145,35,12,ES_AUTOHSCROLL
LTEXT "Max &Varchar:",IDC_STATIC,90,145,45,10
EDITTEXT DRV_VARCHAR_SIZE,135,145,35,12,ES_AUTOHSCROLL
LTEXT "Max Lon&gVarChar:",IDC_STATIC,180,145,60,10
EDITTEXT DRV_LONGVARCHAR_SIZE,240,145,35,12,ES_AUTOHSCROLL
LTEXT "SysTable &Prefixes:",IDC_STATIC,15,160,35,20
EDITTEXT DRV_EXTRASYSTABLEPREFIXES,50,166,75,12,ES_AUTOHSCROLL
RTEXT "Connect &Settings:",IDC_STATIC,10,185,35,20
EDITTEXT DRV_CONNSETTINGS,50,185,225,25,ES_MULTILINE |
ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
DEFPUSHBUTTON "OK",IDOK,45,220,50,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,110,220,50,14
PUSHBUTTON "Defaults",IDDEFAULTS,175,220,50,15
CONTROL "Default",DRV_OR_DSN,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT |
BS_NOTIFY | WS_TABSTOP,233,224,40,10
END
DLG_OPTIONS_DS DIALOG DISCARDABLE 0, 0, 267, 161
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Advanced Options (DataSource)"
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "&ReadOnly",DS_READONLY,"Button",BS_AUTOCHECKBOX |
WS_GROUP | WS_TABSTOP,25,10,53,10
CONTROL "Row &Versioning",DS_ROWVERSIONING,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,130,10,85,10
CONTROL "Show System &Tables",DS_SHOWSYSTEMTABLES,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,25,25,85,10
CONTROL "Disallow &Premature",DS_DISALLOWPREMATURE,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,130,25,85,10
GROUPBOX "Protocol",IDC_STATIC,15,40,180,25
CONTROL "7.X,6.4+",DS_PG64,"Button",BS_AUTORADIOBUTTON | WS_GROUP,25,
50,35,10
CONTROL "6.3",DS_PG63,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,
75,50,26,10
CONTROL "6.2",DS_PG62,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,
130,50,26,10
GROUPBOX "OID Options",IDC_STATIC,15,70,180,25
CONTROL "Show &Column",DS_SHOWOIDCOLUMN,"Button",BS_AUTOCHECKBOX |
WS_GROUP | WS_TABSTOP,25,81,59,10
CONTROL "Fake &Index",DS_FAKEOIDINDEX,"Button",BS_AUTOCHECKBOX |
WS_GROUP | WS_TABSTOP,115,81,51,10
RTEXT "Connect &Settings:",IDC_STATIC,10,105,35,25
EDITTEXT DS_CONNSETTINGS,50,105,200,20,ES_MULTILINE |
ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
DEFPUSHBUTTON "OK",IDOK,71,135,50,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,146,135,50,14
END
#endif
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
#ifdef MULTIBYTE
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
DLG_CONFIG, DIALOG
BEGIN
BOTTOMMARGIN, 112
END
DLG_OPTIONS_DRV, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 301
TOPMARGIN, 5
BOTTOMMARGIN, 206
END
DLG_OPTIONS_DS, DIALOG
BEGIN
LEFTMARGIN, 5
RIGHTMARGIN, 260
TOPMARGIN, 7
BOTTOMMARGIN, 154
END
END
#else
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
DLG_CONFIG, DIALOG
BEGIN
BOTTOMMARGIN, 115
END
DLG_OPTIONS_DRV, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 280
TOPMARGIN, 7
BOTTOMMARGIN, 219
END
DLG_OPTIONS_DS, DIALOG
BEGIN
LEFTMARGIN, 5
RIGHTMARGIN, 260
VERTGUIDE, 55
TOPMARGIN, 7
BOTTOMMARGIN, 154
END
END
#endif // MULTIBYTE
#endif // APSTUDIO_INVOKED
#ifndef _MAC
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 7,1,0,9
PRODUCTVERSION 7,1,0,9
FILEFLAGSMASK 0x3L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "Comments", "PostgreSQL ODBC driver\0"
#ifdef MULTIBYTE
VALUE "CompanyName", "Insight Distribution Systems & Sankyo Unyu Service (MULTIBYTE support)\0"
#else
VALUE "CompanyName", "Insight Distribution Systems\0"
#endif
VALUE "FileDescription", "PostgreSQL Driver\0"
VALUE "FileVersion", " 07.01.0009\0"
VALUE "InternalName", "psqlodbc\0"
VALUE "LegalCopyright", "\0"
VALUE "LegalTrademarks", "ODBC(TM) is a trademark of Microsoft Corporation. Microsoft is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation.\0"
VALUE "OriginalFilename", "psqlodbc.dll\0"
VALUE "PrivateBuild", "\0"
VALUE "ProductName", "Microsoft Open Database Connectivity\0"
VALUE "ProductVersion", " 07.01.0009\0"
VALUE "SpecialBuild", "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
#endif // !_MAC
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE DISCARDABLE
BEGIN
IDS_BADDSN "Invalid DSN entry, please recheck."
IDS_MSGTITLE "Invalid DSN"
END
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
REGEDIT4
[HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI]
[HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI\ODBC Drivers]
"PostgreSQL"="Installed"
[HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI\PostgreSQL]
"APILevel"="1"
"ConnectFunctions"="YYN"
"Driver"="PSQLODBC.DLL"
"DriverODBCVer"="02.50"
"FileUsage"="0"
"Setup"="PSQLODBC.DLL"
"SQLLevel"="1"
"UsageCount"=dword:00000001
LIBRARY psqlodbc
EXPORTS
SQLAllocConnect @1
SQLAllocEnv @2
SQLAllocStmt @3
SQLBindCol @4
SQLCancel @5
SQLColAttributes @6
SQLConnect @7
SQLDescribeCol @8
SQLDisconnect @9
SQLError @10
SQLExecDirect @11
SQLExecute @12
SQLFetch @13
SQLFreeConnect @14
SQLFreeEnv @15
SQLFreeStmt @16
SQLGetCursorName @17
SQLNumResultCols @18
SQLPrepare @19
SQLRowCount @20
SQLSetCursorName @21
SQLTransact @23
SQLColumns @40
SQLDriverConnect @41
SQLGetConnectOption @42
SQLGetData @43
SQLGetFunctions @44
SQLGetInfo @45
SQLGetStmtOption @46
SQLGetTypeInfo @47
SQLParamData @48
SQLPutData @49
SQLSetConnectOption @50
SQLSetStmtOption @51
SQLSpecialColumns @52
SQLStatistics @53
SQLTables @54
SQLBrowseConnect @55
SQLColumnPrivileges @56
SQLDescribeParam @58
SQLExtendedFetch @59
SQLForeignKeys @60
SQLMoreResults @61
SQLNativeSql @62
SQLNumParams @63
SQLParamOptions @64
SQLPrimaryKeys @65
SQLProcedureColumns @66
SQLProcedures @67
SQLSetPos @68
SQLSetScrollOptions @69
SQLTablePrivileges @70
SQLBindParameter @72
SQLDummyOrdinal @199
dconn_FDriverConnectProc @200
DllMain @201
ConfigDSN @202
/*---------
* Module: qresult.c
*
* Description: This module contains functions related to
* managing result information (i.e, fetching rows
* from the backend, managing the tuple cache, etc.)
* and retrieving it. Depending on the situation, a
* QResultClass will hold either data from the backend
* or a manually built result (see "qresult.h" to
* see which functions/macros are for manual or backend
* results. For manually built results, the
* QResultClass simply points to TupleList and
* ColumnInfo structures, which actually hold the data.
*
* Classes: QResultClass (Functions prefix: "QR_")
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*---------
*/
#include "qresult.h"
#include "misc.h"
#include <stdio.h>
#include <string.h>
#ifndef TRUE
#define TRUE (BOOL)1
#endif
#ifndef FALSE
#define FALSE (BOOL)0
#endif
/*
* Used for building a Manual Result only
* All info functions call this function to create the manual result set.
*/
void
QR_set_num_fields(QResultClass *self, int new_num_fields)
{
mylog("in QR_set_num_fields\n");
CI_set_num_fields(self->fields, new_num_fields);
if (self->manual_tuples)
TL_Destructor(self->manual_tuples);
self->manual_tuples = TL_Constructor(new_num_fields);
mylog("exit QR_set_num_fields\n");
}
void
QR_set_position(QResultClass *self, int pos)
{
self->tupleField = self->backend_tuples + ((self->base + pos) * self->num_fields);
}
void
QR_set_cache_size(QResultClass *self, int cache_size)
{
self->cache_size = cache_size;
}
void
QR_set_rowset_size(QResultClass *self, int rowset_size)
{
self->rowset_size = rowset_size;
}
void
QR_inc_base(QResultClass *self, int base_inc)
{
self->base += base_inc;
}
/*
* CLASS QResult
*/
QResultClass *
QR_Constructor()
{
QResultClass *rv;
mylog("in QR_Constructor\n");
rv = (QResultClass *) malloc(sizeof(QResultClass));
if (rv != NULL)
{
rv->status = PGRES_EMPTY_QUERY;
/* construct the column info */
if (!(rv->fields = CI_Constructor()))
{
free(rv);
return NULL;
}
rv->manual_tuples = NULL;
rv->backend_tuples = NULL;
rv->message = NULL;
rv->command = NULL;
rv->notice = NULL;
rv->conn = NULL;
rv->inTuples = FALSE;
rv->fcount = 0;
rv->fetch_count = 0;
rv->base = 0;
rv->currTuple = -1;
rv->num_fields = 0;
rv->tupleField = NULL;
rv->cursor = NULL;
rv->aborted = FALSE;
rv->cache_size = 0;
rv->rowset_size = 1;
}
mylog("exit QR_Constructor\n");
return rv;
}
void
QR_Destructor(QResultClass *self)
{
mylog("QResult: in DESTRUCTOR\n");
/* manual result set tuples */
if (self->manual_tuples)
TL_Destructor(self->manual_tuples);
/*
* If conn is defined, then we may have used "backend_tuples", so in
* case we need to, free it up. Also, close the cursor.
*/
if (self->conn && self->conn->sock && CC_is_in_trans(self->conn))
QR_close(self); /* close the cursor if there is one */
QR_free_memory(self); /* safe to call anyway */
/* Should have been freed in the close() but just in case... */
if (self->cursor)
free(self->cursor);
/* Free up column info */
if (self->fields)
CI_Destructor(self->fields);
/* Free command info (this is from strdup()) */
if (self->command)
free(self->command);
/* Free notice info (this is from strdup()) */
if (self->notice)
free(self->notice);
free(self);
mylog("QResult: exit DESTRUCTOR\n");
}
void
QR_set_command(QResultClass *self, char *msg)
{
if (self->command)
free(self->command);
self->command = msg ? strdup(msg) : NULL;
}
void
QR_set_notice(QResultClass *self, char *msg)
{
if (self->notice)
free(self->notice);
self->notice = msg ? strdup(msg) : NULL;
}
void
QR_free_memory(QResultClass *self)
{
register int lf,
row;
register TupleField *tuple = self->backend_tuples;
int fcount = self->fcount;
int num_fields = self->num_fields;
mylog("QResult: free memory in, fcount=%d\n", fcount);
if (self->backend_tuples)
{
for (row = 0; row < fcount; row++)
{
mylog("row = %d, num_fields = %d\n", row, num_fields);
for (lf = 0; lf < num_fields; lf++)
{
if (tuple[lf].value != NULL)
{
mylog("free [lf=%d] %u\n", lf, tuple[lf].value);
free(tuple[lf].value);
}
}
tuple += num_fields; /* next row */
}
free(self->backend_tuples);
self->backend_tuples = NULL;
}
self->fcount = 0;
mylog("QResult: free memory out\n");
}
/* This function is called by send_query() */
char
QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
{
int tuple_size;
/*
* If called from send_query the first time (conn != NULL), then set
* the inTuples state, and read the tuples. If conn is NULL, it
* implies that we are being called from next_tuple(), like to get
* more rows so don't call next_tuple again!
*/
if (conn != NULL)
{
ConnInfo *ci = &(conn->connInfo);
BOOL fetch_cursor = (ci->drivers.use_declarefetch && cursor && cursor[0]);
self->conn = conn;
mylog("QR_fetch_tuples: cursor = '%s', self->cursor=%u\n", (cursor == NULL) ? "" : cursor, self->cursor);
if (self->cursor)
free(self->cursor);
if (fetch_cursor)
{
if (!cursor || cursor[0] == '\0')
{
self->status = PGRES_INTERNAL_ERROR;
QR_set_message(self, "Internal Error -- no cursor for fetch");
return FALSE;
}
self->cursor = strdup(cursor);
}
/*
* Read the field attributes.
*
* $$$$ Should do some error control HERE! $$$$
*/
if (CI_read_fields(self->fields, self->conn))
{
self->status = PGRES_FIELDS_OK;
self->num_fields = CI_get_num_fields(self->fields);
}
else
{
self->status = PGRES_BAD_RESPONSE;
QR_set_message(self, "Error reading field information");
return FALSE;
}
mylog("QR_fetch_tuples: past CI_read_fields: num_fields = %d\n", self->num_fields);
if (fetch_cursor)
tuple_size = self->cache_size;
else
tuple_size = TUPLE_MALLOC_INC;
/* allocate memory for the tuple cache */
mylog("MALLOC: tuple_size = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size);
self->count_allocated = 0;
self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * tuple_size);
if (!self->backend_tuples)
{
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Could not get memory for tuple cache.");
return FALSE;
}
self->count_allocated = tuple_size;
self->inTuples = TRUE;
/* Force a read to occur in next_tuple */
self->fcount = tuple_size + 1;
self->fetch_count = tuple_size + 1;
self->base = 0;
return QR_next_tuple(self);
}
else
{
/*
* Always have to read the field attributes. But we dont have to
* reallocate memory for them!
*/
if (!CI_read_fields(NULL, self->conn))
{
self->status = PGRES_BAD_RESPONSE;
QR_set_message(self, "Error reading field information");
return FALSE;
}
return TRUE;
}
}
/*
* Close the cursor and end the transaction (if no cursors left)
* We only close cursor/end the transaction if a cursor was used.
*/
int
QR_close(QResultClass *self)
{
QResultClass *res;
if (self->conn && self->cursor && self->conn->connInfo.drivers.use_declarefetch)
{
char buf[64];
sprintf(buf, "close %s", self->cursor);
mylog("QResult: closing cursor: '%s'\n", buf);
res = CC_send_query(self->conn, buf, NULL);
self->inTuples = FALSE;
self->currTuple = -1;
free(self->cursor);
self->cursor = NULL;
if (res == NULL)
{
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Error closing cursor.");
return FALSE;
}
QR_Destructor(res);
/* End the transaction if there are no cursors left on this conn */
if (CC_is_in_autocommit(self->conn) && CC_cursor_count(self->conn) == 0)
{
mylog("QResult: END transaction on conn=%u\n", self->conn);
res = CC_send_query(self->conn, "END", NULL);
CC_set_no_trans(self->conn);
if (res == NULL)
{
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Error ending transaction.");
return FALSE;
}
QR_Destructor(res);
}
}
return TRUE;
}
/* This function is called by fetch_tuples() AND SQLFetch() */
int
QR_next_tuple(QResultClass *self)
{
int id;
QResultClass *res;
SocketClass *sock;
/* Speed up access */
int fetch_count = self->fetch_count;
int fcount = self->fcount;
int fetch_size,
offset = 0;
int end_tuple = self->rowset_size + self->base;
char corrected = FALSE;
TupleField *the_tuples = self->backend_tuples;
/* ERROR_MSG_LENGTH is sufficient */
static char msgbuffer[ERROR_MSG_LENGTH + 1];
/* QR_set_command() dups this string so doesn't need static */
char cmdbuffer[ERROR_MSG_LENGTH + 1];
char fetch[128];
QueryInfo qi;
ConnInfo *ci = NULL;
if (fetch_count < fcount)
{
/* return a row from cache */
mylog("next_tuple: fetch_count < fcount: returning tuple %d, fcount = %d\n", fetch_count, fcount);
self->tupleField = the_tuples + (fetch_count * self->num_fields); /* next row */
self->fetch_count++;
return TRUE;
}
else if (self->fcount < self->cache_size)
{
/* last row from cache */
/* We are done because we didn't even get CACHE_SIZE tuples */
mylog("next_tuple: fcount < CACHE_SIZE: fcount = %d, fetch_count = %d\n", fcount, fetch_count);
self->tupleField = NULL;
self->status = PGRES_END_TUPLES;
/* end of tuples */
return -1;
}
else
{
/*
* See if we need to fetch another group of rows. We may be being
* called from send_query(), and if so, don't send another fetch,
* just fall through and read the tuples.
*/
self->tupleField = NULL;
if (!self->inTuples)
{
ci = &(self->conn->connInfo);
if (!self->cursor || !ci->drivers.use_declarefetch)
{
mylog("next_tuple: ALL_ROWS: done, fcount = %d, fetch_count = %d\n", fcount, fetch_count);
self->tupleField = NULL;
self->status = PGRES_END_TUPLES;
return -1; /* end of tuples */
}
if (self->base == fcount)
{
/* not a correction */
/* Determine the optimum cache size. */
if (ci->drivers.fetch_max % self->rowset_size == 0)
fetch_size = ci->drivers.fetch_max;
else if (self->rowset_size < ci->drivers.fetch_max)
fetch_size = (ci->drivers.fetch_max / self->rowset_size) * self->rowset_size;
else
fetch_size = self->rowset_size;
self->cache_size = fetch_size;
self->fetch_count = 1;
}
else
{
/* need to correct */
corrected = TRUE;
fetch_size = end_tuple - fcount;
self->cache_size += fetch_size;
offset = self->fetch_count;
self->fetch_count++;
}
if (!self->backend_tuples || self->cache_size > self->count_allocated)
{
self->count_allocated = 0;
self->backend_tuples = (TupleField *) realloc(self->backend_tuples, self->num_fields * sizeof(TupleField) * self->cache_size);
if (!self->backend_tuples)
{
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Out of memory while reading tuples.");
return FALSE;
}
self->count_allocated = self->cache_size;
}
sprintf(fetch, "fetch %d in %s", fetch_size, self->cursor);
mylog("next_tuple: sending actual fetch (%d) query '%s'\n", fetch_size, fetch);
/* don't read ahead for the next tuple (self) ! */
qi.row_size = self->cache_size;
qi.result_in = self;
qi.cursor = NULL;
res = CC_send_query(self->conn, fetch, &qi);
if (res == NULL || QR_get_aborted(res))
{
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Error fetching next group.");
if (res)
QR_Destructor(res);
return FALSE;
}
self->inTuples = TRUE;
}
else
{
mylog("next_tuple: inTuples = true, falling through: fcount = %d, fetch_count = %d\n", self->fcount, self->fetch_count);
/*
* This is a pre-fetch (fetching rows right after query but
* before any real SQLFetch() calls. This is done so the
* field attributes are available.
*/
self->fetch_count = 0;
}
}
if (!corrected)
{
self->base = 0;
self->fcount = 0;
}
sock = CC_get_socket(self->conn);
self->tupleField = NULL;
ci = &(self->conn->connInfo);
for (;;)
{
id = SOCK_get_char(sock);
switch (id)
{
case 'T': /* Tuples within tuples cannot be handled */
self->status = PGRES_BAD_RESPONSE;
QR_set_message(self, "Tuples within tuples cannot be handled");
return FALSE;
case 'B': /* Tuples in binary format */
case 'D': /* Tuples in ASCII format */
if ((!self->cursor || !ci->drivers.use_declarefetch) && self->fcount >= self->count_allocated)
{
int tuple_size = self->count_allocated;
mylog("REALLOC: old_count = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size);
tuple_size *= 2;
self->backend_tuples = (TupleField *) realloc(self->backend_tuples,
tuple_size * self->num_fields * sizeof(TupleField));
if (!self->backend_tuples)
{
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Out of memory while reading tuples.");
return FALSE;
}
self->count_allocated = tuple_size;
}
if (!QR_read_tuple(self, (char) (id == 0)))
{
self->status = PGRES_BAD_RESPONSE;
QR_set_message(self, "Error reading the tuple");
return FALSE;
}
self->fcount++;
break; /* continue reading */
case 'C': /* End of tuple list */
SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
QR_set_command(self, cmdbuffer);
mylog("end of tuple list -- setting inUse to false: this = %u\n", self);
self->inTuples = FALSE;
if (self->fcount > 0)
{
qlog(" [ fetched %d rows ]\n", self->fcount);
mylog("_next_tuple: 'C' fetch_max && fcount = %d\n", self->fcount);
/* set to first row */
self->tupleField = self->backend_tuples + (offset * self->num_fields);
return TRUE;
}
else
{
/* We are surely done here (we read 0 tuples) */
qlog(" [ fetched 0 rows ]\n");
mylog("_next_tuple: 'C': DONE (fcount == 0)\n");
return -1; /* end of tuples */
}
case 'E': /* Error */
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
QR_set_message(self, msgbuffer);
self->status = PGRES_FATAL_ERROR;
if (!strncmp(msgbuffer, "FATAL", 5))
CC_set_no_trans(self->conn);
qlog("ERROR from backend in next_tuple: '%s'\n", msgbuffer);
return FALSE;
case 'N': /* Notice */
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
QR_set_message(self, msgbuffer);
self->status = PGRES_NONFATAL_ERROR;
qlog("NOTICE from backend in next_tuple: '%s'\n", msgbuffer);
continue;
default: /* this should only happen if the backend
* dumped core */
mylog("QR_next_tuple: Unexpected result from backend: id = '%c' (%d)\n", id, id);
qlog("QR_next_tuple: Unexpected result from backend: id = '%c' (%d)\n", id, id);
QR_set_message(self, "Unexpected result from backend. It probably crashed");
self->status = PGRES_FATAL_ERROR;
CC_set_no_trans(self->conn);
return FALSE;
}
}
return TRUE;
}
char
QR_read_tuple(QResultClass *self, char binary)
{
Int2 field_lf;
TupleField *this_tuplefield;
char bmp,
bitmap[MAX_FIELDS]; /* Max. len of the bitmap */
Int2 bitmaplen; /* len of the bitmap in bytes */
Int2 bitmap_pos;
Int2 bitcnt;
Int4 len;
char *buffer;
int num_fields = self->num_fields; /* speed up access */
SocketClass *sock = CC_get_socket(self->conn);
ColumnInfoClass *flds;
/* set the current row to read the fields into */
this_tuplefield = self->backend_tuples + (self->fcount * num_fields);
bitmaplen = (Int2) num_fields / BYTELEN;
if ((num_fields % BYTELEN) > 0)
bitmaplen++;
/*
* At first the server sends a bitmap that indicates which database
* fields are null
*/
SOCK_get_n_char(sock, bitmap, bitmaplen);
bitmap_pos = 0;
bitcnt = 0;
bmp = bitmap[bitmap_pos];
for (field_lf = 0; field_lf < num_fields; field_lf++)
{
/* Check if the current field is NULL */
if (!(bmp & 0200))
{
/* YES, it is NULL ! */
this_tuplefield[field_lf].len = 0;
this_tuplefield[field_lf].value = 0;
}
else
{
/*
* NO, the field is not null. so get at first the length of
* the field (four bytes)
*/
len = SOCK_get_int(sock, VARHDRSZ);
if (!binary)
len -= VARHDRSZ;
buffer = (char *) malloc(len + 1);
SOCK_get_n_char(sock, buffer, len);
buffer[len] = '\0';
mylog("qresult: len=%d, buffer='%s'\n", len, buffer);
this_tuplefield[field_lf].len = len;
this_tuplefield[field_lf].value = buffer;
/*
* This can be used to set the longest length of the column
* for any row in the tuple cache. It would not be accurate
* for varchar and text fields to use this since a tuple cache
* is only 100 rows. Bpchar can be handled since the strlen of
* all rows is fixed, assuming there are not 100 nulls in a
* row!
*/
flds = self->fields;
if (flds->display_size[field_lf] < len)
flds->display_size[field_lf] = len;
}
/*
* Now adjust for the next bit to be scanned in the next loop.
*/
bitcnt++;
if (BYTELEN == bitcnt)
{
bitmap_pos++;
bmp = bitmap[bitmap_pos];
bitcnt = 0;
}
else
bmp <<= 1;
}
self->currTuple++;
return TRUE;
}
/* File: qresult.h
*
* Description: See "qresult.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __QRESULT_H__
#define __QRESULT_H__
#include "psqlodbc.h"
#include "connection.h"
#include "socket.h"
#include "columninfo.h"
#include "tuplelist.h"
#include "tuple.h"
enum QueryResultCode_
{
PGRES_EMPTY_QUERY = 0,
PGRES_COMMAND_OK, /* a query command that doesn't return */
/* anything was executed properly by the backend */
PGRES_TUPLES_OK, /* a query command that returns tuples */
/* was executed properly by the backend, PGresult */
/* contains the resulttuples */
PGRES_COPY_OUT,
PGRES_COPY_IN,
PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from
* the backend */
PGRES_NONFATAL_ERROR,
PGRES_FATAL_ERROR,
PGRES_FIELDS_OK, /* field information from a query was
* successful */
PGRES_END_TUPLES,
PGRES_INTERNAL_ERROR
};
typedef enum QueryResultCode_ QueryResultCode;
struct QResultClass_
{
ColumnInfoClass *fields; /* the Column information */
TupleListClass *manual_tuples; /* manual result tuple list */
ConnectionClass *conn; /* the connection this result is using
* (backend) */
/* Stuff for declare/fetch tuples */
int count_allocated; /* m(re)alloced count */
int fetch_count; /* logical rows read so far */
int fcount; /* actual rows read in the fetch */
int currTuple;
int base;
int num_fields; /* number of fields in the result */
int cache_size;
int rowset_size;
QueryResultCode status;
char *message;
char *cursor; /* The name of the cursor for select
* statements */
char *command;
char *notice;
TupleField *backend_tuples; /* data from the backend (the tuple cache) */
TupleField *tupleField; /* current backend tuple being retrieved */
char inTuples; /* is a fetch of rows from the backend in
* progress? */
char aborted; /* was aborted? */
};
#define QR_get_fields(self) (self->fields)
/* These functions are for retrieving data from the qresult */
#define QR_get_value_manual(self, tupleno, fieldno) (TL_get_fieldval(self->manual_tuples, tupleno, fieldno))
#define QR_get_value_backend(self, fieldno) (self->tupleField[fieldno].value)
#define QR_get_value_backend_row(self, tupleno, fieldno) ((self->backend_tuples + (tupleno * self->num_fields))[fieldno].value)
/* These functions are used by both manual and backend results */
#define QR_NumResultCols(self) (CI_get_num_fields(self->fields))
#define QR_get_fieldname(self, fieldno_) (CI_get_fieldname(self->fields, fieldno_))
#define QR_get_fieldsize(self, fieldno_) (CI_get_fieldsize(self->fields, fieldno_))
#define QR_get_display_size(self, fieldno_) (CI_get_display_size(self->fields, fieldno_))
#define QR_get_atttypmod(self, fieldno_) (CI_get_atttypmod(self->fields, fieldno_))
#define QR_get_field_type(self, fieldno_) (CI_get_oid(self->fields, fieldno_))
/* These functions are used only for manual result sets */
#define QR_get_num_tuples(self) (self->manual_tuples ? TL_get_num_tuples(self->manual_tuples) : self->fcount)
#define QR_add_tuple(self, new_tuple) (TL_add_tuple(self->manual_tuples, new_tuple))
#define QR_set_field_info(self, field_num, name, adtid, adtsize) (CI_set_field_info(self->fields, field_num, name, adtid, adtsize, -1))
/* status macros */
#define QR_command_successful(self) ( !(self->status == PGRES_BAD_RESPONSE || self->status == PGRES_NONFATAL_ERROR || self->status == PGRES_FATAL_ERROR))
#define QR_command_nonfatal(self) ( self->status == PGRES_NONFATAL_ERROR)
#define QR_end_tuples(self) ( self->status == PGRES_END_TUPLES)
#define QR_set_status(self, condition) ( self->status = condition )
#define QR_set_message(self, message_) ( self->message = message_)
#define QR_set_aborted(self, aborted_) ( self->aborted = aborted_)
#define QR_get_message(self) (self->message)
#define QR_get_command(self) (self->command)
#define QR_get_notice(self) (self->notice)
#define QR_get_status(self) (self->status)
#define QR_get_aborted(self) (self->aborted)
#define QR_aborted(self) (!self || self->aborted)
/* Core Functions */
QResultClass *QR_Constructor(void);
void QR_Destructor(QResultClass *self);
char QR_read_tuple(QResultClass *self, char binary);
int QR_next_tuple(QResultClass *self);
int QR_close(QResultClass *self);
char QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor);
void QR_free_memory(QResultClass *self);
void QR_set_command(QResultClass *self, char *msg);
void QR_set_notice(QResultClass *self, char *msg);
void QR_set_num_fields(QResultClass *self, int new_num_fields); /* manual result only */
void QR_inc_base(QResultClass *self, int base_inc);
void QR_set_cache_size(QResultClass *self, int cache_size);
void QR_set_rowset_size(QResultClass *self, int rowset_size);
void QR_set_position(QResultClass *self, int pos);
#endif
/* {{NO_DEPENDENCIES}} */
/* Microsoft Developer Studio generated include file. */
/* Used by psqlodbc.rc */
/* */
#define IDS_BADDSN 1
#define IDS_MSGTITLE 2
#define DLG_OPTIONS_DRV 102
#define DLG_OPTIONS_DS 103
#define IDC_DSNAME 400
#define IDC_DSNAMETEXT 401
#define IDC_DESC 404
#define IDC_SERVER 407
#define IDC_DATABASE 408
#define DLG_CONFIG 1001
#define IDC_PORT 1002
#define IDC_USER 1006
#define IDC_PASSWORD 1009
#define DS_READONLY 1011
#define DS_SHOWOIDCOLUMN 1012
#define DS_FAKEOIDINDEX 1013
#define DRV_COMMLOG 1014
#define DS_PG62 1016
#define IDC_DATASOURCE 1018
#define DRV_OPTIMIZER 1019
#define DS_CONNSETTINGS 1020
#define IDC_DRIVER 1021
#define DRV_CONNSETTINGS 1031
#define DRV_UNIQUEINDEX 1032
#define DRV_UNKNOWN_MAX 1035
#define DRV_UNKNOWN_DONTKNOW 1036
#define DRV_READONLY 1037
#define IDC_DESCTEXT 1039
#define DRV_MSG_LABEL 1040
#define DRV_UNKNOWN_LONGEST 1041
#define DRV_TEXT_LONGVARCHAR 1043
#define DRV_UNKNOWNS_LONGVARCHAR 1044
#define DRV_CACHE_SIZE 1045
#define DRV_VARCHAR_SIZE 1046
#define DRV_LONGVARCHAR_SIZE 1047
#define IDDEFAULTS 1048
#define DRV_USEDECLAREFETCH 1049
#define DRV_BOOLS_CHAR 1050
#define DS_SHOWSYSTEMTABLES 1051
#define DRV_EXTRASYSTABLEPREFIXES 1051
#define DS_ROWVERSIONING 1052
#define DRV_PARSE 1052
#define DRV_CANCELASFREESTMT 1053
#define IDC_OPTIONS 1054
#define DRV_KSQO 1055
#define DS_PG64 1057
#define DS_PG63 1058
#define DRV_OR_DSN 1059
#define DRV_DEBUG 1060
#define DS_DISALLOWPREMATURE 1061
/* Next default values for new objects */
/* */
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 105
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1062
#define _APS_NEXT_SYMED_VALUE 101
#endif /* */
#endif /* */
/*-------
* Module: results.c
*
* Description: This module contains functions related to
* retrieving result information through the ODBC API.
*
* Classes: n/a
*
* API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol,
* SQLColAttributes, SQLGetData, SQLFetch, SQLExtendedFetch,
* SQLMoreResults(NI), SQLSetPos, SQLSetScrollOptions(NI),
* SQLSetCursorName, SQLGetCursorName
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "psqlodbc.h"
#include <string.h>
#include "dlg_specific.h"
#include "environ.h"
#include "connection.h"
#include "statement.h"
#include "bind.h"
#include "qresult.h"
#include "convert.h"
#include "pgtypes.h"
#include <stdio.h>
#include "pgapifunc.h"
RETCODE SQL_API
PGAPI_RowCount(
HSTMT hstmt,
SDWORD FAR * pcrow)
{
static char *func = "PGAPI_RowCount";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
char *msg,
*ptr;
ConnInfo *ci;
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
ci = &(SC_get_conn(stmt)->connInfo);
if (stmt->manual_result)
{
if (pcrow)
*pcrow = -1;
return SQL_SUCCESS;
}
if (stmt->statement_type == STMT_TYPE_SELECT)
{
if (stmt->status == STMT_FINISHED)
{
res = SC_get_Result(stmt);
if (res && pcrow)
{
*pcrow = SC_is_fetchcursor(stmt) ? -1 : QR_get_num_tuples(res);
return SQL_SUCCESS;
}
}
}
else
{
res = SC_get_Result(stmt);
if (res && pcrow)
{
msg = QR_get_command(res);
mylog("*** msg = '%s'\n", msg);
trim(msg); /* get rid of trailing spaces */
ptr = strrchr(msg, ' ');
if (ptr)
{
*pcrow = atoi(ptr + 1);
mylog("**** PGAPI_RowCount(): THE ROWS: *pcrow = %d\n", *pcrow);
}
else
{
*pcrow = -1;
mylog("**** PGAPI_RowCount(): NO ROWS: *pcrow = %d\n", *pcrow);
}
return SQL_SUCCESS;
}
}
SC_log_error(func, "Bad return value", stmt);
return SQL_ERROR;
}
/*
* This returns the number of columns associated with the database
* attached to "hstmt".
*/
RETCODE SQL_API
PGAPI_NumResultCols(
HSTMT hstmt,
SWORD FAR * pccol)
{
static char *func = "PGAPI_NumResultCols";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *result;
char parse_ok;
ConnInfo *ci;
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
ci = &(SC_get_conn(stmt)->connInfo);
SC_clear_error(stmt);
parse_ok = FALSE;
if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
{
if (stmt->parse_status == STMT_PARSE_NONE)
{
mylog("PGAPI_NumResultCols: calling parse_statement on stmt=%u\n", stmt);
parse_statement(stmt);
}
if (stmt->parse_status != STMT_PARSE_FATAL)
{
parse_ok = TRUE;
*pccol = stmt->nfld;
mylog("PARSE: PGAPI_NumResultCols: *pccol = %d\n", *pccol);
}
}
if (!parse_ok)
{
SC_pre_execute(stmt);
result = SC_get_Result(stmt);
mylog("PGAPI_NumResultCols: result = %u, status = %d, numcols = %d\n", result, stmt->status, result != NULL ? QR_NumResultCols(result) : -1);
if ((!result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
{
/* no query has been executed on this statement */
stmt->errornumber = STMT_SEQUENCE_ERROR;
stmt->errormsg = "No query has been executed with that handle";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
*pccol = QR_NumResultCols(result);
/* updatable cursors */
if (ci->updatable_cursors &&
stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
*pccol -= 2;
}
return SQL_SUCCESS;
}
/*
* Return information about the database column the user wants
* information about.
*/
RETCODE SQL_API
PGAPI_DescribeCol(
HSTMT hstmt,
UWORD icol,
UCHAR FAR * szColName,
SWORD cbColNameMax,
SWORD FAR * pcbColName,
SWORD FAR * pfSqlType,
UDWORD FAR * pcbColDef,
SWORD FAR * pibScale,
SWORD FAR * pfNullable)
{
static char *func = "PGAPI_DescribeCol";
/* gets all the information about a specific column */
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
char *col_name = NULL;
Int4 fieldtype = 0;
int precision = 0,
scale = 0;
ConnInfo *ci;
char parse_ok;
char buf[255];
int len = 0;
RETCODE result;
mylog("%s: entering...\n", func);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
ci = &(SC_get_conn(stmt)->connInfo);
SC_clear_error(stmt);
/*
* Dont check for bookmark column. This is the responsibility of the
* driver manager.
*/
icol--; /* use zero based column numbers */
parse_ok = FALSE;
if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
{
if (stmt->parse_status == STMT_PARSE_NONE)
{
mylog("PGAPI_DescribeCol: calling parse_statement on stmt=%u\n", stmt);
parse_statement(stmt);
}
mylog("PARSE: DescribeCol: icol=%d, stmt=%u, stmt->nfld=%d, stmt->fi=%u\n", icol, stmt, stmt->nfld, stmt->fi);
if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[icol])
{
if (icol >= stmt->nfld)
{
stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
stmt->errormsg = "Invalid column number in DescribeCol.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
mylog("DescribeCol: getting info for icol=%d\n", icol);
fieldtype = stmt->fi[icol]->type;
if (stmt->fi[icol]->alias[0])
col_name = stmt->fi[icol]->alias;
else
col_name = stmt->fi[icol]->name;
precision = stmt->fi[icol]->precision;
scale = stmt->fi[icol]->scale;
mylog("PARSE: fieldtype=%d, col_name='%s', precision=%d\n", fieldtype, col_name, precision);
if (fieldtype > 0)
parse_ok = TRUE;
}
}
/*
* If couldn't parse it OR the field being described was not parsed
* (i.e., because it was a function or expression, etc, then do it the
* old fashioned way.
*/
if (!parse_ok)
{
SC_pre_execute(stmt);
res = SC_get_Result(stmt);
mylog("**** PGAPI_DescribeCol: res = %u, stmt->status = %d, !finished=%d, !premature=%d\n", res, stmt->status, stmt->status != STMT_FINISHED, stmt->status != STMT_PREMATURE);
if ((NULL == res) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
{
/* no query has been executed on this statement */
stmt->errornumber = STMT_SEQUENCE_ERROR;
stmt->errormsg = "No query has been assigned to this statement.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (icol >= QR_NumResultCols(res))
{
stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
stmt->errormsg = "Invalid column number in DescribeCol.";
sprintf(buf, "Col#=%d, #Cols=%d", icol, QR_NumResultCols(res));
SC_log_error(func, buf, stmt);
return SQL_ERROR;
}
col_name = QR_get_fieldname(res, icol);
fieldtype = QR_get_field_type(res, icol);
/* atoi(ci->unknown_sizes) */
precision = pgtype_precision(stmt, fieldtype, icol, ci->drivers.unknown_sizes);
scale = pgtype_scale(stmt, fieldtype, icol);
}
mylog("describeCol: col %d fieldname = '%s'\n", icol, col_name);
mylog("describeCol: col %d fieldtype = %d\n", icol, fieldtype);
mylog("describeCol: col %d precision = %d\n", icol, precision);
result = SQL_SUCCESS;
/*
* COLUMN NAME
*/
len = strlen(col_name);
if (pcbColName)
*pcbColName = len;
if (szColName)
{
strncpy_null(szColName, col_name, cbColNameMax);
if (len >= cbColNameMax)
{
result = SQL_SUCCESS_WITH_INFO;
stmt->errornumber = STMT_TRUNCATED;
stmt->errormsg = "The buffer was too small for the colName.";
}
}
/*
* SQL TYPE
*/
if (pfSqlType)
{
*pfSqlType = pgtype_to_sqltype(stmt, fieldtype);
mylog("describeCol: col %d *pfSqlType = %d\n", icol, *pfSqlType);
}
/*
* PRECISION
*/
if (pcbColDef)
{
if (precision < 0)
precision = 0; /* "I dont know" */
*pcbColDef = precision;
mylog("describeCol: col %d *pcbColDef = %d\n", icol, *pcbColDef);
}
/*
* SCALE
*/
if (pibScale)
{
if (scale < 0)
scale = 0;
*pibScale = scale;
mylog("describeCol: col %d *pibScale = %d\n", icol, *pibScale);
}
/*
* NULLABILITY
*/
if (pfNullable)
{
*pfNullable = (parse_ok) ? stmt->fi[icol]->nullable : pgtype_nullable(stmt, fieldtype);
mylog("describeCol: col %d *pfNullable = %d\n", icol, *pfNullable);
}
return result;
}
/* Returns result column descriptor information for a result set. */
RETCODE SQL_API
PGAPI_ColAttributes(
HSTMT hstmt,
UWORD icol,
UWORD fDescType,
PTR rgbDesc,
SWORD cbDescMax,
SWORD FAR * pcbDesc,
SDWORD FAR * pfDesc)
{
static char *func = "PGAPI_ColAttributes";
StatementClass *stmt = (StatementClass *) hstmt;
Int4 field_type = 0;
ConnInfo *ci;
int unknown_sizes;
int cols = 0;
char parse_ok;
RETCODE result;
char *p = NULL;
int len = 0,
value = 0;
mylog("%s: entering...\n", func);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
ci = &(SC_get_conn(stmt)->connInfo);
/*
* Dont check for bookmark column. This is the responsibility of the
* driver manager. For certain types of arguments, the column number
* is ignored anyway, so it may be 0.
*/
icol--;
/* atoi(ci->unknown_sizes); */
unknown_sizes = ci->drivers.unknown_sizes;
/* not appropriate for SQLColAttributes() */
if (unknown_sizes == UNKNOWNS_AS_DONTKNOW)
unknown_sizes = UNKNOWNS_AS_MAX;
parse_ok = FALSE;
if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
{
if (stmt->parse_status == STMT_PARSE_NONE)
{
mylog("PGAPI_ColAttributes: calling parse_statement\n");
parse_statement(stmt);
}
cols = stmt->nfld;
/*
* Column Count is a special case. The Column number is ignored
* in this case.
*/
if (fDescType == SQL_COLUMN_COUNT)
{
if (pfDesc)
*pfDesc = cols;
return SQL_SUCCESS;
}
if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[icol])
{
if (icol >= cols)
{
stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
stmt->errormsg = "Invalid column number in ColAttributes.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
field_type = stmt->fi[icol]->type;
if (field_type > 0)
parse_ok = TRUE;
}
}
if (!parse_ok)
{
SC_pre_execute(stmt);
mylog("**** PGAPI_ColAtt: result = %u, status = %d, numcols = %d\n", stmt->result, stmt->status, stmt->result != NULL ? QR_NumResultCols(stmt->result) : -1);
if ((NULL == stmt->result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
{
stmt->errormsg = "Can't get column attributes: no result found.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
cols = QR_NumResultCols(stmt->result);
/*
* Column Count is a special case. The Column number is ignored
* in this case.
*/
if (fDescType == SQL_COLUMN_COUNT)
{
if (pfDesc)
*pfDesc = cols;
return SQL_SUCCESS;
}
if (icol >= cols)
{
stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
stmt->errormsg = "Invalid column number in ColAttributes.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
field_type = QR_get_field_type(stmt->result, icol);
}
mylog("colAttr: col %d field_type = %d\n", icol, field_type);
switch (fDescType)
{
case SQL_COLUMN_AUTO_INCREMENT:
value = pgtype_auto_increment(stmt, field_type);
if (value == -1) /* non-numeric becomes FALSE (ODBC Doc) */
value = FALSE;
break;
case SQL_COLUMN_CASE_SENSITIVE:
value = pgtype_case_sensitive(stmt, field_type);
break;
/*
* This special case is handled above.
*
* case SQL_COLUMN_COUNT:
*/
case SQL_COLUMN_DISPLAY_SIZE:
value = (parse_ok) ? stmt->fi[icol]->display_size : pgtype_display_size(stmt, field_type, icol, unknown_sizes);
mylog("PGAPI_ColAttributes: col %d, display_size= %d\n", icol, value);
break;
case SQL_COLUMN_LABEL:
if (parse_ok && stmt->fi[icol]->alias[0] != '\0')
{
p = stmt->fi[icol]->alias;
mylog("PGAPI_ColAttr: COLUMN_LABEL = '%s'\n", p);
break;
}
/* otherwise same as column name -- FALL THROUGH!!! */
case SQL_COLUMN_NAME:
p = (parse_ok) ? stmt->fi[icol]->name : QR_get_fieldname(stmt->result, icol);
mylog("PGAPI_ColAttr: COLUMN_NAME = '%s'\n", p);
break;
case SQL_COLUMN_LENGTH:
value = (parse_ok) ? stmt->fi[icol]->length : pgtype_length(stmt, field_type, icol, unknown_sizes);
mylog("PGAPI_ColAttributes: col %d, length = %d\n", icol, value);
break;
case SQL_COLUMN_MONEY:
value = pgtype_money(stmt, field_type);
break;
case SQL_COLUMN_NULLABLE:
value = (parse_ok) ? stmt->fi[icol]->nullable : pgtype_nullable(stmt, field_type);
break;
case SQL_COLUMN_OWNER_NAME:
p = "";
break;
case SQL_COLUMN_PRECISION:
value = (parse_ok) ? stmt->fi[icol]->precision : pgtype_precision(stmt, field_type, icol, unknown_sizes);
mylog("PGAPI_ColAttributes: col %d, precision = %d\n", icol, value);
break;
case SQL_COLUMN_QUALIFIER_NAME:
p = "";
break;
case SQL_COLUMN_SCALE:
value = pgtype_scale(stmt, field_type, icol);
break;
case SQL_COLUMN_SEARCHABLE:
value = pgtype_searchable(stmt, field_type);
break;
case SQL_COLUMN_TABLE_NAME:
p = (parse_ok && stmt->fi[icol]->ti) ? stmt->fi[icol]->ti->name : "";
mylog("PGAPI_ColAttr: TABLE_NAME = '%s'\n", p);
break;
case SQL_COLUMN_TYPE:
value = pgtype_to_sqltype(stmt, field_type);
break;
case SQL_COLUMN_TYPE_NAME:
p = pgtype_to_name(stmt, field_type);
break;
case SQL_COLUMN_UNSIGNED:
value = pgtype_unsigned(stmt, field_type);
if (value == -1) /* non-numeric becomes TRUE (ODBC Doc) */
value = TRUE;
break;
case SQL_COLUMN_UPDATABLE:
/*
* Neither Access or Borland care about this.
*
* if (field_type == PG_TYPE_OID) pfDesc = SQL_ATTR_READONLY;
* else
*/
value = SQL_ATTR_WRITE;
mylog("PGAPI_ColAttr: UPDATEABLE = %d\n", value);
break;
}
result = SQL_SUCCESS;
if (p)
{ /* char/binary data */
len = strlen(p);
if (rgbDesc)
{
strncpy_null((char *) rgbDesc, p, (size_t) cbDescMax);
if (len >= cbDescMax)
{
result = SQL_SUCCESS_WITH_INFO;
stmt->errornumber = STMT_TRUNCATED;
stmt->errormsg = "The buffer was too small for the rgbDesc.";
}
}
if (pcbDesc)
*pcbDesc = len;
}
else
{
/* numeric data */
if (pfDesc)
*pfDesc = value;
}
return result;
}
/* Returns result data for a single column in the current row. */
RETCODE SQL_API
PGAPI_GetData(
HSTMT hstmt,
UWORD icol,
SWORD fCType,
PTR rgbValue,
SDWORD cbValueMax,
SDWORD FAR * pcbValue)
{
static char *func = "PGAPI_GetData";
QResultClass *res;
StatementClass *stmt = (StatementClass *) hstmt;
int num_cols,
num_rows;
Int4 field_type;
void *value = NULL;
int result;
char get_bookmark = FALSE;
ConnInfo *ci;
mylog("PGAPI_GetData: enter, stmt=%u\n", stmt);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
ci = &(SC_get_conn(stmt)->connInfo);
res = stmt->result;
if (STMT_EXECUTING == stmt->status)
{
stmt->errormsg = "Can't get data while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status != STMT_FINISHED)
{
stmt->errornumber = STMT_STATUS_ERROR;
stmt->errormsg = "GetData can only be called after the successful execution on a SQL statement";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (icol == 0)
{
if (stmt->options.use_bookmarks == SQL_UB_OFF)
{
stmt->errornumber = STMT_COLNUM_ERROR;
stmt->errormsg = "Attempt to retrieve bookmark with bookmark usage disabled";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/* Make sure it is the bookmark data type */
if (fCType != SQL_C_BOOKMARK)
{
stmt->errormsg = "Column 0 is not of type SQL_C_BOOKMARK";
stmt->errornumber = STMT_PROGRAM_TYPE_OUT_OF_RANGE;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
get_bookmark = TRUE;
}
else
{
/* use zero-based column numbers */
icol--;
/* make sure the column number is valid */
num_cols = QR_NumResultCols(res);
if (icol >= num_cols)
{
stmt->errormsg = "Invalid column number.";
stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
}
if (stmt->manual_result || !SC_is_fetchcursor(stmt))
{
/* make sure we're positioned on a valid row */
num_rows = QR_get_num_tuples(res);
if ((stmt->currTuple < 0) ||
(stmt->currTuple >= num_rows))
{
stmt->errormsg = "Not positioned on a valid row for GetData.";
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
mylog(" num_rows = %d\n", num_rows);
if (!get_bookmark)
{
if (stmt->manual_result)
value = QR_get_value_manual(res, stmt->currTuple, icol);
else
value = QR_get_value_backend_row(res, stmt->currTuple, icol);
mylog(" value = '%s'\n", value);
}
}
else
{
/* it's a SOCKET result (backend data) */
if (stmt->currTuple == -1 || !res || !res->tupleField)
{
stmt->errormsg = "Not positioned on a valid row for GetData.";
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (!get_bookmark)
value = QR_get_value_backend(res, icol);
mylog(" socket: value = '%s'\n", value);
}
if (get_bookmark)
{
*((UDWORD *) rgbValue) = SC_get_bookmark(stmt);
if (pcbValue)
*pcbValue = 4;
return SQL_SUCCESS;
}
field_type = QR_get_field_type(res, icol);
mylog("**** PGAPI_GetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value);
stmt->current_col = icol;
result = copy_and_convert_field(stmt, field_type, value,
fCType, rgbValue, cbValueMax, pcbValue);
stmt->current_col = -1;
switch (result)
{
case COPY_OK:
return SQL_SUCCESS;
case COPY_UNSUPPORTED_TYPE:
stmt->errormsg = "Received an unsupported type from Postgres.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
case COPY_UNSUPPORTED_CONVERSION:
stmt->errormsg = "Couldn't handle the necessary data type conversion.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
case COPY_RESULT_TRUNCATED:
stmt->errornumber = STMT_TRUNCATED;
stmt->errormsg = "The buffer was too small for the GetData.";
return SQL_SUCCESS_WITH_INFO;
case COPY_GENERAL_ERROR: /* error msg already filled in */
SC_log_error(func, "", stmt);
return SQL_ERROR;
case COPY_NO_DATA_FOUND:
/* SC_log_error(func, "no data found", stmt); */
return SQL_NO_DATA_FOUND;
default:
stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
stmt->errornumber = STMT_INTERNAL_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
}
/*
* Returns data for bound columns in the current row ("hstmt->iCursor"),
* advances the cursor.
*/
RETCODE SQL_API
PGAPI_Fetch(
HSTMT hstmt)
{
static char *func = "PGAPI_Fetch";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
mylog("PGAPI_Fetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if (!(res = stmt->result))
{
stmt->errormsg = "Null statement result in PGAPI_Fetch.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/* Not allowed to bind a bookmark column when using SQLFetch. */
if (stmt->bookmark.buffer)
{
stmt->errornumber = STMT_COLNUM_ERROR;
stmt->errormsg = "Not allowed to bind a bookmark column when using PGAPI_Fetch";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status == STMT_EXECUTING)
{
stmt->errormsg = "Can't fetch while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status != STMT_FINISHED)
{
stmt->errornumber = STMT_STATUS_ERROR;
stmt->errormsg = "Fetch can only be called after the successful execution on a SQL statement";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->bindings == NULL)
{
/* just to avoid a crash if the user insists on calling this */
/* function even if SQL_ExecDirect has reported an Error */
stmt->errormsg = "Bindings were not allocated properly.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
QR_set_rowset_size(res, 1);
QR_inc_base(res, stmt->last_fetch_count);
return SC_fetch(stmt);
}
/* This fetchs a block of data (rowset). */
RETCODE SQL_API
PGAPI_ExtendedFetch(
HSTMT hstmt,
UWORD fFetchType,
SDWORD irow,
UDWORD FAR * pcrow,
UWORD FAR * rgfRowStatus)
{
static char *func = "PGAPI_ExtendedFetch";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
int num_tuples,
i,
save_rowset_size;
RETCODE result;
char truncated,
error;
ConnInfo *ci;
mylog("PGAPI_ExtendedFetch: stmt=%u\n", stmt);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
ci = &(SC_get_conn(stmt)->connInfo);
if (SC_is_fetchcursor(stmt) && !stmt->manual_result)
{
if (fFetchType != SQL_FETCH_NEXT)
{
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Unsupported fetch type for PGAPI_ExtendedFetch with UseDeclareFetch option.";
return SQL_ERROR;
}
}
SC_clear_error(stmt);
if (!(res = stmt->result))
{
stmt->errormsg = "Null statement result in PGAPI_ExtendedFetch.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/*
* If a bookmark colunmn is bound but bookmark usage is off, then
* error
*/
if (stmt->bookmark.buffer && stmt->options.use_bookmarks == SQL_UB_OFF)
{
stmt->errornumber = STMT_COLNUM_ERROR;
stmt->errormsg = "Attempt to retrieve bookmark with bookmark usage disabled";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status == STMT_EXECUTING)
{
stmt->errormsg = "Can't fetch while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status != STMT_FINISHED)
{
stmt->errornumber = STMT_STATUS_ERROR;
stmt->errormsg = "ExtendedFetch can only be called after the successful execution on a SQL statement";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->bindings == NULL)
{
/* just to avoid a crash if the user insists on calling this */
/* function even if SQL_ExecDirect has reported an Error */
stmt->errormsg = "Bindings were not allocated properly.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/* Initialize to no rows fetched */
if (rgfRowStatus)
for (i = 0; i < stmt->options.rowset_size; i++)
*(rgfRowStatus + i) = SQL_ROW_NOROW;
if (pcrow)
*pcrow = 0;
num_tuples = QR_get_num_tuples(res);
/* Save and discard the saved rowset size */
save_rowset_size = stmt->save_rowset_size;
stmt->save_rowset_size = -1;
switch (fFetchType)
{
case SQL_FETCH_NEXT:
/*
* From the odbc spec... If positioned before the start of the
* RESULT SET, then this should be equivalent to
* SQL_FETCH_FIRST.
*/
if (stmt->rowset_start < 0)
stmt->rowset_start = 0;
else
stmt->rowset_start += (save_rowset_size > 0 ? save_rowset_size : stmt->options.rowset_size);
mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
break;
case SQL_FETCH_PRIOR:
mylog("SQL_FETCH_PRIOR: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
/*
* From the odbc spec... If positioned after the end of the
* RESULT SET, then this should be equivalent to
* SQL_FETCH_LAST.
*/
if (stmt->rowset_start >= num_tuples)
{
if (stmt->options.rowset_size > num_tuples)
{
stmt->errornumber = STMT_POS_BEFORE_RECORDSET;
stmt->errormsg = "fetch prior from eof and before the beggining";
}
stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size);
}
else
{
if (stmt->rowset_start < stmt->options.rowset_size)
{
stmt->errormsg = "fetch prior and before the beggining";
stmt->errornumber = STMT_POS_BEFORE_RECORDSET;
}
stmt->rowset_start -= stmt->options.rowset_size;
}
break;
case SQL_FETCH_FIRST:
mylog("SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
stmt->rowset_start = 0;
break;
case SQL_FETCH_LAST:
mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size);
break;
case SQL_FETCH_ABSOLUTE:
mylog("SQL_FETCH_ABSOLUTE: num_tuples=%d, currtuple=%d, irow=%d\n", num_tuples, stmt->currTuple, irow);
/* Position before result set, but dont fetch anything */
if (irow == 0)
{
stmt->rowset_start = -1;
stmt->currTuple = -1;
return SQL_NO_DATA_FOUND;
}
/* Position before the desired row */
else if (irow > 0)
stmt->rowset_start = irow - 1;
/* Position with respect to the end of the result set */
else
stmt->rowset_start = num_tuples + irow;
break;
case SQL_FETCH_RELATIVE:
/*
* Refresh the current rowset -- not currently implemented,
* but lie anyway
*/
if (irow == 0)
break;
stmt->rowset_start += irow;
break;
case SQL_FETCH_BOOKMARK:
stmt->rowset_start = irow - 1;
break;
default:
SC_log_error(func, "Unsupported PGAPI_ExtendedFetch Direction", stmt);
return SQL_ERROR;
}
/*
* CHECK FOR PROPER CURSOR STATE
*/
/*
* Handle Declare Fetch style specially because the end is not really
* the end...
*/
if (SC_is_fetchcursor(stmt) && !stmt->manual_result)
{
if (QR_end_tuples(res))
return SQL_NO_DATA_FOUND;
}
else
{
/* If *new* rowset is after the result_set, return no data found */
if (stmt->rowset_start >= num_tuples)
{
stmt->rowset_start = num_tuples;
return SQL_NO_DATA_FOUND;
}
}
/* If *new* rowset is prior to result_set, return no data found */
if (stmt->rowset_start < 0)
{
if (stmt->rowset_start + stmt->options.rowset_size <= 0)
{
stmt->rowset_start = -1;
return SQL_NO_DATA_FOUND;
}
else
{ /* overlap with beginning of result set,
* so get first rowset */
stmt->rowset_start = 0;
}
}
/* currTuple is always 1 row prior to the rowset */
stmt->currTuple = stmt->rowset_start - 1;
/* increment the base row in the tuple cache */
QR_set_rowset_size(res, stmt->options.rowset_size);
/* QR_inc_base(res, stmt->last_fetch_count); */
/* Is inc_base right ? */
res->base = stmt->rowset_start;
/* Physical Row advancement occurs for each row fetched below */
mylog("PGAPI_ExtendedFetch: new currTuple = %d\n", stmt->currTuple);
truncated = error = FALSE;
for (i = 0; i < stmt->options.rowset_size; i++)
{
stmt->bind_row = i; /* set the binding location */
result = SC_fetch(stmt);
/* Determine Function status */
if (result == SQL_NO_DATA_FOUND)
break;
else if (result == SQL_SUCCESS_WITH_INFO)
truncated = TRUE;
else if (result == SQL_ERROR)
error = TRUE;
/* Determine Row Status */
if (rgfRowStatus)
{
if (result == SQL_ERROR)
*(rgfRowStatus + i) = SQL_ROW_ERROR;
#ifdef DRIVER_CURSOR_IMPLEMENT
/* this should be refined */
else if (result > 10 && result < 20)
*(rgfRowStatus + i) = result - 10;
#endif /* DRIVER_CURSOR_IMPLEMENT */
else
*(rgfRowStatus + i) = SQL_ROW_SUCCESS;
}
}
/* Save the fetch count for SQLSetPos */
stmt->last_fetch_count = i;
/* Reset next binding row */
stmt->bind_row = 0;
/* Move the cursor position to the first row in the result set. */
stmt->currTuple = stmt->rowset_start;
/* For declare/fetch, need to reset cursor to beginning of rowset */
if (SC_is_fetchcursor(stmt) && !stmt->manual_result)
QR_set_position(res, 0);
/* Set the number of rows retrieved */
if (pcrow)
*pcrow = i;
if (i == 0)
/* Only DeclareFetch should wind up here */
return SQL_NO_DATA_FOUND;
else if (error)
return SQL_ERROR;
else if (truncated)
return SQL_SUCCESS_WITH_INFO;
else if (stmt->errornumber == STMT_POS_BEFORE_RECORDSET)
return SQL_SUCCESS_WITH_INFO;
else
return SQL_SUCCESS;
}
/*
* This determines whether there are more results sets available for
* the "hstmt".
*/
/* CC: return SQL_NO_DATA_FOUND since we do not support multiple result sets */
RETCODE SQL_API
PGAPI_MoreResults(
HSTMT hstmt)
{
return SQL_NO_DATA_FOUND;
}
#ifdef DRIVER_CURSOR_IMPLEMENT
/*
* Stuff for updatable cursors.
*/
static QResultClass *
positioned_load(StatementClass *stmt, BOOL latest, int res_cols, UInt4 oid, const char *tidval)
{
int i;
QResultClass *qres;
char selstr[4096];
sprintf(selstr, "select");
for (i = 0; i < res_cols; i++)
sprintf(selstr, "%s \"%s\",", selstr, stmt->fi[i]->name);
sprintf(selstr, "%s CTID, OID from \"%s\" where", selstr, stmt->ti[0]->name);
if (tidval)
{
if (latest)
sprintf(selstr, "%s ctid = currtid2('%s', '%s') and",
selstr, stmt->ti[0]->name, tidval);
else
sprintf(selstr, "%s ctid = '%s' and", selstr, tidval);
}
sprintf(selstr, "%s oid = %u", selstr, oid),
mylog("selstr=%s\n", selstr);
qres = CC_send_query(SC_get_conn(stmt), selstr, NULL);
if (qres && QR_aborted(qres))
{
QR_Destructor(qres);
qres = (QResultClass *) 0;
}
return qres;
}
RETCODE SQL_API
SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count)
{
int i,
res_cols;
UWORD rcnt,
global_ridx;
UInt4 oid;
QResultClass *res,
*qres;
RETCODE ret = SQL_ERROR;
char *tidval,
*oidval;
mylog("positioned load fi=%x ti=%x\n", stmt->fi, stmt->ti);
rcnt = 0;
if (count)
*count = 0;
if (!(res = stmt->result))
return SQL_ERROR;
if (!stmt->ti)
parse_statement(stmt); /* not preferable */
if (!stmt->ti || stmt->ntab != 1)
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
return SQL_ERROR;
}
global_ridx = irow + stmt->rowset_start;
res_cols = QR_NumResultCols(res);
if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
return SQL_SUCCESS_WITH_INFO;
sscanf(oidval, "%u", &oid);
tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2);
res_cols -= 2;
if (qres = positioned_load(stmt, TRUE, res_cols, oid, tidval), qres)
{
TupleField *tupleo,
*tuplen;
rcnt = QR_get_num_tuples(qres);
tupleo = res->backend_tuples + res->num_fields * global_ridx;
if (rcnt == 1)
{
QR_set_position(qres, 0);
tuplen = res->tupleField;
for (i = 0; i < res->num_fields; i++)
{
if (tupleo[i].value)
free(tupleo[i].value);
tupleo[i].len = tuplen[i].len;
tuplen[i].len = 0;
tupleo[i].value = tuplen[i].value;
tuplen[i].value = NULL;
}
ret = SQL_SUCCESS;
}
else
{
stmt->errornumber = STMT_ROW_VERSION_CHANGED;
stmt->errormsg = "the content was deleted after last fetch";
ret = SQL_SUCCESS_WITH_INFO;
if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
{
if (tupleo[res_cols + 1].value)
free(tupleo[res_cols + 1].value);
tupleo[res_cols + 1].value = NULL;
tupleo[res_cols + 1].len = 0;
}
}
QR_Destructor(qres);
}
else if (stmt->errornumber == 0)
stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
if (count)
*count = rcnt;
return ret;
}
RETCODE SQL_API
SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval)
{
int i;
QResultClass *res,
*qres;
RETCODE ret = SQL_ERROR;
mylog("positioned new fi=%x ti=%x\n", stmt->fi, stmt->ti);
if (!(res = stmt->result))
return SQL_ERROR;
if (!stmt->ti)
parse_statement(stmt); /* not preferable */
if (!stmt->ti || stmt->ntab != 1)
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
return SQL_ERROR;
}
if (qres = positioned_load(stmt, TRUE, QR_NumResultCols(res) - 2, oid, tidval), qres)
{
TupleField *tupleo,
*tuplen;
int count = QR_get_num_tuples(qres);
QR_set_position(qres, 0);
if (count == 1)
{
tuplen = qres->tupleField;
if (res->fcount >= res->count_allocated)
{
int tuple_size;
if (!res->count_allocated)
tuple_size = TUPLE_MALLOC_INC;
else
tuple_size = res->count_allocated * 2;
res->backend_tuples = (TupleField *) realloc(
res->backend_tuples,
res->num_fields * sizeof(TupleField) * tuple_size);
if (!res->backend_tuples)
{
stmt->errornumber = res->status = PGRES_FATAL_ERROR;
stmt->errormsg = "Out of memory while reading tuples.";
QR_Destructor(qres);
return SQL_ERROR;
}
res->count_allocated = tuple_size;
}
tupleo = res->backend_tuples + res->num_fields * res->fcount;
for (i = 0; i < res->num_fields; i++)
{
tupleo[i].len = tuplen[i].len;
tuplen[i].len = 0;
tupleo[i].value = tuplen[i].value;
tuplen[i].value = NULL;
}
res->fcount++;
ret = SQL_SUCCESS;
}
else
{
stmt->errornumber = STMT_ROW_VERSION_CHANGED;
stmt->errormsg = "the content was changed before updation";
ret = SQL_SUCCESS_WITH_INFO;
}
QR_Destructor(qres);
/* stmt->currTuple = stmt->rowset_start + irow; */
}
return ret;
}
RETCODE SQL_API
SC_pos_update(StatementClass *stmt,
UWORD irow)
{
int i,
res_cols,
num_cols,
upd_cols;
UWORD global_ridx;
QResultClass *res;
BindInfoClass *bindings = stmt->bindings;
char updstr[4096];
RETCODE ret;
char *tidval,
*oidval;
mylog("POS UPDATE %d+%d fi=%x ti=%x\n", irow, stmt->result->base, stmt->fi, stmt->ti);
if (!(res = stmt->result))
return SQL_ERROR;
if (!stmt->ti)
parse_statement(stmt); /* not preferable */
if (!stmt->ti || stmt->ntab != 1)
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
return SQL_ERROR;
}
global_ridx = irow + stmt->rowset_start;
res_cols = QR_NumResultCols(res);
if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
{
stmt->errormsg = "The row is already deleted";
return SQL_ERROR;
}
tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2);
sprintf(updstr, "update \"%s\" set", stmt->ti[0]->name);
num_cols = stmt->nfld;
for (i = upd_cols = 0; i < num_cols; i++)
{
if (bindings[i].used)
{
mylog("%d used=%d\n", i, *bindings[i].used);
if (*bindings[i].used != SQL_IGNORE)
{
if (upd_cols)
sprintf(updstr, "%s, \"%s\" = ?", updstr, stmt->fi[i]->name);
else
sprintf(updstr, "%s \"%s\" = ?", updstr, stmt->fi[i]->name);
upd_cols++;
}
}
else
mylog("%d null bind\n", i);
}
if (upd_cols > 0)
{
HSTMT hstmt;
int j;
int res_cols = QR_NumResultCols(res);
StatementClass *qstmt;
sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr,
tidval, oidval);
mylog("updstr=%s\n", updstr);
if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS)
return SQL_ERROR;
qstmt = (StatementClass *) hstmt;
for (i = j = 0; i < num_cols; i++)
{
if (bindings[i].used)
{
mylog("%d used=%d\n", i, *bindings[i].used);
if (*bindings[i].used != SQL_IGNORE)
{
PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++j,
SQL_PARAM_INPUT, bindings[i].returntype,
pgtype_to_sqltype(stmt, QR_get_field_type(res, i)),
QR_get_fieldsize(res, i),
(SQLSMALLINT) stmt->fi[i]->precision,
bindings[i].buffer,
bindings[i].buflen,
bindings[i].used);
}
}
}
ret = PGAPI_ExecDirect(hstmt, updstr, strlen(updstr));
if (ret == SQL_ERROR)
{
stmt->errornumber = qstmt->errornumber;
stmt->errormsg = qstmt->errormsg;
}
else if (ret == SQL_NEED_DATA) /* must be fixed */
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
stmt->errormsg = "SetPos with data_at_exec not yet supported";
ret = SQL_ERROR;
}
if (ret != SQL_ERROR)
{
int updcnt;
const char *cmdstr = QR_get_command(qstmt->result);
if (cmdstr &&
sscanf(cmdstr, "UPDATE %d", &updcnt) == 1)
{
if (updcnt == 1)
SC_pos_reload(stmt, irow, (UWORD *) 0);
else if (updcnt == 0)
{
stmt->errornumber = STMT_ROW_VERSION_CHANGED;
stmt->errormsg = "the content was changed before updation";
ret = SQL_ERROR;
if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
SC_pos_reload(stmt, irow, (UWORD *) 0);
}
else
ret = SQL_ERROR;
stmt->currTuple = stmt->rowset_start + irow;
}
else
ret = SQL_ERROR;
if (ret == SQL_ERROR && stmt->errornumber == 0)
{
stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
stmt->errormsg = "SetPos update return error";
}
}
PGAPI_FreeStmt(hstmt, SQL_DROP);
}
else
ret = SQL_SUCCESS_WITH_INFO;
return ret;
}
RETCODE SQL_API
SC_pos_delete(StatementClass *stmt,
UWORD irow)
{
int res_cols;
UWORD global_ridx;
QResultClass *res,
*qres;
BindInfoClass *bindings = stmt->bindings;
char dltstr[4096];
RETCODE ret;
char *oidval;
mylog("POS DELETE fi=%x ti=%x\n", stmt->fi, stmt->ti);
if (!(res = stmt->result))
return SQL_ERROR;
if (!stmt->ti)
parse_statement(stmt); /* not preferable */
if (!stmt->ti || stmt->ntab != 1)
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
return SQL_ERROR;
}
res_cols = QR_NumResultCols(res);
global_ridx = irow + stmt->rowset_start;
if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
{
stmt->errormsg = "The row is already deleted";
return SQL_ERROR;
}
sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s",
stmt->ti[0]->name,
QR_get_value_backend_row(stmt->result, global_ridx, res_cols - 2),
oidval);
mylog("dltstr=%s\n", dltstr);
qres = CC_send_query(SC_get_conn(stmt), dltstr, NULL);
if (qres && QR_command_successful(qres))
{
int dltcnt;
const char *cmdstr = QR_get_command(qres);
if (cmdstr &&
sscanf(cmdstr, "DELETE %d", &dltcnt) == 1)
{
if (dltcnt == 1)
SC_pos_reload(stmt, irow, (UWORD *) 0);
else if (dltcnt == 0)
{
stmt->errornumber = STMT_ROW_VERSION_CHANGED;
stmt->errormsg = "the content was changed before deletion";
ret = SQL_ERROR;
if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
SC_pos_reload(stmt, irow, (UWORD *) 0);
}
else
ret = SQL_ERROR;
stmt->currTuple = stmt->rowset_start + irow;
}
else
ret = SQL_ERROR;
}
else
ret = SQL_ERROR;
if (ret == SQL_ERROR && stmt->errornumber == 0)
{
stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
stmt->errormsg = "SetPos delete return error";
}
if (qres)
QR_Destructor(qres);
return ret;
}
RETCODE SQL_API
SC_pos_add(StatementClass *stmt,
UWORD irow)
{
int num_cols,
add_cols,
i;
HSTMT hstmt;
QResultClass *res;
BindInfoClass *bindings = stmt->bindings;
char addstr[4096];
RETCODE ret;
mylog("POS ADD fi=%x ti=%x\n", stmt->fi, stmt->ti);
if (!(res = stmt->result))
return SQL_ERROR;
if (!stmt->ti)
parse_statement(stmt); /* not preferable */
if (!stmt->ti || stmt->ntab != 1)
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
return SQL_ERROR;
}
num_cols = stmt->nfld;
sprintf(addstr, "insert into \"%s\" (", stmt->ti[0]->name);
if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS)
return SQL_ERROR;
for (i = add_cols = 0; i < num_cols; i++)
{
if (bindings[i].used)
{
mylog("%d used=%d\n", i, *bindings[i].used);
if (*bindings[i].used != SQL_IGNORE)
{
if (add_cols)
sprintf(addstr, "%s, \"%s\"", addstr, stmt->fi[i]->name);
else
sprintf(addstr, "%s\"%s\"", addstr, stmt->fi[i]->name);
PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++add_cols,
SQL_PARAM_INPUT, bindings[i].returntype,
pgtype_to_sqltype(stmt, QR_get_field_type(res, i)),
QR_get_fieldsize(res, i),
(SQLSMALLINT) stmt->fi[i]->precision,
bindings[i].buffer,
bindings[i].buflen,
bindings[i].used);
}
}
else
mylog("%d null bind\n", i);
}
if (add_cols > 0)
{
StatementClass *qstmt = (StatementClass *) hstmt;
sprintf(addstr, "%s) values (", addstr);
for (i = 0; i < add_cols; i++)
{
if (i)
strcat(addstr, ", ?");
else
strcat(addstr, "?");
}
strcat(addstr, ")");
mylog("addstr=%s\n", addstr);
ret = PGAPI_ExecDirect(hstmt, addstr, strlen(addstr));
if (ret == SQL_NEED_DATA) /* must be fixed */
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
stmt->errormsg = "SetPos with data_at_exec not yet supported";
ret = SQL_ERROR;
}
if (ret == SQL_ERROR)
{
stmt->errornumber = qstmt->errornumber;
stmt->errormsg = qstmt->errormsg;
}
else
{
int addcnt;
UInt4 oid;
const char *cmdstr = QR_get_command(qstmt->result);
if (cmdstr &&
sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 &&
addcnt == 1)
{
SC_pos_newload(stmt, oid, NULL);
if (stmt->bookmark.buffer)
{
char buf[32];
sprintf(buf, "%ld", res->fcount);
copy_and_convert_field(stmt, 0, buf,
SQL_C_ULONG, stmt->bookmark.buffer,
0, stmt->bookmark.used);
}
}
else
{
stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
stmt->errormsg = "SetPos insert return error";
ret = SQL_ERROR;
}
}
}
else
ret = SQL_SUCCESS_WITH_INFO;
PGAPI_FreeStmt(hstmt, SQL_DROP);
return ret;
}
/*
* Stuff for updatable cursors end.
*/
#endif /* DRIVER_CURSOR_IMPLEMENT */
/*
* This positions the cursor within a rowset, that was positioned using SQLExtendedFetch.
* This will be useful (so far) only when using SQLGetData after SQLExtendedFetch.
*/
RETCODE SQL_API
PGAPI_SetPos(
HSTMT hstmt,
UWORD irow,
UWORD fOption,
UWORD fLock)
{
static char *func = "PGAPI_SetPos";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
int num_cols,
i;
BindInfoClass *bindings = stmt->bindings;
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
#ifdef DRIVER_CURSOR_IMPLEMENT
mylog("SetPos fOption=%d irow=%d lock=%d currt=%d\n", fOption, irow, fLock, stmt->currTuple);
if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
;
else
#endif /* DRIVER_CURSOR_IMPLEMENT */
if (fOption != SQL_POSITION && fOption != SQL_REFRESH)
{
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Only SQL_POSITION/REFRESH is supported for PGAPI_SetPos";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (!(res = stmt->result))
{
stmt->errormsg = "Null statement result in PGAPI_SetPos.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
num_cols = QR_NumResultCols(res);
if (irow == 0)
{
stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
stmt->errormsg = "Driver does not support Bulk operations.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (irow > stmt->last_fetch_count)
{
stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
stmt->errormsg = "Row value out of range";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
irow--;
#ifdef DRIVER_CURSOR_IMPLEMENT
switch (fOption)
{
case SQL_UPDATE:
return SC_pos_update(stmt, irow);
case SQL_DELETE:
return SC_pos_delete(stmt, irow);
case SQL_ADD:
return SC_pos_add(stmt, irow);
}
#endif /* DRIVER_CURSOR_IMPLEMENT */
/* Reset for SQLGetData */
for (i = 0; i < num_cols; i++)
bindings[i].data_left = -1;
if (fOption == SQL_REFRESH)
{
/* save the last_fetch_count */
int last_fetch = stmt->last_fetch_count;
int bind_save = stmt->bind_row;
#ifdef DRIVER_CURSOR_IMPLEMENT
if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
SC_pos_reload(stmt, irow, (UWORD *) 0);
#endif /* DRIVER_CURSOR_IMPLEMENT */
stmt->currTuple = stmt->rowset_start + irow - 1;
stmt->bind_row = irow;
SC_fetch(stmt);
/* restore the last_fetch_count */
stmt->last_fetch_count = last_fetch;
stmt->bind_row = bind_save;
}
else
stmt->currTuple = stmt->rowset_start + irow;
QR_set_position(res, irow);
return SQL_SUCCESS;
}
/* Sets options that control the behavior of cursors. */
RETCODE SQL_API
PGAPI_SetScrollOptions(
HSTMT hstmt,
UWORD fConcurrency,
SDWORD crowKeyset,
UWORD crowRowset)
{
static char *func = "PGAPI_SetScrollOptions";
StatementClass *stmt = (StatementClass *) hstmt;
mylog("PGAPI_SetScrollOptions fConcurrency=%d crowKeyset=%d crowRowset=%d\n",
fConcurrency, crowKeyset, crowRowset);
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "SetScroll option not implemeted";
SC_log_error(func, "Function not implemented", hstmt);
return SQL_ERROR;
}
/* Set the cursor name on a statement handle */
RETCODE SQL_API
PGAPI_SetCursorName(
HSTMT hstmt,
UCHAR FAR * szCursor,
SWORD cbCursor)
{
static char *func = "PGAPI_SetCursorName";
StatementClass *stmt = (StatementClass *) hstmt;
int len;
mylog("PGAPI_SetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d\n", hstmt, szCursor, cbCursor);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
len = (cbCursor == SQL_NTS) ? strlen(szCursor) : cbCursor;
if (len <= 0 || len > sizeof(stmt->cursor_name) - 1)
{
stmt->errornumber = STMT_INVALID_CURSOR_NAME;
stmt->errormsg = "Invalid Cursor Name";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
strncpy_null(stmt->cursor_name, szCursor, len + 1);
return SQL_SUCCESS;
}
/* Return the cursor name for a statement handle */
RETCODE SQL_API
PGAPI_GetCursorName(
HSTMT hstmt,
UCHAR FAR * szCursor,
SWORD cbCursorMax,
SWORD FAR * pcbCursor)
{
static char *func = "PGAPI_GetCursorName";
StatementClass *stmt = (StatementClass *) hstmt;
int len = 0;
RETCODE result;
mylog("PGAPI_GetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d, pcbCursor=%u\n", hstmt, szCursor, cbCursorMax, pcbCursor);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
if (stmt->cursor_name[0] == '\0')
{
stmt->errornumber = STMT_NO_CURSOR_NAME;
stmt->errormsg = "No Cursor name available";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
result = SQL_SUCCESS;
len = strlen(stmt->cursor_name);
if (szCursor)
{
strncpy_null(szCursor, stmt->cursor_name, cbCursorMax);
if (len >= cbCursorMax)
{
result = SQL_SUCCESS_WITH_INFO;
stmt->errornumber = STMT_TRUNCATED;
stmt->errormsg = "The buffer was too small for the GetCursorName.";
}
}
if (pcbCursor)
*pcbCursor = len;
return result;
}
/*-------
* Module: setup.c
*
* Description: This module contains the setup functions for
* adding/modifying a Data Source in the ODBC.INI portion
* of the registry.
*
* Classes: n/a
*
* API functions: ConfigDSN
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "psqlodbc.h"
#include "connection.h"
#include <windowsx.h>
#include <string.h>
#include <stdlib.h>
#include "resource.h"
#include "dlg_specific.h"
#define INTFUNC __stdcall
extern HINSTANCE NEAR s_hModule; /* Saved module handle. */
/* Constants */
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#ifdef WIN32
#define MAXPGPATH (255+1)
#endif
#define MAXKEYLEN (15+1) /* Max keyword length */
#define MAXDESC (255+1) /* Max description length */
#define MAXDSNAME (32+1) /* Max data source name length */
/* Globals */
/* NOTE: All these are used by the dialog procedures */
typedef struct tagSETUPDLG
{
HWND hwndParent; /* Parent window handle */
LPCSTR lpszDrvr; /* Driver description */
ConnInfo ci;
char szDSN[MAXDSNAME]; /* Original data source name */
BOOL fNewDSN; /* New data source flag */
BOOL fDefault; /* Default data source flag */
} SETUPDLG, FAR * LPSETUPDLG;
/* Prototypes */
void INTFUNC CenterDialog(HWND hdlg);
int CALLBACK ConfigDlgProc(HWND hdlg, WORD wMsg, WPARAM wParam, LPARAM lParam);
void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg);
BOOL INTFUNC SetDSNAttributes(HWND hwnd, LPSETUPDLG lpsetupdlg);
/*--------
* ConfigDSN
*
* Description: ODBC Setup entry point
* This entry point is called by the ODBC Installer
* (see file header for more details)
* Input : hwnd ----------- Parent window handle
* fRequest ------- Request type (i.e., add, config, or remove)
* lpszDriver ----- Driver name
* lpszAttributes - data source attribute string
* Output : TRUE success, FALSE otherwise
*--------
*/
BOOL CALLBACK
ConfigDSN(HWND hwnd,
WORD fRequest,
LPCSTR lpszDriver,
LPCSTR lpszAttributes)
{
BOOL fSuccess; /* Success/fail flag */
GLOBALHANDLE hglbAttr;
LPSETUPDLG lpsetupdlg;
/* Allocate attribute array */
hglbAttr = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(SETUPDLG));
if (!hglbAttr)
return FALSE;
lpsetupdlg = (LPSETUPDLG) GlobalLock(hglbAttr);
/* Parse attribute string */
if (lpszAttributes)
ParseAttributes(lpszAttributes, lpsetupdlg);
/* Save original data source name */
if (lpsetupdlg->ci.dsn[0])
lstrcpy(lpsetupdlg->szDSN, lpsetupdlg->ci.dsn);
else
lpsetupdlg->szDSN[0] = '\0';
/* Remove data source */
if (ODBC_REMOVE_DSN == fRequest)
{
/* Fail if no data source name was supplied */
if (!lpsetupdlg->ci.dsn[0])
fSuccess = FALSE;
/* Otherwise remove data source from ODBC.INI */
else
fSuccess = SQLRemoveDSNFromIni(lpsetupdlg->ci.dsn);
}
/* Add or Configure data source */
else
{
/* Save passed variables for global access (e.g., dialog access) */
lpsetupdlg->hwndParent = hwnd;
lpsetupdlg->lpszDrvr = lpszDriver;
lpsetupdlg->fNewDSN = (ODBC_ADD_DSN == fRequest);
lpsetupdlg->fDefault = !lstrcmpi(lpsetupdlg->ci.dsn, INI_DSN);
/*
* Display the appropriate dialog (if parent window handle
* supplied)
*/
if (hwnd)
{
/* Display dialog(s) */
fSuccess = (IDOK == DialogBoxParam(s_hModule,
MAKEINTRESOURCE(DLG_CONFIG),
hwnd,
ConfigDlgProc,
(LONG) (LPSTR) lpsetupdlg));
}
else if (lpsetupdlg->ci.dsn[0])
fSuccess = SetDSNAttributes(hwnd, lpsetupdlg);
else
fSuccess = FALSE;
}
GlobalUnlock(hglbAttr);
GlobalFree(hglbAttr);
return fSuccess;
}
/*-------
* CenterDialog
*
* Description: Center the dialog over the frame window
* Input : hdlg -- Dialog window handle
* Output : None
*-------
*/
void INTFUNC
CenterDialog(HWND hdlg)
{
HWND hwndFrame;
RECT rcDlg,
rcScr,
rcFrame;
int cx,
cy;
hwndFrame = GetParent(hdlg);
GetWindowRect(hdlg, &rcDlg);
cx = rcDlg.right - rcDlg.left;
cy = rcDlg.bottom - rcDlg.top;
GetClientRect(hwndFrame, &rcFrame);
ClientToScreen(hwndFrame, (LPPOINT) (&rcFrame.left));
ClientToScreen(hwndFrame, (LPPOINT) (&rcFrame.right));
rcDlg.top = rcFrame.top + (((rcFrame.bottom - rcFrame.top) - cy) >> 1);
rcDlg.left = rcFrame.left + (((rcFrame.right - rcFrame.left) - cx) >> 1);
rcDlg.bottom = rcDlg.top + cy;
rcDlg.right = rcDlg.left + cx;
GetWindowRect(GetDesktopWindow(), &rcScr);
if (rcDlg.bottom > rcScr.bottom)
{
rcDlg.bottom = rcScr.bottom;
rcDlg.top = rcDlg.bottom - cy;
}
if (rcDlg.right > rcScr.right)
{
rcDlg.right = rcScr.right;
rcDlg.left = rcDlg.right - cx;
}
if (rcDlg.left < 0)
rcDlg.left = 0;
if (rcDlg.top < 0)
rcDlg.top = 0;
MoveWindow(hdlg, rcDlg.left, rcDlg.top, cx, cy, TRUE);
return;
}
/*-------
* ConfigDlgProc
* Description: Manage add data source name dialog
* Input : hdlg --- Dialog window handle
* wMsg --- Message
* wParam - Message parameter
* lParam - Message parameter
* Output : TRUE if message processed, FALSE otherwise
*-------
*/
int CALLBACK
ConfigDlgProc(HWND hdlg,
WORD wMsg,
WPARAM wParam,
LPARAM lParam)
{
LPSETUPDLG lpsetupdlg;
ConnInfo *ci;
switch (wMsg)
{
/* Initialize the dialog */
case WM_INITDIALOG:
lpsetupdlg = (LPSETUPDLG) lParam;
ci = &lpsetupdlg->ci;
/* Hide the driver connect message */
ShowWindow(GetDlgItem(hdlg, DRV_MSG_LABEL), SW_HIDE);
SetWindowLong(hdlg, DWL_USER, lParam);
CenterDialog(hdlg); /* Center dialog */
/*
* NOTE: Values supplied in the attribute string will always
*/
/* override settings in ODBC.INI */
/* Get the rest of the common attributes */
getDSNinfo(ci, CONN_DONT_OVERWRITE);
/* Fill in any defaults */
getDSNdefaults(ci);
/* Initialize dialog fields */
SetDlgStuff(hdlg, ci);
if (lpsetupdlg->fDefault)
{
EnableWindow(GetDlgItem(hdlg, IDC_DSNAME), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), FALSE);
}
else
SendDlgItemMessage(hdlg, IDC_DSNAME,
EM_LIMITTEXT, (WPARAM) (MAXDSNAME - 1), 0L);
SendDlgItemMessage(hdlg, IDC_DESC,
EM_LIMITTEXT, (WPARAM) (MAXDESC - 1), 0L);
return TRUE; /* Focus was not set */
/* Process buttons */
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
/*
* Ensure the OK button is enabled only when a data
* source name
*/
/* is entered */
case IDC_DSNAME:
if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
{
char szItem[MAXDSNAME]; /* Edit control text */
/* Enable/disable the OK button */
EnableWindow(GetDlgItem(hdlg, IDOK),
GetDlgItemText(hdlg, IDC_DSNAME,
szItem, sizeof(szItem)));
return TRUE;
}
break;
/* Accept results */
case IDOK:
lpsetupdlg = (LPSETUPDLG) GetWindowLong(hdlg, DWL_USER);
/* Retrieve dialog values */
if (!lpsetupdlg->fDefault)
GetDlgItemText(hdlg, IDC_DSNAME,
lpsetupdlg->ci.dsn,
sizeof(lpsetupdlg->ci.dsn));
/* Get Dialog Values */
GetDlgStuff(hdlg, &lpsetupdlg->ci);
/* Update ODBC.INI */
SetDSNAttributes(hdlg, lpsetupdlg);
/* Return to caller */
case IDCANCEL:
EndDialog(hdlg, wParam);
return TRUE;
case IDC_DRIVER:
lpsetupdlg = (LPSETUPDLG) GetWindowLong(hdlg, DWL_USER);
DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DRV),
hdlg, driver_optionsProc, (LPARAM) &lpsetupdlg->ci);
return TRUE;
case IDC_DATASOURCE:
lpsetupdlg = (LPSETUPDLG) GetWindowLong(hdlg, DWL_USER);
DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DS),
hdlg, ds_optionsProc, (LPARAM) &lpsetupdlg->ci);
return TRUE;
}
break;
}
/* Message not processed */
return FALSE;
}
/*-------
* ParseAttributes
*
* Description: Parse attribute string moving values into the aAttr array
* Input : lpszAttributes - Pointer to attribute string
* Output : None (global aAttr normally updated)
*-------
*/
void INTFUNC
ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
{
LPCSTR lpsz;
LPCSTR lpszStart;
char aszKey[MAXKEYLEN];
int cbKey;
char value[MAXPGPATH];
memset(&lpsetupdlg->ci, 0, sizeof(ConnInfo));
for (lpsz = lpszAttributes; *lpsz; lpsz++)
{
/*
* Extract key name (e.g., DSN), it must be terminated by an
* equals
*/
lpszStart = lpsz;
for (;; lpsz++)
{
if (!*lpsz)
return; /* No key was found */
else if (*lpsz == '=')
break; /* Valid key found */
}
/* Determine the key's index in the key table (-1 if not found) */
cbKey = lpsz - lpszStart;
if (cbKey < sizeof(aszKey))
{
_fmemcpy(aszKey, lpszStart, cbKey);
aszKey[cbKey] = '\0';
}
/* Locate end of key value */
lpszStart = ++lpsz;
for (; *lpsz; lpsz++)
;
/* lpsetupdlg->aAttr[iElement].fSupplied = TRUE; */
_fmemcpy(value, lpszStart, MIN(lpsz - lpszStart + 1, MAXPGPATH));
mylog("aszKey='%s', value='%s'\n", aszKey, value);
/* Copy the appropriate value to the conninfo */
copyAttributes(&lpsetupdlg->ci, aszKey, value);
}
return;
}
/*--------
* SetDSNAttributes
*
* Description: Write data source attributes to ODBC.INI
* Input : hwnd - Parent window handle (plus globals)
* Output : TRUE if successful, FALSE otherwise
*--------
*/
BOOL INTFUNC
SetDSNAttributes(HWND hwndParent, LPSETUPDLG lpsetupdlg)
{
LPCSTR lpszDSN; /* Pointer to data source name */
lpszDSN = lpsetupdlg->ci.dsn;
/* Validate arguments */
if (lpsetupdlg->fNewDSN && !*lpsetupdlg->ci.dsn)
return FALSE;
/* Write the data source name */
if (!SQLWriteDSNToIni(lpszDSN, lpsetupdlg->lpszDrvr))
{
if (hwndParent)
{
char szBuf[MAXPGPATH];
char szMsg[MAXPGPATH];
LoadString(s_hModule, IDS_BADDSN, szBuf, sizeof(szBuf));
wsprintf(szMsg, szBuf, lpszDSN);
LoadString(s_hModule, IDS_MSGTITLE, szBuf, sizeof(szBuf));
MessageBox(hwndParent, szMsg, szBuf, MB_ICONEXCLAMATION | MB_OK);
}
return FALSE;
}
/* Update ODBC.INI */
writeDSNinfo(&lpsetupdlg->ci);
/* If the data source name has changed, remove the old name */
if (lstrcmpi(lpsetupdlg->szDSN, lpsetupdlg->ci.dsn))
SQLRemoveDSNFromIni(lpsetupdlg->szDSN);
return TRUE;
}
/*
# Insight Distribution Systems - System V - Apr 1998
#ident "@(#)setup.rul 1.13 :/sccs/sql/odbc/s.setup.rul 1/6/99 14:47:48"
*/
/*----------------------------------------------------------------------------*\
*
* PostgreSQL ODBC Driver Installation Script for InstallShield
*
\*----------------------------------------------------------------------------*/
#define APP_NAME "PostgreSQL ODBC Driver"
#define DRIVER_NAME "PostgreSQL"
#define DRIVER_FILE "PSQLODBC.DLL"
#define OLD_DRIVER_FILE "PODBC32.DLL"
#define OLD_DRIVER_FILE_RENAMED "podbc32_sav.dll"
#define COMPANY_NAME "Insight"
#define PRODUCT_NAME "PostgreSQL ODBC Driver"
#define PRODUCT_VERSION "6.3"
#define PRODUCT_KEY "PSQLODBC.DLL"
#define UNINSTALL_KEY "PSQLODBCv6.3"
#define ODBC_DM_KEY "\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs"
#define ODBC_COMP_KEY "\\SOFTWARE\\ODBC\\ODBCINST.INI"
#define ODBC_CORE_KEY "\\SOFTWARE\\ODBC\\ODBCINST.INI\\ODBC Core"
#define ODBC_DRIVERS_KEY "\\SOFTWARE\\ODBC\\ODBCINST.INI\\ODBC Drivers"
declare
// functions
prototype SetupScreen();
prototype FileCompare(STRING, STRING, STRING, STRING);
// variables
STRING svMainDirectory [_MAX_STRING], svGrp, svUninstLogFile, svPath;
STRING svValue, szName, szKey, szMessage;
STRING szMsg, szTmp, szTmp2, szFileSet, szProgram;
NUMBER nResult, pos, nvType, nvSize, nStartup, ComponentUsageCount;
NUMBER nvDoNot, nvVersion, nvInstall, nCore, nDM;
STRING dm, core, szFileName, svFileName;
NUMBER options, nvInfo, nvResult;
LONG lResult;
STRING svCompVersion, svFileVersion, svCompDate, svCompTime, svFileDate, svFileTime;
program
StartHere:
Disable( BACKGROUND );
// Set up the installation screen.
SetupScreen();
InstallationInfo(COMPANY_NAME, PRODUCT_NAME, PRODUCT_VERSION, PRODUCT_KEY);
RegDBSetAppInfo("Location", REGDB_STRING, WINSYSDIR ^ DRIVER_FILE, -1);
// Create a Welcome dialog.
WelcomeDlg:
Disable( BACKBUTTON );
Welcome( "Welcome to the PostgreSQL Odbc Driver Installation", 0 );
Enable( BACKBUTTON );
Enable( NEXTBUTTON );
GetTargetDirectory:
svMainDirectory = WINSYSDIR;
OptionsDlg:
RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
szKey = ODBC_DM_KEY;
nCore = RegDBKeyExist(szKey);
szName = WINSYSDIR ^ "ODBC32.DLL";
nDM = RegDBGetKeyValueEx(szKey, szName, nvType, svValue, nvSize);
szMessage = "Select options for installing the ODBC Driver Manager.\n" +
"Analysis of your system suggests that the ODBC Driver Manager\n";
nvDoNot = FALSE;
nvInstall = FALSE;
nvVersion = FALSE;
if (nCore >= 0 && nDM >= 0) then
nvDoNot = TRUE;
szMessage = szMessage + "is already installed. Therefore, you may choose not to install it.";
else
nvInstall = TRUE;
szMessage = szMessage + "is not installed. Therefore, you should install it now.";
endif;
Enable(FINISHBUTTON);
nResult = AskOptions(EXCLUSIVE, szMessage,
"Do not install Driver Manager", nvDoNot,
"Install Driver Manager ", nvInstall,
"Install Driver Manager (with version checking)", nvVersion);
if (nResult = BACK) then
Disable(FINISHBUTTON);
goto WelcomeDlg;
endif;
Version:
CompressInfo("driver.z", DRIVER_FILE, COMP_INFO_VERSIONMS|COMP_INFO_VERSIONLS, nvInfo, svCompVersion);
szFileName = WINSYSDIR ^ DRIVER_FILE;
nResult = VerGetFileVersion(szFileName, svFileVersion);
// MessageBox("System file PSQLODBC.dll version is " + svFileVersion, INFORMATION);
lResult = VerCompare(svCompVersion, svFileVersion, VERSION);
if (lResult = EQUALS) then
//date
CompressInfo("driver.z", DRIVER_FILE, COMP_INFO_DATE, nvInfo, svCompDate);
GetFileInfo(szFileName, FILE_DATE, nvResult, svFileDate);
//time
CompressInfo("driver.z", DRIVER_FILE, COMP_INFO_TIME, nvInfo, svCompTime);
GetFileInfo(szFileName, FILE_TIME, nvResult, svFileTime);
// If compressed file date/time is earlier than system file date/time
// then
nResult = FileCompare(svCompDate, svCompTime, svFileDate, svFileTime);
if (nResult < 0) then
lResult = LESS_THAN;
endif;
NumToStr(szTmp, nResult);
// MessageBox("File Compare = " + szTmp, INFORMATION);
endif;
if (lResult = LESS_THAN) then
MessageBeep(0);
nResult = AskYesNo("The " + PRODUCT_NAME + " is already installed on your system \nand is a newer version than the one that is about to be installed.\n\n" +
"Would you like to continue the installation anyway (not recommended)?", NO);
if (nResult = NO) then
MessageBeep(0);
MessageBox("Installation has been aborted.\nNo changes have been made to your system.", WARNING);
exit;
endif;
else
/*
nResult = AskYesNo("Ready to install " + PRODUCT_NAME + ".\n\nPress Yes to proceed with the installation.\nPress No to abort the installation.", YES);
if (nResult = NO) then
MessageBeep(0);
MessageBox("Installation has been aborted.\nNo changes have been made to your system.", WARNING);
exit;
endif;
*/
endif;
CheckRegistry:
Enable(STATUSDLG);
SetStatusWindow(5, "Checking registry entries...");
Delay(1);
RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
szKey = ODBC_DM_KEY;
nResult = RegDBKeyExist(szKey);
if (nResult < 0 && nvDoNot = TRUE) then
MessageBeep(0);
MessageBox("ODBC Core Components are not installed!", SEVERE);
Disable(STATUSDLG);
MessageBeep(0);
MessageBox("Please install the ODBC Core Components\nand rerun this setup program.", INFORMATION);
exit;
endif;
szName = WINSYSDIR ^ "ODBC32.DLL";
nResult = RegDBGetKeyValueEx(szKey, szName, nvType, svValue, nvSize);
if (nResult < 0 && nvDoNot = TRUE) then
MessageBeep(0);
MessageBox("ODBC Driver Manager (ODBC32.DLL) is not installed!", SEVERE);
Disable(STATUSDLG);
MessageBeep(0);
MessageBox("Please install the ODBC Driver Manager\nand rerun this setup program.", INFORMATION);
exit;
endif;
FileSetup:
SetStatusWindow( 10, "Copying program files...");
StatusUpdate(ON, 90);
DeinstallStart(svMainDirectory, svUninstLogFile, UNINSTALL_KEY, 0);
// Show the uninstall under Add/Remove Programs in Control Panel
RegDBSetItem(REGDB_UNINSTALL_NAME, PRODUCT_NAME);
szFileSet = "psqlodbc";
TARGETDIR = svMainDirectory; // winsys
FileSetBeginDefine(szFileSet);
nResult = CompressGet("driver.z", "*.*", COMP_NORMAL);
if (nResult < 0) then
NumToStr(szTmp, nResult);
MessageBox("Compress Get Error on driver.z files.\n\nError # " + szTmp, SEVERE);
exit;
endif;
TARGETDIR = svMainDirectory; // winsys
// Driver Manager stuff
if (! nvDoNot) then
if (nvVersion) then
options = COMP_UPDATE_VERSION;
else
options = COMP_NORMAL;
endif;
// The File usage count increments are handled by CompressGet
// with the SHAREDFILE option.
nResult = CompressGet("redist.z", "*.*", options|SHAREDFILE);
if (nResult < 0) then
NumToStr(szTmp, nResult);
MessageBox("Compress Get Error on redist.z files.\n\nError # " + szTmp, SEVERE);
exit;
endif;
endif;
FileSetEndDefine(szFileSet);
FileTransfer:
nResult = FileSetPerformEz(szFileSet, 0);
switch(nResult)
case FS_DONE:
case FS_CREATEDIR:
MessageBeep(0);
MessageBox("Unable to create a required subdirectory under " + TARGETDIR + "."
+ "\nPlease check write access to this directory.", SEVERE);
abort;
default:
NumToStr(szTmp, nResult);
MessageBeep(0);
MessageBox("Error copying files to " + TARGETDIR + "."
+ "\nPlease check this location and try again."
+ "\n\nError Number:"+szTmp, SEVERE);
abort;
endswitch;
UpdateRegistry:
SetStatusWindow(95, "Creating registry entries...");
Delay(2);
RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
Disable(LOGGING);
// Create ODBC Core Subkey (if it doesn't exist)
// (But don't create uninstall information for it)
szKey = ODBC_CORE_KEY;
nResult = RegDBCreateKeyEx(szKey, "");
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create ODBC Core subkey.", SEVERE);
exit;
endif;
// Create Installed Driver Key (if it doesn't exist)
// (But don't create uninstall information for it)
szKey = ODBC_DRIVERS_KEY;
nResult = RegDBCreateKeyEx(szKey, "");
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create ODBC Drivers subkey.", SEVERE);
exit;
endif;
// Increment Driver Manager Component UsageCount
szKey = ODBC_CORE_KEY;
szName = "UsageCount";
if (RegDBGetKeyValueEx(szKey, szName, nvType, svValue, nvSize) < 0) then
ComponentUsageCount = 0;
endif;
// MessageBox("Current Driver Manager Component Usage Count = " + svValue, INFORMATION);
StrToNum(ComponentUsageCount, svValue);
ComponentUsageCount = ComponentUsageCount + 1;
NumToStr(szTmp, ComponentUsageCount);
// MessageBox("New Driver Manager Component Usage Count = " + szTmp, INFORMATION);
nResult = RegDBSetKeyValueEx(szKey, szName, REGDB_NUMBER, szTmp, -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to increment Driver Manager component usage count.", SEVERE);
exit;
endif;
// Re-enable logging now
Enable(LOGGING);
// set ODBC Drivers Subkey (installed)
szKey = ODBC_DRIVERS_KEY;
nResult = RegDBSetKeyValueEx(szKey, DRIVER_NAME, REGDB_STRING, "Installed", -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create 'Installed' key value.", SEVERE);
exit;
endif;
// Driver Specification Subkey (PostgreSQL)
szKey = ODBC_COMP_KEY + "\\" + DRIVER_NAME;
nResult = RegDBCreateKeyEx(szKey, "");
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create ODBC Driver Key.", SEVERE);
exit;
endif;
nResult = RegDBSetKeyValueEx(szKey, "APILevel", REGDB_STRING, "1", -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create 'APILevel' key value.", SEVERE);
exit;
endif;
nResult = RegDBSetKeyValueEx(szKey, "ConnectFunctions", REGDB_STRING, "YYN", -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create 'ConnectFunctions' key value.", SEVERE);
exit;
endif;
nResult = RegDBSetKeyValueEx(szKey, "Driver", REGDB_STRING, WINSYSDIR ^ DRIVER_FILE, -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create 'Driver' key value.", SEVERE);
exit;
endif;
nResult = RegDBSetKeyValueEx(szKey, "DriverODBCVer", REGDB_STRING, "02.00", -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create 'DriverODBCVer' key value.", SEVERE);
exit;
endif;
nResult = RegDBSetKeyValueEx(szKey, "FileUsage", REGDB_STRING, "0", -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create 'FileUsage' key value.", SEVERE);
exit;
endif;
nResult = RegDBSetKeyValueEx(szKey, "Setup", REGDB_STRING, WINSYSDIR ^ DRIVER_FILE, -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create 'Setup' key value.", SEVERE);
exit;
endif;
nResult = RegDBSetKeyValueEx(szKey, "SQLLevel", REGDB_STRING, "1", -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create 'SQLLevel' key value.", SEVERE);
exit;
endif;
nResult = RegDBSetKeyValueEx(szKey, "UsageCount", REGDB_NUMBER, "1", -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create 'UsageCount' key value.", SEVERE);
exit;
endif;
pos = StrFind(CMDLINE, "UseDeclareFetch=");
if (pos >= 0) then
StrSub(svValue, CMDLINE, pos + 16, 1);
nResult = RegDBSetKeyValueEx(szKey, "UseDeclareFetch", REGDB_STRING, svValue, -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create 'UseDeclareFetch' key value.", SEVERE);
exit;
endif;
endif;
pos = StrFind(CMDLINE, "Protocol=");
if (pos >= 0) then
StrSub(svValue, CMDLINE, pos + 9, 3);
nResult = RegDBSetKeyValueEx(szKey, "Protocol", REGDB_STRING, svValue, -1);
if (nResult < 0) then
MessageBeep(0);
MessageBox("Unable to create 'Protocol' key value.", SEVERE);
exit;
endif;
endif;
RenameOld:
if (FindFile(WINSYSDIR, OLD_DRIVER_FILE, svFileName) = 0) then
szMessage = "Renaming old driver to " + OLD_DRIVER_FILE_RENAMED + " ...";
SetStatusWindow(98, szMessage);
Delay(1);
Disable(LOGGING);
SRCDIR= WINSYSDIR;
TARGETDIR = WINSYSDIR;
RenameFile(OLD_DRIVER_FILE, OLD_DRIVER_FILE_RENAMED);
Enable(LOGGING);
endif;
Done:
Delay(1);
SetStatusWindow(100, "Installation complete");
Delay(1);
Disable(STATUSDLG);
if (BATCH_INSTALL = TRUE) then
szMsg = "Some files could not be updated because they are " +
"currently in use by other programs on the system. " +
"Files in use will be updated the next time you restart " +
"your system.";
RebootDialog("Restart Windows", szMsg, SYS_BOOTMACHINE);
CommitSharedFiles(0);
szMsg = "Driver setup complete.\n\nReboot your system to complete the installation.";
MessageBeep(0);
MessageBox(szMsg, INFORMATION);
else
szMsg = "Driver installation completed successfully.";
MessageBeep(0);
MessageBox(szMsg, INFORMATION);
endif;
exit;
/*---------------------------------------------------------------------------*\
*
* Function: SetupScreen
*
* Purpose: This function will set up the screen look. This includes
* colors, fonts, text to be displayed, etc.
*
*
* Input:
*
* Returns:
*
* Comments:
\*---------------------------------------------------------------------------*/
function SetupScreen()
begin
Enable( INDVFILESTATUS );
SetTitle( APP_NAME + " Setup", 28, WHITE );
SetTitle( "Setup", 0, BACKGROUNDCAPTION ); // Caption bar text.
Enable( BACKGROUND );
end;
function FileCompare(szCompInfoDate, szCompInfoTime, szFileInfoDate, szFileInfoTime)
STRING year, month, day, file_date, file_time;
NUMBER nResult;
begin
StrSub(year, szFileInfoDate, 2, 2);
StrSub(month, szFileInfoDate, 5, 2);
StrSub(day, szFileInfoDate, 8, 2);
file_date = month + "-" + day + "-" + year;
nResult = StrCompare(szCompInfoDate, file_date);
if (nResult != 0) then
return nResult;
endif;
StrSub(file_time, szFileInfoTime, 0, 5);
// MessageBox("Comp = " + szCompInfoDate + " " + szCompInfoTime + ", File = " + file_date + " " + file_time, INFORMATION);
nResult = StrCompare(szCompInfoTime, file_time);
return nResult;
end;
/*-------
* Module: socket.c
*
* Description: This module contains functions for low level socket
* operations (connecting/reading/writing to the backend)
*
* Classes: SocketClass (Functions prefix: "SOCK_")
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "socket.h"
#include "connection.h"
#ifndef WIN32
#include <stdlib.h>
#include <string.h> /* for memset */
#endif
extern GLOBAL_VALUES globals;
#ifndef BOOL
#define BOOL int
#endif
#ifndef TRUE
#define TRUE (BOOL)1
#endif
#ifndef FALSE
#define FALSE (BOOL)0
#endif
void
SOCK_clear_error(SocketClass *self)
{
self->errornumber = 0;
self->errormsg = NULL;
}
SocketClass *
SOCK_Constructor(const ConnectionClass *conn)
{
SocketClass *rv;
rv = (SocketClass *) malloc(sizeof(SocketClass));
if (rv != NULL)
{
rv->socket = (SOCKETFD) - 1;
rv->buffer_filled_in = 0;
rv->buffer_filled_out = 0;
rv->buffer_read_in = 0;
if (rv)
rv->buffer_size = conn->connInfo.drivers.socket_buffersize;
else
rv->buffer_size = globals.socket_buffersize;
rv->buffer_in = (unsigned char *) malloc(rv->buffer_size);
if (!rv->buffer_in)
{
free(rv);
return NULL;
}
rv->buffer_out = (unsigned char *) malloc(rv->buffer_size);
if (!rv->buffer_out)
{
free(rv->buffer_in);
free(rv);
return NULL;
}
rv->errormsg = NULL;
rv->errornumber = 0;
rv->reverse = FALSE;
}
return rv;
}
void
SOCK_Destructor(SocketClass *self)
{
mylog("SOCK_Destructor\n");
if (self->socket != -1)
{
SOCK_put_char(self, 'X');
SOCK_flush_output(self);
closesocket(self->socket);
}
if (self->buffer_in)
free(self->buffer_in);
if (self->buffer_out)
free(self->buffer_out);
free(self);
}
char
SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname)
{
struct hostent *host;
struct sockaddr_in sadr;
unsigned long iaddr;
if (self->socket != -1)
{
self->errornumber = SOCKET_ALREADY_CONNECTED;
self->errormsg = "Socket is already connected";
return 0;
}
memset((char *) &sadr, 0, sizeof(sadr));
/*
* If it is a valid IP address, use it. Otherwise use hostname lookup.
*/
iaddr = inet_addr(hostname);
if (iaddr == INADDR_NONE)
{
host = gethostbyname(hostname);
if (host == NULL)
{
self->errornumber = SOCKET_HOST_NOT_FOUND;
self->errormsg = "Could not resolve hostname.";
return 0;
}
memcpy(&(sadr.sin_addr), host->h_addr, host->h_length);
}
else
memcpy(&(sadr.sin_addr), (struct in_addr *) & iaddr, sizeof(iaddr));
sadr.sin_family = AF_INET;
sadr.sin_port = htons(port);
self->socket = socket(AF_INET, SOCK_STREAM, 0);
if (self->socket == -1)
{
self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET;
self->errormsg = "Could not create Socket.";
return 0;
}
if (connect(self->socket, (struct sockaddr *) & (sadr),
sizeof(sadr)) < 0)
{
self->errornumber = SOCKET_COULD_NOT_CONNECT;
self->errormsg = "Could not connect to remote socket.";
closesocket(self->socket);
self->socket = (SOCKETFD) - 1;
return 0;
}
return 1;
}
void
SOCK_get_n_char(SocketClass *self, char *buffer, int len)
{
int lf;
if (!buffer)
{
self->errornumber = SOCKET_NULLPOINTER_PARAMETER;
self->errormsg = "get_n_char was called with NULL-Pointer";
return;
}
for (lf = 0; lf < len; lf++)
buffer[lf] = SOCK_get_next_byte(self);
}
void
SOCK_put_n_char(SocketClass *self, char *buffer, int len)
{
int lf;
if (!buffer)
{
self->errornumber = SOCKET_NULLPOINTER_PARAMETER;
self->errormsg = "put_n_char was called with NULL-Pointer";
return;
}
for (lf = 0; lf < len; lf++)
SOCK_put_next_byte(self, (unsigned char) buffer[lf]);
}
/*
* bufsize must include room for the null terminator
* will read at most bufsize-1 characters + null.
* returns TRUE if truncation occurs.
*/
BOOL
SOCK_get_string(SocketClass *self, char *buffer, int bufsize)
{
register int lf = 0;
for (lf = 0; lf < bufsize - 1; lf++)
if (!(buffer[lf] = SOCK_get_next_byte(self)))
return FALSE;
buffer[bufsize - 1] = '\0';
return TRUE;
}
void
SOCK_put_string(SocketClass *self, char *string)
{
register int lf;
int len;
len = strlen(string) + 1;
for (lf = 0; lf < len; lf++)
SOCK_put_next_byte(self, (unsigned char) string[lf]);
}
int
SOCK_get_int(SocketClass *self, short len)
{
switch (len)
{
case 2:
{
unsigned short buf;
SOCK_get_n_char(self, (char *) &buf, len);
if (self->reverse)
return buf;
else
return ntohs(buf);
}
case 4:
{
unsigned int buf;
SOCK_get_n_char(self, (char *) &buf, len);
if (self->reverse)
return buf;
else
return ntohl(buf);
}
default:
self->errornumber = SOCKET_GET_INT_WRONG_LENGTH;
self->errormsg = "Cannot read ints of that length";
return 0;
}
}
void
SOCK_put_int(SocketClass *self, int value, short len)
{
unsigned int rv;
switch (len)
{
case 2:
rv = self->reverse ? value : htons((unsigned short) value);
SOCK_put_n_char(self, (char *) &rv, 2);
return;
case 4:
rv = self->reverse ? value : htonl((unsigned int) value);
SOCK_put_n_char(self, (char *) &rv, 4);
return;
default:
self->errornumber = SOCKET_PUT_INT_WRONG_LENGTH;
self->errormsg = "Cannot write ints of that length";
return;
}
}
void
SOCK_flush_output(SocketClass *self)
{
int written;
written = send(self->socket, (char *) self->buffer_out, self->buffer_filled_out, 0);
if (written != self->buffer_filled_out)
{
self->errornumber = SOCKET_WRITE_ERROR;
self->errormsg = "Could not flush socket buffer.";
}
self->buffer_filled_out = 0;
}
unsigned char
SOCK_get_next_byte(SocketClass *self)
{
if (self->buffer_read_in >= self->buffer_filled_in)
{
/*
* there are no more bytes left in the buffer so reload the buffer
*/
self->buffer_read_in = 0;
self->buffer_filled_in = recv(self->socket, (char *) self->buffer_in, self->buffer_size, 0);
mylog("read %d, global_socket_buffersize=%d\n", self->buffer_filled_in, self->buffer_size);
if (self->buffer_filled_in < 0)
{
self->errornumber = SOCKET_READ_ERROR;
self->errormsg = "Error while reading from the socket.";
self->buffer_filled_in = 0;
return 0;
}
if (self->buffer_filled_in == 0)
{
self->errornumber = SOCKET_CLOSED;
self->errormsg = "Socket has been closed.";
self->buffer_filled_in = 0;
return 0;
}
}
return self->buffer_in[self->buffer_read_in++];
}
void
SOCK_put_next_byte(SocketClass *self, unsigned char next_byte)
{
int bytes_sent;
self->buffer_out[self->buffer_filled_out++] = next_byte;
if (self->buffer_filled_out == self->buffer_size)
{
/* buffer is full, so write it out */
bytes_sent = send(self->socket, (char *) self->buffer_out, self->buffer_size, 0);
if (bytes_sent != self->buffer_size)
{
self->errornumber = SOCKET_WRITE_ERROR;
self->errormsg = "Error while writing to the socket.";
}
self->buffer_filled_out = 0;
}
}
/* File: socket.h
*
* Description: See "socket.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __SOCKET_H__
#define __SOCKET_H__
#include "psqlodbc.h"
#ifndef WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define closesocket(xxx) close(xxx)
#define SOCKETFD int
#ifndef INADDR_NONE
#ifndef _IN_ADDR_T
#define _IN_ADDR_T
typedef unsigned int in_addr_t;
#endif
#define INADDR_NONE ((in_addr_t)-1)
#endif
#else
#include <winsock.h>
#define SOCKETFD SOCKET
#endif
#define SOCKET_ALREADY_CONNECTED 1
#define SOCKET_HOST_NOT_FOUND 2
#define SOCKET_COULD_NOT_CREATE_SOCKET 3
#define SOCKET_COULD_NOT_CONNECT 4
#define SOCKET_READ_ERROR 5
#define SOCKET_WRITE_ERROR 6
#define SOCKET_NULLPOINTER_PARAMETER 7
#define SOCKET_PUT_INT_WRONG_LENGTH 8
#define SOCKET_GET_INT_WRONG_LENGTH 9
#define SOCKET_CLOSED 10
struct SocketClass_
{
int buffer_size;
int buffer_filled_in;
int buffer_filled_out;
int buffer_read_in;
unsigned char *buffer_in;
unsigned char *buffer_out;
SOCKETFD socket;
char *errormsg;
int errornumber;
char reverse; /* used to handle Postgres 6.2 protocol
* (reverse byte order) */
};
#define SOCK_get_char(self) (SOCK_get_next_byte(self))
#define SOCK_put_char(self, c) (SOCK_put_next_byte(self, c))
/* error functions */
#define SOCK_get_errcode(self) (self->errornumber)
#define SOCK_get_errmsg(self) (self->errormsg)
/* Socket prototypes */
SocketClass *SOCK_Constructor(const ConnectionClass *conn);
void SOCK_Destructor(SocketClass *self);
char SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname);
void SOCK_get_n_char(SocketClass *self, char *buffer, int len);
void SOCK_put_n_char(SocketClass *self, char *buffer, int len);
BOOL SOCK_get_string(SocketClass *self, char *buffer, int bufsize);
void SOCK_put_string(SocketClass *self, char *string);
int SOCK_get_int(SocketClass *self, short len);
void SOCK_put_int(SocketClass *self, int value, short len);
void SOCK_flush_output(SocketClass *self);
unsigned char SOCK_get_next_byte(SocketClass *self);
void SOCK_put_next_byte(SocketClass *self, unsigned char next_byte);
void SOCK_clear_error(SocketClass *self);
#endif
/*-------
* Module: statement.c
*
* Description: This module contains functions related to creating
* and manipulating a statement.
*
* Classes: StatementClass (Functions prefix: "SC_")
*
* API functions: SQLAllocStmt, SQLFreeStmt
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "statement.h"
#include "bind.h"
#include "connection.h"
#include "qresult.h"
#include "convert.h"
#include "environ.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "pgapifunc.h"
#define PRN_NULLCHECK
/* Map sql commands to statement types */
static struct
{
int type;
char *s;
} Statement_Type[] =
{
{
STMT_TYPE_SELECT, "SELECT"
},
{
STMT_TYPE_INSERT, "INSERT"
},
{
STMT_TYPE_UPDATE, "UPDATE"
},
{
STMT_TYPE_DELETE, "DELETE"
},
{
STMT_TYPE_CREATE, "CREATE"
},
{
STMT_TYPE_ALTER, "ALTER"
},
{
STMT_TYPE_DROP, "DROP"
},
{
STMT_TYPE_GRANT, "GRANT"
},
{
STMT_TYPE_REVOKE, "REVOKE"
},
{
STMT_TYPE_PROCCALL, "{"
},
{
0, NULL
}
};
RETCODE SQL_API
PGAPI_AllocStmt(HDBC hdbc,
HSTMT FAR * phstmt)
{
static char *func = "PGAPI_AllocStmt";
ConnectionClass *conn = (ConnectionClass *) hdbc;
StatementClass *stmt;
mylog("%s: entering...\n", func);
if (!conn)
{
CC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
stmt = SC_Constructor();
mylog("**** PGAPI_AllocStmt: hdbc = %u, stmt = %u\n", hdbc, stmt);
if (!stmt)
{
conn->errornumber = CONN_STMT_ALLOC_ERROR;
conn->errormsg = "No more memory to allocate a further SQL-statement";
*phstmt = SQL_NULL_HSTMT;
CC_log_error(func, "", conn);
return SQL_ERROR;
}
if (!CC_add_statement(conn, stmt))
{
conn->errormsg = "Maximum number of connections exceeded.";
conn->errornumber = CONN_STMT_ALLOC_ERROR;
CC_log_error(func, "", conn);
SC_Destructor(stmt);
*phstmt = SQL_NULL_HSTMT;
return SQL_ERROR;
}
*phstmt = (HSTMT) stmt;
/* Copy default statement options based from Connection options */
stmt->options = conn->stmtOptions;
stmt->stmt_size_limit = CC_get_max_query_len(conn);
/* Save the handle for later */
stmt->phstmt = phstmt;
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_FreeStmt(HSTMT hstmt,
UWORD fOption)
{
static char *func = "PGAPI_FreeStmt";
StatementClass *stmt = (StatementClass *) hstmt;
mylog("%s: entering...hstmt=%u, fOption=%d\n", func, hstmt, fOption);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if (fOption == SQL_DROP)
{
ConnectionClass *conn = stmt->hdbc;
/* Remove the statement from the connection's statement list */
if (conn)
{
if (!CC_remove_statement(conn, stmt))
{
stmt->errornumber = STMT_SEQUENCE_ERROR;
stmt->errormsg = "Statement is currently executing a transaction.";
SC_log_error(func, "", stmt);
return SQL_ERROR; /* stmt may be executing a
* transaction */
}
/* Free any cursors and discard any result info */
if (stmt->result)
{
QR_Destructor(stmt->result);
stmt->result = NULL;
}
}
/* Destroy the statement and free any results, cursors, etc. */
SC_Destructor(stmt);
}
else if (fOption == SQL_UNBIND)
SC_unbind_cols(stmt);
else if (fOption == SQL_CLOSE)
{
/*
* this should discard all the results, but leave the statement
* itself in place (it can be executed again)
*/
if (!SC_recycle_statement(stmt))
{
/* errormsg passed in above */
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
}
else if (fOption == SQL_RESET_PARAMS)
SC_free_params(stmt, STMT_FREE_PARAMS_ALL);
else
{
stmt->errormsg = "Invalid option passed to PGAPI_FreeStmt.";
stmt->errornumber = STMT_OPTION_OUT_OF_RANGE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
return SQL_SUCCESS;
}
/*
* StatementClass implementation
*/
void
InitializeStatementOptions(StatementOptions *opt)
{
memset(opt, 0, sizeof(StatementOptions));
opt->maxRows = 0; /* driver returns all rows */
opt->maxLength = 0; /* driver returns all data for char/binary */
opt->rowset_size = 1;
opt->keyset_size = 0; /* fully keyset driven is the default */
opt->scroll_concurrency = SQL_CONCUR_READ_ONLY;
opt->cursor_type = SQL_CURSOR_FORWARD_ONLY;
opt->bind_size = 0; /* default is to bind by column */
opt->retrieve_data = SQL_RD_ON;
opt->use_bookmarks = SQL_UB_OFF;
opt->paramset_size = 1;
opt->param_bind_type = 0; /* default is column-wise binding */
}
StatementClass *
SC_Constructor(void)
{
StatementClass *rv;
rv = (StatementClass *) malloc(sizeof(StatementClass));
if (rv)
{
rv->hdbc = NULL; /* no connection associated yet */
rv->phstmt = NULL;
rv->result = NULL;
rv->manual_result = FALSE;
rv->prepare = FALSE;
rv->status = STMT_ALLOCATED;
rv->internal = FALSE;
rv->errormsg = NULL;
rv->errornumber = 0;
rv->errormsg_created = FALSE;
rv->errormsg_malloced = FALSE;
rv->statement = NULL;
rv->stmt_with_params = NULL;
rv->stmt_size_limit = -1;
rv->statement_type = STMT_TYPE_UNKNOWN;
rv->bindings = NULL;
rv->bindings_allocated = 0;
rv->bookmark.buffer = NULL;
rv->bookmark.used = NULL;
rv->parameters_allocated = 0;
rv->parameters = 0;
rv->currTuple = -1;
rv->rowset_start = -1;
rv->current_col = -1;
rv->bind_row = 0;
rv->last_fetch_count = 0;
rv->save_rowset_size = -1;
rv->data_at_exec = -1;
rv->current_exec_param = -1;
rv->put_data = FALSE;
rv->lobj_fd = -1;
rv->cursor_name[0] = '\0';
/* Parse Stuff */
rv->ti = NULL;
rv->fi = NULL;
rv->ntab = 0;
rv->nfld = 0;
rv->parse_status = STMT_PARSE_NONE;
/* Clear Statement Options -- defaults will be set in AllocStmt */
memset(&rv->options, 0, sizeof(StatementOptions));
rv->pre_executing = FALSE;
rv->inaccurate_result = FALSE;
rv->miscinfo = 0;
}
return rv;
}
char
SC_Destructor(StatementClass *self)
{
mylog("SC_Destructor: self=%u, self->result=%u, self->hdbc=%u\n", self, self->result, self->hdbc);
SC_clear_error(self);
if (STMT_EXECUTING == self->status)
{
self->errornumber = STMT_SEQUENCE_ERROR;
self->errormsg = "Statement is currently executing a transaction.";
return FALSE;
}
if (self->result)
{
if (!self->hdbc)
self->result->conn = NULL; /* prevent any dbase activity */
QR_Destructor(self->result);
}
if (self->statement)
free(self->statement);
if (self->stmt_with_params)
{
free(self->stmt_with_params);
self->stmt_with_params = NULL;
}
SC_free_params(self, STMT_FREE_PARAMS_ALL);
/*
* the memory pointed to by the bindings is not deallocated by the
* driver but by the application that uses that driver, so we don't
* have to care
*/
/* about that here. */
if (self->bindings)
{
int lf;
for (lf = 0; lf < self->bindings_allocated; lf++)
{
if (self->bindings[lf].ttlbuf != NULL)
free(self->bindings[lf].ttlbuf);
}
free(self->bindings);
}
/* Free the parsed table information */
if (self->ti)
{
int i;
for (i = 0; i < self->ntab; i++)
free(self->ti[i]);
free(self->ti);
}
/* Free the parsed field information */
if (self->fi)
{
int i;
for (i = 0; i < self->nfld; i++)
free(self->fi[i]);
free(self->fi);
}
free(self);
mylog("SC_Destructor: EXIT\n");
return TRUE;
}
/*
* Free parameters and free the memory from the
* data-at-execution parameters that was allocated in SQLPutData.
*/
void
SC_free_params(StatementClass *self, char option)
{
int i;
mylog("SC_free_params: ENTER, self=%d\n", self);
if (!self->parameters)
return;
for (i = 0; i < self->parameters_allocated; i++)
{
if (self->parameters[i].data_at_exec == TRUE)
{
if (self->parameters[i].EXEC_used)
{
free(self->parameters[i].EXEC_used);
self->parameters[i].EXEC_used = NULL;
}
if (self->parameters[i].EXEC_buffer)
{
if (self->parameters[i].SQLType != SQL_LONGVARBINARY)
free(self->parameters[i].EXEC_buffer);
self->parameters[i].EXEC_buffer = NULL;
}
}
}
self->data_at_exec = -1;
self->current_exec_param = -1;
self->put_data = FALSE;
if (option == STMT_FREE_PARAMS_ALL)
{
free(self->parameters);
self->parameters = NULL;
self->parameters_allocated = 0;
}
mylog("SC_free_params: EXIT\n");
}
int
statement_type(char *statement)
{
int i;
/* ignore leading whitespace in query string */
while (*statement && isspace((unsigned char) *statement))
statement++;
for (i = 0; Statement_Type[i].s; i++)
if (!strnicmp(statement, Statement_Type[i].s, strlen(Statement_Type[i].s)))
return Statement_Type[i].type;
return STMT_TYPE_OTHER;
}
/*
* Called from SQLPrepare if STMT_PREMATURE, or
* from SQLExecute if STMT_FINISHED, or
* from SQLFreeStmt(SQL_CLOSE)
*/
char
SC_recycle_statement(StatementClass *self)
{
ConnectionClass *conn;
mylog("recycle statement: self= %u\n", self);
SC_clear_error(self);
/* This would not happen */
if (self->status == STMT_EXECUTING)
{
self->errornumber = STMT_SEQUENCE_ERROR;
self->errormsg = "Statement is currently executing a transaction.";
return FALSE;
}
switch (self->status)
{
case STMT_ALLOCATED:
/* this statement does not need to be recycled */
return TRUE;
case STMT_READY:
break;
case STMT_PREMATURE:
/*
* Premature execution of the statement might have caused the
* start of a transaction. If so, we have to rollback that
* transaction.
*/
conn = SC_get_conn(self);
if (!CC_is_in_autocommit(conn) && CC_is_in_trans(conn))
{
if (SC_is_pre_executable(self) && !conn->connInfo.disallow_premature)
CC_abort(conn);
}
break;
case STMT_FINISHED:
break;
default:
self->errormsg = "An internal error occured while recycling statements";
self->errornumber = STMT_INTERNAL_ERROR;
return FALSE;
}
/* Free the parsed table information */
if (self->ti)
{
int i;
for (i = 0; i < self->ntab; i++)
free(self->ti[i]);
free(self->ti);
self->ti = NULL;
self->ntab = 0;
}
/* Free the parsed field information */
if (self->fi)
{
int i;
for (i = 0; i < self->nfld; i++)
free(self->fi[i]);
free(self->fi);
self->fi = NULL;
self->nfld = 0;
}
self->parse_status = STMT_PARSE_NONE;
/* Free any cursors */
if (self->result)
{
QR_Destructor(self->result);
self->result = NULL;
}
self->inaccurate_result = FALSE;
/*
* Reset only parameters that have anything to do with results
*/
self->status = STMT_READY;
self->manual_result = FALSE; /* very important */
self->currTuple = -1;
self->rowset_start = -1;
self->current_col = -1;
self->bind_row = 0;
self->last_fetch_count = 0;
if (self->errormsg_malloced && self->errormsg)
free(self->errormsg);
self->errormsg = NULL;
self->errornumber = 0;
self->errormsg_created = FALSE;
self->errormsg_malloced = FALSE;
self->lobj_fd = -1;
/*
* Free any data at exec params before the statement is executed
* again. If not, then there will be a memory leak when the next
* SQLParamData/SQLPutData is called.
*/
SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
return TRUE;
}
/* Pre-execute a statement (SQLPrepare/SQLDescribeCol) */
void
SC_pre_execute(StatementClass *self)
{
mylog("SC_pre_execute: status = %d\n", self->status);
if (self->status == STMT_READY)
{
mylog(" preprocess: status = READY\n");
self->miscinfo = 0;
if (self->statement_type == STMT_TYPE_SELECT)
{
char old_pre_executing = self->pre_executing;
self->pre_executing = TRUE;
self->inaccurate_result = FALSE;
PGAPI_Execute(self);
self->pre_executing = old_pre_executing;
if (self->status == STMT_FINISHED)
{
mylog(" preprocess: after status = FINISHED, so set PREMATURE\n");
self->status = STMT_PREMATURE;
}
}
if (!SC_is_pre_executable(self))
{
self->result = QR_Constructor();
QR_set_status(self->result, PGRES_TUPLES_OK);
self->inaccurate_result = TRUE;
self->status = STMT_PREMATURE;
}
}
}
/* This is only called from SQLFreeStmt(SQL_UNBIND) */
char
SC_unbind_cols(StatementClass *self)
{
Int2 lf;
for (lf = 0; lf < self->bindings_allocated; lf++)
{
self->bindings[lf].data_left = -1;
self->bindings[lf].buflen = 0;
self->bindings[lf].buffer = NULL;
self->bindings[lf].used = NULL;
self->bindings[lf].returntype = SQL_C_CHAR;
}
self->bookmark.buffer = NULL;
self->bookmark.used = NULL;
return 1;
}
void
SC_clear_error(StatementClass *self)
{
if (self->errormsg_malloced && self->errormsg)
free(self->errormsg);
self->errornumber = 0;
self->errormsg = NULL;
self->errormsg_created = FALSE;
self->errormsg_malloced = FALSE;
}
/*
* This function creates an error msg which is the concatenation
* of the result, statement, connection, and socket messages.
*/
char *
SC_create_errormsg(StatementClass *self)
{
QResultClass *res = self->result;
ConnectionClass *conn = self->hdbc;
int pos;
static char msg[4096];
msg[0] = '\0';
if (res && res->message)
strcpy(msg, res->message);
else if (self->errormsg)
strcpy(msg, self->errormsg);
if (conn)
{
SocketClass *sock = conn->sock;
if (conn->errormsg && conn->errormsg[0] != '\0')
{
pos = strlen(msg);
sprintf(&msg[pos], ";\n%s", conn->errormsg);
}
if (sock && sock->errormsg && sock->errormsg[0] != '\0')
{
pos = strlen(msg);
sprintf(&msg[pos], ";\n%s", sock->errormsg);
}
}
if (!msg[0] && res && QR_get_notice(res))
return QR_get_notice(res);
return msg;
}
char
SC_get_error(StatementClass *self, int *number, char **message)
{
char rv;
/* Create a very informative errormsg if it hasn't been done yet. */
if (!self->errormsg_created)
{
self->errormsg = SC_create_errormsg(self);
self->errormsg_created = TRUE;
}
if (self->errornumber)
{
*number = self->errornumber;
*message = self->errormsg;
if (!self->errormsg_malloced)
self->errormsg = NULL;
}
rv = (self->errornumber != 0);
self->errornumber = 0;
return rv;
}
/*
* Currently, the driver offers very simple bookmark support -- it is
* just the current row number. But it could be more sophisticated
* someday, such as mapping a key to a 32 bit value
*/
unsigned long
SC_get_bookmark(StatementClass *self)
{
return (self->currTuple + 1);
}
RETCODE
SC_fetch(StatementClass *self)
{
static char *func = "SC_fetch";
QResultClass *res = self->result;
int retval,
result;
#ifdef DRIVER_CURSOR_IMPLEMENT
int updret;
#endif /* DRIVER_CURSOR_IMPLEMENT */
Int2 num_cols,
lf;
Oid type;
char *value;
ColumnInfoClass *coli;
/* TupleField *tupleField; */
ConnInfo *ci = &(SC_get_conn(self)->connInfo);
self->last_fetch_count = 0;
coli = QR_get_fields(res); /* the column info */
mylog("manual_result = %d, use_declarefetch = %d\n", self->manual_result, ci->drivers.use_declarefetch);
if (self->manual_result || !SC_is_fetchcursor(self))
{
if (self->currTuple >= QR_get_num_tuples(res) - 1 ||
(self->options.maxRows > 0 && self->currTuple == self->options.maxRows - 1))
{
/*
* if at the end of the tuples, return "no data found" and set
* the cursor past the end of the result set
*/
self->currTuple = QR_get_num_tuples(res);
return SQL_NO_DATA_FOUND;
}
mylog("**** SC_fetch: manual_result\n");
(self->currTuple)++;
}
else
{
/* read from the cache or the physical next tuple */
retval = QR_next_tuple(res);
if (retval < 0)
{
mylog("**** SC_fetch: end_tuples\n");
return SQL_NO_DATA_FOUND;
}
else if (retval > 0)
(self->currTuple)++; /* all is well */
else
{
mylog("SC_fetch: error\n");
self->errornumber = STMT_EXEC_ERROR;
self->errormsg = "Error fetching next row";
SC_log_error(func, "", self);
return SQL_ERROR;
}
}
num_cols = QR_NumResultCols(res);
result = SQL_SUCCESS;
self->last_fetch_count = 1;
/*
* If the bookmark column was bound then return a bookmark. Since this
* is used with SQLExtendedFetch, and the rowset size may be greater
* than 1, and an application can use row or column wise binding, use
* the code in copy_and_convert_field() to handle that.
*/
if (self->bookmark.buffer)
{
char buf[32];
sprintf(buf, "%ld", SC_get_bookmark(self));
result = copy_and_convert_field(self, 0, buf,
SQL_C_ULONG, self->bookmark.buffer, 0, self->bookmark.used);
}
#ifdef DRIVER_CURSOR_IMPLEMENT
updret = 0;
if (self->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
{
if (!QR_get_value_backend_row(res, self->currTuple, num_cols - 1))
updret = SQL_ROW_DELETED;
num_cols -= 2;
}
#endif /* DRIVER_CURSOR_IMPLEMENT */
if (self->options.retrieve_data == SQL_RD_OFF) /* data isn't required */
#ifdef DRIVER_CURSOR_IMPLEMENT
return updret ? updret + 10 : SQL_SUCCESS;
#else
return SQL_SUCCESS;
#endif /* DRIVER_CURSOR_IMPLEMENT */
for (lf = 0; lf < num_cols; lf++)
{
mylog("fetch: cols=%d, lf=%d, self = %u, self->bindings = %u, buffer[] = %u\n", num_cols, lf, self, self->bindings, self->bindings[lf].buffer);
/* reset for SQLGetData */
self->bindings[lf].data_left = -1;
if (self->bindings[lf].buffer != NULL)
{
/* this column has a binding */
/* type = QR_get_field_type(res, lf); */
type = CI_get_oid(coli, lf); /* speed things up */
mylog("type = %d\n", type);
if (self->manual_result)
{
value = QR_get_value_manual(res, self->currTuple, lf);
mylog("manual_result\n");
}
else if (SC_is_fetchcursor(self))
value = QR_get_value_backend(res, lf);
else
value = QR_get_value_backend_row(res, self->currTuple, lf);
mylog("value = '%s'\n", (value == NULL) ? "<NULL>" : value);
retval = copy_and_convert_field_bindinfo(self, type, value, lf);
mylog("copy_and_convert: retval = %d\n", retval);
switch (retval)
{
case COPY_OK:
break; /* OK, do next bound column */
case COPY_UNSUPPORTED_TYPE:
self->errormsg = "Received an unsupported type from Postgres.";
self->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
SC_log_error(func, "", self);
result = SQL_ERROR;
break;
case COPY_UNSUPPORTED_CONVERSION:
self->errormsg = "Couldn't handle the necessary data type conversion.";
self->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
SC_log_error(func, "", self);
result = SQL_ERROR;
break;
case COPY_RESULT_TRUNCATED:
self->errornumber = STMT_TRUNCATED;
self->errormsg = "Fetched item was truncated.";
qlog("The %dth item was truncated\n", lf + 1);
qlog("The buffer size = %d", self->bindings[lf].buflen);
qlog(" and the value is '%s'\n", value);
result = SQL_SUCCESS_WITH_INFO;
break;
/* error msg already filled in */
case COPY_GENERAL_ERROR:
SC_log_error(func, "", self);
result = SQL_ERROR;
break;
/* This would not be meaningful in SQLFetch. */
case COPY_NO_DATA_FOUND:
break;
default:
self->errormsg = "Unrecognized return value from copy_and_convert_field.";
self->errornumber = STMT_INTERNAL_ERROR;
SC_log_error(func, "", self);
result = SQL_ERROR;
break;
}
}
}
#ifdef DRIVER_CURSOR_IMPLEMENT
if (updret)
result = updret + 10;
#endif /* DRIVER_CURSOR_IMPLEMENT */
return result;
}
RETCODE
SC_execute(StatementClass *self)
{
static char *func = "SC_execute";
ConnectionClass *conn;
QResultClass *res;
char ok,
was_ok,
was_nonfatal;
Int2 oldstatus,
numcols;
QueryInfo qi;
ConnInfo *ci;
conn = SC_get_conn(self);
ci = &(conn->connInfo);
/* Begin a transaction if one is not already in progress */
/*
* Basically we don't have to begin a transaction in autocommit mode
* because Postgres backend runs in autocomit mode. We issue "BEGIN"
* in the following cases. 1) we use declare/fetch and the statement
* is SELECT (because declare/fetch must be called in a transaction).
* 2) we are in autocommit off state and the statement isn't of type
* OTHER.
*/
if (!self->internal && !CC_is_in_trans(conn) &&
(SC_is_fetchcursor(self) ||
(!CC_is_in_autocommit(conn) && self->statement_type != STMT_TYPE_OTHER)))
{
mylog(" about to begin a transaction on statement = %u\n", self);
res = CC_send_query(conn, "BEGIN", NULL);
if (QR_aborted(res))
{
self->errormsg = "Could not begin a transaction";
self->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", self);
return SQL_ERROR;
}
ok = QR_command_successful(res);
mylog("SC_exec: begin ok = %d, status = %d\n", ok, QR_get_status(res));
QR_Destructor(res);
if (!ok)
{
self->errormsg = "Could not begin a transaction";
self->errornumber = STMT_EXEC_ERROR;
SC_log_error(func, "", self);
return SQL_ERROR;
}
else
CC_set_in_trans(conn);
}
oldstatus = conn->status;
conn->status = CONN_EXECUTING;
self->status = STMT_EXECUTING;
/* If it's a SELECT statement, use a cursor. */
/*
* Note that the declare cursor has already been prepended to the
* statement
*/
/* in copy_statement... */
if (self->statement_type == STMT_TYPE_SELECT)
{
char fetch[128];
mylog(" Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name);
/* send the declare/select */
self->result = CC_send_query(conn, self->stmt_with_params, NULL);
if (SC_is_fetchcursor(self) && self->result != NULL &&
QR_command_successful(self->result))
{
QR_Destructor(self->result);
/*
* That worked, so now send the fetch to start getting data
* back
*/
qi.result_in = NULL;
qi.cursor = self->cursor_name;
qi.row_size = ci->drivers.fetch_max;
/*
* Most likely the rowset size will not be set by the
* application until after the statement is executed, so might
* as well use the cache size. The qr_next_tuple() function
* will correct for any discrepancies in sizes and adjust the
* cache accordingly.
*/
sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name);
self->result = CC_send_query(conn, fetch, &qi);
}
mylog(" done sending the query:\n");
}
else
{
/* not a SELECT statement so don't use a cursor */
mylog(" it's NOT a select statement: stmt=%u\n", self);
self->result = CC_send_query(conn, self->stmt_with_params, NULL);
/*
* We shouldn't send COMMIT. Postgres backend does the autocommit
* if neccessary. (Zoltan, 04/26/2000)
*/
/*
* Above seems wrong. Even in case of autocommit, started
* transactions must be committed. (Hiroshi, 02/11/2001)
*/
if (!self->internal && CC_is_in_autocommit(conn) && CC_is_in_trans(conn))
{
res = CC_send_query(conn, "COMMIT", NULL);
QR_Destructor(res);
CC_set_no_trans(conn);
}
}
conn->status = oldstatus;
self->status = STMT_FINISHED;
/* Check the status of the result */
if (self->result)
{
was_ok = QR_command_successful(self->result);
was_nonfatal = QR_command_nonfatal(self->result);
if (was_ok)
self->errornumber = STMT_OK;
else
self->errornumber = was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND;
/* set cursor before the first tuple in the list */
self->currTuple = -1;
self->current_col = -1;
self->rowset_start = -1;
/* see if the query did return any result columns */
numcols = QR_NumResultCols(self->result);
/* now allocate the array to hold the binding info */
if (numcols > 0)
{
extend_bindings(self, numcols);
if (self->bindings == NULL)
{
self->errornumber = STMT_NO_MEMORY_ERROR;
self->errormsg = "Could not get enough free memory to store the binding information";
SC_log_error(func, "", self);
return SQL_ERROR;
}
}
/* issue "ABORT" when query aborted */
if (QR_get_aborted(self->result) && !self->internal)
CC_abort(conn);
}
else
{
/* Bad Error -- The error message will be in the Connection */
if (self->statement_type == STMT_TYPE_CREATE)
{
self->errornumber = STMT_CREATE_TABLE_ERROR;
self->errormsg = "Error creating the table";
/*
* This would allow the table to already exists, thus
* appending rows to it. BUT, if the table didn't have the
* same attributes, it would fail. return
* SQL_SUCCESS_WITH_INFO;
*/
}
else
{
self->errornumber = STMT_EXEC_ERROR;
self->errormsg = "Error while executing the query";
}
if (!self->internal)
CC_abort(conn);
}
if (self->statement_type == STMT_TYPE_PROCCALL &&
(self->errornumber == STMT_OK ||
self->errornumber == STMT_INFO_ONLY) &&
self->parameters &&
self->parameters[0].buffer &&
self->parameters[0].paramType == SQL_PARAM_OUTPUT)
{ /* get the return value of the procedure
* call */
RETCODE ret;
HSTMT hstmt = (HSTMT) self;
ret = SC_fetch(hstmt);
if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
ret = PGAPI_GetData(hstmt, 1, self->parameters[0].CType, self->parameters[0].buffer, self->parameters[0].buflen, self->parameters[0].used);
if (ret != SQL_SUCCESS)
{
self->errornumber = STMT_EXEC_ERROR;
self->errormsg = "GetData to Procedure return failed.";
}
}
else
{
self->errornumber = STMT_EXEC_ERROR;
self->errormsg = "SC_fetch to get a Procedure return failed.";
}
}
if (self->errornumber == STMT_OK)
return SQL_SUCCESS;
else if (self->errornumber == STMT_INFO_ONLY)
return SQL_SUCCESS_WITH_INFO;
else
{
self->errormsg = "Error while executing the query";
SC_log_error(func, "", self);
return SQL_ERROR;
}
}
void
SC_log_error(char *func, char *desc, StatementClass *self)
{
#ifdef PRN_NULLCHECK
#define nullcheck(a) (a ? a : "(NULL)")
#endif
if (self)
{
qlog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, nullcheck(self->errormsg));
mylog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, nullcheck(self->errormsg));
qlog(" ------------------------------------------------------------\n");
qlog(" hdbc=%u, stmt=%u, result=%u\n", self->hdbc, self, self->result);
qlog(" manual_result=%d, prepare=%d, internal=%d\n", self->manual_result, self->prepare, self->internal);
qlog(" bindings=%u, bindings_allocated=%d\n", self->bindings, self->bindings_allocated);
qlog(" parameters=%u, parameters_allocated=%d\n", self->parameters, self->parameters_allocated);
qlog(" statement_type=%d, statement='%s'\n", self->statement_type, nullcheck(self->statement));
qlog(" stmt_with_params='%s'\n", nullcheck(self->stmt_with_params));
qlog(" data_at_exec=%d, current_exec_param=%d, put_data=%d\n", self->data_at_exec, self->current_exec_param, self->put_data);
qlog(" currTuple=%d, current_col=%d, lobj_fd=%d\n", self->currTuple, self->current_col, self->lobj_fd);
qlog(" maxRows=%d, rowset_size=%d, keyset_size=%d, cursor_type=%d, scroll_concurrency=%d\n", self->options.maxRows, self->options.rowset_size, self->options.keyset_size, self->options.cursor_type, self->options.scroll_concurrency);
qlog(" cursor_name='%s'\n", nullcheck(self->cursor_name));
qlog(" ----------------QResult Info -------------------------------\n");
if (self->result)
{
QResultClass *res = self->result;
qlog(" fields=%u, manual_tuples=%u, backend_tuples=%u, tupleField=%d, conn=%u\n", res->fields, res->manual_tuples, res->backend_tuples, res->tupleField, res->conn);
qlog(" fetch_count=%d, fcount=%d, num_fields=%d, cursor='%s'\n", res->fetch_count, res->fcount, res->num_fields, nullcheck(res->cursor));
qlog(" message='%s', command='%s', notice='%s'\n", nullcheck(res->message), nullcheck(res->command), nullcheck(res->notice));
qlog(" status=%d, inTuples=%d\n", res->status, res->inTuples);
}
/* Log the connection error if there is one */
CC_log_error(func, desc, self->hdbc);
}
else
qlog("INVALID STATEMENT HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
#undef PRN_NULLCHECK
}
/* File: statement.h
*
* Description: See "statement.c"
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __STATEMENT_H__
#define __STATEMENT_H__
#include "psqlodbc.h"
#include "bind.h"
#ifndef FALSE
#define FALSE (BOOL)0
#endif
#ifndef TRUE
#define TRUE (BOOL)1
#endif
typedef enum
{
STMT_ALLOCATED, /* The statement handle is allocated, but
* not used so far */
STMT_READY, /* the statement is waiting to be executed */
STMT_PREMATURE, /* ODBC states that it is legal to call
* e.g. SQLDescribeCol before a call to
* SQLExecute, but after SQLPrepare. To
* get all the necessary information in
* such a case, we simply execute the
* query _before_ the actual call to
* SQLExecute, so that statement is
* considered to be "premature". */
STMT_FINISHED, /* statement execution has finished */
STMT_EXECUTING /* statement execution is still going on */
} STMT_Status;
#define STMT_ROW_VERSION_CHANGED (-4)
#define STMT_POS_BEFORE_RECORDSET (-3)
#define STMT_TRUNCATED (-2)
#define STMT_INFO_ONLY (-1) /* not an error message,
* just a notification
* to be returned by
* SQLError */
#define STMT_OK 0 /* will be interpreted
* as "no error pending" */
#define STMT_EXEC_ERROR 1
#define STMT_STATUS_ERROR 2
#define STMT_SEQUENCE_ERROR 3
#define STMT_NO_MEMORY_ERROR 4
#define STMT_COLNUM_ERROR 5
#define STMT_NO_STMTSTRING 6
#define STMT_ERROR_TAKEN_FROM_BACKEND 7
#define STMT_INTERNAL_ERROR 8
#define STMT_STILL_EXECUTING 9
#define STMT_NOT_IMPLEMENTED_ERROR 10
#define STMT_BAD_PARAMETER_NUMBER_ERROR 11
#define STMT_OPTION_OUT_OF_RANGE_ERROR 12
#define STMT_INVALID_COLUMN_NUMBER_ERROR 13
#define STMT_RESTRICTED_DATA_TYPE_ERROR 14
#define STMT_INVALID_CURSOR_STATE_ERROR 15
#define STMT_OPTION_VALUE_CHANGED 16
#define STMT_CREATE_TABLE_ERROR 17
#define STMT_NO_CURSOR_NAME 18
#define STMT_INVALID_CURSOR_NAME 19
#define STMT_INVALID_ARGUMENT_NO 20
#define STMT_ROW_OUT_OF_RANGE 21
#define STMT_OPERATION_CANCELLED 22
#define STMT_INVALID_CURSOR_POSITION 23
#define STMT_VALUE_OUT_OF_RANGE 24
#define STMT_OPERATION_INVALID 25
#define STMT_PROGRAM_TYPE_OUT_OF_RANGE 26
#define STMT_BAD_ERROR 27
#define STMT_INVALID_OPTION_IDENTIFIER 28
/* statement types */
enum
{
STMT_TYPE_UNKNOWN = -2,
STMT_TYPE_OTHER = -1,
STMT_TYPE_SELECT = 0,
STMT_TYPE_INSERT,
STMT_TYPE_UPDATE,
STMT_TYPE_DELETE,
STMT_TYPE_CREATE,
STMT_TYPE_ALTER,
STMT_TYPE_DROP,
STMT_TYPE_GRANT,
STMT_TYPE_REVOKE,
STMT_TYPE_PROCCALL
};
#define STMT_UPDATE(stmt) (stmt->statement_type > STMT_TYPE_SELECT)
/* Parsing status */
enum
{
STMT_PARSE_NONE = 0,
STMT_PARSE_COMPLETE,
STMT_PARSE_INCOMPLETE,
STMT_PARSE_FATAL,
};
/* Result style */
enum
{
STMT_FETCH_NONE = 0,
STMT_FETCH_NORMAL,
STMT_FETCH_EXTENDED,
};
typedef struct
{
COL_INFO *col_info; /* cached SQLColumns info for this table */
char name[MAX_TABLE_LEN + 1];
char alias[MAX_TABLE_LEN + 1];
} TABLE_INFO;
typedef struct
{
TABLE_INFO *ti; /* resolve to explicit table names */
int precision;
int scale;
int display_size;
int length;
int type;
char nullable;
char func;
char expr;
char quote;
char dquote;
char numeric;
char dot[MAX_TABLE_LEN + 1];
char name[MAX_COLUMN_LEN + 1];
char alias[MAX_COLUMN_LEN + 1];
} FIELD_INFO;
/******** Statement Handle ***********/
struct StatementClass_
{
ConnectionClass *hdbc; /* pointer to ConnectionClass this
* statement belongs to */
QResultClass *result; /* result of the current statement */
HSTMT FAR *phstmt;
StatementOptions options;
STMT_Status status;
char *errormsg;
int errornumber;
/* information on bindings */
BindInfoClass *bindings; /* array to store the binding information */
BindInfoClass bookmark;
int bindings_allocated;
/* information on statement parameters */
int parameters_allocated;
ParameterInfoClass *parameters;
Int4 currTuple; /* current absolute row number (GetData,
* SetPos, SQLFetch) */
int save_rowset_size; /* saved rowset size in case of
* change/FETCH_NEXT */
int rowset_start; /* start of rowset (an absolute row
* number) */
int bind_row; /* current offset for Multiple row/column
* binding */
int last_fetch_count; /* number of rows retrieved in
* last fetch/extended fetch */
int current_col; /* current column for GetData -- used to
* handle multiple calls */
int lobj_fd; /* fd of the current large object */
char *statement; /* if non--null pointer to the SQL
* statement that has been executed */
TABLE_INFO **ti;
FIELD_INFO **fi;
int nfld;
int ntab;
int parse_status;
int statement_type; /* According to the defines above */
int data_at_exec; /* Number of params needing SQLPutData */
int current_exec_param; /* The current parameter for
* SQLPutData */
char put_data; /* Has SQLPutData been called yet? */
char errormsg_created; /* has an informative error msg
* been created? */
char manual_result; /* Is the statement result manually built? */
char prepare; /* is this statement a prepared statement
* or direct */
char internal; /* Is this statement being called
* internally? */
char cursor_name[MAX_CURSOR_LEN + 1];
char *stmt_with_params; /* statement after parameter
* substitution */
int stmt_size_limit;
char pre_executing; /* This statement is prematurely executing */
char inaccurate_result; /* Current status is PREMATURE but
* result is inaccurate */
char errormsg_malloced; /* Current error message is
* malloed (not in a static
* variable) ? */
char miscinfo;
};
#define SC_get_conn(a) (a->hdbc)
#define SC_get_Result(a) (a->result);
/* options for SC_free_params() */
#define STMT_FREE_PARAMS_ALL 0
#define STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY 1
/* misc info */
#define SC_set_pre_executable(a) (a->miscinfo |= 1L)
#define SC_no_pre_executable(a) (a->miscinfo &= ~1L)
#define SC_is_pre_executable(a) ((a->miscinfo & 1L) != 0)
#define SC_set_fetchcursor(a) (a->miscinfo |= 2L)
#define SC_no_fetchcursor(a) (a->miscinfo &= ~2L)
#define SC_is_fetchcursor(a) ((a->miscinfo & 2L) != 0)
/* Statement prototypes */
StatementClass *SC_Constructor(void);
void InitializeStatementOptions(StatementOptions *opt);
char SC_Destructor(StatementClass *self);
int statement_type(char *statement);
char parse_statement(StatementClass *stmt);
void SC_pre_execute(StatementClass *self);
char SC_unbind_cols(StatementClass *self);
char SC_recycle_statement(StatementClass *self);
void SC_clear_error(StatementClass *self);
char SC_get_error(StatementClass *self, int *number, char **message);
char *SC_create_errormsg(StatementClass *self);
RETCODE SC_execute(StatementClass *self);
RETCODE SC_fetch(StatementClass *self);
void SC_free_params(StatementClass *self, char option);
void SC_log_error(char *func, char *desc, StatementClass *self);
unsigned long SC_get_bookmark(StatementClass *self);
#endif
/*-------
* Module: tuple.c
*
* Description: This module contains functions for setting the data
* for individual fields (TupleField structure) of a
* manual result set.
*
* Important Note: These functions are ONLY used in building manual
* result sets for info functions (SQLTables,
* SQLColumns, etc.)
*
* Classes: n/a
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "tuple.h"
#include <string.h>
#include <stdlib.h>
void
set_tuplefield_null(TupleField *tuple_field)
{
tuple_field->len = 0;
tuple_field->value = NULL; /* strdup(""); */
}
void
set_tuplefield_string(TupleField *tuple_field, char *string)
{
tuple_field->len = strlen(string);
tuple_field->value = malloc(strlen(string) + 1);
strcpy(tuple_field->value, string);
}
void
set_tuplefield_int2(TupleField *tuple_field, Int2 value)
{
char buffer[10];
sprintf(buffer, "%d", value);
tuple_field->len = strlen(buffer) + 1;
/* +1 ... is this correct (better be on the save side-...) */
tuple_field->value = strdup(buffer);
}
void
set_tuplefield_int4(TupleField *tuple_field, Int4 value)
{
char buffer[15];
sprintf(buffer, "%ld", value);
tuple_field->len = strlen(buffer) + 1;
/* +1 ... is this correct (better be on the save side-...) */
tuple_field->value = strdup(buffer);
}
/* File: tuple.h
*
* Description: See "tuple.c"
*
* Important NOTE: The TupleField structure is used both to hold backend data and
* manual result set data. The "set_" functions and the TupleNode
* structure are only used for manual result sets by info routines.
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __TUPLE_H__
#define __TUPLE_H__
#include "psqlodbc.h"
/* Used by backend data AND manual result sets */
struct TupleField_
{
Int4 len; /* length of the current Tuple */
void *value; /* an array representing the value */
};
/* Used ONLY for manual result sets */
struct TupleNode_
{
struct TupleNode_ *prev,
*next;
TupleField tuple[1];
};
/* These macros are wrappers for the corresponding set_tuplefield functions
but these handle automatic NULL determination and call set_tuplefield_null()
if appropriate for the datatype (used by SQLGetTypeInfo).
*/
#define set_nullfield_string(FLD, VAL) ((VAL) ? set_tuplefield_string(FLD, (VAL)) : set_tuplefield_null(FLD))
#define set_nullfield_int2(FLD, VAL) ((VAL) != -1 ? set_tuplefield_int2(FLD, (VAL)) : set_tuplefield_null(FLD))
#define set_nullfield_int4(FLD, VAL) ((VAL) != -1 ? set_tuplefield_int4(FLD, (VAL)) : set_tuplefield_null(FLD))
void set_tuplefield_null(TupleField *tuple_field);
void set_tuplefield_string(TupleField *tuple_field, char *string);
void set_tuplefield_int2(TupleField *tuple_field, Int2 value);
void set_tuplefield_int4(TupleField *tuple_field, Int4 value);
#endif
/*--------
* Module: tuplelist.c
*
* Description: This module contains functions for creating a manual
* result set (the TupleList) and retrieving data from
* it for a specific row/column.
*
* Classes: TupleListClass (Functions prefix: "TL_")
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*--------
*/
#include "tuplelist.h"
#include <stdlib.h>
#include "tuple.h"
TupleListClass *
TL_Constructor(UInt4 fieldcnt)
{
TupleListClass *rv;
mylog("in TL_Constructor\n");
rv = (TupleListClass *) malloc(sizeof(TupleListClass));
if (rv)
{
rv->num_fields = fieldcnt;
rv->num_tuples = 0;
rv->list_start = NULL;
rv->list_end = NULL;
rv->lastref = NULL;
rv->last_indexed = -1;
}
mylog("exit TL_Constructor\n");
return rv;
}
void
TL_Destructor(TupleListClass *self)
{
int lf;
TupleNode *node,
*tp;
mylog("TupleList: in DESTRUCTOR\n");
node = self->list_start;
while (node != NULL)
{
for (lf = 0; lf < self->num_fields; lf++)
if (node->tuple[lf].value != NULL)
free(node->tuple[lf].value);
tp = node->next;
free(node);
node = tp;
}
free(self);
mylog("TupleList: exit DESTRUCTOR\n");
}
void *
TL_get_fieldval(TupleListClass *self, Int4 tupleno, Int2 fieldno)
{
Int4 lf;
Int4 delta,
from_end;
char end_is_closer,
start_is_closer;
TupleNode *rv;
if (self->last_indexed == -1)
/* we have an empty tuple list */
return NULL;
/* some more sanity checks */
if ((tupleno >= self->num_tuples) || (tupleno < 0))
/* illegal tuple number range */
return NULL;
if ((fieldno >= self->num_fields) || (fieldno < 0))
/* illegel field number range */
return NULL;
/*
* check if we are accessing the same tuple that was used in the last
* fetch (e.g: for fetching all the fields one after another. Do this
* to speed things up
*/
if (tupleno == self->last_indexed)
return self->lastref->tuple[fieldno].value;
/* now for the tricky part... */
/*
* Since random access is quite inefficient for linked lists we use
* the lastref pointer that points to the last element referenced by a
* get_fieldval() call in conjunction with the its index number that
* is stored in last_indexed. (So we use some locality of reference
* principle to speed things up)
*/
delta = tupleno - self->last_indexed;
/* if delta is positive, we have to go forward */
/*
* now check if we are closer to the start or the end of the list than
* to our last_indexed pointer
*/
from_end = (self->num_tuples - 1) - tupleno;
start_is_closer = labs(delta) > tupleno;
/*
* true if we are closer to the start of the list than to the
* last_indexed pointer
*/
end_is_closer = labs(delta) > from_end;
/* true if we are closer at the end of the list */
if (end_is_closer)
{
/* scanning from the end is the shortest way. so we do that... */
rv = self->list_end;
for (lf = 0; lf < from_end; lf++)
rv = rv->prev;
}
else if (start_is_closer)
{
/*
* the shortest way is to start the search from the head of the
* list
*/
rv = self->list_start;
for (lf = 0; lf < tupleno; lf++)
rv = rv->next;
}
else
{
/* the closest way is starting from our lastref - pointer */
rv = self->lastref;
/*
* at first determine whether we have to search forward or
* backwards
*/
if (delta < 0)
{
/* we have to search backwards */
for (lf = 0; lf < (-1) * delta; lf++)
rv = rv->prev;
}
else
{
/* ok, we have to search forward... */
for (lf = 0; lf < delta; lf++)
rv = rv->next;
}
}
/*
* now we have got our return pointer, so update the lastref and the
* last_indexed values
*/
self->lastref = rv;
self->last_indexed = tupleno;
return rv->tuple[fieldno].value;
}
char
TL_add_tuple(TupleListClass *self, TupleNode *new_field)
{
/*
* we append the tuple at the end of the doubly linked list of the
* tuples we have already read in
*/
new_field->prev = NULL;
new_field->next = NULL;
if (self->list_start == NULL)
{
/* the list is empty, we have to add the first tuple */
self->list_start = new_field;
self->list_end = new_field;
self->lastref = new_field;
self->last_indexed = 0;
}
else
{
/*
* there is already an element in the list, so add the new one at
* the end of the list
*/
self->list_end->next = new_field;
new_field->prev = self->list_end;
self->list_end = new_field;
}
self->num_tuples++;
/* this method of building a list cannot fail, so we return 1 */
return 1;
}
/* File: tuplelist.h
*
* Description: See "tuplelist.c"
*
* Important Note: This structure and its functions are ONLY used in building manual result
* sets for info functions (SQLTables, SQLColumns, etc.)
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#ifndef __TUPLELIST_H__
#define __TUPLELIST_H__
#include "psqlodbc.h"
struct TupleListClass_
{
Int4 num_fields;
Int4 num_tuples;
TupleNode *list_start,
*list_end,
*lastref;
Int4 last_indexed;
};
#define TL_get_num_tuples(x) (x->num_tuples)
/* Create a TupleList. Each tuple consits of fieldcnt columns */
TupleListClass *TL_Constructor(UInt4 fieldcnt);
void TL_Destructor(TupleListClass *self);
void *TL_get_fieldval(TupleListClass *self, Int4 tupleno, Int2 fieldno);
char TL_add_tuple(TupleListClass *self, TupleNode *new_field);
#endif
#
# File: win32.mak
#
# Description: psqlodbc Makefile for Win32.
#
# Configurations: Debug, Release, MultibyteDebug, MultibyteRelease
# Build Types: ALL, CLEAN
# Usage: NMAKE /f win32.mak CFG=[Release | Debug | MultibyteRelease | MultiByteDebug] [ALL | CLEAN]
#
# Comments: Created by Dave Page, 2001-02-12
#
!MESSAGE Building the PostgreSQL ODBC Driver for Win32...
!MESSAGE
!IF "$(CFG)" == ""
CFG=Release
!MESSAGE No configuration specified. Defaulting to Release.
!MESSAGE
!ENDIF
!IF "$(CFG)" != "Release" && "$(CFG)" != "Debug" && "$(CFG)" != "MultibyteRelease" && "$(CFG)" != "MultibyteDebug"
!MESSAGE Invalid configuration "$(CFG)" specified.
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f win32.mak CFG=[Release | Debug | MultibyteRelease | MultiByteDebug] [ALL | CLEAN]
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "Release" (Win32 Release DLL)
!MESSAGE "Debug" (Win32 Debug DLL)
!MESSAGE "MultibyteRelease" (Win32 Release DLL with Multibyte support)
!MESSAGE "MultibyteDebug" (Win32 Release DLL with Multibyte support)
!MESSAGE
!ERROR An invalid configuration was specified.
!ENDIF
!IF "$(OS)" == "Windows_NT"
NULL=
!ELSE
NULL=nul
!ENDIF
!IF "$(CFG)" == "Release" || "$(CFG)" == "MultibyteRelease"
!IF "$(CFG)" == "MultibyteRelease"
OUTDIR=.\MultibyteRelease
INTDIR=.\MultibyteRelease
!ELSE
OUTDIR=.\Release
INTDIR=.\Release
!ENDIF
ALL : "$(OUTDIR)\psqlodbc.dll"
CLEAN :
-@erase "$(INTDIR)\bind.obj"
-@erase "$(INTDIR)\columninfo.obj"
-@erase "$(INTDIR)\connection.obj"
-@erase "$(INTDIR)\convert.obj"
-@erase "$(INTDIR)\dlg_specific.obj"
-@erase "$(INTDIR)\drvconn.obj"
-@erase "$(INTDIR)\environ.obj"
-@erase "$(INTDIR)\execute.obj"
-@erase "$(INTDIR)\gpps.obj"
-@erase "$(INTDIR)\info.obj"
-@erase "$(INTDIR)\lobj.obj"
-@erase "$(INTDIR)\win_md5.obj"
-@erase "$(INTDIR)\misc.obj"
!IF "$(CFG)" == "MultibyteRelease"
-@erase "$(INTDIR)\multibyte.obj"
!ENDIF
-@erase "$(INTDIR)\options.obj"
-@erase "$(INTDIR)\parse.obj"
-@erase "$(INTDIR)\pgtypes.obj"
-@erase "$(INTDIR)\psqlodbc.obj"
-@erase "$(INTDIR)\psqlodbc.res"
-@erase "$(INTDIR)\qresult.obj"
-@erase "$(INTDIR)\results.obj"
-@erase "$(INTDIR)\setup.obj"
-@erase "$(INTDIR)\socket.obj"
-@erase "$(INTDIR)\statement.obj"
-@erase "$(INTDIR)\tuple.obj"
-@erase "$(INTDIR)\tuplelist.obj"
-@erase "$(INTDIR)\odbcapi.obj"
-@erase "$(INTDIR)\vc60.idb"
-@erase "$(OUTDIR)\psqlodbc.dll"
-@erase "$(OUTDIR)\psqlodbc.exp"
-@erase "$(OUTDIR)\psqlodbc.lib"
"$(OUTDIR)" :
if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
CPP=cl.exe
!IF "$(CFG)" == "MultibyteRelease"
CPP_PROJ=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "MULTIBYTE" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
!ELSE
CPP_PROJ=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
!ENDIF
.c{$(INTDIR)}.obj::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.cpp{$(INTDIR)}.obj::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.cxx{$(INTDIR)}.obj::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.c{$(INTDIR)}.sbr::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.cpp{$(INTDIR)}.sbr::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.cxx{$(INTDIR)}.sbr::
$(CPP) @<<
$(CPP_PROJ) $<
<<
MTL=midl.exe
MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
RSC=rc.exe
RSC_PROJ=/l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "NDEBUG"
BSC32=bscmake.exe
BSC32_FLAGS=/nologo /o"$(OUTDIR)\psqlodbc.bsc"
BSC32_SBRS= \
LINK32=link.exe
LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /incremental:no /pdb:"$(OUTDIR)\psqlodbc.pdb" /machine:I386 /def:"psqlodbc_win32.def" /out:"$(OUTDIR)\psqlodbc.dll" /implib:"$(OUTDIR)\psqlodbc.lib"
DEF_FILE= "psqlodbc_win32.def"
LINK32_OBJS= \
"$(INTDIR)\bind.obj" \
"$(INTDIR)\columninfo.obj" \
"$(INTDIR)\connection.obj" \
"$(INTDIR)\convert.obj" \
"$(INTDIR)\dlg_specific.obj" \
"$(INTDIR)\drvconn.obj" \
"$(INTDIR)\environ.obj" \
"$(INTDIR)\execute.obj" \
"$(INTDIR)\gpps.obj" \
"$(INTDIR)\info.obj" \
"$(INTDIR)\lobj.obj" \
"$(INTDIR)\win_md5.obj" \
"$(INTDIR)\misc.obj" \
!IF "$(CFG)" == "MultibyteRelease"
"$(INTDIR)\multibyte.obj" \
!ENDIF
"$(INTDIR)\options.obj" \
"$(INTDIR)\parse.obj" \
"$(INTDIR)\pgtypes.obj" \
"$(INTDIR)\psqlodbc.obj" \
"$(INTDIR)\qresult.obj" \
"$(INTDIR)\results.obj" \
"$(INTDIR)\setup.obj" \
"$(INTDIR)\socket.obj" \
"$(INTDIR)\statement.obj" \
"$(INTDIR)\tuple.obj" \
"$(INTDIR)\tuplelist.obj" \
"$(INTDIR)\odbcapi.obj" \
"$(INTDIR)\psqlodbc.res"
"$(OUTDIR)\psqlodbc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
$(LINK32) @<<
$(LINK32_FLAGS) $(LINK32_OBJS)
<<
!ELSEIF "$(CFG)" == "Debug" || "$(CFG)" == "MultibyteDebug"
!IF "$(CFG)" == "MultibyteDebug"
OUTDIR=.\MultibyteDebug
INTDIR=.\MultibyteDebug
!ELSE
OUTDIR=.\Debug
INTDIR=.\Debug
!ENDIF
ALL : "$(OUTDIR)\psqlodbc.dll"
CLEAN :
-@erase "$(INTDIR)\bind.obj"
-@erase "$(INTDIR)\columninfo.obj"
-@erase "$(INTDIR)\connection.obj"
-@erase "$(INTDIR)\convert.obj"
-@erase "$(INTDIR)\dlg_specific.obj"
-@erase "$(INTDIR)\drvconn.obj"
-@erase "$(INTDIR)\environ.obj"
-@erase "$(INTDIR)\execute.obj"
-@erase "$(INTDIR)\gpps.obj"
-@erase "$(INTDIR)\info.obj"
-@erase "$(INTDIR)\lobj.obj"
-@erase "$(INTDIR)\win_md5.obj"
-@erase "$(INTDIR)\misc.obj"
!IF "$(CFG)" == "MultibyteDebug"
-@erase "$(INTDIR)\multibyte.obj"
!ENDIF
-@erase "$(INTDIR)\options.obj"
-@erase "$(INTDIR)\parse.obj"
-@erase "$(INTDIR)\pgtypes.obj"
-@erase "$(INTDIR)\psqlodbc.obj"
-@erase "$(INTDIR)\psqlodbc.res"
-@erase "$(INTDIR)\qresult.obj"
-@erase "$(INTDIR)\results.obj"
-@erase "$(INTDIR)\setup.obj"
-@erase "$(INTDIR)\socket.obj"
-@erase "$(INTDIR)\statement.obj"
-@erase "$(INTDIR)\tuple.obj"
-@erase "$(INTDIR)\tuplelist.obj"
-@erase "$(INTDIR)\odbcapi.obj"
-@erase "$(INTDIR)\vc60.idb"
-@erase "$(INTDIR)\vc60.pdb"
-@erase "$(OUTDIR)\psqlodbc.dll"
-@erase "$(OUTDIR)\psqlodbc.exp"
-@erase "$(OUTDIR)\psqlodbc.ilk"
-@erase "$(OUTDIR)\psqlodbc.lib"
-@erase "$(OUTDIR)\psqlodbc.pdb"
"$(OUTDIR)" :
if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
CPP=cl.exe
!IF "$(CFG)" == "MultibyteDebug"
CPP_PROJ=/nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "MULTIBYTE" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
!ELSE
CPP_PROJ=/nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
!ENDIF
.c{$(INTDIR)}.obj::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.cpp{$(INTDIR)}.obj::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.cxx{$(INTDIR)}.obj::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.c{$(INTDIR)}.sbr::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.cpp{$(INTDIR)}.sbr::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.cxx{$(INTDIR)}.sbr::
$(CPP) @<<
$(CPP_PROJ) $<
<<
MTL=midl.exe
MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
RSC=rc.exe
RSC_PROJ=/l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "_DEBUG"
BSC32=bscmake.exe
BSC32_FLAGS=/nologo /o"$(OUTDIR)\psqlodbc.bsc"
BSC32_SBRS= \
LINK32=link.exe
LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\psqlodbc.pdb" /debug /machine:I386 /def:"psqlodbc_win32.def" /out:"$(OUTDIR)\psqlodbc.dll" /implib:"$(OUTDIR)\psqlodbc.lib" /pdbtype:sept
DEF_FILE= "psqlodbc_win32.def"
LINK32_OBJS= \
"$(INTDIR)\bind.obj" \
"$(INTDIR)\columninfo.obj" \
"$(INTDIR)\connection.obj" \
"$(INTDIR)\convert.obj" \
"$(INTDIR)\dlg_specific.obj" \
"$(INTDIR)\drvconn.obj" \
"$(INTDIR)\environ.obj" \
"$(INTDIR)\execute.obj" \
"$(INTDIR)\gpps.obj" \
"$(INTDIR)\info.obj" \
"$(INTDIR)\lobj.obj" \
"$(INTDIR)\win_md5.obj"
"$(INTDIR)\misc.obj" \
!IF "$(CFG)" == "MultibyteDebug"
"$(INTDIR)\multibyte.obj" \
!ENDIF
"$(INTDIR)\options.obj" \
"$(INTDIR)\parse.obj" \
"$(INTDIR)\pgtypes.obj" \
"$(INTDIR)\psqlodbc.obj" \
"$(INTDIR)\qresult.obj" \
"$(INTDIR)\results.obj" \
"$(INTDIR)\setup.obj" \
"$(INTDIR)\socket.obj" \
"$(INTDIR)\statement.obj" \
"$(INTDIR)\tuple.obj" \
"$(INTDIR)\tuplelist.obj" \
"$(INTDIR)\odbcapi.obj" \
"$(INTDIR)\psqlodbc.res"
"$(OUTDIR)\psqlodbc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
$(LINK32) @<<
$(LINK32_FLAGS) $(LINK32_OBJS)
<<
!ENDIF
!IF "$(CFG)" == "Release" || "$(CFG)" == "Debug" || "$(CFG)" == "MultibyteRelease" || "$(CFG)" == "MultibyteDebug"
SOURCE=bind.c
"$(INTDIR)\bind.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=columninfo.c
"$(INTDIR)\columninfo.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=connection.c
"$(INTDIR)\connection.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=convert.c
"$(INTDIR)\convert.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=dlg_specific.c
"$(INTDIR)\dlg_specific.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=drvconn.c
"$(INTDIR)\drvconn.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=environ.c
"$(INTDIR)\environ.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=execute.c
"$(INTDIR)\execute.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=gpps.c
"$(INTDIR)\gpps.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=info.c
"$(INTDIR)\info.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=lobj.c
"$(INTDIR)\lobj.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=misc.c
"$(INTDIR)\misc.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
!IF "$(CFG)" == "MultibyteRelease" || "$(CFG)" == "MultibyteDebug"
SOURCE=multibyte.c
"$(INTDIR)\multibyte.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
!ENDIF
SOURCE=options.c
"$(INTDIR)\options.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=parse.c
"$(INTDIR)\parse.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=pgtypes.c
"$(INTDIR)\pgtypes.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=psqlodbc.c
"$(INTDIR)\psqlodbc.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=psqlodbc.rc
!IF "$(CFG)" == "Release"
"$(INTDIR)\psqlodbc.res" : $(SOURCE) "$(INTDIR)"
$(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "NDEBUG" $(SOURCE)
!ENDIF
!IF "$(CFG)" == "MultibyteRelease"
"$(INTDIR)\psqlodbc.res" : $(SOURCE) "$(INTDIR)"
$(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "NDEBUG" /d "MULTIBYTE" $(SOURCE)
!ENDIF
!IF "$(CFG)" == "Debug"
"$(INTDIR)\psqlodbc.res" : $(SOURCE) "$(INTDIR)"
$(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "_DEBUG" $(SOURCE)
!ENDIF
!IF "$(CFG)" == "MultibyteDebug"
"$(INTDIR)\psqlodbc.res" : $(SOURCE) "$(INTDIR)"
$(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "_DEBUG" /d "MULTIBYTE" $(SOURCE)
!ENDIF
SOURCE=qresult.c
"$(INTDIR)\qresult.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=results.c
"$(INTDIR)\results.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=setup.c
"$(INTDIR)\setup.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=socket.c
"$(INTDIR)\socket.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=statement.c
"$(INTDIR)\statement.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=tuple.c
"$(INTDIR)\tuple.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=tuplelist.c
"$(INTDIR)\tuplelist.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=win_md5.c
"$(INTDIR)\win_md5.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=odbcapi.c
"$(INTDIR)\odbcapi.obj" : $(SOURCE) "$(INTDIR)"
$(CPP) $(CPP_PROJ) $(SOURCE)
!ENDIF
/*
* win_md5.c
* Under Windows I don't love the following /D in makefiles. - inoue
*/
#define MD5_ODBC
#define FRONTEND
/*
* md5.c is the exact copy of the src/backend/libpq/md5.c.
*
* psqlodbc driver stuff never refer(link) to other
* stuff directly.
*
*/
#include "md5.c"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment