Update odbc driver to current version V.0244

This commit is contained in:
Byron Nikolaidis 1998-06-03 20:33:45 +00:00
parent 85f91d0e8e
commit 99d21d5b62
39 changed files with 5468 additions and 3771 deletions

View File

@ -79,6 +79,7 @@ StatementClass *stmt = (StatementClass *) hstmt;
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;
}
@ -114,6 +115,8 @@ StatementClass *stmt = (StatementClass *) hstmt;
else
stmt->parameters[ipar].data_at_exec = FALSE;
mylog("SQLBindParamater: ipar = %d, *pcbValue = %d, data_at_exec = %d\n",
ipar, pcbValue ? *pcbValue: -777, stmt->parameters[ipar].data_at_exec);
return SQL_SUCCESS;
}
@ -188,6 +191,8 @@ mylog("**** SQLBindCol: stmt = %u, icol = %d\n", stmt, icol);
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;
@ -228,7 +233,7 @@ StatementClass *stmt = (StatementClass *) hstmt;
*pibScale = stmt->parameters[ipar].scale;
if(pfNullable)
*pfNullable = pgtype_nullable(stmt->parameters[ipar].paramType);
*pfNullable = pgtype_nullable(stmt, stmt->parameters[ipar].paramType);
return SQL_SUCCESS;
}
@ -247,37 +252,25 @@ RETCODE SQL_API SQLParamOptions(
// - - - - - - - - -
// Returns the number of parameter markers.
// Returns the number of parameters in an SQL statement
RETCODE SQL_API SQLNumParams(
HSTMT hstmt,
SWORD FAR *pcpar)
{
StatementClass *stmt = (StatementClass *) hstmt;
unsigned int i;
// I guess this is the number of actual parameter markers
// in the statement, not the number of parameters that are bound.
// why does this have to be driver-specific?
if(!stmt)
return SQL_INVALID_HANDLE;
if(!stmt->statement) {
// no statement has been allocated
*pcpar = 0;
stmt->errormsg = "SQLNumParams called with no statement ready.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
return SQL_ERROR;
} else {
*pcpar = 0;
for(i=0; i < strlen(stmt->statement); i++) {
if(stmt->statement[i] == '?')
(*pcpar)++;
}
// If the statement does not have parameters, it should just return 0.
return SQL_SUCCESS;
if (pcpar) {
*pcpar = stmt->parameters_allocated;
}
return SQL_SUCCESS;
}
/********************************************************************
@ -309,7 +302,7 @@ extend_bindings(StatementClass *stmt, int num_columns)
BindInfoClass *new_bindings;
int i;
mylog("in extend_bindings\n");
mylog("in extend_bindings: stmt=%u, bindings_allocated=%d, num_columns=%d\n", stmt, stmt->bindings_allocated, num_columns);
/* if we have too few, allocate room for more, and copy the old */
/* entries into the new structure */
@ -325,6 +318,7 @@ int i;
}
stmt->bindings = new_bindings; // null indicates error
stmt->bindings_allocated = num_columns;
} else {
/* if we have too many, make sure the extra ones are emptied out */

View File

@ -34,8 +34,9 @@ struct ParameterInfoClass_ {
Int2 SQLType;
UInt4 precision;
Int2 scale;
Int4 *EXEC_used;
char *EXEC_buffer;
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;
};

View File

@ -29,6 +29,7 @@ ColumnInfoClass *rv;
rv->name = NULL;
rv->adtid = NULL;
rv->adtsize = NULL;
rv->display_size = NULL;
}
return rv;
@ -98,6 +99,7 @@ int num_fields = self->num_fields;
free(self->name);
free(self->adtid);
free(self->adtsize);
free(self->display_size);
}
void
@ -110,6 +112,7 @@ CI_set_num_fields(ColumnInfoClass *self, int new_num_fields)
self->name = (char **) malloc (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);
}
void
@ -126,34 +129,7 @@ CI_set_field_info(ColumnInfoClass *self, int field_num, char *new_name,
self->name[field_num] = strdup(new_name);
self->adtid[field_num] = new_adtid;
self->adtsize[field_num] = new_adtsize;
}
char *
CI_get_fieldname(ColumnInfoClass *self, Int2 which)
{
char *rv = NULL;
if ( ! self->name)
return NULL;
if ((which >= 0) && (which < self->num_fields))
rv = self->name[which];
return rv;
}
Int2
CI_get_fieldsize(ColumnInfoClass *self, Int2 which)
{
Int2 rv = 0;
if ( ! self->adtsize)
return 0;
if ((which >= 0) && (which < self->num_fields))
rv = self->adtsize[which];
return rv;
self->display_size[field_num] = 0;
}

View File

@ -17,14 +17,18 @@ struct ColumnInfoClass_ {
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) */
};
#define CI_get_num_fields(self) (self->num_fields)
#define CI_get_oid(self, col) (self->adtid[col])
#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])
ColumnInfoClass *CI_Constructor();
void CI_Destructor(ColumnInfoClass *self);
void CI_free_memory(ColumnInfoClass *self);
char CI_read_fields(ColumnInfoClass *self, SocketClass *sock);
/* functions for setting up the fields from within the program, */
@ -33,8 +37,5 @@ 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);
char *CI_get_fieldname(ColumnInfoClass *self, Int2 which);
Int2 CI_get_fieldsize(ColumnInfoClass *self, Int2 which);
void CI_free_memory(ColumnInfoClass *self);
#endif

View File

@ -18,6 +18,8 @@
#include "socket.h"
#include "statement.h"
#include "qresult.h"
#include "lobj.h"
#include "dlg_specific.h"
#include <stdio.h>
#include <odbcinst.h>
@ -25,6 +27,7 @@
extern GLOBAL_VALUES globals;
// void CC_test(ConnectionClass *self);
RETCODE SQL_API SQLAllocConnect(
HENV henv,
@ -70,25 +73,28 @@ RETCODE SQL_API SQLConnect(
SWORD cbAuthStr)
{
ConnectionClass *conn = (ConnectionClass *) hdbc;
ConnInfo *ci;
if ( ! conn)
return SQL_INVALID_HANDLE;
make_string(szDSN, cbDSN, conn->connInfo.dsn);
ci = &conn->connInfo;
make_string(szDSN, cbDSN, ci->dsn);
/* get the values for the DSN from the registry */
CC_DSN_info(conn, CONN_OVERWRITE);
getDSNinfo(ci, CONN_OVERWRITE);
/* override values from DSN info with UID and authStr(pwd)
This only occurs if the values are actually there.
*/
make_string(szUID, cbUID, conn->connInfo.username);
make_string(szAuthStr, cbAuthStr, conn->connInfo.password);
make_string(szUID, cbUID, ci->username);
make_string(szAuthStr, cbAuthStr, ci->password);
/* fill in any defaults */
CC_set_defaults(conn);
getDSNdefaults(ci);
qlog("conn = %u, SQLConnect(DSN='%s', UID='%s', PWD='%s')\n", conn->connInfo.dsn, conn->connInfo.username, conn->connInfo.password);
qlog("conn = %u, SQLConnect(DSN='%s', UID='%s', PWD='%s')\n", ci->dsn, ci->username, ci->password);
if ( CC_connect(conn, FALSE) <= 0)
// Error messages are filled in
@ -207,6 +213,7 @@ ConnectionClass *rv;
rv->num_stmts = STMT_INCREMENT;
rv->lobj_type = PG_TYPE_LO;
}
return rv;
}
@ -239,6 +246,26 @@ CC_Destructor(ConnectionClass *self)
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)
{
@ -316,69 +343,6 @@ StatementClass *stmt;
return TRUE;
}
void
CC_set_defaults(ConnectionClass *self)
{
ConnInfo *ci = &(self->connInfo);
if (ci->port[0] == '\0')
strcpy(ci->port, DEFAULT_PORT);
if (ci->readonly[0] == '\0')
strcpy(ci->readonly, DEFAULT_READONLY);
}
void
CC_DSN_info(ConnectionClass *self, char overwrite)
{
ConnInfo *ci = &(self->connInfo);
char *DSN = ci->dsn;
// 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.
if ( DSN[0] == '\0') {
if ( ci->driver[0] != '\0')
return;
else
strcpy(DSN, "DEFAULT");
}
// Proceed with getting info for the given DSN.
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->readonly[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_READONLY, "", ci->readonly, sizeof(ci->readonly), 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, "", ci->conn_settings, sizeof(ci->conn_settings), ODBC_INI);
qlog("conn=%u, DSN info(DSN='%s',server='%s',dbase='%s',user='%s',passwd='%s',port='%s',readonly='%s',protocol='%s',conn_settings='%s')\n",
self, DSN,
ci->server,
ci->database,
ci->username,
ci->password,
ci->port,
ci->readonly,
ci->protocol,
ci->conn_settings);
}
char
@ -400,6 +364,24 @@ char salt[2];
else {
qlog("Global Options: fetch=%d, socket=%d, unknown_sizes=%d, max_varchar_size=%d, max_longvarchar_size=%d\n",
globals.fetch_max,
globals.socket_buffersize,
globals.unknown_sizes,
globals.max_varchar_size,
globals.max_longvarchar_size);
qlog(" disable_optimizer=%d, unique_index=%d, use_declarefetch=%d\n",
globals.disable_optimizer,
globals.unique_index,
globals.use_declarefetch);
qlog(" text_as_longvarchar=%d, unknowns_as_longvarchar=%d, bools_as_char=%d\n",
globals.text_as_longvarchar,
globals.unknowns_as_longvarchar,
globals.bools_as_char);
qlog(" extra_systable_prefixes='%s', conn_settings='%s'\n",
globals.extra_systable_prefixes,
globals.conn_settings);
if (self->status != CONN_NOT_CONNECTED) {
self->errormsg = "Already connected.";
self->errornumber = CONN_OPENDB_ERROR;
@ -595,8 +577,12 @@ char salt[2];
/******* Send any initial settings *********/
/**********************************************/
CC_send_settings(self);
if ( ! CC_send_settings(self))
return 0;
CC_lookup_lo(self); /* a hack to get the oid of our large object oid type */
// CC_test(self);
CC_clear_error(self); /* clear any initial command errors */
self->status = CONN_CONNECTED;
@ -825,6 +811,13 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
mylog("send_query: read command '%s'\n", cmdbuffer);
clear = (cmdbuffer[0] == 'I');
if (cmdbuffer[0] == 'N')
qlog("NOTICE from backend during send_query: '%s'\n", &cmdbuffer[1]);
else if (cmdbuffer[0] == 'E')
qlog("ERROR from backend during send_query: '%s'\n", &cmdbuffer[1]);
else if (cmdbuffer[0] == 'C')
qlog("Command response: '%s'\n", &cmdbuffer[1]);
}
mylog("send_query: returning res = %u\n", res);
@ -926,18 +919,171 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
}
}
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;
static char msgbuffer[MAX_MESSAGE_LEN+1];
int i;
mylog("send_function(): conn=%u, fnid=%d, result_is_int=%d, nargs=%d\n", self, fnid, result_is_int, nargs);
// qlog("conn=%u, func=%d\n", self, fnid);
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;
default:
self->errornumber = CONNECTION_BACKEND_CRAZY;
self->errormsg = "Unexpected protocol character from backend";
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";
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;
// QResultClass *res;
HSTMT hstmt;
StatementClass *stmt;
RETCODE result;
SWORD cols = 0;
result = SQLAllocStmt( self, &hstmt);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
return FALSE;
}
stmt = (StatementClass *) hstmt;
ini_query[0] = '\0';
/* Turn on/off genetic optimizer based on global flag */
if (globals.optimizer[0] != '\0')
sprintf(ini_query, "set geqo to '%s'", globals.optimizer);
/* Set the Datestyle to the format the driver expects it to be in */
sprintf(ini_query, "set DateStyle to 'ISO'");
/* Disable genetic optimizer based on global flag */
if (globals.disable_optimizer)
sprintf(&ini_query[strlen(ini_query)], "%sset geqo to 'OFF'",
ini_query[0] != '\0' ? "; " : "");
/* Global settings */
if (globals.conn_settings[0] != '\0')
@ -954,26 +1100,88 @@ QResultClass *res;
if (ini_query[0] != '\0') {
mylog("Sending Initial Connection query: '%s'\n", ini_query);
res = CC_send_query(self, ini_query, NULL, NULL);
if (res && QR_get_status(res) != PGRES_FATAL_ERROR) {
mylog("Initial Query response: '%s'\n", QR_get_notice(res));
result = SQLExecDirect(hstmt, ini_query, SQL_NTS);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
SQLFreeStmt(hstmt, SQL_DROP);
return FALSE;
}
if ( res == NULL ||
QR_get_status(res) == PGRES_BAD_RESPONSE ||
QR_get_status(res) == PGRES_FATAL_ERROR ||
QR_get_status(res) == PGRES_INTERNAL_ERROR) {
SQLFreeStmt(hstmt, SQL_DROP);
self->errornumber = CONNECTION_COULD_NOT_SEND;
self->errormsg = "Error sending ConnSettings";
if (res)
QR_Destructor(res);
return 0;
}
if (res)
QR_Destructor(res);
}
return TRUE;
}
/* 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;
result = SQLAllocStmt( self, &hstmt);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
return;
}
stmt = (StatementClass *) hstmt;
result = SQLExecDirect(hstmt, "select oid from pg_type where typname='" \
PG_TYPE_LO_NAME \
"'", SQL_NTS);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
SQLFreeStmt(hstmt, SQL_DROP);
return;
}
result = SQLFetch(hstmt);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
SQLFreeStmt(hstmt, SQL_DROP);
return;
}
result = SQLGetData(hstmt, 1, SQL_C_SLONG, &self->lobj_type, sizeof(self->lobj_type), NULL);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
SQLFreeStmt(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 = SQLFreeStmt(hstmt, SQL_DROP);
}
/*
void
CC_test(ConnectionClass *self)
{
HSTMT hstmt1;
RETCODE result;
SDWORD pcbValue;
UDWORD pcrow;
UWORD rgfRowStatus;
char buf[255];
result = SQLAllocStmt( self, &hstmt1);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
return;
}
result = SQLExtendedFetch(hstmt1, SQL_FETCH_ABSOLUTE, -2, &pcrow, &rgfRowStatus);
SQLGetData(hstmt1, 1, SQL_C_CHAR, buf, sizeof(buf), &pcbValue);
qlog("FETCH_ABSOLUTE, -2: result=%d, Col1 = '%s'\n", result, buf);
result = SQLFetch(hstmt1);
while (result != SQL_NO_DATA_FOUND) {
result = SQLFetch(hstmt1);
qlog("fetch on stmt1\n");
}
SQLFreeStmt(hstmt1, SQL_DROP);
}
*/

View File

@ -12,6 +12,7 @@
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include "psqlodbc.h"
typedef enum {
@ -119,6 +120,7 @@ typedef struct _StartupPacket6_2
*/
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];
@ -128,6 +130,10 @@ typedef struct {
char protocol[SMALL_REGISTRY_LEN];
char port[SMALL_REGISTRY_LEN];
char readonly[SMALL_REGISTRY_LEN];
// char unknown_sizes[SMALL_REGISTRY_LEN];
char fake_oid_index[SMALL_REGISTRY_LEN];
char show_oid_column[SMALL_REGISTRY_LEN];
char show_system_tables[SMALL_REGISTRY_LEN];
char focus_password;
} ConnInfo;
@ -135,6 +141,7 @@ typedef struct {
#define PROTOCOL_62(conninfo_) (strncmp((conninfo_)->protocol, PG62, strlen(PG62)) == 0)
/******* The Connection handle ************/
struct ConnectionClass_ {
HENV henv; /* environment this connection was created on */
@ -145,6 +152,7 @@ struct ConnectionClass_ {
StatementClass **stmts;
int num_stmts;
SocketClass *sock;
int lobj_type;
char transact_status; /* Is a transaction is currently in progress */
char errormsg_created; /* has an informative error msg been created? */
};
@ -167,10 +175,9 @@ struct ConnectionClass_ {
/* prototypes */
ConnectionClass *CC_Constructor();
char CC_Destructor(ConnectionClass *self);
int CC_cursor_count(ConnectionClass *self);
char CC_cleanup(ConnectionClass *self);
char CC_abort(ConnectionClass *self);
void CC_DSN_info(ConnectionClass *self, char overwrite);
void CC_set_defaults(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);
@ -178,6 +185,8 @@ char CC_get_error(ConnectionClass *self, int *number, char **message);
QResultClass *CC_send_query(ConnectionClass *self, char *query, QResultClass *result_in, char *cursor);
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);
#endif

View File

@ -27,6 +27,26 @@
#include "statement.h"
#include "bind.h"
#include "pgtypes.h"
#include "lobj.h"
#include "connection.h"
extern GLOBAL_VALUES globals;
/* How to map ODBC scalar functions {fn func(args)} to Postgres */
/* This is just a simple substitution */
char *mapFuncs[][2] = {
{ "CONCAT", "textcat" },
{ "LCASE", "lower" },
{ "LOCATE", "strpos" },
{ "LENGTH", "textlen" },
{ "LTRIM", "ltrim" },
{ "RTRIM", "rtrim" },
{ "SUBSTRING", "substr" },
{ "UCASE", "upper" },
{ "NOW", "now" },
{ 0, 0 }
};
/******** A Guide for date/time/timestamp conversions **************
@ -47,23 +67,23 @@
/* This is called by SQLFetch() */
int
copy_and_convert_field_bindinfo(Int4 field_type, void *value, BindInfoClass *bic)
copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col)
{
return copy_and_convert_field(field_type, value, (Int2)bic->returntype, (PTR)bic->buffer,
(SDWORD)bic->buflen, (SDWORD *)bic->used);
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, FALSE);
}
/* This is called by SQLGetData() */
int
copy_and_convert_field(Int4 field_type, void *value, Int2 fCType, PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue)
copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType,
PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue, char multiple)
{
Int4 len = 0, nf;
char day[4], mon[4], tz[4];
Int4 len = 0;
SIMPLE_TIME st;
time_t t = time(NULL);
struct tm *tim;
int bool;
memset(&st, 0, sizeof(SIMPLE_TIME));
@ -73,8 +93,6 @@ int bool;
st.d = tim->tm_mday;
st.y = tim->tm_year + 1900;
bool = 0;
mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, value, cbValueMax);
if(value) {
@ -88,7 +106,7 @@ int bool;
switch(field_type) {
/* $$$ need to add parsing for date/time/timestamp strings in PG_TYPE_CHAR,VARCHAR $$$ */
case PG_TYPE_DATE:
sscanf(value, "%2d-%2d-%4d", &st.m, &st.d, &st.y);
sscanf(value, "%4d-%2d-%2d", &st.y, &st.m, &st.d);
break;
case PG_TYPE_TIME:
@ -98,12 +116,8 @@ int bool;
case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME:
if (strnicmp(value, "invalid", 7) != 0) {
nf = sscanf(value, "%3s %3s %2d %2d:%2d:%2d %4d %3s", &day, &mon, &st.d, &st.hh, &st.mm, &st.ss, &st.y, &tz);
sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m, &st.d, &st.hh, &st.mm, &st.ss);
if (nf == 7 || nf == 8) {
/* convert month name to month number */
st.m = monthToNumber(mon);
}
} else { /* The timestamp is invalid so set something conspicuous, like the epoch */
t = 0;
tim = localtime(&t);
@ -118,10 +132,10 @@ int bool;
case PG_TYPE_BOOL: { /* change T/F to 1/0 */
char *s = (char *) value;
if (s[0] == 'T' || s[0] == 't' || s[0] == '1')
bool = 1;
if (s[0] == 'T' || s[0] == 't')
s[0] = '1';
else
bool = 0;
s[0] = '0';
}
break;
@ -149,11 +163,20 @@ int bool;
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, rgbValue, cbValueMax, pcbValue, multiple);
default:
if (field_type == stmt->hdbc->lobj_type) /* hack until permanent type available */
return convert_lo( stmt, value, fCType, rgbValue, cbValueMax, pcbValue, multiple);
}
/* Change default into something useable */
if (fCType == SQL_C_DEFAULT) {
fCType = pgtype_to_ctype(field_type);
fCType = pgtype_to_ctype(stmt, field_type);
mylog("copy_and_convert, SQL_C_DEFAULT: fCType = %d\n", fCType);
}
@ -165,20 +188,20 @@ int bool;
switch(field_type) {
case PG_TYPE_DATE:
len = 11;
if (cbValueMax > len)
if (cbValueMax >= len)
sprintf((char *)rgbValue, "%.4d-%.2d-%.2d", st.y, st.m, st.d);
break;
case PG_TYPE_TIME:
len = 9;
if (cbValueMax > len)
if (cbValueMax >= len)
sprintf((char *)rgbValue, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss);
break;
case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME:
len = 19;
if (cbValueMax > len)
if (cbValueMax >= len)
sprintf((char *) rgbValue, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
st.y, st.m, st.d, st.hh, st.mm, st.ss);
break;
@ -186,7 +209,7 @@ int bool;
case PG_TYPE_BOOL:
len = 1;
if (cbValueMax > len) {
strcpy((char *) rgbValue, bool ? "1" : "0");
strcpy((char *) rgbValue, value);
mylog("PG_TYPE_BOOL: rgbValue = '%s'\n", rgbValue);
}
break;
@ -250,7 +273,7 @@ int bool;
case SQL_C_BIT:
len = 1;
if (cbValueMax >= len || field_type == PG_TYPE_BOOL) {
*((UCHAR *)rgbValue) = (UCHAR) bool;
*((UCHAR *)rgbValue) = atoi(value);
mylog("SQL_C_BIT: val = %d, cb = %d, rgb=%d\n", atoi(value), cbValueMax, *((UCHAR *)rgbValue));
}
break;
@ -307,6 +330,7 @@ int bool;
break;
case SQL_C_BINARY:
// truncate if necessary
// convert octal escapes to bytes
len = convert_from_pgbinary(value, rgbValue, cbValueMax);
@ -366,8 +390,9 @@ char *new_statement = stmt->stmt_with_params;
SIMPLE_TIME st;
time_t t = time(NULL);
struct tm *tim;
SDWORD FAR *used;
SDWORD used;
char *buffer, *buf;
char in_quote = FALSE;
if ( ! old_statement)
@ -382,11 +407,13 @@ char *buffer, *buf;
st.d = tim->tm_mday;
st.y = tim->tm_year + 1900;
/* If the application hasn't set a cursor name, then generate one */
if ( stmt->cursor_name[0] == '\0')
sprintf(stmt->cursor_name, "SQL_CUR%u", stmt);
// For selects, prepend a declare cursor to the statement
if (stmt->statement_type == STMT_TYPE_SELECT) {
sprintf(new_statement, "declare C%u cursor for ", stmt);
if (stmt->statement_type == STMT_TYPE_SELECT && globals.use_declarefetch) {
sprintf(new_statement, "declare %s cursor for ", stmt->cursor_name);
npos = strlen(new_statement);
}
else {
@ -419,19 +446,34 @@ char *buffer, *buf;
memcpy(&new_statement[npos], esc, strlen(esc));
npos += strlen(esc);
}
else { /* its not a valid literal so just copy */
*end = '}';
new_statement[npos++] = old_statement[opos];
continue;
}
opos += end - begin + 2;
opos += end - begin + 1;
*end = '}';
continue;
}
else if (old_statement[opos] != '?') { // a regular character
/* 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 (old_statement[opos] == '?' && !in_quote)
; /* ok */
else {
if (old_statement[opos] == '\'')
in_quote = (in_quote ? FALSE : TRUE);
new_statement[npos++] = old_statement[opos];
continue;
}
/****************************************************/
/* Its a '?' parameter alright */
/****************************************************/
@ -443,16 +485,16 @@ char *buffer, *buf;
/* 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;
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;
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 && *used == SQL_NULL_DATA) {
if (used == SQL_NULL_DATA) {
strcpy(&new_statement[npos], "NULL");
npos += 4;
continue;
@ -535,7 +577,7 @@ char *buffer, *buf;
case SQL_C_BIT: {
int i = *((UCHAR *) buffer);
sprintf(param_string, "'%s'", i ? "t" : "f");
sprintf(param_string, "%d", i ? 1 : 0);
break;
}
@ -593,7 +635,7 @@ char *buffer, *buf;
/* it was a SQL_C_CHAR */
if (buf) {
convert_returns(buf, &new_statement[npos], used ? *used : SQL_NTS);
convert_special_chars(buf, &new_statement[npos], used);
npos += strlen(&new_statement[npos]);
}
@ -605,9 +647,11 @@ char *buffer, *buf;
/* it was date,time,timestamp -- use m,d,y,hh,mm,ss */
else {
char *buf = convert_time(&st);
strcpy(&new_statement[npos], buf);
npos += strlen(buf);
sprintf(tmp, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
st.y, st.m, st.d, st.hh, st.mm, st.ss);
strcpy(&new_statement[npos], tmp);
npos += strlen(tmp);
}
new_statement[npos++] = '\''; /* Close Quote */
@ -615,20 +659,20 @@ char *buffer, *buf;
break;
case SQL_DATE:
if (buf && used) { /* copy char data to time */
my_strcpy(cbuf, sizeof(cbuf), buf, *used);
if (buf) { /* copy char data to time */
my_strcpy(cbuf, sizeof(cbuf), buf, used);
parse_datetime(cbuf, &st);
}
sprintf(tmp, "'%.2d-%.2d-%.4d'", st.m, st.d, st.y);
sprintf(tmp, "'%.4d-%.2d-%.2d'", st.y, st.m, st.d);
strcpy(&new_statement[npos], tmp);
npos += strlen(tmp);
break;
case SQL_TIME:
if (buf && used) { /* copy char data to time */
my_strcpy(cbuf, sizeof(cbuf), buf, *used);
if (buf) { /* copy char data to time */
my_strcpy(cbuf, sizeof(cbuf), buf, used);
parse_datetime(cbuf, &st);
}
@ -638,39 +682,61 @@ char *buffer, *buf;
npos += strlen(tmp);
break;
case SQL_TIMESTAMP: {
char *tbuf;
case SQL_TIMESTAMP:
if (buf && used) {
my_strcpy(cbuf, sizeof(cbuf), buf, *used);
if (buf) {
my_strcpy(cbuf, sizeof(cbuf), buf, used);
parse_datetime(cbuf, &st);
}
tbuf = convert_time(&st);
sprintf(tmp, "'%.4d-%.2d-%.2d %.2d:%.2d:%.2d'",
st.y, st.m, st.d, st.hh, st.mm, st.ss);
sprintf(&new_statement[npos], "'%s'", tbuf);
npos += strlen(tbuf) + 2;
strcpy(&new_statement[npos], tmp);
npos += strlen(tmp);
break;
}
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY: /* non-ascii characters should be converted to octal */
case SQL_VARBINARY: /* non-ascii characters should be converted to octal */
new_statement[npos++] = '\''; /* Open Quote */
mylog("SQL_LONGVARBINARY: about to call convert_to_pgbinary, *used = %d\n", *used);
mylog("SQL_LONGVARBINARY: about to call convert_to_pgbinary, used = %d\n", used);
npos += convert_to_pgbinary(buf, &new_statement[npos], *used);
npos += convert_to_pgbinary(buf, &new_statement[npos], used);
new_statement[npos++] = '\''; /* Close Quote */
break;
default: /* a numeric type */
case SQL_LONGVARBINARY:
/* 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", stmt->parameters[param_number].lobj_oid);
strcpy(&new_statement[npos], param_string);
npos += strlen(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)
default: /* a numeric type or SQL_BIT */
if (param_sqltype == SQL_BIT)
new_statement[npos++] = '\''; /* Open Quote */
if (buf) {
my_strcpy(&new_statement[npos], sizeof(stmt->stmt_with_params) - npos, buf, used);
npos += strlen(&new_statement[npos]);
}
else {
strcpy(&new_statement[npos], param_string);
npos += strlen(param_string);
}
if (param_sqltype == SQL_BIT)
new_statement[npos++] = '\''; /* Close Quote */
break;
}
@ -683,29 +749,47 @@ char *buffer, *buf;
return SQL_SUCCESS;
}
char *
mapFunction(char *func)
{
int i;
for (i = 0; mapFuncs[i][0]; i++)
if ( ! stricmp(mapFuncs[i][0], func))
return mapFuncs[i][1];
return NULL;
}
// This function returns a pointer to static memory!
char *
convert_escape(char *value)
{
char key[32], val[256];
static char escape[256];
SIMPLE_TIME st;
static char escape[1024];
char func[32], the_rest[1024];
char *mapFunc;
sscanf(value, "%[^'] '%[^']'", key, val);
sscanf(value, "%s %[^\r]", key, val);
mylog("convert_escape: key='%s', val='%s'\n", key, val);
if ( ! strncmp(key, "d", 1)) {
sscanf(val, "%4d-%2d-%2d", &st.y, &st.m, &st.d);
sprintf(escape, "'%.2d-%.2d-%.4d'", st.m, st.d, st.y);
if ( ! strcmp(key, "d") ||
! strcmp(key, "t") ||
! strcmp(key, "ts")) {
} else if (! strncmp(key, "t", 1)) {
sprintf(escape, "'%s'", val);
strcpy(escape, val);
}
else if ( ! strcmp(key, "fn")) {
sscanf(val, "%[^(]%[^\r]", func, the_rest);
mapFunc = mapFunction(func);
if ( ! mapFunc)
return NULL;
else {
strcpy(escape, mapFunc);
strcat(escape, the_rest);
}
} else if (! strncmp(key, "ts", 2)) {
sscanf(val, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m, &st.d, &st.hh, &st.mm, &st.ss);
strcpy(escape, convert_time(&st));
}
else {
return NULL;
@ -716,40 +800,6 @@ SIMPLE_TIME st;
}
int
monthToNumber(char *mon)
{
int m = 0;
if ( ! stricmp(mon, "Jan"))
m = 1;
else if ( ! stricmp(mon, "Feb"))
m = 2;
else if ( ! stricmp(mon, "Mar"))
m = 3;
else if ( ! stricmp(mon, "Apr"))
m = 4;
else if ( ! stricmp(mon, "May"))
m = 5;
else if ( ! stricmp(mon, "Jun"))
m = 6;
else if ( ! stricmp(mon, "Jul"))
m = 7;
else if ( ! stricmp(mon, "Aug"))
m = 8;
else if ( ! stricmp(mon, "Sep"))
m = 9;
else if ( ! stricmp(mon, "Oct"))
m = 10;
else if ( ! stricmp(mon, "Nov"))
m = 11;
else if ( ! stricmp(mon, "Dec"))
m = 12;
return m;
}
char *
convert_money(char *s)
{
@ -767,39 +817,7 @@ size_t i = 0, out = 0;
return s;
}
/* Convert a discrete time into a localized string */
char *
convert_time(SIMPLE_TIME *st)
{
struct tm tim;
static char buf[1024];
mylog("convert_time: 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);
memset(&tim, 0, sizeof(tim));
tim.tm_mon = st->m - 1;
tim.tm_mday = st->d;
tim.tm_year = st->y - 1900;
tim.tm_hour = st->hh;
tim.tm_min = st->mm;
tim.tm_sec = st->ss;
/* Dont bother trying to figure out the day of week because
postgres will determine it correctly. However, the timezone
should be taken into account. $$$$
*/
// tim.tm_isdst = _daylight;
strftime(buf, sizeof(buf), "%b %d %H:%M:%S %Y",
&tim);
mylog("convert_time: buf = '%s'\n", buf);
return buf;
}
/* 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 */
@ -869,7 +887,7 @@ char *p;
p[0] = '\0';
for (i = 0; i < strlen(si) && out < max-2; i++) {
for (i = 0; i < strlen(si) && out < max; i++) {
if (si[i] == '\n') {
p[out++] = '\r';
p[out++] = '\n';
@ -881,9 +899,11 @@ char *p;
return p;
}
/* Change carriage-return/linefeed to just linefeed */
/* Change carriage-return/linefeed to just linefeed
Plus, escape any special characters.
*/
char *
convert_returns(char *si, char *dst, int used)
convert_special_chars(char *si, char *dst, int used)
{
size_t i = 0, out = 0, max;
static char sout[TEXT_FIELD_SIZE+5];
@ -904,13 +924,16 @@ char *p;
for (i = 0; i < max; i++) {
if (si[i] == '\r' && i+1 < strlen(si) && si[i+1] == '\n')
continue;
else
p[out++] = si[i];
if (si[i] == '\'')
p[out++] = '\\';
p[out++] = si[i];
}
p[out] = '\0';
return p;
}
/* !!! Need to implement this function !!! */
int
convert_pgbinary_to_char(char *value, char *rgbValue, int cbValueMax)
{
@ -993,3 +1016,67 @@ int i, o=0;
return o;
}
/* 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, void *value, Int2 fCType, PTR rgbValue,
SDWORD cbValueMax, SDWORD *pcbValue, char multiple)
{
Oid oid;
int retval;
/* if this is the first call for this column,
open the large object for reading
*/
if ( ! multiple) {
oid = atoi(value);
stmt->lobj_fd = lo_open(stmt->hdbc, oid, INV_READ);
if (stmt->lobj_fd < 0) {
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Couldnt open large object for writing.";
return COPY_GENERAL_ERROR;
}
}
if (stmt->lobj_fd < 0)
return COPY_NO_DATA_FOUND;
retval = lo_read(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValueMax);
if (retval < 0) {
lo_close(stmt->hdbc, stmt->lobj_fd);
stmt->lobj_fd = -1;
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Error reading from large object.";
return COPY_GENERAL_ERROR;
}
else if (retval < cbValueMax) { /* success, all done */
lo_close(stmt->hdbc, stmt->lobj_fd);
stmt->lobj_fd = -1; /* prevent further reading */
if (pcbValue)
*pcbValue = retval;
return COPY_OK;
}
else { /* retval == cbVaueMax -- assume truncated */
if (pcbValue)
*pcbValue = SQL_NO_TOTAL;
return COPY_RESULT_TRUNCATED;
}
}

View File

@ -13,10 +13,12 @@
#include "psqlodbc.h"
/* copy_and_convert results */
#define COPY_OK 0
#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;
@ -27,21 +29,21 @@ typedef struct {
int ss;
} SIMPLE_TIME;
int copy_and_convert_field_bindinfo(Int4 field_type, void *value, BindInfoClass *bic);
int copy_and_convert_field(Int4 field_type, void *value,
Int2 fCType, PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue);
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, char multiple);
int copy_statement_with_parameters(StatementClass *stmt);
char *convert_escape(char *value);
char *convert_money(char *s);
int monthToNumber(char *mon);
char *convert_time(SIMPLE_TIME *st);
char parse_datetime(char *buf, SIMPLE_TIME *st);
char *convert_linefeeds(char *s, char *dst, size_t max);
char *convert_returns(char *si, char *dst, int used);
char *convert_special_chars(char *si, char *dst, int used);
int convert_pgbinary_to_char(char *value, char *rgbValue, int cbValueMax);
int convert_from_pgbinary(unsigned char *value, unsigned char *rgbValue, int cbValueMax);
int convert_to_pgbinary(unsigned char *in, char *out, int len);
int convert_lo(StatementClass *stmt, void *value, Int2 fCType, PTR rgbValue,
SDWORD cbValueMax, SDWORD *pcbValue, char multiple);
#endif

View File

@ -0,0 +1,762 @@
/* 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.
*
*/
#include "dlg_specific.h"
extern GLOBAL_VALUES globals;
void
SetDlgStuff(HWND hdlg, 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));
}
int CALLBACK driver_optionsProc(HWND hdlg,
WORD wMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (wMsg) {
case WM_INITDIALOG:
CheckDlgButton(hdlg, DRV_COMMLOG, globals.commlog);
CheckDlgButton(hdlg, DRV_OPTIMIZER, globals.disable_optimizer);
CheckDlgButton(hdlg, DRV_UNIQUEINDEX, globals.unique_index);
CheckDlgButton(hdlg, DRV_READONLY, globals.readonly);
CheckDlgButton(hdlg, DRV_USEDECLAREFETCH, globals.use_declarefetch);
/* Unknown (Default) Data Type sizes */
switch(globals.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, globals.text_as_longvarchar);
CheckDlgButton(hdlg, DRV_UNKNOWNS_LONGVARCHAR, globals.unknowns_as_longvarchar);
CheckDlgButton(hdlg, DRV_BOOLS_CHAR, globals.bools_as_char);
SetDlgItemInt(hdlg, DRV_CACHE_SIZE, globals.fetch_max, FALSE);
SetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, globals.max_varchar_size, FALSE);
SetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, globals.max_longvarchar_size, TRUE);
SetDlgItemText(hdlg, DRV_EXTRASYSTABLEPREFIXES, globals.extra_systable_prefixes);
/* Driver Connection Settings */
SetDlgItemText(hdlg, DRV_CONNSETTINGS, globals.conn_settings);
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDOK:
globals.commlog = IsDlgButtonChecked(hdlg, DRV_COMMLOG);
globals.disable_optimizer = IsDlgButtonChecked(hdlg, DRV_OPTIMIZER);
globals.unique_index = IsDlgButtonChecked(hdlg, DRV_UNIQUEINDEX);
globals.readonly = IsDlgButtonChecked(hdlg, DRV_READONLY);
globals.use_declarefetch = IsDlgButtonChecked(hdlg, DRV_USEDECLAREFETCH);
/* Unknown (Default) Data Type sizes */
if (IsDlgButtonChecked(hdlg, DRV_UNKNOWN_MAX))
globals.unknown_sizes = UNKNOWNS_AS_MAX;
else if (IsDlgButtonChecked(hdlg, DRV_UNKNOWN_DONTKNOW))
globals.unknown_sizes = UNKNOWNS_AS_DONTKNOW;
else if (IsDlgButtonChecked(hdlg, DRV_UNKNOWN_LONGEST))
globals.unknown_sizes = UNKNOWNS_AS_LONGEST;
else
globals.unknown_sizes = UNKNOWNS_AS_MAX;
globals.text_as_longvarchar = IsDlgButtonChecked(hdlg, DRV_TEXT_LONGVARCHAR);
globals.unknowns_as_longvarchar = IsDlgButtonChecked(hdlg, DRV_UNKNOWNS_LONGVARCHAR);
globals.bools_as_char = IsDlgButtonChecked(hdlg, DRV_BOOLS_CHAR);
globals.fetch_max = GetDlgItemInt(hdlg, DRV_CACHE_SIZE, NULL, FALSE);
globals.max_varchar_size = GetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, NULL, FALSE);
globals.max_longvarchar_size= GetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, NULL, TRUE); // allows for SQL_NO_TOTAL
GetDlgItemText(hdlg, DRV_EXTRASYSTABLEPREFIXES, globals.extra_systable_prefixes, sizeof(globals.extra_systable_prefixes));
/* Driver Connection Settings */
GetDlgItemText(hdlg, DRV_CONNSETTINGS, globals.conn_settings, sizeof(globals.conn_settings));
updateGlobals();
// fall through
case IDCANCEL:
EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
return TRUE;
case IDDEFAULTS:
CheckDlgButton(hdlg, DRV_COMMLOG, DEFAULT_COMMLOG);
CheckDlgButton(hdlg, DRV_OPTIMIZER, DEFAULT_OPTIMIZER);
CheckDlgButton(hdlg, DRV_UNIQUEINDEX, DEFAULT_UNIQUEINDEX);
CheckDlgButton(hdlg, DRV_READONLY, DEFAULT_READONLY);
CheckDlgButton(hdlg, DRV_USEDECLAREFETCH, DEFAULT_USEDECLAREFETCH);
/* Unknown Sizes */
CheckDlgButton(hdlg, DRV_UNKNOWN_DONTKNOW, 0);
CheckDlgButton(hdlg, DRV_UNKNOWN_LONGEST, 0);
CheckDlgButton(hdlg, DRV_UNKNOWN_MAX, 0);
switch(DEFAULT_UNKNOWNSIZES) {
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:
CheckDlgButton(hdlg, DRV_UNKNOWN_MAX, 1);
break;
}
CheckDlgButton(hdlg, DRV_TEXT_LONGVARCHAR, DEFAULT_TEXTASLONGVARCHAR);
CheckDlgButton(hdlg, DRV_UNKNOWNS_LONGVARCHAR, DEFAULT_UNKNOWNSASLONGVARCHAR);
CheckDlgButton(hdlg, DRV_BOOLS_CHAR, DEFAULT_BOOLSASCHAR);
SetDlgItemInt(hdlg, DRV_CACHE_SIZE, FETCH_MAX, FALSE);
SetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, MAX_VARCHAR_SIZE, FALSE);
SetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, TEXT_FIELD_SIZE, TRUE);
SetDlgItemText(hdlg, DRV_EXTRASYSTABLEPREFIXES, DEFAULT_EXTRASYSTABLEPREFIXES);
/* Driver Connection Settings */
SetDlgItemText(hdlg, DRV_CONNSETTINGS, "");
break;
}
}
return FALSE;
}
int CALLBACK ds_optionsProc(HWND hdlg,
WORD wMsg,
WPARAM wParam,
LPARAM lParam)
{
ConnInfo *ci;
char buf[128];
// int unknown_sizes;
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->readonly));
/* Protocol */
if (strncmp(ci->protocol, PG62, strlen(PG62)) == 0)
CheckDlgButton(hdlg, DS_PG62, 1);
else
CheckDlgButton(hdlg, DS_PG62, 0);
/* Unknown Data Type sizes -- currently only needed in Driver options.
switch (atoi(ci->unknown_sizes)) {
case UNKNOWNS_AS_DONTKNOW:
CheckDlgButton(hdlg, DS_UNKNOWN_DONTKNOW, 1);
break;
case UNKNOWNS_AS_LONGEST:
CheckDlgButton(hdlg, DS_UNKNOWN_LONGEST, 1);
break;
case UNKNOWNS_AS_MAX:
default:
CheckDlgButton(hdlg, DS_UNKNOWN_MAX, 1);
break;
}
*/
CheckDlgButton(hdlg, DS_SHOWOIDCOLUMN, atoi(ci->show_oid_column));
CheckDlgButton(hdlg, DS_FAKEOIDINDEX, atoi(ci->fake_oid_index));
CheckDlgButton(hdlg, DS_SHOWSYSTEMTABLES, atoi(ci->show_system_tables));
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->readonly, "%d", IsDlgButtonChecked(hdlg, DS_READONLY));
/* Protocol */
if ( IsDlgButtonChecked(hdlg, DS_PG62))
strcpy(ci->protocol, PG62);
else
ci->protocol[0] = '\0';
/* Unknown Data Type sizes -- currently only needed in Driver options.
if (IsDlgButtonChecked(hdlg, DS_UNKNOWN_MAX))
unknown_sizes = UNKNOWNS_AS_MAX;
else if (IsDlgButtonChecked(hdlg, DS_UNKNOWN_DONTKNOW))
unknown_sizes = UNKNOWNS_AS_DONTKNOW;
else if (IsDlgButtonChecked(hdlg, DS_UNKNOWN_LONGEST))
unknown_sizes = UNKNOWNS_AS_LONGEST;
else
unknown_sizes = UNKNOWNS_AS_MAX;
sprintf(ci->unknown_sizes, "%d", unknown_sizes);
*/
sprintf(ci->show_system_tables, "%d", IsDlgButtonChecked(hdlg, DS_SHOWSYSTEMTABLES));
/* 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;
}
void
makeConnectString(char *connect_string, ConnInfo *ci)
{
char got_dsn = (ci->dsn[0] != '\0');
sprintf(connect_string, "%s=%s;DATABASE=%s;SERVER=%s;PORT=%s;UID=%s;READONLY=%s;PWD=%s;PROTOCOL=%s;FAKEOIDINDEX=%s;SHOWOIDCOLUMN=%s;SHOWSYSTEMTABLES=%s;CONNSETTINGS=%s",
got_dsn ? "DSN" : "DRIVER",
got_dsn ? ci->dsn : ci->driver,
ci->database,
ci->server,
ci->port,
ci->username,
ci->readonly,
ci->password,
ci->protocol,
// ci->unknown_sizes, -- currently only needed in Driver options.
ci->fake_oid_index,
ci->show_oid_column,
ci->show_system_tables,
ci->conn_settings);
}
void
copyAttributes(ConnInfo *ci, char *attribute, 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)
strcpy(ci->readonly, value);
else if (stricmp(attribute, INI_PROTOCOL) == 0)
strcpy(ci->protocol, value);
/*
else if (stricmp(attribute, INI_UNKNOWNSIZES) == 0)
strcpy(ci->unknown_sizes, value);
*/
else if (stricmp(attribute, INI_SHOWOIDCOLUMN) == 0)
strcpy(ci->show_oid_column, value);
else if (stricmp(attribute, INI_FAKEOIDINDEX) == 0)
strcpy(ci->fake_oid_index, value);
else if (stricmp(attribute, INI_SHOWSYSTEMTABLES) == 0)
strcpy(ci->show_system_tables, value);
else if (stricmp(attribute, INI_CONNSETTINGS) == 0)
strcpy(ci->conn_settings, value);
mylog("copyAttributes: DSN='%s',server='%s',dbase='%s',user='%s',passwd='%s',port='%s',readonly='%s',protocol='%s', conn_settings='%s')\n",
ci->dsn,
ci->server,
ci->database,
ci->username,
ci->password,
ci->port,
ci->readonly,
ci->protocol,
// ci->unknown_sizes,
ci->conn_settings);
}
void
getDSNdefaults(ConnInfo *ci)
{
if (ci->port[0] == '\0')
strcpy(ci->port, DEFAULT_PORT);
if (ci->readonly[0] == '\0')
sprintf(ci->readonly, "%d", globals.readonly);
/* -- currently only needed in Driver options.
if (ci->unknown_sizes[0] == '\0')
sprintf(ci->unknown_sizes, "%d", globals.unknown_sizes);
*/
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);
}
void
getDSNinfo(ConnInfo *ci, char overwrite)
{
char *DSN = ci->dsn;
// 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.
if ( DSN[0] == '\0') {
if ( ci->driver[0] != '\0')
return;
else
strcpy(DSN, INI_DSN);
}
// 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->readonly[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_READONLY, "", ci->readonly, sizeof(ci->readonly), ODBC_INI);
/* -- currently only needed in Driver options.
if ( ci->unknown_sizes[0] == '\0' || overwrite)
SQLGetPrivateProfileString(DSN, INI_UNKNOWNSIZES, "", ci->unknown_sizes, sizeof(ci->unknown_sizes), 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->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, "", ci->conn_settings, sizeof(ci->conn_settings), ODBC_INI);
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(" readonly='%s',protocol='%s',showoid='%s',fakeoidindex='%s',showsystable='%s'\n",
ci->readonly,
ci->protocol,
ci->show_oid_column,
ci->fake_oid_index,
// ci->unknown_sizes,
ci->show_system_tables);
qlog(" conn_settings='%s'\n",
ci->conn_settings);
}
/* This is for datasource based options only */
void
writeDSNinfo(ConnInfo *ci)
{
char *DSN = ci->dsn;
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->readonly,
ODBC_INI);
/* -- currently only needed in Driver options.
SQLWritePrivateProfileString(DSN,
INI_UNKNOWNSIZES,
ci->unknown_sizes,
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_SHOWSYSTEMTABLES,
ci->show_system_tables,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_PROTOCOL,
ci->protocol,
ODBC_INI);
SQLWritePrivateProfileString(DSN,
INI_CONNSETTINGS,
ci->conn_settings,
ODBC_INI);
}
/* This function reads the ODBCINST.INI portion of
the registry and gets any driver defaults.
*/
void getGlobalDefaults(void)
{
char temp[128];
// Fetch Count is stored in driver section
SQLGetPrivateProfileString(DBMS_NAME, INI_FETCH, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] ) {
globals.fetch_max = atoi(temp);
/* sanity check if using cursors */
if (globals.fetch_max <= 0)
globals.fetch_max = FETCH_MAX;
}
else
globals.fetch_max = FETCH_MAX;
// Socket Buffersize is stored in driver section
SQLGetPrivateProfileString(DBMS_NAME, INI_SOCKET, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] )
globals.socket_buffersize = atoi(temp);
else
globals.socket_buffersize = SOCK_BUFFER_SIZE;
// Debug is stored in the driver section
SQLGetPrivateProfileString(DBMS_NAME, INI_DEBUG, "0",
temp, sizeof(temp), ODBCINST_INI);
globals.debug = atoi(temp);
// CommLog is stored in the driver section
SQLGetPrivateProfileString(DBMS_NAME, INI_COMMLOG, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] == '\0')
globals.commlog = DEFAULT_COMMLOG;
else
globals.commlog = atoi(temp);
// Optimizer is stored in the driver section only
SQLGetPrivateProfileString(DBMS_NAME, INI_OPTIMIZER, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] == '\0')
globals.disable_optimizer = DEFAULT_OPTIMIZER;
else
globals.disable_optimizer = atoi(temp);
// Recognize Unique Index is stored in the driver section only
SQLGetPrivateProfileString(DBMS_NAME, INI_UNIQUEINDEX, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] == '\0')
globals.unique_index = DEFAULT_UNIQUEINDEX;
else
globals.unique_index = atoi(temp);
// Unknown Sizes is stored in the driver section AND per datasource
SQLGetPrivateProfileString(DBMS_NAME, INI_UNKNOWNSIZES, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] == '\0')
globals.unknown_sizes = DEFAULT_UNKNOWNSIZES;
else
globals.unknown_sizes = atoi(temp);
// Readonly is stored in the driver section AND per datasource
SQLGetPrivateProfileString(DBMS_NAME, INI_READONLY, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] == '\0')
globals.readonly = DEFAULT_READONLY;
else
globals.readonly = atoi(temp);
// UseDeclareFetch is stored in the driver section only
SQLGetPrivateProfileString(DBMS_NAME, INI_USEDECLAREFETCH, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] == '\0')
globals.use_declarefetch = DEFAULT_USEDECLAREFETCH;
else
globals.use_declarefetch = atoi(temp);
// Max Varchar Size
SQLGetPrivateProfileString(DBMS_NAME, INI_MAXVARCHARSIZE, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] == '\0')
globals.max_varchar_size = MAX_VARCHAR_SIZE;
else
globals.max_varchar_size = atoi(temp);
// Max TextField Size
SQLGetPrivateProfileString(DBMS_NAME, INI_MAXLONGVARCHARSIZE, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] == '\0')
globals.max_longvarchar_size = TEXT_FIELD_SIZE;
else
globals.max_longvarchar_size = atoi(temp);
// Text As LongVarchar
SQLGetPrivateProfileString(DBMS_NAME, INI_TEXTASLONGVARCHAR, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] == '\0')
globals.text_as_longvarchar = DEFAULT_TEXTASLONGVARCHAR;
else
globals.text_as_longvarchar = atoi(temp);
// Unknowns As LongVarchar
SQLGetPrivateProfileString(DBMS_NAME, INI_UNKNOWNSASLONGVARCHAR, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] == '\0')
globals.unknowns_as_longvarchar = DEFAULT_UNKNOWNSASLONGVARCHAR;
else
globals.unknowns_as_longvarchar = atoi(temp);
// Bools As Char
SQLGetPrivateProfileString(DBMS_NAME, INI_BOOLSASCHAR, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] == '\0')
globals.bools_as_char = DEFAULT_BOOLSASCHAR;
else
globals.bools_as_char = atoi(temp);
// Extra System Table prefixes
SQLGetPrivateProfileString(DBMS_NAME, INI_EXTRASYSTABLEPREFIXES, "@@@",
globals.extra_systable_prefixes, sizeof(globals.extra_systable_prefixes), ODBCINST_INI);
if ( ! strcmp(globals.extra_systable_prefixes, "@@@")) {
strcpy(globals.extra_systable_prefixes, DEFAULT_EXTRASYSTABLEPREFIXES);
}
mylog("globals.extra_systable_prefixes = '%s'\n", globals.extra_systable_prefixes);
// ConnSettings is stored in the driver section and per datasource for override
SQLGetPrivateProfileString(DBMS_NAME, INI_CONNSETTINGS, "",
globals.conn_settings, sizeof(globals.conn_settings), ODBCINST_INI);
}
/* This function writes any global parameters (that can be manipulated)
to the ODBCINST.INI portion of the registry
*/
void updateGlobals(void)
{
char tmp[128];
sprintf(tmp, "%d", globals.fetch_max);
SQLWritePrivateProfileString(DBMS_NAME,
INI_FETCH, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.commlog);
SQLWritePrivateProfileString(DBMS_NAME,
INI_COMMLOG, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.disable_optimizer);
SQLWritePrivateProfileString(DBMS_NAME,
INI_OPTIMIZER, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.unique_index);
SQLWritePrivateProfileString(DBMS_NAME,
INI_UNIQUEINDEX, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.readonly);
SQLWritePrivateProfileString(DBMS_NAME,
INI_READONLY, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.use_declarefetch);
SQLWritePrivateProfileString(DBMS_NAME,
INI_USEDECLAREFETCH, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.unknown_sizes);
SQLWritePrivateProfileString(DBMS_NAME,
INI_UNKNOWNSIZES, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.text_as_longvarchar);
SQLWritePrivateProfileString(DBMS_NAME,
INI_TEXTASLONGVARCHAR, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.unknowns_as_longvarchar);
SQLWritePrivateProfileString(DBMS_NAME,
INI_UNKNOWNSASLONGVARCHAR, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.bools_as_char);
SQLWritePrivateProfileString(DBMS_NAME,
INI_BOOLSASCHAR, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.max_varchar_size);
SQLWritePrivateProfileString(DBMS_NAME,
INI_MAXVARCHARSIZE, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.max_longvarchar_size);
SQLWritePrivateProfileString(DBMS_NAME,
INI_MAXLONGVARCHARSIZE, tmp, ODBCINST_INI);
SQLWritePrivateProfileString(DBMS_NAME,
INI_EXTRASYSTABLEPREFIXES, globals.extra_systable_prefixes, ODBCINST_INI);
SQLWritePrivateProfileString(DBMS_NAME,
INI_CONNSETTINGS, globals.conn_settings, ODBCINST_INI);
}

View File

@ -0,0 +1,103 @@
/* 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"
#include <windows.h>
#include <windowsx.h>
#include <odbcinst.h>
#include "resource.h"
/* Unknown data type sizes */
#define UNKNOWNS_AS_MAX 0
#define UNKNOWNS_AS_DONTKNOW 1
#define UNKNOWNS_AS_LONGEST 2
/* INI File Stuff */
#define ODBC_INI "ODBC.INI" /* ODBC initialization file */
#define ODBCINST_INI "ODBCINST.INI" /* ODBC Installation file */
#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_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_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_SHOWSYSTEMTABLES "ShowSystemTables"
#define INI_EXTRASYSTABLEPREFIXES "ExtraSysTablePrefixes"
/* Connection Defaults */
#define DEFAULT_PORT "5432"
#define DEFAULT_READONLY 1
#define DEFAULT_USEDECLAREFETCH 1
#define DEFAULT_TEXTASLONGVARCHAR 1
#define DEFAULT_UNKNOWNSASLONGVARCHAR 0
#define DEFAULT_BOOLSASCHAR 1
#define DEFAULT_OPTIMIZER 1 // disable
#define DEFAULT_UNIQUEINDEX 0 // dont recognize
#define DEFAULT_COMMLOG 0 // dont log
#define DEFAULT_UNKNOWNSIZES UNKNOWNS_AS_MAX
#define DEFAULT_FAKEOIDINDEX 0
#define DEFAULT_SHOWOIDCOLUMN 0
#define DEFAULT_SHOWSYSTEMTABLES 0 // dont show system tables
#define DEFAULT_EXTRASYSTABLEPREFIXES "dd_;"
/* prototypes */
void updateGlobals(void);
void getGlobalDefaults(void);
void SetDlgStuff(HWND hdlg, 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);
void makeConnectString(char *connect_string, ConnInfo *ci);
void copyAttributes(ConnInfo *ci, char *attribute, char *value);
void getDSNdefaults(ConnInfo *ci);
void getDSNinfo(ConnInfo *ci, char overwrite);
void writeDSNinfo(ConnInfo *ci);
#endif

View File

@ -26,15 +26,17 @@
#include <odbcinst.h>
#include "resource.h"
#include "dlg_specific.h"
/* prototypes */
BOOL FAR PASCAL dconn_FDriverConnectProc(HWND hdlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
RETCODE dconn_DoDialog(HWND hwnd, ConnInfo *ci);
void dconn_get_connect_attributes(UCHAR FAR *connect_string, ConnInfo *ci);
extern HINSTANCE NEAR s_hModule; /* Saved module handle. */
extern GLOBAL_VALUES globals;
RETCODE SQL_API SQLDriverConnect(
HDBC hdbc,
HWND hwnd,
@ -67,10 +69,10 @@ char password_required = FALSE;
// 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!)
CC_DSN_info(conn, CONN_DONT_OVERWRITE);
getDSNinfo(ci, CONN_DONT_OVERWRITE);
// Fill in any default parameters if they are not there.
CC_set_defaults(conn);
getDSNdefaults(ci);
dialog:
ci->focus_password = password_required;
@ -119,19 +121,7 @@ dialog:
// return the completed string to the caller.
char got_dsn = (ci->dsn[0] != '\0');
sprintf(connect_string, "%s=%s;DATABASE=%s;SERVER=%s;PORT=%s;UID=%s;READONLY=%s;PWD=%s;PROTOCOL=%s;CONNSETTINGS=%s",
got_dsn ? "DSN" : "DRIVER",
got_dsn ? ci->dsn : ci->driver,
ci->database,
ci->server,
ci->port,
ci->username,
ci->readonly,
ci->password,
ci->protocol,
ci->conn_settings);
makeConnectString(connect_string, ci);
if(pcbConnStrOut) {
*pcbConnStrOut = strlen(connect_string);
@ -169,7 +159,7 @@ int dialog_result;
mylog("dconn_DoDialog: ci = %u\n", ci);
if(hwnd) {
dialog_result = DialogBoxParam(s_hModule, MAKEINTRESOURCE(DRIVERCONNDIALOG),
dialog_result = DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_CONFIG),
hwnd, dconn_FDriverConnectProc, (LPARAM) ci);
if(!dialog_result || (dialog_result == -1)) {
return SQL_NO_DATA_FOUND;
@ -188,34 +178,37 @@ BOOL FAR PASCAL dconn_FDriverConnectProc(
WPARAM wParam,
LPARAM lParam)
{
static ConnInfo *ci;
ConnInfo *ci;
switch (wMsg) {
case WM_INITDIALOG:
ci = (ConnInfo *) lParam; // Save the ConnInfo for the "OK"
ci = (ConnInfo *) lParam;
SetDlgItemText(hdlg, SERVER_EDIT, ci->server);
SetDlgItemText(hdlg, DATABASE_EDIT, ci->database);
SetDlgItemText(hdlg, USERNAME_EDIT, ci->username);
SetDlgItemText(hdlg, PASSWORD_EDIT, ci->password);
SetDlgItemText(hdlg, PORT_EDIT, ci->port);
CheckDlgButton(hdlg, READONLY_EDIT, atoi(ci->readonly));
/* Change the caption for the setup dialog */
SetWindowText(hdlg, "PostgreSQL Connection");
CheckDlgButton(hdlg, PG62_EDIT, PROTOCOL_62(ci));
SetWindowText(GetDlgItem(hdlg, IDC_DATASOURCE), "Connection");
/* The driver connect dialog box allows manipulating this global variable */
CheckDlgButton(hdlg, COMMLOG_EDIT, globals.commlog);
/* 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, SERVER_EDIT));
SetFocus(GetDlgItem(hdlg, IDC_SERVER));
else if (ci->port[0] == '\0')
SetFocus(GetDlgItem(hdlg, PORT_EDIT));
SetFocus(GetDlgItem(hdlg, IDC_PORT));
else if (ci->username[0] == '\0')
SetFocus(GetDlgItem(hdlg, USERNAME_EDIT));
SetFocus(GetDlgItem(hdlg, IDC_USER));
else if (ci->focus_password)
SetFocus(GetDlgItem(hdlg, PASSWORD_EDIT));
SetFocus(GetDlgItem(hdlg, IDC_PASSWORD));
break;
@ -223,26 +216,30 @@ static ConnInfo *ci;
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDOK:
GetDlgItemText(hdlg, SERVER_EDIT, ci->server, sizeof(ci->server));
GetDlgItemText(hdlg, DATABASE_EDIT, ci->database, sizeof(ci->database));
GetDlgItemText(hdlg, USERNAME_EDIT, ci->username, sizeof(ci->username));
GetDlgItemText(hdlg, PASSWORD_EDIT, ci->password, sizeof(ci->password));
GetDlgItemText(hdlg, PORT_EDIT, ci->port, sizeof(ci->port));
ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
sprintf(ci->readonly, "%d", IsDlgButtonChecked(hdlg, READONLY_EDIT));
GetDlgStuff(hdlg, ci);
if (IsDlgButtonChecked(hdlg, PG62_EDIT))
strcpy(ci->protocol, PG62);
else
ci->protocol[0] = '\0';
/* The driver connect dialog box allows manipulating this global variable */
globals.commlog = IsDlgButtonChecked(hdlg, COMMLOG_EDIT);
updateGlobals();
case IDCANCEL:
EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
return TRUE;
case IDC_DRIVER:
DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DRV),
hdlg, driver_optionsProc, (LPARAM) NULL);
break;
case IDC_DATASOURCE:
ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DS),
hdlg, ds_optionsProc, (LPARAM) ci);
break;
}
}
@ -286,42 +283,12 @@ char *strtok_arg;
if( !attribute || !value)
continue;
/*********************************************************/
/* PARSE ATTRIBUTES */
/*********************************************************/
if(stricmp(attribute, "DSN") == 0)
strcpy(ci->dsn, value);
else if(stricmp(attribute, "driver") == 0)
strcpy(ci->driver, value);
else if(stricmp(attribute, "uid") == 0)
strcpy(ci->username, value);
else if(stricmp(attribute, "pwd") == 0)
strcpy(ci->password, value);
else if ((stricmp(attribute, "server") == 0) ||
(stricmp(attribute, "servername") == 0))
strcpy(ci->server, value);
else if(stricmp(attribute, "port") == 0)
strcpy(ci->port, value);
else if(stricmp(attribute, "database") == 0)
strcpy(ci->database, value);
else if (stricmp(attribute, "readonly") == 0)
strcpy(ci->readonly, value);
else if (stricmp(attribute, "protocol") == 0)
strcpy(ci->protocol, value);
else if (stricmp(attribute, "connsettings") == 0)
strcpy(ci->conn_settings, value);
// Copy the appropriate value to the conninfo
copyAttributes(ci, attribute, value);
}
free(our_connect_string);
}

View File

@ -113,6 +113,10 @@ int status;
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");
@ -158,6 +162,12 @@ int status;
break;
case STMT_OPTION_VALUE_CHANGED:
strcpy(szSqlState, "01S02");
break;
case STMT_INVALID_CURSOR_NAME:
strcpy(szSqlState, "34000");
break;
case STMT_NO_CURSOR_NAME:
strcpy(szSqlState, "S1015");
break;
default:
strcpy(szSqlState, "S1000");

View File

@ -13,6 +13,7 @@
#include "psqlodbc.h"
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#define ENV_ALLOC_ERROR 1

View File

@ -24,6 +24,7 @@
#include "qresult.h"
#include "convert.h"
#include "bind.h"
#include "lobj.h"
// Perform a Prepare on the SQL statement
@ -36,72 +37,29 @@ StatementClass *self = (StatementClass *) hstmt;
if ( ! self)
return SQL_INVALID_HANDLE;
/* CC: 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 */
/* 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) {
switch(self->status) {
case STMT_PREMATURE:
mylog("**** SQLPrepare: STMT_PREMATURE, recycle\n");
SC_recycle_statement(self); /* recycle the statement, but do not remove parameter bindings */
/* NO Break! -- Contiue the same way as with a newly allocated statement ! */
case STMT_ALLOCATED:
// it is not really necessary to do any conversion of the statement
// here--just copy it, and deal with it when it's ready to be
// executed.
mylog("**** SQLPrepare: STMT_ALLOCATED, copy\n");
self->statement = make_string(szSqlStr, cbSqlStr, NULL);
if ( ! self->statement) {
self->errornumber = STMT_NO_MEMORY_ERROR;
self->errormsg = "No memory available to store statement";
return SQL_ERROR;
}
self->statement_type = statement_type(self->statement);
// Check if connection is readonly (only selects are allowed)
if ( CC_is_readonly(self->hdbc) && self->statement_type != STMT_TYPE_SELECT ) {
self->errornumber = STMT_EXEC_ERROR;
self->errormsg = "Connection is readonly, only select statements are allowed.";
return SQL_ERROR;
}
self->prepare = TRUE;
self->status = STMT_READY;
return SQL_SUCCESS;
case STMT_READY: /* SQLPrepare has already been called -- Just changed the SQL statement that is assigned to the handle */
mylog("**** SQLPrepare: STMT_READY, change SQL\n");
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";
return SQL_ERROR;
}
self->prepare = TRUE;
self->statement_type = statement_type(self->statement);
// Check if connection is readonly (only selects are allowed)
if ( CC_is_readonly(self->hdbc) && self->statement_type != STMT_TYPE_SELECT ) {
self->errornumber = STMT_EXEC_ERROR;
self->errormsg = "Connection is readonly, only select statements are allowed.";
return SQL_ERROR;
}
return SQL_SUCCESS;
break;
case STMT_FINISHED:
mylog("**** SQLPrepare: STMT_FINISHED\n");
/* No BREAK: continue as with STMT_EXECUTING */
mylog("**** SQLPrepare: STMT_FINISHED, recycle\n");
SC_recycle_statement(self); /* recycle the statement, but do not remove parameter bindings */
break;
case STMT_ALLOCATED:
mylog("**** SQLPrepare: STMT_ALLOCATED, copy\n");
self->status = STMT_READY;
break;
case STMT_READY:
mylog("**** SQLPrepare: STMT_READY, change SQL\n");
break;
case STMT_EXECUTING:
mylog("**** SQLPrepare: STMT_EXECUTING, error!\n");
@ -116,6 +74,30 @@ StatementClass *self = (StatementClass *) hstmt;
self->errormsg = "An Internal Error has occured -- Unknown statement status.";
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";
return SQL_ERROR;
}
self->prepare = TRUE;
self->statement_type = statement_type(self->statement);
// Check if connection is readonly (only selects are allowed)
if ( CC_is_readonly(self->hdbc) && STMT_UPDATE(self)) {
self->errornumber = STMT_EXEC_ERROR;
self->errormsg = "Connection is readonly, only select statements are allowed.";
return SQL_ERROR;
}
return SQL_SUCCESS;
}
// - - - - - - - - -
@ -150,7 +132,7 @@ StatementClass *stmt = (StatementClass *) hstmt;
stmt->statement_type = statement_type(stmt->statement);
// Check if connection is readonly (only selects are allowed)
if ( CC_is_readonly(stmt->hdbc) && stmt->statement_type != STMT_TYPE_SELECT ) {
if ( CC_is_readonly(stmt->hdbc) && STMT_UPDATE(stmt)) {
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Connection is readonly, only select statements are allowed.";
return SQL_ERROR;
@ -378,6 +360,9 @@ int i, retval;
if ( ! stmt)
return SQL_INVALID_HANDLE;
mylog("SQLParamData, enter: data_at_exec=%d, params_alloc=%d\n",
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";
@ -390,22 +375,37 @@ int i, retval;
return SQL_ERROR;
}
/* close the large object */
if ( stmt->lobj_fd >= 0) {
lo_close(stmt->hdbc, stmt->lobj_fd);
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 = 0; i < stmt->parameters_allocated; i++) {
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;
}
}
@ -423,9 +423,9 @@ RETCODE SQL_API SQLPutData(
SDWORD cbValue)
{
StatementClass *stmt = (StatementClass *) hstmt;
int old_pos, retval;
ParameterInfoClass *current_param;
char *buffer;
SDWORD *used;
int old_pos;
if ( ! stmt)
@ -438,94 +438,137 @@ int old_pos;
return SQL_ERROR;
}
current_param = &(stmt->parameters[stmt->current_exec_param]);
if ( ! stmt->put_data) { /* first call */
mylog("SQLPutData: (1) cbValue = %d\n", cbValue);
stmt->put_data = TRUE;
used = (SDWORD *) malloc(sizeof(SDWORD));
if ( ! used) {
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 SQLPutData (1)";
return SQL_ERROR;
}
*used = cbValue;
stmt->parameters[stmt->current_exec_param].EXEC_used = used;
*current_param->EXEC_used = cbValue;
if (cbValue == SQL_NULL_DATA)
return SQL_SUCCESS;
if (cbValue == SQL_NTS) {
buffer = strdup(rgbValue);
if ( ! buffer) {
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Out of memory in SQLPutData (2)";
return SQL_ERROR;
}
}
else {
buffer = malloc(cbValue + 1);
if ( ! buffer) {
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Out of memory in SQLPutData (2)";
return SQL_ERROR;
}
memcpy(buffer, rgbValue, cbValue);
buffer[cbValue] = '\0';
}
stmt->parameters[stmt->current_exec_param].EXEC_buffer = buffer;
/* Handle Long Var Binary with Large Objects */
if ( current_param->SQLType == SQL_LONGVARBINARY) {
/* 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.";
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.";
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 text fields and small binaries */
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 SQLPutData (2)";
return SQL_ERROR;
}
}
else {
current_param->EXEC_buffer = malloc(cbValue + 1);
if ( ! current_param->EXEC_buffer) {
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Out of memory in SQLPutData (2)";
return SQL_ERROR;
}
memcpy(current_param->EXEC_buffer, rgbValue, cbValue);
current_param->EXEC_buffer[cbValue] = '\0';
}
}
}
else { /* calling SQLPutData more than once */
mylog("SQLPutData: (>1) cbValue = %d\n", cbValue);
used = stmt->parameters[stmt->current_exec_param].EXEC_used;
buffer = stmt->parameters[stmt->current_exec_param].EXEC_buffer;
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 SQLPutData (3)";
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;
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 SQLPutData (3)";
return SQL_ERROR;
}
strcat(buffer, rgbValue);
else if (cbValue > 0) {
mylog(" cbValue = SQL_NTS: strlen(buffer) = %d\n", strlen(buffer));
old_pos = *current_param->EXEC_used;
*used = cbValue;
*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 SQLPutData (3)";
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
return SQL_ERROR;
}
else if (cbValue > 0) {
old_pos = *used;
*used += cbValue;
mylog(" cbValue = %d, old_pos = %d, *used = %d\n", cbValue, old_pos, *used);
buffer = realloc(buffer, *used + 1);
if ( ! buffer) {
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Out of memory in SQLPutData (3)";
return SQL_ERROR;
}
memcpy(&buffer[old_pos], rgbValue, cbValue);
buffer[*used] = '\0';
}
else
return SQL_ERROR;
/* reassign buffer incase realloc moved it */
stmt->parameters[stmt->current_exec_param].EXEC_buffer = buffer;
}

File diff suppressed because it is too large Load Diff

View File

@ -13,16 +13,15 @@
*/
#include <stdio.h>
#include <windows.h>
#include <sql.h>
#include <varargs.h>
#include "psqlodbc.h"
extern GLOBAL_VALUES globals;
#ifdef MY_LOG
#include <varargs.h>
void
mylog(va_alist)
@ -52,7 +51,6 @@ static FILE *LOGFP = 0;
#ifdef Q_LOG
#include <varargs.h>
void qlog(va_alist)
va_dcl
@ -78,10 +76,17 @@ static FILE *LOGFP = 0;
}
#endif
/* Undefine these because windows.h will redefine and cause a warning */
#undef va_start
#undef va_end
#include <windows.h>
#include <sql.h>
/* returns STRCPY_FAIL, STRCPY_TRUNCATED, or #bytes copied (not including null term) */
int
my_strcpy(char *dst, size_t dst_len, char *src, size_t src_len)
my_strcpy(char *dst, int dst_len, char *src, int src_len)
{
if (dst_len <= 0)
return STRCPY_FAIL;
@ -90,18 +95,10 @@ my_strcpy(char *dst, size_t dst_len, char *src, size_t src_len)
dst[0] = '\0';
return STRCPY_NULL;
}
else if (src_len == SQL_NTS)
src_len = strlen(src);
else if (src_len == SQL_NTS) {
if (src_len < dst_len)
strcpy(dst, src);
else {
memcpy(dst, src, dst_len-1);
dst[dst_len-1] = '\0'; /* truncated */
return STRCPY_TRUNCATED;
}
}
else if (src_len <= 0)
if (src_len <= 0)
return STRCPY_FAIL;
else {
@ -123,9 +120,9 @@ my_strcpy(char *dst, size_t dst_len, char *src, size_t src_len)
// 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, size_t len)
char *strncpy_null(char *dst, const char *src, int len)
{
unsigned int i;
int i;
if (NULL != dst) {

View File

@ -42,7 +42,7 @@ void qlog(); /* prototype */
#endif
void remove_newlines(char *string);
char *strncpy_null(char *dst, const char *src, size_t len);
char *strncpy_null(char *dst, const char *src, int len);
char *trim(char *string);
char *make_string(char *s, int len, char *buf);
char *my_strcat(char *buf, char *fmt, char *s, int len);
@ -53,6 +53,6 @@ char *my_strcat(char *buf, char *fmt, char *s, int len);
#define STRCPY_TRUNCATED -1
#define STRCPY_NULL -2
int my_strcpy(char *dst, size_t dst_len, char *src, size_t src_len);
int my_strcpy(char *dst, int dst_len, char *src, int src_len);
#endif

View File

@ -16,10 +16,15 @@
#include "psqlodbc.h"
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include "environ.h"
#include "connection.h"
#include "statement.h"
extern GLOBAL_VALUES globals;
/* Implements only SQL_AUTOCOMMIT */
RETCODE SQL_API SQLSetConnectOption(
HDBC hdbc,
@ -81,58 +86,6 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
// - - - - - - - - -
RETCODE SQL_API SQLSetStmtOption(
HSTMT hstmt,
UWORD fOption,
UDWORD vParam)
{
StatementClass *stmt = (StatementClass *) hstmt;
// 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) {
return SQL_INVALID_HANDLE;
}
switch(fOption) {
case SQL_QUERY_TIMEOUT:
mylog("SetStmtOption: vParam = %d\n", vParam);
/*
stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
stmt->errormsg = "Query Timeout: value changed to 0";
return SQL_SUCCESS_WITH_INFO;
*/
return SQL_SUCCESS;
break;
case SQL_MAX_LENGTH:
/* CC: Some apps consider returning SQL_SUCCESS_WITH_INFO to be an error */
/* so if we're going to return SQL_SUCCESS, we better not set an */
/* error message. (otherwise, if a subsequent function call returns */
/* SQL_ERROR without setting a message, things can get confused.) */
/*
stmt->errormsg = "Requested value changed.";
stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
*/
return SQL_SUCCESS;
break;
case SQL_MAX_ROWS:
mylog("SetStmtOption(): SQL_MAX_ROWS = %d, returning success\n", vParam);
stmt->maxRows = vParam;
return SQL_SUCCESS;
break;
default:
return SQL_ERROR;
}
return SQL_SUCCESS;
}
// - - - - - - - - -
/* This function just can tell you whether you are in Autcommit mode or not */
RETCODE SQL_API SQLGetConnectOption(
HDBC hdbc,
@ -141,32 +94,127 @@ RETCODE SQL_API SQLGetConnectOption(
{
ConnectionClass *conn = (ConnectionClass *) hdbc;
if (! conn)
return SQL_INVALID_HANDLE;
if (! conn)
return SQL_INVALID_HANDLE;
switch (fOption) {
case SQL_AUTOCOMMIT:
*((UDWORD *)pvParam) = (UDWORD)( CC_is_in_autocommit(conn) ?
SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
break;
/* don't use qualifiers */
case SQL_CURRENT_QUALIFIER:
if(pvParam)
strcpy(pvParam, "");
break;
default:
conn->errormsg = "This option is currently unsupported by the driver";
conn->errornumber = CONN_UNSUPPORTED_OPTION;
return SQL_ERROR;
break;
}
return SQL_SUCCESS;
}
// - - - - - - - - -
RETCODE SQL_API SQLSetStmtOption(
HSTMT hstmt,
UWORD fOption,
UDWORD vParam)
{
StatementClass *stmt = (StatementClass *) hstmt;
char changed = FALSE;
// 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)
return SQL_INVALID_HANDLE;
switch(fOption) {
case SQL_QUERY_TIMEOUT:
mylog("SetStmtOption: vParam = %d\n", vParam);
// "0" returned in SQLGetStmtOption
break;
case SQL_MAX_LENGTH:
// "4096" returned in SQLGetStmtOption
break;
case SQL_MAX_ROWS:
mylog("SetStmtOption(): SQL_MAX_ROWS = %d, returning success\n", vParam);
stmt->maxRows = vParam;
return SQL_SUCCESS;
break;
case SQL_ROWSET_SIZE:
mylog("SetStmtOption(): SQL_ROWSET_SIZE = %d\n", vParam);
stmt->rowset_size = 1; // only support 1 row at a time
if (vParam != 1)
changed = TRUE;
break;
case SQL_CONCURRENCY:
// positioned update isn't supported so cursor concurrency is read-only
mylog("SetStmtOption(): SQL_CONCURRENCY = %d\n", vParam);
stmt->scroll_concurrency = SQL_CONCUR_READ_ONLY;
if (vParam != SQL_CONCUR_READ_ONLY)
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 (globals.use_declarefetch) {
stmt->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)
stmt->cursor_type = vParam; // valid type
else {
stmt->cursor_type = SQL_CURSOR_STATIC;
changed = TRUE;
}
}
break;
case SQL_SIMULATE_CURSOR:
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Simulated positioned update/delete not supported";
return SQL_ERROR;
switch (fOption) {
case SQL_AUTOCOMMIT:
/* CC 28.05.96: Do not set fOption, but pvParam */
*((UDWORD *)pvParam) = (UDWORD)( CC_is_in_autocommit(conn) ?
SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
break;
/* we don't use qualifiers */
case SQL_CURRENT_QUALIFIER:
if(pvParam) {
strcpy(pvParam, "");
}
break;
default:
conn->errormsg = "This option is currently unsupported by the driver";
conn->errornumber = CONN_UNSUPPORTED_OPTION;
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Driver does not support this statement option";
return SQL_ERROR;
break;
}
return SQL_SUCCESS;
if (changed) {
stmt->errormsg = "Requested value changed.";
stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
return SQL_SUCCESS_WITH_INFO;
}
else
return SQL_SUCCESS;
}
// - - - - - - - - -
RETCODE SQL_API SQLGetStmtOption(
@ -180,31 +228,54 @@ StatementClass *stmt = (StatementClass *) hstmt;
// 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) {
return SQL_INVALID_HANDLE;
}
if( ! stmt)
return SQL_INVALID_HANDLE;
switch(fOption) {
case SQL_QUERY_TIMEOUT:
// how long we wait on a query before returning to the
// application (0 == forever)
*((SDWORD *)pvParam) = 0;
break;
case SQL_MAX_LENGTH:
// what is the maximum length that will be returned in
// a single column
*((SDWORD *)pvParam) = 4096;
break;
switch(fOption) {
case SQL_QUERY_TIMEOUT:
// how long we wait on a query before returning to the
// application (0 == forever)
*((SDWORD *)pvParam) = 0;
break;
case SQL_MAX_LENGTH:
// what is the maximum length that will be returned in
// a single column
*((SDWORD *)pvParam) = 4096;
break;
case SQL_MAX_ROWS:
*((SDWORD *)pvParam) = stmt->maxRows;
mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->maxRows);
break;
default:
return SQL_ERROR;
}
return SQL_SUCCESS;
case SQL_ROWSET_SIZE:
mylog("GetStmtOption(): SQL_ROWSET_SIZE\n");
*((SDWORD *)pvParam) = stmt->rowset_size;
break;
case SQL_CONCURRENCY:
mylog("GetStmtOption(): SQL_CONCURRENCY\n");
*((SDWORD *)pvParam) = stmt->scroll_concurrency;
break;
case SQL_CURSOR_TYPE:
mylog("GetStmtOption(): SQL_CURSOR_TYPE\n");
*((SDWORD *)pvParam) = stmt->cursor_type;
break;
case SQL_SIMULATE_CURSOR:
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Simulated positioned update/delete not supported";
return SQL_ERROR;
default:
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Driver does not support this statement option";
return SQL_ERROR;
}
return SQL_SUCCESS;
}
// - - - - - - - - -

View File

@ -16,11 +16,19 @@
*/
#include "psqlodbc.h"
#include "dlg_specific.h"
#include "pgtypes.h"
#include "statement.h"
#include "connection.h"
#include "qresult.h"
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
extern GLOBAL_VALUES globals;
/* these are the types we support. all of the pgtype_ functions should */
/* return values for each one of these. */
@ -32,13 +40,14 @@ Int4 pgtypes_defined[] = {
PG_TYPE_CHAR2,
PG_TYPE_CHAR4,
PG_TYPE_CHAR8,
PG_TYPE_BPCHAR,
PG_TYPE_CHAR16,
PG_TYPE_NAME,
PG_TYPE_VARCHAR,
PG_TYPE_BPCHAR,
PG_TYPE_DATE,
PG_TYPE_TIME,
PG_TYPE_ABSTIME, /* a timestamp, sort of */
PG_TYPE_TEXT,
PG_TYPE_NAME,
PG_TYPE_INT2,
PG_TYPE_INT4,
PG_TYPE_FLOAT4,
@ -46,9 +55,9 @@ Int4 pgtypes_defined[] = {
PG_TYPE_OID,
PG_TYPE_MONEY,
PG_TYPE_BOOL,
PG_TYPE_CHAR16,
PG_TYPE_DATETIME,
PG_TYPE_BYTEA,
PG_TYPE_LO,
0 };
@ -57,80 +66,97 @@ Int4 pgtypes_defined[] = {
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 PG_UNKNOWN when it does not know. The calling
routine checks for this and changes it to a char type. This allows for supporting
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(Int4 type)
Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type)
{
switch(type) {
case PG_TYPE_CHAR:
switch(type) {
case PG_TYPE_CHAR:
case PG_TYPE_CHAR2:
case PG_TYPE_CHAR4:
case PG_TYPE_CHAR8:
case PG_TYPE_CHAR16: return SQL_CHAR;
case PG_TYPE_CHAR8:
case PG_TYPE_CHAR16:
case PG_TYPE_NAME: return SQL_CHAR;
case PG_TYPE_BPCHAR:
case PG_TYPE_NAME:
case PG_TYPE_VARCHAR: return SQL_VARCHAR;
case PG_TYPE_BPCHAR: return SQL_CHAR; // temporary?
case PG_TYPE_TEXT: return SQL_LONGVARCHAR;
case PG_TYPE_BYTEA: return SQL_LONGVARBINARY;
case PG_TYPE_VARCHAR: return SQL_VARCHAR;
case PG_TYPE_INT2: return SQL_SMALLINT;
case PG_TYPE_OID:
case PG_TYPE_INT4: return SQL_INTEGER;
case PG_TYPE_FLOAT4: return SQL_REAL;
case PG_TYPE_FLOAT8: return SQL_FLOAT;
case PG_TYPE_TEXT: return globals.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_INT4: return SQL_INTEGER;
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: return SQL_TIMESTAMP;
case PG_TYPE_MONEY: return SQL_FLOAT;
case PG_TYPE_BOOL: return SQL_CHAR;
case PG_TYPE_BOOL: return globals.bools_as_char ? SQL_CHAR : SQL_BIT;
default: return PG_UNKNOWN; /* check return for this */
}
default:
/* first, check to see if 'type' is in list. If not, look up with query.
Add oid, name to list. If its already in list, just return.
*/
if (type == stmt->hdbc->lobj_type) /* hack until permanent type is available */
return SQL_LONGVARBINARY;
return globals.unknowns_as_longvarchar ? SQL_LONGVARCHAR : SQL_VARCHAR;
}
}
Int2 pgtype_to_ctype(Int4 type)
Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type)
{
switch(type) {
case PG_TYPE_INT2: return SQL_C_SSHORT;
case PG_TYPE_OID:
case PG_TYPE_INT4: return SQL_C_SLONG;
case PG_TYPE_FLOAT4: return SQL_C_FLOAT;
case PG_TYPE_FLOAT8: return SQL_C_DOUBLE;
switch(type) {
case PG_TYPE_INT2: return SQL_C_SSHORT;
case PG_TYPE_OID:
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: return SQL_C_TIMESTAMP;
case PG_TYPE_MONEY: return SQL_C_FLOAT;
case PG_TYPE_BOOL: return SQL_C_CHAR;
case PG_TYPE_BOOL: return globals.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: return SQL_C_CHAR;
}
default:
if (type == stmt->hdbc->lobj_type) /* hack until permanent type is available */
return SQL_C_BINARY;
return SQL_C_CHAR;
}
}
char *pgtype_to_name(Int4 type)
char *pgtype_to_name(StatementClass *stmt, Int4 type)
{
switch(type) {
case PG_TYPE_CHAR: return "char";
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_CHAR8: return "char8";
case PG_TYPE_CHAR16: return "char16";
case PG_TYPE_VARCHAR: return "varchar";
case PG_TYPE_BPCHAR: return "bpchar";
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_VARCHAR: return "varchar";
case PG_TYPE_BPCHAR: return "bpchar";
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";
@ -139,16 +165,86 @@ char *pgtype_to_name(Int4 type)
case PG_TYPE_BOOL: return "bool";
case PG_TYPE_BYTEA: return "bytea";
/* "unknown" can actually be used in alter table because it is a real PG type! */
default: return "unknown";
}
case PG_TYPE_LO: return PG_TYPE_LO_NAME;
default:
if (type == stmt->hdbc->lobj_type) /* hack until permanent type is available */
return PG_TYPE_LO_NAME;
/* "unknown" can actually be used in alter table because it is a real PG type! */
return "unknown";
}
}
Int4
getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
{
int p = -1, maxsize;
QResultClass *result;
ColumnInfoClass *flds;
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 (globals.text_as_longvarchar)
maxsize = globals.max_longvarchar_size;
else
maxsize = globals.max_varchar_size;
break;
case PG_TYPE_VARCHAR:
case PG_TYPE_BPCHAR:
maxsize = globals.max_varchar_size;
break;
default:
if (globals.unknowns_as_longvarchar)
maxsize = globals.max_longvarchar_size;
else
maxsize = globals.max_varchar_size;
break;
}
/* Static Precision (i.e., the Maximum Precision of the datatype)
This has nothing to do with a result set.
*/
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 (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;
}
/* For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, SQLColumns will
override this length with the atttypmod length from pg_attribute
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(Int4 type)
Int4 pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
{
switch(type) {
case PG_TYPE_CHAR: return 1;
@ -157,10 +253,7 @@ Int4 pgtype_precision(Int4 type)
case PG_TYPE_CHAR8: return 8;
case PG_TYPE_CHAR16: return 16;
case PG_TYPE_NAME: return 32;
case PG_TYPE_VARCHAR:
case PG_TYPE_BPCHAR: return MAX_VARCHAR_SIZE;
case PG_TYPE_NAME: return NAME_FIELD_SIZE;
case PG_TYPE_INT2: return 5;
@ -180,29 +273,46 @@ Int4 pgtype_precision(Int4 type)
case PG_TYPE_BOOL: return 1;
case PG_TYPE_LO: return SQL_NO_TOTAL;
default:
return TEXT_FIELD_SIZE; /* text field types and unknown types */
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: return 10;
case PG_TYPE_INT4: return 11;
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(Int4 type)
Int4 pgtype_length(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_CHAR16: return 16;
case PG_TYPE_NAME: return 32;
case PG_TYPE_VARCHAR:
case PG_TYPE_BPCHAR: return MAX_VARCHAR_SIZE;
case PG_TYPE_INT2: return 2;
case PG_TYPE_OID:
@ -219,14 +329,14 @@ Int4 pgtype_length(Int4 type)
case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME: return 16;
case PG_TYPE_BOOL: return 1;
/* Character types use the default precision */
default:
return TEXT_FIELD_SIZE; /* text field types and unknown types */
return pgtype_precision(stmt, type, col, handle_unknown_size_as);
}
}
Int2 pgtype_scale(Int4 type)
Int2 pgtype_scale(StatementClass *stmt, Int4 type)
{
switch(type) {
@ -247,7 +357,7 @@ Int2 pgtype_scale(Int4 type)
}
Int2 pgtype_radix(Int4 type)
Int2 pgtype_radix(StatementClass *stmt, Int4 type)
{
switch(type) {
case PG_TYPE_INT2:
@ -261,12 +371,12 @@ Int2 pgtype_radix(Int4 type)
}
}
Int2 pgtype_nullable(Int4 type)
Int2 pgtype_nullable(StatementClass *stmt, Int4 type)
{
return SQL_NULLABLE; /* everything should be nullable */
}
Int2 pgtype_auto_increment(Int4 type)
Int2 pgtype_auto_increment(StatementClass *stmt, Int4 type)
{
switch(type) {
@ -287,7 +397,7 @@ Int2 pgtype_auto_increment(Int4 type)
}
}
Int2 pgtype_case_sensitive(Int4 type)
Int2 pgtype_case_sensitive(StatementClass *stmt, Int4 type)
{
switch(type) {
case PG_TYPE_CHAR:
@ -306,7 +416,7 @@ Int2 pgtype_case_sensitive(Int4 type)
}
}
Int2 pgtype_money(Int4 type)
Int2 pgtype_money(StatementClass *stmt, Int4 type)
{
switch(type) {
case PG_TYPE_MONEY: return TRUE;
@ -314,7 +424,7 @@ Int2 pgtype_money(Int4 type)
}
}
Int2 pgtype_searchable(Int4 type)
Int2 pgtype_searchable(StatementClass *stmt, Int4 type)
{
switch(type) {
case PG_TYPE_CHAR:
@ -329,11 +439,10 @@ Int2 pgtype_searchable(Int4 type)
case PG_TYPE_NAME: return SQL_SEARCHABLE;
default: return SQL_ALL_EXCEPT_LIKE;
}
}
Int2 pgtype_unsigned(Int4 type)
Int2 pgtype_unsigned(StatementClass *stmt, Int4 type)
{
switch(type) {
case PG_TYPE_OID: return TRUE;
@ -348,7 +457,7 @@ Int2 pgtype_unsigned(Int4 type)
}
}
char *pgtype_literal_prefix(Int4 type)
char *pgtype_literal_prefix(StatementClass *stmt, Int4 type)
{
switch(type) {
@ -363,7 +472,7 @@ char *pgtype_literal_prefix(Int4 type)
}
}
char *pgtype_literal_suffix(Int4 type)
char *pgtype_literal_suffix(StatementClass *stmt, Int4 type)
{
switch(type) {
@ -378,7 +487,7 @@ char *pgtype_literal_suffix(Int4 type)
}
}
char *pgtype_create_params(Int4 type)
char *pgtype_create_params(StatementClass *stmt, Int4 type)
{
switch(type) {
case PG_TYPE_CHAR:

View File

@ -10,10 +10,13 @@
#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 */
#define PG_UNKNOWN -666 /* returned only from pgtype_to_sqltype() */
// #define PG_TYPE_LO ???? /* waiting for permanent type */
#define PG_TYPE_BOOL 16
#define PG_TYPE_BYTEA 17
@ -58,22 +61,30 @@
extern Int4 pgtypes_defined[];
Int2 pgtype_to_sqltype(Int4 type);
Int2 pgtype_to_ctype(Int4 type);
char *pgtype_to_name(Int4 type);
Int4 pgtype_precision(Int4 type);
Int4 pgtype_length(Int4 type);
Int2 pgtype_scale(Int4 type);
Int2 pgtype_radix(Int4 type);
Int2 pgtype_nullable(Int4 type);
Int2 pgtype_auto_increment(Int4 type);
Int2 pgtype_case_sensitive(Int4 type);
Int2 pgtype_money(Int4 type);
Int2 pgtype_searchable(Int4 type);
Int2 pgtype_unsigned(Int4 type);
char *pgtype_literal_prefix(Int4 type);
char *pgtype_literal_suffix(Int4 type);
char *pgtype_create_params(Int4 type);
/* Defines for pgtype_precision */
#define PG_STATIC -1
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);
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);

View File

@ -14,6 +14,7 @@
*/
#include "psqlodbc.h"
#include "dlg_specific.h"
#include <winsock.h>
#include <windows.h>
#include <sql.h>
@ -23,67 +24,6 @@ HINSTANCE NEAR s_hModule; /* Saved module handle. */
GLOBAL_VALUES globals;
/* This function reads the ODBCINST.INI portion of
the registry and gets any driver defaults.
*/
void getGlobalDefaults(void)
{
char temp[128];
// Fetch Count is stored in driver section
SQLGetPrivateProfileString(DBMS_NAME, INI_FETCH, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] )
globals.fetch_max = atoi(temp);
else
globals.fetch_max = FETCH_MAX;
// Socket Buffersize is stored in driver section
SQLGetPrivateProfileString(DBMS_NAME, INI_SOCKET, "",
temp, sizeof(temp), ODBCINST_INI);
if ( temp[0] )
globals.socket_buffersize = atoi(temp);
else
globals.socket_buffersize = SOCK_BUFFER_SIZE;
// Debug is stored in the driver section
SQLGetPrivateProfileString(DBMS_NAME, INI_DEBUG, "0",
temp, sizeof(temp), ODBCINST_INI);
globals.debug = atoi(temp);
// CommLog is stored in the driver section
SQLGetPrivateProfileString(DBMS_NAME, INI_COMMLOG, "0",
temp, sizeof(temp), ODBCINST_INI);
globals.commlog = atoi(temp);
// Optimizer is stored in the driver section only (OFF, ON, or ON=x)
SQLGetPrivateProfileString(DBMS_NAME, INI_OPTIMIZER, "",
globals.optimizer, sizeof(globals.optimizer), ODBCINST_INI);
// ConnSettings is stored in the driver section and per datasource for override
SQLGetPrivateProfileString(DBMS_NAME, INI_CONNSETTINGS, "",
globals.conn_settings, sizeof(globals.conn_settings), ODBCINST_INI);
}
/* This function writes any global parameters (that can be manipulated)
to the ODBCINST.INI portion of the registry
*/
void updateGlobals(void)
{
char tmp[128];
sprintf(tmp, "%d", globals.commlog);
SQLWritePrivateProfileString(DBMS_NAME,
INI_COMMLOG, tmp, ODBCINST_INI);
}
/* This is where the Driver Manager attaches to this Driver */
BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{

View File

@ -24,6 +24,7 @@ typedef UInt4 Oid;
#define MAX_CONNECT_STRING 4096
#define ERROR_MSG_LENGTH 4096
#define FETCH_MAX 100 /* default number of rows to cache for declare/fetch */
#define FETCH_INCR 1000
#define SOCK_BUFFER_SIZE 4096 /* default socket buffer size */
#define MAX_CONNECTIONS 128 /* conns per environment (arbitrary) */
#define MAX_FIELDS 512
@ -32,16 +33,11 @@ typedef UInt4 Oid;
/* Registry length limits */
#define LARGE_REGISTRY_LEN 4096 /* used for special cases */
#define MEDIUM_REGISTRY_LEN 128 /* normal size for user,database,etc. */
#define MEDIUM_REGISTRY_LEN 256 /* normal size for user,database,etc. */
#define SMALL_REGISTRY_LEN 10 /* for 1/0 settings */
/* Connection Defaults */
#define DEFAULT_PORT "5432"
#define DEFAULT_READONLY "1"
/* These prefixes denote system tables */
#define INSIGHT_SYS_PREFIX "dd_"
#define POSTGRES_SYS_PREFIX "pg_"
#define KEYS_TABLE "dd_fkey"
@ -54,33 +50,13 @@ typedef UInt4 Oid;
/* Driver stuff */
#define DRIVERNAME "PostgreSQL ODBC"
#define DBMS_NAME "PostgreSQL"
#define DBMS_VERSION "06.30.0000 PostgreSQL 6.3"
#define POSTGRESDRIVERVERSION "06.30.0000"
#define DBMS_VERSION "06.30.0244 PostgreSQL 6.3"
#define POSTGRESDRIVERVERSION "06.30.0244"
#define DRIVER_FILE_NAME "PSQLODBC.DLL"
#define PG62 "6.2" /* "Protocol" key setting to force Postgres 6.2 */
/* INI File Stuff */
#define ODBC_INI "ODBC.INI" /* ODBC initialization file */
#define ODBCINST_INI "ODBCINST.INI" /* ODBC Installation file */
#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_CONNSETTINGS "ConnSettings" /* Anything to send to backend on successful connection */
typedef struct ConnectionClass_ ConnectionClass;
typedef struct StatementClass_ StatementClass;
@ -94,26 +70,39 @@ typedef struct EnvironmentClass_ EnvironmentClass;
typedef struct TupleNode_ TupleNode;
typedef struct TupleField_ TupleField;
typedef struct lo_arg LO_ARG;
typedef struct GlobalValues_
{
int fetch_max;
int socket_buffersize;
int debug;
int commlog;
char optimizer[MEDIUM_REGISTRY_LEN];
int unknown_sizes;
int max_varchar_size;
int max_longvarchar_size;
char debug;
char commlog;
char disable_optimizer;
char unique_index;
char readonly;
char use_declarefetch;
char text_as_longvarchar;
char unknowns_as_longvarchar;
char bools_as_char;
char extra_systable_prefixes[MEDIUM_REGISTRY_LEN];
char conn_settings[LARGE_REGISTRY_LEN];
} GLOBAL_VALUES;
#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 4094 /* 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) */
/* global prototypes */
void updateGlobals(void);
#include "misc.h"

View File

@ -52,69 +52,109 @@ END
// Dialog
//
DRIVERCONNDIALOG DIALOG DISCARDABLE 0, 0, 269, 133
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "PostgreSQL Connection"
FONT 8, "MS Sans Serif"
BEGIN
RTEXT "&Database:",IDC_STATIC,16,25,37,8
EDITTEXT DATABASE_EDIT,55,25,72,12,ES_AUTOHSCROLL
RTEXT "&Server:",IDC_STATIC,26,40,27,8
EDITTEXT SERVER_EDIT,55,40,72,12,ES_AUTOHSCROLL
RTEXT "&Port:",IDC_STATIC,150,40,20,8
EDITTEXT PORT_EDIT,172,40,72,12,ES_AUTOHSCROLL
RTEXT "&User Name:",IDC_STATIC,16,56,37,8
EDITTEXT USERNAME_EDIT,55,56,72,12,ES_AUTOHSCROLL
RTEXT "Pass&word:",IDC_STATIC,137,56,33,8
EDITTEXT PASSWORD_EDIT,172,56,72,12,ES_PASSWORD | ES_AUTOHSCROLL
GROUPBOX "Options:",IDC_STATIC,25,71,200,25
CONTROL "&ReadOnly:",READONLY_EDIT,"Button",BS_AUTOCHECKBOX |
BS_LEFTTEXT | BS_RIGHT | WS_GROUP | WS_TABSTOP,45,80,45,
14
CONTROL "&CommLog (Global):",COMMLOG_EDIT,"Button",
BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_RIGHT | WS_TABSTOP,
100,80,75,14
CONTROL "6.2",PG62_EDIT,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT |
BS_RIGHT | WS_TABSTOP,185,80,25,14
DEFPUSHBUTTON "OK",IDOK,84,108,40,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,146,108,40,14
CTEXT "Please supply any missing information needed to connect.",
IDC_STATIC,40,7,188,11
END
CONFIGDSN DIALOG DISCARDABLE 65, 43, 292, 151
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,30,50,12,NOT WS_GROUP
EDITTEXT IDC_DSNAME,57,30,72,12,ES_AUTOHSCROLL | WS_GROUP
RTEXT "Des&cription:",IDC_STATIC,135,30,39,12,NOT WS_GROUP
EDITTEXT IDC_DESC,175,30,108,12,ES_AUTOHSCROLL
RTEXT "Data&base:",IDC_STATIC,17,45,38,12,NOT WS_GROUP
EDITTEXT IDC_DATABASE,57,45,72,12,ES_AUTOHSCROLL
RTEXT "&Server:",IDC_STATIC,27,60,29,12,NOT WS_GROUP
EDITTEXT IDC_SERVER,57,60,72,12,ES_AUTOHSCROLL
RTEXT "&Port:",IDC_STATIC,153,60,22,12
EDITTEXT IDC_PORT,175,60,37,12,ES_AUTOHSCROLL
RTEXT "&User Name:",IDC_STATIC,17,75,39,12
EDITTEXT IDC_USER,57,75,72,12,ES_AUTOHSCROLL
RTEXT "Pass&word:",IDC_STATIC,141,75,34,12
EDITTEXT IDC_PASSWORD,175,75,72,12,ES_PASSWORD | ES_AUTOHSCROLL
GROUPBOX "Options:",IDC_STATIC,35,92,205,25
CONTROL "&ReadOnly:",IDC_READONLY,"Button",BS_AUTOCHECKBOX |
BS_LEFTTEXT | BS_RIGHT | WS_GROUP | WS_TABSTOP,50,100,45,
14
CONTROL "&CommLog (Global):",IDC_COMMLOG,"Button",
BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_RIGHT | WS_TABSTOP,
105,100,75,14
CONTROL "6.2",IDC_PG62,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT |
BS_RIGHT | WS_TABSTOP,195,100,25,14
DEFPUSHBUTTON "OK",IDOK,85,129,40,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,145,129,40,14
CTEXT "Change data source name, description, or options. Then choose OK.",
IDC_STATIC,44,5,180,17
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_STATIC,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, 226
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,10,97,10
CONTROL "Comm&Log (C:\\psqlodbc.log)",DRV_COMMLOG,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,140,10,113,10
CONTROL "Recognize Unique &Indexes",DRV_UNIQUEINDEX,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,15,25,101,10
CONTROL "&ReadOnly (Default)",DRV_READONLY,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,140,25,80,10
CONTROL "&Use Declare/Fetch",DRV_USEDECLAREFETCH,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,15,40,80,10
GROUPBOX "Unknown Sizes",IDC_STATIC,10,55,175,25
CONTROL "Maximum",DRV_UNKNOWN_MAX,"Button",BS_AUTORADIOBUTTON |
WS_GROUP | WS_TABSTOP,15,65,45,10
CONTROL "Don't Know",DRV_UNKNOWN_DONTKNOW,"Button",
BS_AUTORADIOBUTTON | WS_TABSTOP,70,65,53,10
CONTROL "Longest",DRV_UNKNOWN_LONGEST,"Button",
BS_AUTORADIOBUTTON | WS_TABSTOP,130,65,50,10
GROUPBOX "Data Type Options",IDC_STATIC,10,85,270,25
CONTROL "Text as LongVarChar",DRV_TEXT_LONGVARCHAR,"Button",
BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,15,95,80,10
CONTROL "Unknowns as LongVarChar",DRV_UNKNOWNS_LONGVARCHAR,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,105,95,100,10
CONTROL "Bools as Char",DRV_BOOLS_CHAR,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,215,95,60,10
LTEXT "&Cache Size:",IDC_STATIC,10,120,40,10
EDITTEXT DRV_CACHE_SIZE,50,120,35,12,ES_AUTOHSCROLL
LTEXT "Max &Varchar:",IDC_STATIC,90,120,45,10
EDITTEXT DRV_VARCHAR_SIZE,135,120,35,12,ES_AUTOHSCROLL
LTEXT "Max Lon&gVarChar:",IDC_STATIC,180,120,60,10
EDITTEXT DRV_LONGVARCHAR_SIZE,240,120,35,12,ES_AUTOHSCROLL
LTEXT "SysTable &Prefixes:",IDC_STATIC,15,135,35,20
EDITTEXT DRV_EXTRASYSTABLEPREFIXES,50,140,75,12,ES_AUTOHSCROLL
RTEXT "Connect &Settings:",IDC_STATIC,10,165,35,25
EDITTEXT DRV_CONNSETTINGS,50,160,225,35,ES_MULTILINE |
ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
DEFPUSHBUTTON "OK",IDOK,45,205,50,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,115,205,50,14
PUSHBUTTON "Defaults",IDDEFAULTS,185,205,50,15
END
DLG_OPTIONS_DS DIALOG DISCARDABLE 0, 0, 267, 170
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,45,15
CONTROL "&6.2 Protocol",DS_PG62,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,130,10,60,14
CONTROL "Show System &Tables",DS_SHOWSYSTEMTABLES,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,25,30,85,10
GROUPBOX "OID Options",IDC_STATIC,15,50,180,25
CONTROL "Show &Column",DS_SHOWOIDCOLUMN,"Button",BS_AUTOCHECKBOX |
WS_GROUP | WS_TABSTOP,25,60,59,10
CONTROL "Fake &Index",DS_FAKEOIDINDEX,"Button",BS_AUTOCHECKBOX |
WS_GROUP | WS_TABSTOP,115,60,51,10
RTEXT "Connect &Settings:",IDC_STATIC,10,90,35,25
EDITTEXT DS_CONNSETTINGS,50,85,200,35,ES_MULTILINE |
ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
DEFPUSHBUTTON "OK",IDOK,65,130,50,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,140,130,50,14
GROUPBOX "Unknown Sizes",IDC_STATIC,10,145,175,25,NOT WS_VISIBLE
CONTROL "Maximum",DS_UNKNOWN_MAX,"Button",BS_AUTORADIOBUTTON |
NOT WS_VISIBLE | WS_GROUP | WS_TABSTOP,15,155,45,10
CONTROL "Don't Know",DS_UNKNOWN_DONTKNOW,"Button",
BS_AUTORADIOBUTTON | NOT WS_VISIBLE | WS_TABSTOP,70,155,
53,10
CONTROL "Longest",DS_UNKNOWN_LONGEST,"Button",BS_AUTORADIOBUTTON |
NOT WS_VISIBLE | WS_TABSTOP,130,155,50,10
END
@ -126,9 +166,26 @@ END
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
DRIVERCONNDIALOG, DIALOG
DLG_CONFIG, DIALOG
BEGIN
RIGHTMARGIN, 268
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, 163
END
END
#endif // APSTUDIO_INVOKED
@ -141,8 +198,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 6,30,0,0
PRODUCTVERSION 6,30,0,0
FILEVERSION 6,30,2,44
PRODUCTVERSION 6,30,2,44
FILEFLAGSMASK 0x3L
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -160,12 +217,12 @@ BEGIN
VALUE "Comments", "PostgreSQL ODBC driver for Windows 95\0"
VALUE "CompanyName", "Insight Distribution Systems\0"
VALUE "FileDescription", "PostgreSQL Driver\0"
VALUE "FileVersion", " 6.30.0000\0"
VALUE "FileVersion", " 6.30.0244\0"
VALUE "InternalName", "psqlodbc\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 "ProductName", "Microsoft Open Database Connectivity\0"
VALUE "ProductVersion", " 6.30.0000\0"
VALUE "ProductVersion", " 6.30.0244\0"
END
END
BLOCK "VarFileInfo"

View File

@ -183,12 +183,14 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
if (self->cursor)
free(self->cursor);
if ( ! cursor || cursor[0] == '\0') {
self->status = PGRES_INTERNAL_ERROR;
QR_set_message(self, "Internal Error -- no cursor for fetch");
return FALSE;
if ( globals.use_declarefetch) {
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);
}
self->cursor = strdup(cursor);
// Read the field attributes.
// $$$$ Should do some error control HERE! $$$$
@ -205,6 +207,7 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
mylog("QR_fetch_tuples: past CI_read_fields: num_fields = %d\n", self->num_fields);
/* allocate memory for the tuple cache */
mylog("MALLOC: fetch_max = %d, size = %d\n", globals.fetch_max, self->num_fields * sizeof(TupleField) * globals.fetch_max);
self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * globals.fetch_max);
if ( ! self->backend_tuples) {
self->status = PGRES_FATAL_ERROR;
@ -232,24 +235,23 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
}
}
// Close the cursor and end the transaction
// 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) {
if (globals.use_declarefetch && self->conn && self->cursor) {
char buf[64];
sprintf(buf, "close %s; END", self->cursor);
sprintf(buf, "close %s", self->cursor);
mylog("QResult: closing cursor: '%s'\n", buf);
res = CC_send_query(self->conn, buf, NULL, NULL);
CC_set_no_trans(self->conn);
self->inTuples = FALSE;
free(self->cursor);
self->cursor = NULL;
@ -259,6 +261,21 @@ QResultClass *res;
return FALSE;
}
/* End the transaction if there are no cursors left on this conn */
if (CC_cursor_count(self->conn) == 0) {
mylog("QResult: END transaction on conn=%u\n", self->conn);
res = CC_send_query(self->conn, "END", NULL, NULL);
CC_set_no_trans(self->conn);
if (res == NULL) {
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Error ending transaction.");
return FALSE;
}
}
}
return TRUE;
@ -301,6 +318,13 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
if ( ! self->inTuples) {
char fetch[128];
if ( ! globals.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 */
}
sprintf(fetch, "fetch %d in %s", globals.fetch_max, self->cursor);
mylog("next_tuple: sending actual fetch (%d) query '%s'\n", globals.fetch_max, fetch);
@ -336,65 +360,81 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
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 ( ! QR_read_tuple(self, (char) (id == 0))) {
self->status = PGRES_BAD_RESPONSE;
QR_set_message(self, "Error reading the tuple");
return FALSE;
}
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 */
self->fcount++;
break; // continue reading
if ( ! globals.use_declarefetch && self->fcount > 0 && ! (self->fcount % globals.fetch_max)) {
size_t old_size = self->fcount * self->num_fields * sizeof(TupleField);
mylog("REALLOC: old_size = %d\n", old_size);
case 'C': /* End of tuple list */
SOCK_get_string(sock, cmdbuffer, MAX_MESSAGE_LEN);
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 = the_tuples;
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 */
self->backend_tuples = (TupleField *) realloc(self->backend_tuples, old_size + (self->num_fields * sizeof(TupleField) * globals.fetch_max));
if ( ! self->backend_tuples) {
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Out of memory while reading tuples.");
return FALSE;
}
}
case 'E': /* Error */
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
QR_set_message(self, msgbuffer);
self->status = PGRES_FATAL_ERROR;
CC_set_no_trans(self->conn);
qlog("ERROR from backend in next_tuple: '%s'\n", msgbuffer);
if ( ! QR_read_tuple(self, (char) (id == 0))) {
self->status = PGRES_BAD_RESPONSE;
QR_set_message(self, "Error reading the tuple");
return FALSE;
}
return FALSE;
self->fcount++;
break; // continue reading
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 */
QR_set_message(self, "Unexpected result from backend. It probably crashed");
self->status = PGRES_FATAL_ERROR;
CC_set_no_trans(self->conn);
return FALSE;
case 'C': /* End of tuple list */
SOCK_get_string(sock, cmdbuffer, MAX_MESSAGE_LEN);
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; // the_tuples;
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 */
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;
@ -413,6 +453,7 @@ 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 */
@ -455,6 +496,17 @@ SocketClass *sock = CC_get_socket(self->conn);
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

View File

@ -65,15 +65,18 @@ struct QResultClass_ {
/* 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_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) : 0)
#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))

View File

@ -0,0 +1,72 @@
Readme for psqlodbc.dll 4/15/98
-------------------------------------------------------------------------------
Latest binary and source updates available at http://www.insightdist.com/psqlodbc
I. Building the Driver from the source code
This section describes how to build the PostgreSQL ODBC Driver (psqlodbc.dll).
Microsoft Visual C++ version 4.0 or higher is required. There is no manually
constructed Makefile. The visual C++ environment automatically generates one
during the build process. Thus, the project binary files (".ncb", ".mdp", ".aps")
nor the makefile are really distributed as part of the source code release
(although they are probably in there anyway).
1. Create a new project workspace with the type DLL. For the name, type in the
name "psqlodbc".
2. The above step creates the directory "psqlodbc" under the
"\<Visual C++ top level directory>\projects" path to hold the source files.
(example, \msdev\projects\psqlodbc). Now, either unzip the source code release
into this directory or just copy all the files into this directory.
3. Insert all of the source files (*.c, *.h, *.rc, *.def) into the Visual project
using the "Insert files into project" command. You may have to do 2 inserts --
the first to get the 'c' and header files, and the second to get the def file.
Don't forget the .def file since it is an important part of the release.
You can even insert ".txt" files into the projects -- they will do nothing.
4. Add the "wsock32.lib" library to the end of the list of libraries for linking
using the Build settings menu.
5. Select the type of build on the toolbar (i.e., Release or Debug). This is
one of the useful features of the visual c++ environment in that you can
browse the entire project if you build the "Debug" release. For release
purposes however, select "Release" build.
6. Build the dll by selecting Build from the build menu.
7. When complete, the "psqlodbc.dll" file is under the "Release" subdirectory.
(i.e., "\msdev\projects\psqlodbc\release\psqlodbc.dll")
II. Using Large Objects for handling LongVarBinary (OLE Objects in Access)
Large objects are mapped to LONGVARBINARY in the driver to allow storing things like
OLE objects in Microsoft Access. Multiple SQLPutData and SQLGetData calls are usually
used to send and retrieve these objects. The driver creates a new large object and simply
inserts its 'identifier' into the respective table. However, since Postgres uses an 'Oid'
to identify a Large Object, it is necessary to create a new Postgres type to be able
to discriminate between an ordinary Oid and a Large Object Oid. Until this new type
becomes an official part of Postgres, it must be added into the desired database and
looked up for each connection. The type used in the driver is simply called "lo" and
here is the command used to create it:
create type lo (internallength=4,externallength=10,input=int4in,output=int4out,
default='',passedbyvalue);
Once this is done, simply use the new 'lo' type to define columns in that database. Note
that this must be done for each database you want to use large objects in with the driver.
When the driver sees an 'lo' type, it will handle it as LONGVARBINARY.
Another important note is that this new type is lacking in functionality. It will not
cleanup after itself on updates and deletes, thus leaving orphans around and using up
extra disk space. And currently, Postgres does not support the vacuuming of large
objects. Hopefully in the future, a real large object data type will be available.
But for now, it sure is fun to stick a Word document, Visio document, or avi of a dancing
baby into a database column, even if you will fill up your server's hard disk after a while!

View File

@ -4,36 +4,55 @@
//
#define IDS_BADDSN 1
#define IDS_MSGTITLE 2
#define DRIVERCONNDIALOG 101
#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 CONFIGDSN 1001
#define DLG_CONFIG 1001
#define IDC_PORT 1002
#define IDC_USER 1006
#define IDC_PASSWORD 1009
#define IDC_READONLY 1011
#define READONLY_EDIT 1012
#define SAVEPASSWORD_EDIT 1013
#define IDC_COMMLOG 1014
#define COMMLOG_EDIT 1015
#define IDC_PG62 1016
#define PG62_EDIT 1017
#define SERVER_EDIT 1501
#define PORT_EDIT 1502
#define DATABASE_EDIT 1503
#define USERNAME_EDIT 1504
#define PASSWORD_EDIT 1505
#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 DS_UNKNOWN_MAX 1023
#define DS_UNKNOWN_DONTKNOW 1024
#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 DS_UNKNOWN_LONGEST 1042
#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
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_RESOURCE_VALUE 104
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1018
#define _APS_NEXT_CONTROL_VALUE 1054
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -9,7 +9,7 @@
* API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol, SQLColAttributes,
* SQLGetData, SQLFetch, SQLExtendedFetch,
* SQLMoreResults(NI), SQLSetPos(NI), SQLSetScrollOptions(NI),
* SQLSetCursorName(NI), SQLGetCursorName(NI)
* SQLSetCursorName, SQLGetCursorName
*
* Comments: See "notice.txt" for copyright and license information.
*
@ -17,6 +17,7 @@
#include <string.h>
#include "psqlodbc.h"
#include "dlg_specific.h"
#include "environ.h"
#include "connection.h"
#include "statement.h"
@ -29,6 +30,7 @@
#include <windows.h>
#include <sqlext.h>
extern GLOBAL_VALUES globals;
RETCODE SQL_API SQLRowCount(
HSTMT hstmt,
@ -46,7 +48,7 @@ char *msg, *ptr;
res = SC_get_Result(stmt);
if(res && pcrow) {
*pcrow = QR_get_num_tuples(res);
*pcrow = globals.use_declarefetch ? 0 : QR_get_num_tuples(res);
return SQL_SUCCESS;
}
}
@ -115,11 +117,11 @@ QResultClass *result;
return SQL_SUCCESS;
}
// - - - - - - - - -
// Return information about the database column the user wants
// information about.
/* CC: preliminary implementation */
RETCODE SQL_API SQLDescribeCol(
HSTMT hstmt,
UWORD icol,
@ -136,10 +138,14 @@ StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *result;
char *name;
Int4 fieldtype;
int p;
ConnInfo *ci;
if ( ! stmt)
return SQL_INVALID_HANDLE;
ci = &(stmt->hdbc->connInfo);
SC_clear_error(stmt);
/* CC: Now check for the "prepared, but not executed" situation, that enables us to
@ -159,9 +165,18 @@ Int4 fieldtype;
return SQL_ERROR;
}
if(icol < 1) {
// we do not support bookmarks
stmt->errormsg = "Bookmarks are not currently supported.";
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
return SQL_ERROR;
}
icol--; /* use zero based column numbers */
if (cbColNameMax >= 1) {
name = QR_get_fieldname(result, (Int2) (icol-1));
mylog("describeCol: col %d fieldname = '%s'\n", icol - 1, name);
name = QR_get_fieldname(result, icol);
mylog("describeCol: col %d fieldname = '%s'\n", icol, name);
/* our indices start from 0 whereas ODBC defines indices starting from 1 */
if (NULL != pcbColName) {
// we want to get the total number of bytes in the column name
@ -179,27 +194,51 @@ Int4 fieldtype;
}
}
fieldtype = QR_get_field_type(result, (Int2) (icol-1));
mylog("describeCol: col %d fieldtype = %d\n", icol - 1, fieldtype);
fieldtype = QR_get_field_type(result, icol);
mylog("describeCol: col %d fieldtype = %d\n", icol, fieldtype);
if (NULL != pfSqlType) {
*pfSqlType = pgtype_to_sqltype(fieldtype);
if (*pfSqlType == PG_UNKNOWN)
*pfSqlType = SQL_CHAR;
*pfSqlType = pgtype_to_sqltype(stmt, fieldtype);
mylog("describeCol: col %d *pfSqlType = %d\n", icol, *pfSqlType);
}
if (NULL != pcbColDef)
*pcbColDef = pgtype_precision(fieldtype);
if (NULL != pcbColDef) {
/* If type is BPCHAR, then precision is length of column because all
columns in the result set will be blank padded to the column length.
If type is VARCHAR or TEXT, then precision can not be accurately
determined. Possibilities are:
1. return 0 (I dont know -- seems to work ok with Borland)
2. return MAXIMUM PRECISION for that datatype (Borland bad!)
3. return longest column thus far (that would be the longest
strlen of any row in the tuple cache, which may not be a
good representation if the result set is more than one
tuple cache long.)
*/
p = pgtype_precision(stmt, fieldtype, icol, globals.unknown_sizes); // atoi(ci->unknown_sizes)
if ( p < 0)
p = 0; // "I dont know"
*pcbColDef = p;
mylog("describeCol: col %d *pcbColDef = %d\n", icol, *pcbColDef);
}
if (NULL != pibScale) {
Int2 scale;
scale = pgtype_scale(fieldtype);
scale = pgtype_scale(stmt, fieldtype);
if(scale == -1) { scale = 0; }
*pibScale = scale;
mylog("describeCol: col %d *pibScale = %d\n", icol, *pibScale);
}
if (NULL != pfNullable) {
*pfNullable = pgtype_nullable(fieldtype);
*pfNullable = pgtype_nullable(stmt, fieldtype);
mylog("describeCol: col %d *pfNullable = %d\n", icol, *pfNullable);
}
return SQL_SUCCESS;
@ -219,132 +258,169 @@ RETCODE SQL_API SQLColAttributes(
StatementClass *stmt = (StatementClass *) hstmt;
char *value;
Int4 field_type;
ConnInfo *ci;
int unknown_sizes;
if( ! stmt) {
return SQL_INVALID_HANDLE;
}
if( ! stmt)
return SQL_INVALID_HANDLE;
ci = &(stmt->hdbc->connInfo);
/* CC: Now check for the "prepared, but not executed" situation, that enables us to
deal with "SQLPrepare -- SQLDescribeCol -- ... -- SQLExecute" situations.
(AutoCAD 13 ASE/ASI just _loves_ that ;-) )
*/
SC_pre_execute(stmt);
SC_pre_execute(stmt);
mylog("**** SQLColAtt: 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;
return SQL_ERROR;
}
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;
return SQL_ERROR;
}
if(icol < 1) {
// we do not support bookmarks
stmt->errormsg = "Bookmarks are not currently supported.";
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
return SQL_ERROR;
}
if(icol < 1) {
// we do not support bookmarks
stmt->errormsg = "Bookmarks are not currently supported.";
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
return SQL_ERROR;
}
icol -= 1;
field_type = QR_get_field_type(stmt->result, icol);
icol -= 1;
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:
if (NULL != pfDesc) {
*pfDesc = pgtype_auto_increment(field_type);
if(*pfDesc == -1) { /* "not applicable" becomes false */
*pfDesc = FALSE;
}
}
break;
case SQL_COLUMN_CASE_SENSITIVE:
if (NULL != pfDesc)
*pfDesc = pgtype_case_sensitive(field_type);
break;
case SQL_COLUMN_COUNT:
if (NULL != pfDesc)
*pfDesc = QR_NumResultCols(stmt->result);
break;
case SQL_COLUMN_DISPLAY_SIZE:
if (NULL != pfDesc)
*pfDesc = pgtype_precision(field_type);
unknown_sizes = globals.unknown_sizes; // atoi(ci->unknown_sizes);
if (unknown_sizes == UNKNOWNS_AS_DONTKNOW) // not appropriate for SQLColAttributes()
unknown_sizes = UNKNOWNS_AS_MAX;
mylog("colAttr: col %d fieldsize = %d\n", icol, *pfDesc);
switch(fDescType) {
case SQL_COLUMN_AUTO_INCREMENT:
if (NULL != pfDesc) {
*pfDesc = pgtype_auto_increment(stmt, field_type);
if (*pfDesc == -1) /* non-numeric becomes FALSE (ODBC Doc) */
*pfDesc = FALSE;
break;
case SQL_COLUMN_LABEL:
case SQL_COLUMN_NAME:
value = QR_get_fieldname(stmt->result, icol);
strncpy_null((char *)rgbDesc, value, cbDescMax);
/* CC: Check for Nullpointesr */
if (NULL != pcbDesc)
*pcbDesc = strlen(value);
break;
case SQL_COLUMN_LENGTH:
if (NULL != pfDesc)
*pfDesc = pgtype_precision(field_type);
return SQL_SUCCESS;
break;
case SQL_COLUMN_MONEY:
if (NULL != pfDesc)
*pfDesc = pgtype_money(field_type);
break;
case SQL_COLUMN_NULLABLE:
if (NULL != pfDesc)
*pfDesc = pgtype_nullable(field_type);
break;
case SQL_COLUMN_OWNER_NAME:
return SQL_ERROR;
break;
case SQL_COLUMN_PRECISION:
if (NULL != pfDesc)
*pfDesc = pgtype_precision(field_type);
break;
case SQL_COLUMN_QUALIFIER_NAME:
strncpy_null((char *)rgbDesc, "", cbDescMax);
if (NULL != pfDesc)
*pcbDesc = 1;
break;
case SQL_COLUMN_SCALE:
if (NULL != pfDesc)
*pfDesc = pgtype_scale(field_type);
break;
case SQL_COLUMN_SEARCHABLE:
if (NULL != pfDesc)
*pfDesc = pgtype_searchable(field_type);
break;
case SQL_COLUMN_TABLE_NAME:
return SQL_ERROR;
break;
case SQL_COLUMN_TYPE:
if (NULL != pfDesc) {
*pfDesc = pgtype_to_sqltype(field_type);
if (*pfDesc == PG_UNKNOWN)
*pfDesc = SQL_CHAR;
}
break;
case SQL_COLUMN_CASE_SENSITIVE:
if (NULL != pfDesc)
*pfDesc = pgtype_case_sensitive(stmt, field_type);
break;
case SQL_COLUMN_COUNT:
if (NULL != pfDesc)
*pfDesc = QR_NumResultCols(stmt->result);
break;
case SQL_COLUMN_DISPLAY_SIZE:
if (NULL != pfDesc) {
*pfDesc = pgtype_display_size(stmt, field_type, icol, unknown_sizes);
}
mylog("SQLColAttributes: col %d, display_size= %d\n", icol, *pfDesc);
break;
case SQL_COLUMN_TYPE_NAME:
value = pgtype_to_name(field_type);
strncpy_null((char *)rgbDesc, value, cbDescMax);
if (NULL != pcbDesc)
*pcbDesc = strlen(value);
case SQL_COLUMN_LABEL:
case SQL_COLUMN_NAME:
value = QR_get_fieldname(stmt->result, icol);
strncpy_null((char *)rgbDesc, value, cbDescMax);
if (NULL != pcbDesc)
*pcbDesc = strlen(value);
break;
case SQL_COLUMN_LENGTH:
if (NULL != pfDesc) {
*pfDesc = pgtype_length(stmt, field_type, icol, unknown_sizes);
}
mylog("SQLColAttributes: col %d, length = %d\n", icol, *pfDesc);
break;
case SQL_COLUMN_UNSIGNED:
if (NULL != pfDesc) {
*pfDesc = pgtype_unsigned(field_type);
if(*pfDesc == -1) {
*pfDesc = FALSE;
}
}
case SQL_COLUMN_MONEY:
if (NULL != pfDesc)
*pfDesc = pgtype_money(stmt, field_type);
break;
case SQL_COLUMN_NULLABLE:
if (NULL != pfDesc)
*pfDesc = pgtype_nullable(stmt, field_type);
break;
case SQL_COLUMN_OWNER_NAME:
strncpy_null((char *)rgbDesc, "", cbDescMax);
if (NULL != pcbDesc)
*pcbDesc = 0;
break;
case SQL_COLUMN_PRECISION:
if (NULL != pfDesc) {
*pfDesc = pgtype_precision(stmt, field_type, icol, unknown_sizes);
}
mylog("SQLColAttributes: col %d, precision = %d\n", icol, *pfDesc);
break;
case SQL_COLUMN_UPDATABLE:
// everything should be updatable, I guess, unless access permissions
// prevent it--are we supposed to check for that here? seems kind
// of complicated. hmm...
if (NULL != pfDesc)
*pfDesc = SQL_ATTR_WRITE;
case SQL_COLUMN_QUALIFIER_NAME:
strncpy_null((char *)rgbDesc, "", cbDescMax);
if (NULL != pcbDesc)
*pcbDesc = 0;
break;
case SQL_COLUMN_SCALE:
if (NULL != pfDesc)
*pfDesc = pgtype_scale(stmt, field_type);
break;
case SQL_COLUMN_SEARCHABLE:
if (NULL != pfDesc)
*pfDesc = pgtype_searchable(stmt, field_type);
break;
case SQL_COLUMN_TABLE_NAME:
strncpy_null((char *)rgbDesc, "", cbDescMax);
if (NULL != pcbDesc)
*pcbDesc = 0;
break;
case SQL_COLUMN_TYPE:
if (NULL != pfDesc) {
*pfDesc = pgtype_to_sqltype(stmt, field_type);
}
break;
case SQL_COLUMN_TYPE_NAME:
value = pgtype_to_name(stmt, field_type);
strncpy_null((char *)rgbDesc, value, cbDescMax);
if (NULL != pcbDesc)
*pcbDesc = strlen(value);
break;
case SQL_COLUMN_UNSIGNED:
if (NULL != pfDesc) {
*pfDesc = pgtype_unsigned(stmt, field_type);
if(*pfDesc == -1) /* non-numeric becomes TRUE (ODBC Doc) */
*pfDesc = TRUE;
}
break;
case SQL_COLUMN_UPDATABLE:
// everything should be updatable, I guess, unless access permissions
// prevent it--are we supposed to check for that here? seems kind
// of complicated. hmm...
if (NULL != pfDesc) {
/* Neither Access or Borland care about this.
if (field_type == PG_TYPE_OID)
*pfDesc = SQL_ATTR_READONLY;
else
*/
*pfDesc = SQL_ATTR_WRITE;
}
break;
}
return SQL_SUCCESS;
@ -366,6 +442,10 @@ int num_cols, num_rows;
Int4 field_type;
void *value;
int result;
char multiple;
mylog("SQLGetData: enter, stmt=%u\n", stmt);
if( ! stmt) {
return SQL_INVALID_HANDLE;
@ -401,7 +481,7 @@ int result;
return SQL_ERROR;
}
if ( stmt->manual_result) {
if ( stmt->manual_result || ! globals.use_declarefetch) {
// make sure we're positioned on a valid row
num_rows = QR_get_num_tuples(res);
if((stmt->currTuple < 0) ||
@ -410,7 +490,14 @@ int result;
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
return SQL_ERROR;
}
value = QR_get_value_manual(res, stmt->currTuple, icol);
mylog(" num_rows = %d\n", num_rows);
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 { /* its a SOCKET result (backend data) */
if (stmt->currTuple == -1 || ! res || QR_end_tuples(res)) {
@ -421,35 +508,52 @@ int result;
value = QR_get_value_backend(res, icol);
mylog(" socket: value = '%s'\n", value);
}
field_type = QR_get_field_type(res, icol);
mylog("**** SQLGetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value);
result = copy_and_convert_field(field_type, value,
fCType, rgbValue, cbValueMax, pcbValue);
/* Is this another call for the same column to retrieve more data? */
multiple = (icol == stmt->current_col) ? TRUE : FALSE;
result = copy_and_convert_field(stmt, field_type, value,
fCType, rgbValue, cbValueMax, pcbValue, multiple);
if(result == COPY_UNSUPPORTED_TYPE) {
stmt->errormsg = "Received an unsupported type from Postgres.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
return SQL_ERROR;
} else if(result == COPY_UNSUPPORTED_CONVERSION) {
stmt->errormsg = "Couldn't handle the necessary data type conversion.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
return SQL_ERROR;
} else if(result == COPY_RESULT_TRUNCATED) {
stmt->errornumber = STMT_TRUNCATED;
stmt->errormsg = "The buffer was too small for the result.";
return SQL_SUCCESS_WITH_INFO;
} else if(result != COPY_OK) {
stmt->current_col = icol;
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;
return SQL_ERROR;
case COPY_UNSUPPORTED_CONVERSION:
stmt->errormsg = "Couldn't handle the necessary data type conversion.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
return SQL_ERROR;
case COPY_RESULT_TRUNCATED:
stmt->errornumber = STMT_TRUNCATED;
stmt->errormsg = "The buffer was too small for the result.";
return SQL_SUCCESS_WITH_INFO;
case COPY_GENERAL_ERROR: /* error msg already filled in */
return SQL_ERROR;
case COPY_NO_DATA_FOUND:
return SQL_NO_DATA_FOUND;
default:
stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
stmt->errornumber = STMT_INTERNAL_ERROR;
return SQL_ERROR;
}
return SQL_SUCCESS;
}
// Returns data for bound columns in the current row ("hstmt->iCursor"),
@ -465,120 +569,136 @@ Int2 num_cols, lf;
Oid type;
char *value;
ColumnInfoClass *ci;
// TupleField *tupleField;
if ( ! stmt)
return SQL_INVALID_HANDLE;
SC_clear_error(stmt);
if ( ! (res = stmt->result)) {
stmt->errormsg = "Null statement result in SQLFetch.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
return SQL_ERROR;
}
ci = QR_get_fields(res); /* the column info */
if (stmt->status == STMT_EXECUTING) {
stmt->errormsg = "Can't fetch while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
return SQL_ERROR;
}
if ( ! stmt)
return SQL_INVALID_HANDLE;
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";
return SQL_ERROR;
}
SC_clear_error(stmt);
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;
return SQL_ERROR;
}
if ( ! (res = stmt->result)) {
stmt->errormsg = "Null statement result in SQLFetch.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
return SQL_ERROR;
}
mylog("manual_result = %d, use_declarefetch = %d\n",
stmt->manual_result, globals.use_declarefetch);
ci = QR_get_fields(res); /* the column info */
if ( stmt->manual_result || ! globals.use_declarefetch) {
if (stmt->status == STMT_EXECUTING) {
stmt->errormsg = "Can't fetch while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
return SQL_ERROR;
}
if (stmt->currTuple >= QR_get_num_tuples(res) -1 ||
(stmt->maxRows > 0 && stmt->currTuple == stmt->maxRows - 1)) {
/* if at the end of the tuples, return "no data found"
and set the cursor past the end of the result set
*/
stmt->currTuple = QR_get_num_tuples(res);
return SQL_NO_DATA_FOUND;
}
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";
return SQL_ERROR;
}
mylog("**** SQLFetch: manual_result\n");
(stmt->currTuple)++;
}
else {
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;
return SQL_ERROR;
}
// read from the cache or the physical next tuple
retval = QR_next_tuple(res);
if (retval < 0) {
mylog("**** SQLFetch: end_tuples\n");
return SQL_NO_DATA_FOUND;
}
else if (retval > 0)
(stmt->currTuple)++; // all is well
else {
mylog("SQLFetch: error\n");
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Error fetching next row";
return SQL_ERROR;
}
}
if ( stmt->manual_result) {
if (QR_get_num_tuples(res) -1 == stmt->currTuple ||
(stmt->maxRows > 0 && stmt->currTuple == stmt->maxRows - 1))
/* if we are at the end of a tuple list, we return a "no data found" */
return SQL_NO_DATA_FOUND;
num_cols = QR_NumResultCols(res);
mylog("**** SQLFetch: manual_result\n");
(stmt->currTuple)++;
}
else {
for (lf=0; lf < num_cols; lf++) {
// read from the cache or the physical next tuple
retval = QR_next_tuple(res);
if (retval < 0) {
mylog("**** SQLFetch: end_tuples\n");
return SQL_NO_DATA_FOUND;
}
else if (retval > 0)
(stmt->currTuple)++; // all is well
mylog("fetch: cols=%d, lf=%d, stmt = %u, stmt->bindings = %u, buffer[] = %u\n",
num_cols, lf, stmt, stmt->bindings, stmt->bindings[lf].buffer);
else {
mylog("SQLFetch: error\n");
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Error fetching next row";
return SQL_ERROR;
}
}
num_cols = QR_NumResultCols(res);
for (lf=0; lf < num_cols; lf++) {
mylog("fetch: cols=%d, lf=%d, buffer[] = %u\n",
num_cols, lf, stmt->bindings[lf].buffer);
if (stmt->bindings[lf].buffer != NULL) {
if (stmt->bindings[lf].buffer != NULL) {
// this column has a binding
// type = QR_get_field_type(res, lf);
type = CI_get_oid(ci, lf); /* speed things up */
mylog("type = %d\n", type);
if (stmt->manual_result)
value = QR_get_value_manual(res, stmt->currTuple, lf);
else
else if (globals.use_declarefetch)
value = QR_get_value_backend(res, lf);
else {
value = QR_get_value_backend_row(res, stmt->currTuple, lf);
}
retval = copy_and_convert_field_bindinfo(type, value, &(stmt->bindings[lf]));
mylog("value = '%s'\n", value);
// check whether the complete result was copied
if(retval == COPY_UNSUPPORTED_TYPE) {
stmt->errormsg = "Received an unsupported type from Postgres.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
return SQL_ERROR;
retval = copy_and_convert_field_bindinfo(stmt, type, value, lf);
} else if(retval == COPY_UNSUPPORTED_CONVERSION) {
stmt->errormsg = "Couldn't handle the necessary data type conversion.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
return SQL_ERROR;
mylog("copy_and_convert: retval = %d\n", retval);
} else if(retval == COPY_RESULT_TRUNCATED) {
/* The result has been truncated during the copy */
/* this will generate a SQL_SUCCESS_WITH_INFO result */
stmt->errornumber = STMT_TRUNCATED;
stmt->errormsg = "A buffer was too small for the return value to fit in";
// check whether the complete result was copied
if(retval == COPY_UNSUPPORTED_TYPE) {
stmt->errormsg = "Received an unsupported type from Postgres.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
return SQL_ERROR;
} else if(retval == COPY_UNSUPPORTED_CONVERSION) {
stmt->errormsg = "Couldn't handle the necessary data type conversion.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
return SQL_ERROR;
} else if(retval == COPY_RESULT_TRUNCATED) {
/* The result has been truncated during the copy */
/* this will generate a SQL_SUCCESS_WITH_INFO result */
stmt->errornumber = STMT_TRUNCATED;
stmt->errormsg = "A buffer was too small for the return value to fit in";
return SQL_SUCCESS_WITH_INFO;
} else if(retval != COPY_OK) {
stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
stmt->errornumber = STMT_INTERNAL_ERROR;
return SQL_ERROR;
} else if(retval != COPY_OK) {
stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
stmt->errornumber = STMT_INTERNAL_ERROR;
return SQL_ERROR;
}
}
}
}
}
}
return SQL_SUCCESS;
return SQL_SUCCESS;
}
// This fetchs a block of data (rowset).
@ -591,52 +711,91 @@ RETCODE SQL_API SQLExtendedFetch(
UWORD FAR *rgfRowStatus)
{
StatementClass *stmt = (StatementClass *) hstmt;
int num_tuples;
RETCODE result;
if ( ! stmt)
return SQL_INVALID_HANDLE;
/* Currently, only for manual results can this be done
because not all the tuples are read in ahead of time.
*/
if ( ! stmt->manual_result)
return SQL_ERROR;
mylog("SQLExtendedFetch: stmt=%u\n", stmt);
// CC: we currently only support fetches in one row bits
if (NULL != pcrow)
*pcrow = 1;
if (NULL != rgfRowStatus)
*rgfRowStatus = SQL_ROW_SUCCESS;
if ( ! stmt)
return SQL_INVALID_HANDLE;
if ( globals.use_declarefetch)
return SQL_ERROR;
/* Initialize to no rows fetched */
if (rgfRowStatus)
*rgfRowStatus = SQL_ROW_NOROW;
if (pcrow)
*pcrow = 0;
num_tuples = QR_get_num_tuples(stmt->result);
switch (fFetchType) {
case SQL_FETCH_NEXT:
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);
/* If already before result set, return no data found */
if (stmt->currTuple <= 0)
return SQL_NO_DATA_FOUND;
stmt->currTuple -= 2;
break;
case SQL_FETCH_FIRST:
mylog("SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
stmt->currTuple = -1;
break;
case SQL_FETCH_LAST:
mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
stmt->currTuple = num_tuples <= 0 ? -1 : (num_tuples - 2);
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->currTuple = -1;
return SQL_NO_DATA_FOUND;
}
/* Position before the desired row */
else if (irow > 0) {
stmt->currTuple = irow-2;
}
/* Position with respect to the end of the result set */
else {
stmt->currTuple = num_tuples + irow - 1;
}
break;
default:
return SQL_ERROR;
}
mylog("SQLExtendedFetch: new currTuple = %d\n", stmt->currTuple);
result = SQLFetch(hstmt);
if (result == SQL_SUCCESS) {
if (rgfRowStatus)
*rgfRowStatus = SQL_ROW_SUCCESS;
if (pcrow)
*pcrow = 1;
}
return result;
switch (fFetchType) {
case SQL_FETCH_NEXT:
return SQLFetch(hstmt);
case SQL_FETCH_PRIOR:
if (stmt->currTuple <= 0)
return SQL_ERROR;
stmt->currTuple--;
return SQLFetch(hstmt);
case SQL_FETCH_FIRST:
stmt->currTuple = -1;
return SQLFetch(hstmt);
case SQL_FETCH_LAST:
stmt->currTuple = QR_get_num_tuples(stmt->result)-1;
return SQLFetch(hstmt);
case SQL_FETCH_ABSOLUTE:
if (irow == 0) {
stmt->currTuple = stmt->currTuple > 0 ? stmt->currTuple-2 : -1;
} else if (irow > 0) {
stmt->currTuple = irow-2;
return SQLFetch(hstmt);
} else {
// CC: ??? not sure about the specification in that case
return SQL_ERROR;
}
default:
return SQL_ERROR;
}
return SQL_SUCCESS;
}
// This determines whether there are more results sets available for
// the "hstmt".
@ -677,7 +836,24 @@ RETCODE SQL_API SQLSetCursorName(
UCHAR FAR *szCursor,
SWORD cbCursor)
{
return SQL_SUCCESS;
StatementClass *stmt = (StatementClass *) hstmt;
int len;
mylog("SQLSetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d\n",
hstmt, szCursor, cbCursor);
if ( ! stmt)
return SQL_INVALID_HANDLE;
len = (cbCursor == SQL_NTS) ? strlen(szCursor) : cbCursor;
mylog("cursor len = %d\n", len);
if (len <= 0 || len > sizeof(stmt->cursor_name) - 1) {
stmt->errornumber = STMT_INVALID_CURSOR_NAME;
stmt->errormsg = "Invalid Cursor Name";
return SQL_ERROR;
}
strncpy_null(stmt->cursor_name, szCursor, cbCursor);
return SQL_SUCCESS;
}
// Return the cursor name for a statement handle
@ -688,7 +864,27 @@ RETCODE SQL_API SQLGetCursorName(
SWORD cbCursorMax,
SWORD FAR *pcbCursor)
{
return SQL_ERROR;
StatementClass *stmt = (StatementClass *) hstmt;
mylog("SQLGetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d, pcbCursor=%u\n",
hstmt, szCursor, cbCursorMax, pcbCursor);
if ( ! stmt)
return SQL_INVALID_HANDLE;
if ( stmt->cursor_name[0] == '\0') {
stmt->errornumber = STMT_NO_CURSOR_NAME;
stmt->errormsg = "No Cursor name available";
return SQL_ERROR;
}
strncpy_null(szCursor, stmt->cursor_name, cbCursorMax);
if (pcbCursor)
*pcbCursor = strlen(szCursor);
return SQL_SUCCESS;
}

View File

@ -13,83 +13,16 @@
*
*************************************************************************************/
/*
** SETUP.C - This is the ODBC sample driver code for
** setup.
**
** This code is furnished on an as-is basis as part of the ODBC SDK and is
** intended for example purposes only.
**
*/
/*--------------------------------------------------------------------------
setup.c -- Sample ODBC setup
This code demonstrates how to interact with the ODBC Installer. These
functions may be part of your ODBC driver or in a separate DLL.
The ODBC Installer allows a driver to control the management of
data sources by calling the ConfigDSN entry point in the appropriate
DLL. When called, ConfigDSN receives four parameters:
hwndParent ---- Handle of the parent window for any dialogs which
may need to be created. If this handle is NULL,
then no dialogs should be displayed (that is, the
request should be processed silently).
fRequest ------ Flag indicating the type of request (add, configure
(edit), or remove).
lpszDriver ---- Far pointer to a null-terminated string containing
the name of your driver. This is the same string you
supply in the ODBC.INF file as your section header
and which ODBC Setup displays to the user in lieu
of the actual driver filename. This string needs to
be passed back to the ODBC Installer when adding a
new data source name.
lpszAttributes- Far pointer to a list of null-terminated attribute
keywords. This list is similar to the list passed
to SQLDriverConnect, except that each key-value
pair is separated by a null-byte rather than a
semicolon. The entire list is then terminated with
a null-byte (that is, two consecutive null-bytes
mark the end of the list). The keywords accepted
should be those for SQLDriverConnect which are
applicable, any new keywords you define for ODBC.INI,
and any additional keywords you decide to document.
ConfigDSN should return TRUE if the requested operation succeeds and
FALSE otherwise. The complete prototype for ConfigDSN is:
BOOL FAR PASCAL ConfigDSN(HWND hwndParent,
WORD fRequest,
LPSTR lpszDriver,
LPCSTR lpszAttributes)
Your setup code should not write to ODBC.INI directly to add or remove
data source names. Instead, link with ODBCINST.LIB (the ODBC Installer
library) and call SQLWriteDSNToIni and SQLRemoveDSNFromIni.
Use SQLWriteDSNToIni to add data source names. If the data source name
already exists, SQLWriteDSNToIni will delete it (removing all of its
associated keys) and rewrite it. SQLRemoveDSNToIni removes a data
source name and all of its associated keys.
For NT compatibility, the driver code should not use the
Get/WritePrivateProfileString windows functions for ODBC.INI, but instead,
use SQLGet/SQLWritePrivateProfileString functions that are macros (16 bit) or
calls to the odbcinst.dll (32 bit).
--------------------------------------------------------------------------*/
// Includes ----------------------------------------------------------------
#include "psqlodbc.h" // Local include files
#include "psqlodbc.h"
#include "connection.h"
#include <windows.h>
#include <windowsx.h>
#include <odbcinst.h> // ODBC installer prototypes
#include <string.h> // C include files
#include <odbcinst.h>
#include <string.h>
#include <stdlib.h>
#include "resource.h"
#include "dlg_specific.h"
#define INTFUNC __stdcall
@ -104,81 +37,27 @@ extern GLOBAL_VALUES globals;
#define MAXDESC (255+1) // Max description length
#define MAXDSNAME (32+1) // Max data source name length
static char far EMPTYSTR []= "";
static char far OPTIONON []= "Yes";
static char far OPTIONOFF []= "No";
// Attribute key indexes (into an array of Attr structs, see below)
#define KEY_DSN 0
#define KEY_DESC 1
#define KEY_PORT 2
#define KEY_SERVER 3
#define KEY_DATABASE 4
#define KEY_USER 5
#define KEY_PASSWORD 6
#define KEY_DEBUG 7
#define KEY_FETCH 8
#define KEY_READONLY 9
#define KEY_PROTOCOL 10
#define NUMOFKEYS 11 // Number of keys supported
// Attribute string look-up table (maps keys to associated indexes)
static struct {
char szKey[MAXKEYLEN];
int iKey;
} s_aLookup[] = { "DSN", KEY_DSN,
INI_KDESC, KEY_DESC,
INI_PORT, KEY_PORT,
INI_SERVER, KEY_SERVER,
INI_DATABASE, KEY_DATABASE,
INI_USER, KEY_USER,
INI_PASSWORD, KEY_PASSWORD,
INI_DEBUG, KEY_DEBUG,
INI_FETCH, KEY_FETCH,
INI_READONLY, KEY_READONLY,
INI_PROTOCOL, KEY_PROTOCOL,
"", 0
};
// Types -------------------------------------------------------------------
typedef struct tagAttr {
BOOL fSupplied;
char szAttr[MAXPATHLEN];
} Attr, FAR * LPAttr;
// Globals -----------------------------------------------------------------
// NOTE: All these are used by the dialog procedures
typedef struct tagSETUPDLG {
HWND hwndParent; // Parent window handle
LPCSTR lpszDrvr; // Driver description
Attr aAttr[NUMOFKEYS]; // Attribute array
char szDSN[MAXDSNAME]; // Original data source name
BOOL fNewDSN; // New data source flag
BOOL fDefault; // Default data source flag
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);
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);
int CALLBACK ConfigDlgProc (HWND hdlg,
WORD wMsg,
WPARAM wParam,
LPARAM lParam);
void INTFUNC ParseAttributes (LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg);
/* CC: SetDSNAttributes is declared as "INTFUNC" below, but here it is declared as
"CALLBACK" -- Watcom complained about disagreeing modifiers. Changed
"CALLBACK" to "INTFUNC" here.
BOOL CALLBACK SetDSNAttributes(HWND hwnd, LPSETUPDLG lpsetupdlg);
*/
BOOL INTFUNC SetDSNAttributes(HWND hwnd, LPSETUPDLG lpsetupdlg);
/* ConfigDSN ---------------------------------------------------------------
Description: ODBC Setup entry point
@ -196,9 +75,9 @@ BOOL CALLBACK ConfigDSN (HWND hwnd,
LPCSTR lpszDriver,
LPCSTR lpszAttributes)
{
BOOL fSuccess; // Success/fail flag
GLOBALHANDLE hglbAttr;
LPSETUPDLG lpsetupdlg;
BOOL fSuccess; // Success/fail flag
GLOBALHANDLE hglbAttr;
LPSETUPDLG lpsetupdlg;
// Allocate attribute array
@ -212,20 +91,20 @@ BOOL CALLBACK ConfigDSN (HWND hwnd,
ParseAttributes(lpszAttributes, lpsetupdlg);
// Save original data source name
if (lpsetupdlg->aAttr[KEY_DSN].fSupplied)
lstrcpy(lpsetupdlg->szDSN, lpsetupdlg->aAttr[KEY_DSN].szAttr);
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->aAttr[KEY_DSN].fSupplied)
if (!lpsetupdlg->ci.dsn[0])
fSuccess = FALSE;
// Otherwise remove data source from ODBC.INI
else
fSuccess = SQLRemoveDSNFromIni(lpsetupdlg->aAttr[KEY_DSN].szAttr);
fSuccess = SQLRemoveDSNFromIni(lpsetupdlg->ci.dsn);
}
// Add or Configure data source
@ -235,19 +114,19 @@ BOOL CALLBACK ConfigDSN (HWND hwnd,
lpsetupdlg->lpszDrvr = lpszDriver;
lpsetupdlg->fNewDSN = (ODBC_ADD_DSN == fRequest);
lpsetupdlg->fDefault =
!lstrcmpi(lpsetupdlg->aAttr[KEY_DSN].szAttr, INI_DSN);
!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(CONFIGDSN),
hwnd,
ConfigDlgProc,
(LONG)(LPSTR)lpsetupdlg));
MAKEINTRESOURCE(DLG_CONFIG),
hwnd,
ConfigDlgProc,
(LONG)(LPSTR)lpsetupdlg));
}
else if (lpsetupdlg->aAttr[KEY_DSN].fSupplied)
else if (lpsetupdlg->ci.dsn[0])
fSuccess = SetDSNAttributes(hwnd, lpsetupdlg);
else
fSuccess = FALSE;
@ -314,208 +193,123 @@ void INTFUNC CenterDialog(HWND hdlg)
--------------------------------------------------------------------------*/
int CALLBACK ConfigDlgProc
(HWND hdlg,
WORD wMsg,
WPARAM wParam,
LPARAM lParam)
int CALLBACK ConfigDlgProc(HWND hdlg,
WORD wMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (wMsg) {
// Initialize the dialog
case WM_INITDIALOG:
{
LPSETUPDLG lpsetupdlg;
LPCSTR lpszDSN;
switch (wMsg) {
// Initialize the dialog
case WM_INITDIALOG:
{
LPSETUPDLG lpsetupdlg = (LPSETUPDLG) lParam;
ConnInfo *ci = &lpsetupdlg->ci;
SetWindowLong(hdlg, DWL_USER, lParam);
CenterDialog(hdlg); // Center dialog
/* Hide the driver connect message */
ShowWindow(GetDlgItem(hdlg, DRV_MSG_LABEL), SW_HIDE);
lpsetupdlg = (LPSETUPDLG) lParam;
lpszDSN = lpsetupdlg->aAttr[KEY_DSN].szAttr;
// Initialize dialog fields
// NOTE: Values supplied in the attribute string will always
// override settings in ODBC.INI
SetDlgItemText(hdlg, IDC_DSNAME, lpszDSN);
SetWindowLong(hdlg, DWL_USER, lParam);
CenterDialog(hdlg); // Center dialog
// Description
if (!lpsetupdlg->aAttr[KEY_DESC].fSupplied)
SQLGetPrivateProfileString(lpszDSN, INI_KDESC,
EMPTYSTR,
lpsetupdlg->aAttr[KEY_DESC].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_DESC].szAttr),
ODBC_INI);
SetDlgItemText(hdlg, IDC_DESC, lpsetupdlg->aAttr[KEY_DESC].szAttr);
// NOTE: Values supplied in the attribute string will always
// override settings in ODBC.INI
// Database
if (!lpsetupdlg->aAttr[KEY_DATABASE].fSupplied)
SQLGetPrivateProfileString(lpszDSN, INI_DATABASE,
EMPTYSTR,
lpsetupdlg->aAttr[KEY_DATABASE].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_DATABASE].szAttr),
ODBC_INI);
SetDlgItemText(hdlg, IDC_DATABASE, lpsetupdlg->aAttr[KEY_DATABASE].szAttr);
// Get the rest of the common attributes
getDSNinfo(ci, CONN_DONT_OVERWRITE);
// Server
if (!lpsetupdlg->aAttr[KEY_SERVER].fSupplied)
SQLGetPrivateProfileString(lpszDSN, INI_SERVER,
EMPTYSTR,
lpsetupdlg->aAttr[KEY_SERVER].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_SERVER].szAttr),
ODBC_INI);
SetDlgItemText(hdlg, IDC_SERVER, lpsetupdlg->aAttr[KEY_SERVER].szAttr);
// Port
if (!lpsetupdlg->aAttr[KEY_PORT].fSupplied)
SQLGetPrivateProfileString(lpszDSN, INI_PORT,
EMPTYSTR,
lpsetupdlg->aAttr[KEY_PORT].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_PORT].szAttr),
ODBC_INI);
if (lpsetupdlg->aAttr[KEY_PORT].szAttr[0] == '\0')
strcpy(lpsetupdlg->aAttr[KEY_PORT].szAttr, DEFAULT_PORT);
SetDlgItemText(hdlg, IDC_PORT, lpsetupdlg->aAttr[KEY_PORT].szAttr);
/* Username */
if (!lpsetupdlg->aAttr[KEY_USER].fSupplied)
SQLGetPrivateProfileString(lpszDSN, INI_USER,
EMPTYSTR,
lpsetupdlg->aAttr[KEY_USER].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_USER].szAttr),
ODBC_INI);
SetDlgItemText(hdlg, IDC_USER, lpsetupdlg->aAttr[KEY_USER].szAttr);
// Password
if (!lpsetupdlg->aAttr[KEY_PASSWORD].fSupplied)
SQLGetPrivateProfileString(lpszDSN, INI_PASSWORD,
EMPTYSTR,
lpsetupdlg->aAttr[KEY_PASSWORD].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_PASSWORD].szAttr),
ODBC_INI);
SetDlgItemText(hdlg, IDC_PASSWORD, lpsetupdlg->aAttr[KEY_PASSWORD].szAttr);
// ReadOnly Parameter
if (!lpsetupdlg->aAttr[KEY_READONLY].fSupplied) {
SQLGetPrivateProfileString(lpszDSN, INI_READONLY,
EMPTYSTR,
lpsetupdlg->aAttr[KEY_READONLY].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_READONLY].szAttr),
ODBC_INI);
}
if (lpsetupdlg->aAttr[KEY_READONLY].szAttr[0] == '\0')
strcpy(lpsetupdlg->aAttr[KEY_READONLY].szAttr, DEFAULT_READONLY);
CheckDlgButton(hdlg, IDC_READONLY, atoi(lpsetupdlg->aAttr[KEY_READONLY].szAttr));
// Protocol Parameter
if (!lpsetupdlg->aAttr[KEY_PROTOCOL].fSupplied) {
SQLGetPrivateProfileString(lpszDSN, INI_PROTOCOL,
EMPTYSTR,
lpsetupdlg->aAttr[KEY_PROTOCOL].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_PROTOCOL].szAttr),
ODBC_INI);
}
if (strncmp(lpsetupdlg->aAttr[KEY_PROTOCOL].szAttr, PG62, strlen(PG62)) == 0)
CheckDlgButton(hdlg, IDC_PG62, 1);
else
CheckDlgButton(hdlg, IDC_PG62, 0);
// Fill in any defaults
getDSNdefaults(ci);
// CommLog Parameter (this is global)
CheckDlgButton(hdlg, IDC_COMMLOG, globals.commlog);
// 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
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
// Process buttons
case WM_COMMAND:
// Enable/disable the OK button
EnableWindow(GetDlgItem(hdlg, IDOK),
GetDlgItemText(hdlg, IDC_DSNAME,
szItem, sizeof(szItem)));
return TRUE;
}
break;
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
// Accept results
case IDOK:
{
LPSETUPDLG lpsetupdlg;
// Enable/disable the OK button
EnableWindow(GetDlgItem(hdlg, IDOK),
GetDlgItemText(hdlg, IDC_DSNAME,
szItem, sizeof(szItem)));
lpsetupdlg = (LPSETUPDLG)GetWindowLong(hdlg, DWL_USER);
// Retrieve dialog values
if (!lpsetupdlg->fDefault)
GetDlgItemText(hdlg, IDC_DSNAME,
lpsetupdlg->aAttr[KEY_DSN].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_DSN].szAttr));
GetDlgItemText(hdlg, IDC_DESC,
lpsetupdlg->aAttr[KEY_DESC].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_DESC].szAttr));
return TRUE;
}
break;
GetDlgItemText(hdlg, IDC_DATABASE,
lpsetupdlg->aAttr[KEY_DATABASE].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_DATABASE].szAttr));
// Accept results
case IDOK:
{
LPSETUPDLG lpsetupdlg;
GetDlgItemText(hdlg, IDC_PORT,
lpsetupdlg->aAttr[KEY_PORT].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_PORT].szAttr));
GetDlgItemText(hdlg, IDC_SERVER,
lpsetupdlg->aAttr[KEY_SERVER].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_SERVER].szAttr));
GetDlgItemText(hdlg, IDC_USER,
lpsetupdlg->aAttr[KEY_USER].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_USER].szAttr));
GetDlgItemText(hdlg, IDC_PASSWORD,
lpsetupdlg->aAttr[KEY_PASSWORD].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_PASSWORD].szAttr));
if ( IsDlgButtonChecked(hdlg, IDC_PG62))
strcpy(lpsetupdlg->aAttr[KEY_PROTOCOL].szAttr, PG62);
else
lpsetupdlg->aAttr[KEY_PROTOCOL].szAttr[0] = '\0';
sprintf(lpsetupdlg->aAttr[KEY_READONLY].szAttr, "%d", IsDlgButtonChecked(hdlg, IDC_READONLY));
globals.commlog = IsDlgButtonChecked(hdlg, IDC_COMMLOG);
lpsetupdlg = (LPSETUPDLG)GetWindowLong(hdlg, DWL_USER);
// Retrieve dialog values
if (!lpsetupdlg->fDefault)
GetDlgItemText(hdlg, IDC_DSNAME,
lpsetupdlg->ci.dsn,
sizeof(lpsetupdlg->ci.dsn));
// Update ODBC.INI
SetDSNAttributes(hdlg, lpsetupdlg);
// Get Dialog Values
GetDlgStuff(hdlg, &lpsetupdlg->ci);
// Update ODBC.INI
SetDSNAttributes(hdlg, lpsetupdlg);
}
// Return to caller
case IDCANCEL:
EndDialog(hdlg, wParam);
return TRUE;
}
break;
}
// Return to caller
case IDCANCEL:
EndDialog(hdlg, wParam);
return TRUE;
// Message not processed
return FALSE;
case IDC_DRIVER:
DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DRV),
hdlg, driver_optionsProc, (LPARAM) NULL);
return TRUE;
case IDC_DATASOURCE:
{
LPSETUPDLG lpsetupdlg;
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;
}
@ -526,11 +320,13 @@ int CALLBACK ConfigDlgProc
--------------------------------------------------------------------------*/
void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
{
LPCSTR lpsz;
LPCSTR lpszStart;
char aszKey[MAXKEYLEN];
int iElement;
int cbKey;
LPCSTR lpsz;
LPCSTR lpszStart;
char aszKey[MAXKEYLEN];
int cbKey;
char value[MAXPATHLEN];
memset(&lpsetupdlg->ci, 0, sizeof(ConnInfo));
for (lpsz=lpszAttributes; *lpsz; lpsz++)
{ // Extract key name (e.g., DSN), it must be terminated by an equals
@ -543,38 +339,26 @@ void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
break; // Valid key found
}
// Determine the key's index in the key table (-1 if not found)
iElement = -1;
cbKey = lpsz - lpszStart;
if (cbKey < sizeof(aszKey))
{
register int j;
_fmemcpy(aszKey, lpszStart, cbKey);
aszKey[cbKey] = '\0';
for (j = 0; *s_aLookup[j].szKey; j++)
{
if (!lstrcmpi(s_aLookup[j].szKey, aszKey))
{
iElement = s_aLookup[j].iKey;
break;
}
}
}
// Locate end of key value
lpszStart = ++lpsz;
for (; *lpsz; lpsz++);
// Save value if key is known
// NOTE: This code assumes the szAttr buffers in aAttr have been
// zero initialized
if (iElement >= 0)
{
lpsetupdlg->aAttr[iElement].fSupplied = TRUE;
_fmemcpy(lpsetupdlg->aAttr[iElement].szAttr,
lpszStart,
MIN(lpsz-lpszStart+1, sizeof(lpsetupdlg->aAttr[0].szAttr)-1));
}
// lpsetupdlg->aAttr[iElement].fSupplied = TRUE;
_fmemcpy(value, lpszStart, MIN(lpsz-lpszStart+1, MAXPATHLEN));
mylog("aszKey='%s', value='%s'\n", aszKey, value);
// Copy the appropriate value to the conninfo
copyAttributes(&lpsetupdlg->ci, aszKey, value);
}
return;
}
@ -588,12 +372,12 @@ void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
BOOL INTFUNC SetDSNAttributes(HWND hwndParent, LPSETUPDLG lpsetupdlg)
{
LPCSTR lpszDSN; // Pointer to data source name
LPCSTR lpszDSN; // Pointer to data source name
lpszDSN = lpsetupdlg->aAttr[KEY_DSN].szAttr;
lpszDSN = lpsetupdlg->ci.dsn;
// Validate arguments
if (lpsetupdlg->fNewDSN && !*lpsetupdlg->aAttr[KEY_DSN].szAttr)
if (lpsetupdlg->fNewDSN && !*lpsetupdlg->ci.dsn)
return FALSE;
// Write the data source name
@ -614,64 +398,11 @@ BOOL INTFUNC SetDSNAttributes(HWND hwndParent, LPSETUPDLG lpsetupdlg)
// Update ODBC.INI
// Save the value if the data source is new, if it was edited, or if
// it was explicitly supplied
if (hwndParent || lpsetupdlg->aAttr[KEY_DESC].fSupplied )
SQLWritePrivateProfileString(lpszDSN,
INI_KDESC,
lpsetupdlg->aAttr[KEY_DESC].szAttr,
ODBC_INI);
writeDSNinfo(&lpsetupdlg->ci);
if (hwndParent || lpsetupdlg->aAttr[KEY_DATABASE].fSupplied )
SQLWritePrivateProfileString(lpszDSN,
INI_DATABASE,
lpsetupdlg->aAttr[KEY_DATABASE].szAttr,
ODBC_INI);
if (hwndParent || lpsetupdlg->aAttr[KEY_PORT].fSupplied )
SQLWritePrivateProfileString(lpszDSN,
INI_PORT,
lpsetupdlg->aAttr[KEY_PORT].szAttr,
ODBC_INI);
if (hwndParent || lpsetupdlg->aAttr[KEY_SERVER].fSupplied )
SQLWritePrivateProfileString(lpszDSN,
INI_SERVER,
lpsetupdlg->aAttr[KEY_SERVER].szAttr,
ODBC_INI);
if (hwndParent || lpsetupdlg->aAttr[KEY_USER].fSupplied )
SQLWritePrivateProfileString(lpszDSN,
INI_USER,
lpsetupdlg->aAttr[KEY_USER].szAttr,
ODBC_INI);
if (hwndParent || lpsetupdlg->aAttr[KEY_PASSWORD].fSupplied )
SQLWritePrivateProfileString(lpszDSN,
INI_PASSWORD,
lpsetupdlg->aAttr[KEY_PASSWORD].szAttr,
ODBC_INI);
if (hwndParent || lpsetupdlg->aAttr[KEY_READONLY].fSupplied )
SQLWritePrivateProfileString(lpszDSN,
INI_READONLY,
lpsetupdlg->aAttr[KEY_READONLY].szAttr,
ODBC_INI);
if (hwndParent || lpsetupdlg->aAttr[KEY_PROTOCOL].fSupplied )
SQLWritePrivateProfileString(lpszDSN,
INI_PROTOCOL,
lpsetupdlg->aAttr[KEY_PROTOCOL].szAttr,
ODBC_INI);
// CommLog Parameter -- write to ODBCINST_INI (for the whole driver)
if (hwndParent ) {
updateGlobals();
}
// If the data source name has changed, remove the old name
if (lpsetupdlg->aAttr[KEY_DSN].fSupplied &&
lstrcmpi(lpsetupdlg->szDSN, lpsetupdlg->aAttr[KEY_DSN].szAttr))
if (lstrcmpi(lpsetupdlg->szDSN, lpsetupdlg->ci.dsn))
{
SQLRemoveDSNFromIni(lpsetupdlg->szDSN);
}

View File

@ -77,6 +77,7 @@ 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;
@ -84,15 +85,24 @@ struct sockaddr_in sadr;
return 0;
}
host = gethostbyname(hostname);
if (host == NULL) {
self->errornumber = SOCKET_HOST_NOT_FOUND;
self->errormsg = "Could not resolve hostname.";
return 0;
}
memset((char *)&sadr, 0, sizeof(sadr));
memcpy(&(sadr.sin_addr), host->h_addr, host->h_length);
/* 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);

View File

@ -25,6 +25,23 @@
extern GLOBAL_VALUES globals;
/* 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" },
{ 0, NULL }
};
RETCODE SQL_API SQLAllocStmt(HDBC hdbc,
HSTMT FAR *phstmt)
@ -133,6 +150,9 @@ StatementClass *rv;
rv->prepare = FALSE;
rv->status = STMT_ALLOCATED;
rv->maxRows = 0; // driver returns all rows
rv->rowset_size = 1;
rv->scroll_concurrency = SQL_CONCUR_READ_ONLY;
rv->cursor_type = SQL_CURSOR_FORWARD_ONLY;
rv->errormsg = NULL;
rv->errornumber = 0;
rv->errormsg_created = FALSE;
@ -144,10 +164,14 @@ StatementClass *rv;
rv->parameters_allocated = 0;
rv->parameters = 0;
rv->currTuple = -1;
rv->current_col = -1;
rv->result = 0;
rv->data_at_exec = -1;
rv->current_exec_param = -1;
rv->put_data = FALSE;
rv->lobj_fd = -1;
rv->internal = FALSE;
rv->cursor_name[0] = '\0';
}
return rv;
}
@ -183,6 +207,8 @@ SC_Destructor(StatementClass *self)
free(self);
mylog("SC_Destructor: EXIT\n");
return TRUE;
}
@ -194,6 +220,8 @@ SC_free_params(StatementClass *self, char option)
{
int i;
mylog("SC_free_params: ENTER, self=%d\n", self);
if( ! self->parameters)
return;
@ -220,25 +248,22 @@ int i;
self->parameters = NULL;
self->parameters_allocated = 0;
}
mylog("SC_free_params: EXIT\n");
}
int
statement_type(char *statement)
{
if(strnicmp(statement, "SELECT", 6) == 0)
return STMT_TYPE_SELECT;
int i;
else if(strnicmp(statement, "INSERT", 6) == 0)
return STMT_TYPE_INSERT;
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;
else if(strnicmp(statement, "UPDATE", 6) == 0)
return STMT_TYPE_UPDATE;
else if(strnicmp(statement, "DELETE", 6) == 0)
return STMT_TYPE_DELETE;
else
return STMT_TYPE_OTHER;
return STMT_TYPE_OTHER;
}
/* Called from SQLPrepare if STMT_PREMATURE, or
@ -298,12 +323,17 @@ ConnectionClass *conn;
}
self->status = STMT_READY;
self->manual_result = FALSE; // very important
self->currTuple = -1;
self->current_col = -1;
self->errormsg = NULL;
self->errornumber = 0;
self->errormsg_created = 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.
@ -428,8 +458,8 @@ Int2 oldstatus, numcols;
/* The reason is because we can't use declare/fetch cursors without
starting a transaction first.
*/
if ( ! CC_is_in_trans(conn) && (globals.use_declarefetch || STMT_UPDATE(self))) {
if ( ! CC_is_in_trans(conn)) {
mylog(" about to begin a transaction on statement = %u\n", self);
res = CC_send_query(conn, "BEGIN", NULL, NULL);
if ( ! res) {
@ -465,32 +495,32 @@ Int2 oldstatus, numcols;
// in copy_statement...
if (self->statement_type == STMT_TYPE_SELECT) {
char cursor[32];
char fetch[64];
char fetch[128];
sprintf(cursor, "C%u", self);
mylog(" Sending SELECT statement on stmt=%u\n", self);
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, NULL);
if (self->result != NULL) {
if (globals.use_declarefetch && self->result != NULL) {
/* That worked, so now send the fetch to start getting data back */
sprintf(fetch, "fetch %d in %s", globals.fetch_max, cursor);
sprintf(fetch, "fetch %d in %s", globals.fetch_max, self->cursor_name);
// Save the cursor in the result for later use
self->result = CC_send_query( conn, fetch, NULL, cursor);
self->result = CC_send_query( conn, fetch, NULL, self->cursor_name);
}
mylog(" done sending the query:\n");
}
else { // not a SELECT statement so don't use a cursor
mylog(" its NOT a select statement: stmt=%u\n", self);
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
// If we are in autocommit, we must send the commit.
if (CC_is_in_autocommit(conn)) {
if (CC_is_in_autocommit(conn) && STMT_UPDATE(self)) {
CC_send_query(conn, "COMMIT", NULL, NULL);
CC_set_no_trans(conn);
}
@ -512,6 +542,7 @@ Int2 oldstatus, numcols;
self->errornumber = was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND;
self->currTuple = -1; /* set cursor before the first tuple in the list */
self->current_col = -1;
/* see if the query did return any result columns */
numcols = QR_NumResultCols(self->result);
@ -528,9 +559,19 @@ Int2 oldstatus, numcols;
} else { /* Bad Error -- The error message will be in the Connection */
self->errornumber = STMT_EXEC_ERROR;
self->errormsg = "Error while executing the query";
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";
}
CC_abort(conn);
}

View File

@ -45,18 +45,26 @@ typedef enum {
#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
/* statement types */
#define STMT_TYPE_SELECT 0
#define STMT_TYPE_INSERT 1
#define STMT_TYPE_UPDATE 2
#define STMT_TYPE_DELETE 3
#define STMT_TYPE_OTHER 4
#define STMT_TYPE_UNKNOWN 666 // 'unknown' means we don't have the statement yet,
// or haven't looked at it to see what type it is.
// 'other' means we looked, but couldn't tell.
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,
};
#define STMT_UPDATE(stmt) (stmt->statement_type > STMT_TYPE_SELECT)
/******** Statement Handle ***********/
struct StatementClass_ {
@ -68,6 +76,9 @@ struct StatementClass_ {
char *errormsg;
int errornumber;
int maxRows;
int rowset_size;
int cursor_type;
int scroll_concurrency;
/* information on bindings */
BindInfoClass *bindings; /* array to store the binding information */
@ -78,6 +89,8 @@ struct StatementClass_ {
ParameterInfoClass *parameters;
Int4 currTuple;
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 */
@ -91,6 +104,9 @@ struct StatementClass_ {
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[32];
char stmt_with_params[65536 /* MAX_STATEMENT_LEN */]; /* statement after parameter substitution */
};