Update odbc driver to current version V.0244
This commit is contained in:
parent
85f91d0e8e
commit
99d21d5b62
|
@ -1,17 +1,17 @@
|
|||
|
||||
/* Module: bind.c
|
||||
*
|
||||
* Description: This module contains routines related to binding
|
||||
* columns and parameters.
|
||||
*
|
||||
* Classes: BindInfoClass, ParameterInfoClass
|
||||
*
|
||||
* API functions: SQLBindParameter, SQLBindCol, SQLDescribeParam, SQLNumParams,
|
||||
* SQLParamOptions(NI)
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: bind.c
|
||||
*
|
||||
* Description: This module contains routines related to binding
|
||||
* columns and parameters.
|
||||
*
|
||||
* Classes: BindInfoClass, ParameterInfoClass
|
||||
*
|
||||
* API functions: SQLBindParameter, SQLBindCol, SQLDescribeParam, SQLNumParams,
|
||||
* SQLParamOptions(NI)
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
#include "bind.h"
|
||||
#include "environ.h"
|
||||
#include "statement.h"
|
||||
|
@ -19,8 +19,8 @@
|
|||
#include "pgtypes.h"
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
||||
|
||||
// Bind parameters on a statement handle
|
||||
|
||||
|
@ -77,10 +77,11 @@ StatementClass *stmt = (StatementClass *) hstmt;
|
|||
stmt->parameters[i].CType = 0;
|
||||
stmt->parameters[i].SQLType = 0;
|
||||
stmt->parameters[i].precision = 0;
|
||||
stmt->parameters[i].scale = 0;
|
||||
stmt->parameters[i].data_at_exec = FALSE;
|
||||
stmt->parameters[i].EXEC_used = NULL;
|
||||
stmt->parameters[i].EXEC_buffer = NULL;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,26 +95,28 @@ StatementClass *stmt = (StatementClass *) hstmt;
|
|||
stmt->parameters[ipar].CType = fCType;
|
||||
stmt->parameters[ipar].SQLType = fSqlType;
|
||||
stmt->parameters[ipar].precision = cbColDef;
|
||||
stmt->parameters[ipar].scale = ibScale;
|
||||
|
||||
/* If rebinding a parameter that had data-at-exec stuff in it,
|
||||
then free that stuff
|
||||
*/
|
||||
if (stmt->parameters[ipar].EXEC_used) {
|
||||
free(stmt->parameters[ipar].EXEC_used);
|
||||
stmt->parameters[ipar].EXEC_used = NULL;
|
||||
}
|
||||
|
||||
if (stmt->parameters[ipar].EXEC_buffer) {
|
||||
free(stmt->parameters[ipar].EXEC_buffer);
|
||||
stmt->parameters[ipar].EXEC_buffer = NULL;
|
||||
}
|
||||
|
||||
if (pcbValue && *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)
|
||||
stmt->parameters[ipar].data_at_exec = TRUE;
|
||||
else
|
||||
stmt->parameters[ipar].data_at_exec = FALSE;
|
||||
|
||||
stmt->parameters[ipar].scale = ibScale;
|
||||
|
||||
/* If rebinding a parameter that had data-at-exec stuff in it,
|
||||
then free that stuff
|
||||
*/
|
||||
if (stmt->parameters[ipar].EXEC_used) {
|
||||
free(stmt->parameters[ipar].EXEC_used);
|
||||
stmt->parameters[ipar].EXEC_used = NULL;
|
||||
}
|
||||
|
||||
if (stmt->parameters[ipar].EXEC_buffer) {
|
||||
free(stmt->parameters[ipar].EXEC_buffer);
|
||||
stmt->parameters[ipar].EXEC_buffer = NULL;
|
||||
}
|
||||
|
||||
if (pcbValue && *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)
|
||||
stmt->parameters[ipar].data_at_exec = TRUE;
|
||||
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 */
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
|
||||
/* File: bind.h
|
||||
*
|
||||
* Description: See "bind.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: bind.h
|
||||
*
|
||||
* Description: See "bind.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __BIND_H__
|
||||
#define __BIND_H__
|
||||
|
||||
#include "psqlodbc.h"
|
||||
|
||||
/*
|
||||
* BindInfoClass -- stores information about a bound column
|
||||
*/
|
||||
/*
|
||||
* BindInfoClass -- stores information about a bound column
|
||||
*/
|
||||
struct BindInfoClass_ {
|
||||
Int4 buflen; /* size of buffer */
|
||||
char *buffer; /* pointer to the buffer */
|
||||
|
@ -33,10 +33,11 @@ struct ParameterInfoClass_ {
|
|||
Int2 CType;
|
||||
Int2 SQLType;
|
||||
UInt4 precision;
|
||||
Int2 scale;
|
||||
Int4 *EXEC_used;
|
||||
char *EXEC_buffer;
|
||||
char data_at_exec;
|
||||
Int2 scale;
|
||||
Oid lobj_oid;
|
||||
Int4 *EXEC_used; /* amount of data OR the oid of the large object */
|
||||
char *EXEC_buffer; /* the data or the FD of the large object */
|
||||
char data_at_exec;
|
||||
};
|
||||
|
||||
BindInfoClass *create_empty_bindings(int num_columns);
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
|
||||
/* Module: columninfo.c
|
||||
*
|
||||
* Description: This module contains routines related to
|
||||
* reading and storing the field information from a query.
|
||||
*
|
||||
* Classes: ColumnInfoClass (Functions prefix: "CI_")
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "columninfo.h"
|
||||
/* Module: columninfo.c
|
||||
*
|
||||
* Description: This module contains routines related to
|
||||
* reading and storing the field information from a query.
|
||||
*
|
||||
* Classes: ColumnInfoClass (Functions prefix: "CI_")
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "columninfo.h"
|
||||
#include "socket.h"
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
@ -29,6 +29,7 @@ ColumnInfoClass *rv;
|
|||
rv->name = NULL;
|
||||
rv->adtid = NULL;
|
||||
rv->adtsize = NULL;
|
||||
rv->display_size = NULL;
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -54,7 +55,7 @@ int new_num_fields;
|
|||
Oid new_adtid;
|
||||
Int2 new_adtsize;
|
||||
char new_field_name[MAX_MESSAGE_LEN+1];
|
||||
|
||||
|
||||
|
||||
/* at first read in the number of fields that are in the query */
|
||||
new_num_fields = (Int2) SOCK_get_int(sock, sizeof(Int2));
|
||||
|
@ -93,11 +94,12 @@ int num_fields = self->num_fields;
|
|||
if( self->name[lf])
|
||||
free (self->name[lf]);
|
||||
}
|
||||
|
||||
|
||||
/* Safe to call even if null */
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
/* File: columninfo.h
|
||||
*
|
||||
* Description: See "columninfo.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: columninfo.h
|
||||
*
|
||||
* Description: See "columninfo.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __COLUMNINFO_H__
|
||||
#define __COLUMNINFO_H__
|
||||
|
@ -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
|
||||
|
|
|
@ -1,30 +1,33 @@
|
|||
|
||||
/* Module: connection.c
|
||||
*
|
||||
* Description: This module contains routines related to
|
||||
* connecting to and disconnecting from the Postgres DBMS.
|
||||
*
|
||||
* Classes: ConnectionClass (Functions prefix: "CC_")
|
||||
*
|
||||
* API functions: SQLAllocConnect, SQLConnect, SQLDisconnect, SQLFreeConnect,
|
||||
* SQLBrowseConnect(NI)
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: connection.c
|
||||
*
|
||||
* Description: This module contains routines related to
|
||||
* connecting to and disconnecting from the Postgres DBMS.
|
||||
*
|
||||
* Classes: ConnectionClass (Functions prefix: "CC_")
|
||||
*
|
||||
* API functions: SQLAllocConnect, SQLConnect, SQLDisconnect, SQLFreeConnect,
|
||||
* SQLBrowseConnect(NI)
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "environ.h"
|
||||
#include "connection.h"
|
||||
#include "socket.h"
|
||||
#include "statement.h"
|
||||
#include "qresult.h"
|
||||
#include "lobj.h"
|
||||
#include "dlg_specific.h"
|
||||
#include <stdio.h>
|
||||
#include <odbcinst.h>
|
||||
|
||||
#define STMT_INCREMENT 16 /* how many statement holders to allocate at a time */
|
||||
|
||||
extern GLOBAL_VALUES globals;
|
||||
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
|
||||
|
@ -124,12 +130,12 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
|
|||
return SQL_INVALID_HANDLE;
|
||||
|
||||
qlog("conn=%u, SQLDisconnect\n", conn);
|
||||
|
||||
if (conn->status == CONN_EXECUTING) {
|
||||
conn->errornumber = CONN_IN_USE;
|
||||
conn->errormsg = "A transaction is currently being executed";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
if (conn->status == CONN_EXECUTING) {
|
||||
conn->errornumber = CONN_IN_USE;
|
||||
conn->errormsg = "A transaction is currently being executed";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
mylog("SQLDisconnect: about to CC_cleanup\n");
|
||||
|
||||
|
@ -155,12 +161,12 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
|
|||
if ( ! conn)
|
||||
return SQL_INVALID_HANDLE;
|
||||
|
||||
/* Remove the connection from the environment */
|
||||
if ( ! EN_remove_connection(conn->henv, conn)) {
|
||||
conn->errornumber = CONN_IN_USE;
|
||||
conn->errormsg = "A transaction is currently being executed";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
/* Remove the connection from the environment */
|
||||
if ( ! EN_remove_connection(conn->henv, conn)) {
|
||||
conn->errornumber = CONN_IN_USE;
|
||||
conn->errormsg = "A transaction is currently being executed";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
CC_Destructor(conn);
|
||||
|
||||
|
@ -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,75 +343,12 @@ 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
|
||||
CC_connect(ConnectionClass *self, char do_password)
|
||||
{
|
||||
StartupPacket sp;
|
||||
StartupPacket sp;
|
||||
StartupPacket6_2 sp62;
|
||||
QResultClass *res;
|
||||
SocketClass *sock;
|
||||
|
@ -392,7 +356,7 @@ ConnInfo *ci = &(self->connInfo);
|
|||
int areq = -1;
|
||||
int beresp;
|
||||
char msgbuffer[ERROR_MSG_LENGTH];
|
||||
char salt[2];
|
||||
char salt[2];
|
||||
|
||||
if ( do_password)
|
||||
|
||||
|
@ -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;
|
||||
|
@ -439,18 +421,18 @@ char salt[2];
|
|||
return 0;
|
||||
}
|
||||
mylog("connection to the server socket succeeded.\n");
|
||||
|
||||
if ( PROTOCOL_62(ci)) {
|
||||
sock->reverse = TRUE; /* make put_int and get_int work for 6.2 */
|
||||
|
||||
memset(&sp62, 0, sizeof(StartupPacket6_2));
|
||||
SOCK_put_int(sock, htonl(4+sizeof(StartupPacket6_2)), 4);
|
||||
sp62.authtype = htonl(NO_AUTHENTICATION);
|
||||
strncpy(sp62.database, ci->database, PATH_SIZE);
|
||||
strncpy(sp62.user, ci->username, NAMEDATALEN);
|
||||
SOCK_put_n_char(sock, (char *) &sp62, sizeof(StartupPacket6_2));
|
||||
SOCK_flush_output(sock);
|
||||
}
|
||||
|
||||
if ( PROTOCOL_62(ci)) {
|
||||
sock->reverse = TRUE; /* make put_int and get_int work for 6.2 */
|
||||
|
||||
memset(&sp62, 0, sizeof(StartupPacket6_2));
|
||||
SOCK_put_int(sock, htonl(4+sizeof(StartupPacket6_2)), 4);
|
||||
sp62.authtype = htonl(NO_AUTHENTICATION);
|
||||
strncpy(sp62.database, ci->database, PATH_SIZE);
|
||||
strncpy(sp62.user, ci->username, NAMEDATALEN);
|
||||
SOCK_put_n_char(sock, (char *) &sp62, sizeof(StartupPacket6_2));
|
||||
SOCK_flush_output(sock);
|
||||
}
|
||||
else {
|
||||
memset(&sp, 0, sizeof(StartupPacket));
|
||||
|
||||
|
@ -465,7 +447,7 @@ char salt[2];
|
|||
|
||||
SOCK_put_n_char(sock, (char *) &sp, sizeof(StartupPacket));
|
||||
SOCK_flush_output(sock);
|
||||
}
|
||||
}
|
||||
|
||||
mylog("sent the authentication block.\n");
|
||||
|
||||
|
@ -484,9 +466,9 @@ char salt[2];
|
|||
|
||||
// ***************************************************
|
||||
// Now get the authentication request from backend
|
||||
// ***************************************************
|
||||
// ***************************************************
|
||||
|
||||
if ( ! PROTOCOL_62(ci)) do {
|
||||
if ( ! PROTOCOL_62(ci)) do {
|
||||
|
||||
if (do_password)
|
||||
beresp = 'R';
|
||||
|
@ -590,15 +572,19 @@ char salt[2];
|
|||
|
||||
mylog("empty query seems to be OK.\n");
|
||||
|
||||
|
||||
/**********************************************/
|
||||
/******* Send any initial settings *********/
|
||||
/**********************************************/
|
||||
|
||||
CC_send_settings(self);
|
||||
|
||||
|
||||
CC_clear_error(self); /* clear any initial command errors */
|
||||
/**********************************************/
|
||||
/******* Send any initial settings *********/
|
||||
/**********************************************/
|
||||
|
||||
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;
|
||||
|
||||
return 1;
|
||||
|
@ -650,8 +636,8 @@ int i;
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/* Create a more informative error message by concatenating the connection
|
||||
error message with its socket error message.
|
||||
/* Create a more informative error message by concatenating the connection
|
||||
error message with its socket error message.
|
||||
*/
|
||||
char *
|
||||
CC_create_errormsg(ConnectionClass *self)
|
||||
|
@ -705,14 +691,14 @@ int rv;
|
|||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/* The "result_in" is only used by QR_next_tuple() to fetch another group of rows into
|
||||
the same existing QResultClass (this occurs when the tuple cache is depleted and
|
||||
needs to be re-filled).
|
||||
|
||||
The "cursor" is used by SQLExecute to associate a statement handle as the cursor name
|
||||
(i.e., C3326857) for SQL select statements. This cursor is then used in future
|
||||
'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements.
|
||||
|
||||
/* The "result_in" is only used by QR_next_tuple() to fetch another group of rows into
|
||||
the same existing QResultClass (this occurs when the tuple cache is depleted and
|
||||
needs to be re-filled).
|
||||
|
||||
The "cursor" is used by SQLExecute to associate a statement handle as the cursor name
|
||||
(i.e., C3326857) for SQL select statements. This cursor is then used in future
|
||||
'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements.
|
||||
*/
|
||||
QResultClass *
|
||||
CC_send_query(ConnectionClass *self, char *query, QResultClass *result_in, char *cursor)
|
||||
|
@ -822,9 +808,16 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
|
|||
SOCK_put_string(sock, "Q ");
|
||||
SOCK_flush_output(sock);
|
||||
while(!clear) {
|
||||
SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
|
||||
mylog("send_query: read command '%s'\n", cmdbuffer);
|
||||
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);
|
||||
|
@ -925,55 +918,270 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
char
|
||||
CC_send_settings(ConnectionClass *self)
|
||||
{
|
||||
char ini_query[MAX_MESSAGE_LEN];
|
||||
ConnInfo *ci = &(self->connInfo);
|
||||
QResultClass *res;
|
||||
|
||||
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);
|
||||
|
||||
/* Global settings */
|
||||
if (globals.conn_settings[0] != '\0')
|
||||
sprintf(&ini_query[strlen(ini_query)], "%s%s",
|
||||
ini_query[0] != '\0' ? "; " : "",
|
||||
globals.conn_settings);
|
||||
|
||||
/* Per Datasource settings */
|
||||
if (ci->conn_settings[0] != '\0')
|
||||
sprintf(&ini_query[strlen(ini_query)], "%s%s",
|
||||
ini_query[0] != '\0' ? "; " : "",
|
||||
ci->conn_settings);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
if ( res == NULL ||
|
||||
QR_get_status(res) == PGRES_BAD_RESPONSE ||
|
||||
QR_get_status(res) == PGRES_FATAL_ERROR ||
|
||||
QR_get_status(res) == PGRES_INTERNAL_ERROR) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
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';
|
||||
|
||||
/* 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')
|
||||
sprintf(&ini_query[strlen(ini_query)], "%s%s",
|
||||
ini_query[0] != '\0' ? "; " : "",
|
||||
globals.conn_settings);
|
||||
|
||||
/* Per Datasource settings */
|
||||
if (ci->conn_settings[0] != '\0')
|
||||
sprintf(&ini_query[strlen(ini_query)], "%s%s",
|
||||
ini_query[0] != '\0' ? "; " : "",
|
||||
ci->conn_settings);
|
||||
|
||||
if (ini_query[0] != '\0') {
|
||||
mylog("Sending Initial Connection query: '%s'\n", ini_query);
|
||||
|
||||
result = SQLExecDirect(hstmt, ini_query, SQL_NTS);
|
||||
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
|
||||
SQLFreeStmt(hstmt, SQL_DROP);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SQLFreeStmt(hstmt, SQL_DROP);
|
||||
|
||||
}
|
||||
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);
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
|
||||
/* File: connection.h
|
||||
*
|
||||
* Description: See "connection.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: connection.h
|
||||
*
|
||||
* Description: See "connection.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __CONNECTION_H__
|
||||
#define __CONNECTION_H__
|
||||
|
||||
#include <windows.h>
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
||||
#include "psqlodbc.h"
|
||||
|
||||
typedef enum {
|
||||
|
@ -44,9 +45,9 @@ typedef enum {
|
|||
/* SetConnectOption: corresponds to ODBC--"S1009" */
|
||||
#define CONN_TRANSACT_IN_PROGRES 7
|
||||
#define CONN_NO_MEMORY_ERROR 8
|
||||
#define CONN_NOT_IMPLEMENTED_ERROR 9
|
||||
#define CONN_NOT_IMPLEMENTED_ERROR 9
|
||||
#define CONN_INVALID_AUTHENTICATION 10
|
||||
#define CONN_AUTH_TYPE_UNSUPPORTED 11
|
||||
#define CONN_AUTH_TYPE_UNSUPPORTED 11
|
||||
|
||||
|
||||
/* Conn_status defines */
|
||||
|
@ -77,12 +78,12 @@ typedef enum {
|
|||
#define SM_OPTIONS 64
|
||||
#define SM_UNUSED 64
|
||||
#define SM_TTY 64
|
||||
|
||||
/* Old 6.2 protocol defines */
|
||||
#define NO_AUTHENTICATION 7
|
||||
#define PATH_SIZE 64
|
||||
#define ARGV_SIZE 64
|
||||
#define NAMEDATALEN 16
|
||||
|
||||
/* Old 6.2 protocol defines */
|
||||
#define NO_AUTHENTICATION 7
|
||||
#define PATH_SIZE 64
|
||||
#define ARGV_SIZE 64
|
||||
#define NAMEDATALEN 16
|
||||
|
||||
typedef unsigned int ProtocolVersion;
|
||||
|
||||
|
@ -90,7 +91,7 @@ typedef unsigned int ProtocolVersion;
|
|||
#define PG_PROTOCOL_LATEST PG_PROTOCOL(1, 0)
|
||||
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(0, 0)
|
||||
|
||||
/* This startup packet is to support latest Postgres protocol (6.3) */
|
||||
/* This startup packet is to support latest Postgres protocol (6.3) */
|
||||
typedef struct _StartupPacket
|
||||
{
|
||||
ProtocolVersion protoVersion;
|
||||
|
@ -100,41 +101,47 @@ typedef struct _StartupPacket
|
|||
char unused[SM_UNUSED];
|
||||
char tty[SM_TTY];
|
||||
} StartupPacket;
|
||||
|
||||
|
||||
|
||||
|
||||
/* This startup packet is to support pre-Postgres 6.3 protocol */
|
||||
typedef struct _StartupPacket6_2
|
||||
{
|
||||
unsigned int authtype;
|
||||
char database[PATH_SIZE];
|
||||
char user[NAMEDATALEN];
|
||||
char options[ARGV_SIZE];
|
||||
char execfile[ARGV_SIZE];
|
||||
char tty[PATH_SIZE];
|
||||
} StartupPacket6_2;
|
||||
|
||||
|
||||
/* Structure to hold all the connection attributes for a specific
|
||||
connection (used for both registry and file, DSN and DRIVER)
|
||||
typedef struct _StartupPacket6_2
|
||||
{
|
||||
unsigned int authtype;
|
||||
char database[PATH_SIZE];
|
||||
char user[NAMEDATALEN];
|
||||
char options[ARGV_SIZE];
|
||||
char execfile[ARGV_SIZE];
|
||||
char tty[PATH_SIZE];
|
||||
} StartupPacket6_2;
|
||||
|
||||
|
||||
/* Structure to hold all the connection attributes for a specific
|
||||
connection (used for both registry and file, DSN and DRIVER)
|
||||
*/
|
||||
typedef struct {
|
||||
char dsn[MEDIUM_REGISTRY_LEN];
|
||||
char desc[MEDIUM_REGISTRY_LEN];
|
||||
char driver[MEDIUM_REGISTRY_LEN];
|
||||
char server[MEDIUM_REGISTRY_LEN];
|
||||
char database[MEDIUM_REGISTRY_LEN];
|
||||
char username[MEDIUM_REGISTRY_LEN];
|
||||
char password[MEDIUM_REGISTRY_LEN];
|
||||
char conn_settings[LARGE_REGISTRY_LEN];
|
||||
char password[MEDIUM_REGISTRY_LEN];
|
||||
char conn_settings[LARGE_REGISTRY_LEN];
|
||||
char protocol[SMALL_REGISTRY_LEN];
|
||||
char port[SMALL_REGISTRY_LEN];
|
||||
char readonly[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;
|
||||
|
||||
|
||||
/* Macro to determine is the connection using 6.2 protocol? */
|
||||
#define PROTOCOL_62(conninfo_) (strncmp((conninfo_)->protocol, PG62, strlen(PG62)) == 0)
|
||||
|
||||
|
||||
#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? */
|
||||
};
|
||||
|
@ -162,15 +170,14 @@ struct ConnectionClass_ {
|
|||
/* for CC_DSN_info */
|
||||
#define CONN_DONT_OVERWRITE 0
|
||||
#define CONN_OVERWRITE 1
|
||||
|
||||
|
||||
|
||||
|
||||
/* 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);
|
||||
char CC_send_settings(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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +1,11 @@
|
|||
|
||||
/* File: convert.h
|
||||
*
|
||||
* Description: See "convert.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: convert.h
|
||||
*
|
||||
* Description: See "convert.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __CONVERT_H__
|
||||
#define __CONVERT_H__
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
char *convert_linefeeds(char *s, char *dst, size_t max);
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -1,16 +1,16 @@
|
|||
|
||||
/* Module: drvconn.c
|
||||
*
|
||||
* Description: This module contains only routines related to
|
||||
* implementing SQLDriverConnect.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: SQLDriverConnect
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: drvconn.c
|
||||
*
|
||||
* Description: This module contains only routines related to
|
||||
* implementing SQLDriverConnect.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: SQLDriverConnect
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -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;
|
||||
|
@ -102,14 +104,14 @@ dialog:
|
|||
break;
|
||||
}
|
||||
|
||||
/* Password is not a required parameter unless authentication asks for it.
|
||||
For now, I think its better to just let the application ask over and over until
|
||||
a password is entered (the user can always hit Cancel to get out)
|
||||
*/
|
||||
/* Password is not a required parameter unless authentication asks for it.
|
||||
For now, I think its better to just let the application ask over and over until
|
||||
a password is entered (the user can always hit Cancel to get out)
|
||||
*/
|
||||
if( ci->username[0] == '\0' ||
|
||||
ci->server[0] == '\0' ||
|
||||
ci->database[0] == '\0' ||
|
||||
ci->port[0] == '\0') {
|
||||
ci->port[0] == '\0') {
|
||||
// (password_required && ci->password[0] == '\0'))
|
||||
|
||||
return SQL_NO_DATA_FOUND;
|
||||
|
@ -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));
|
||||
|
||||
CheckDlgButton(hdlg, PG62_EDIT, PROTOCOL_62(ci));
|
||||
|
||||
/* The driver connect dialog box allows manipulating this global variable */
|
||||
CheckDlgButton(hdlg, COMMLOG_EDIT, globals.commlog);
|
||||
/* Change the caption for the setup dialog */
|
||||
SetWindowText(hdlg, "PostgreSQL Connection");
|
||||
|
||||
SetWindowText(GetDlgItem(hdlg, IDC_DATASOURCE), "Connection");
|
||||
|
||||
/* Hide the DSN and description fields */
|
||||
ShowWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), SW_HIDE);
|
||||
ShowWindow(GetDlgItem(hdlg, IDC_DSNAME), SW_HIDE);
|
||||
ShowWindow(GetDlgItem(hdlg, IDC_DESCTEXT), SW_HIDE);
|
||||
ShowWindow(GetDlgItem(hdlg, IDC_DESC), SW_HIDE);
|
||||
|
||||
SetWindowLong(hdlg, DWL_USER, lParam);// Save the ConnInfo for the "OK"
|
||||
|
||||
SetDlgStuff(hdlg, ci);
|
||||
|
||||
if (ci->database[0] == '\0')
|
||||
; /* default focus */
|
||||
else if (ci->server[0] == '\0')
|
||||
SetFocus(GetDlgItem(hdlg, 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);
|
||||
|
||||
GetDlgStuff(hdlg, ci);
|
||||
|
||||
sprintf(ci->readonly, "%d", IsDlgButtonChecked(hdlg, READONLY_EDIT));
|
||||
|
||||
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);
|
||||
// Copy the appropriate value to the conninfo
|
||||
copyAttributes(ci, attribute, 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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
free(our_connect_string);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
|
||||
/* Module: environ.c
|
||||
*
|
||||
* Description: This module contains routines related to
|
||||
* the environment, such as storing connection handles,
|
||||
* and returning errors.
|
||||
*
|
||||
* Classes: EnvironmentClass (Functions prefix: "EN_")
|
||||
*
|
||||
* API functions: SQLAllocEnv, SQLFreeEnv, SQLError
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: environ.c
|
||||
*
|
||||
* Description: This module contains routines related to
|
||||
* the environment, such as storing connection handles,
|
||||
* and returning errors.
|
||||
*
|
||||
* Classes: EnvironmentClass (Functions prefix: "EN_")
|
||||
*
|
||||
* API functions: SQLAllocEnv, SQLFreeEnv, SQLError
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "environ.h"
|
||||
#include "connection.h"
|
||||
|
@ -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");
|
||||
|
@ -213,10 +223,10 @@ int status;
|
|||
case CONN_OPENDB_ERROR:
|
||||
strcpy(szSqlState, "08001");
|
||||
// unable to connect to data source
|
||||
break;
|
||||
case CONN_INVALID_AUTHENTICATION:
|
||||
case CONN_AUTH_TYPE_UNSUPPORTED:
|
||||
strcpy(szSqlState, "28000");
|
||||
break;
|
||||
case CONN_INVALID_AUTHENTICATION:
|
||||
case CONN_AUTH_TYPE_UNSUPPORTED:
|
||||
strcpy(szSqlState, "28000");
|
||||
break;
|
||||
case CONN_STMT_ALLOC_ERROR:
|
||||
strcpy(szSqlState, "S1001");
|
||||
|
@ -339,7 +349,7 @@ EnvironmentClass *rv;
|
|||
rv->errormsg = 0;
|
||||
rv->errornumber = 0;
|
||||
}
|
||||
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -356,7 +366,7 @@ char rv = 1;
|
|||
// the source--they should not be freed
|
||||
|
||||
/* Free any connections belonging to this environment */
|
||||
for (lf = 0; lf < MAX_CONNECTIONS; lf++) {
|
||||
for (lf = 0; lf < MAX_CONNECTIONS; lf++) {
|
||||
if (conns[lf] && conns[lf]->henv == self)
|
||||
rv = rv && CC_Destructor(conns[lf]);
|
||||
}
|
||||
|
@ -383,16 +393,16 @@ char
|
|||
EN_add_connection(EnvironmentClass *self, ConnectionClass *conn)
|
||||
{
|
||||
int i;
|
||||
|
||||
mylog("EN_add_connection: self = %u, conn = %u\n", self, conn);
|
||||
|
||||
mylog("EN_add_connection: self = %u, conn = %u\n", self, conn);
|
||||
|
||||
for (i = 0; i < MAX_CONNECTIONS; i++) {
|
||||
if ( ! conns[i]) {
|
||||
if ( ! conns[i]) {
|
||||
conn->henv = self;
|
||||
conns[i] = conn;
|
||||
|
||||
mylog(" added at i =%d, conn->henv = %u, conns[i]->henv = %u\n",
|
||||
i, conn->henv, conns[i]->henv);
|
||||
conns[i] = conn;
|
||||
|
||||
mylog(" added at i =%d, conn->henv = %u, conns[i]->henv = %u\n",
|
||||
i, conn->henv, conns[i]->henv);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
/* File: environ.h
|
||||
*
|
||||
* Description: See "environ.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: environ.h
|
||||
*
|
||||
* Description: See "environ.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ENVIRON_H__
|
||||
#define __ENVIRON_H__
|
||||
|
@ -13,15 +13,16 @@
|
|||
#include "psqlodbc.h"
|
||||
#include <windows.h>
|
||||
#include <sql.h>
|
||||
|
||||
#include <sqlext.h>
|
||||
|
||||
#define ENV_ALLOC_ERROR 1
|
||||
|
||||
|
||||
/********** Environment Handle *************/
|
||||
struct EnvironmentClass_ {
|
||||
char *errormsg;
|
||||
int errornumber;
|
||||
};
|
||||
|
||||
|
||||
/* Environment prototypes */
|
||||
EnvironmentClass *EN_Constructor(void);
|
||||
char EN_Destructor(EnvironmentClass *self);
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
|
||||
/* Module: execute.c
|
||||
*
|
||||
* Description: This module contains routines related to
|
||||
* preparing and executing an SQL statement.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: SQLPrepare, SQLExecute, SQLExecDirect, SQLTransact,
|
||||
* SQLCancel, SQLNativeSql, SQLParamData, SQLPutData
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: execute.c
|
||||
*
|
||||
* Description: This module contains routines related to
|
||||
* preparing and executing an SQL statement.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: SQLPrepare, SQLExecute, SQLExecDirect, SQLTransact,
|
||||
* SQLCancel, SQLNativeSql, SQLParamData, SQLPutData
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "psqlodbc.h"
|
||||
#include <stdio.h>
|
||||
|
@ -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 */
|
||||
break;
|
||||
|
||||
/* NO Break! -- Contiue the same way as with a newly allocated statement ! */
|
||||
case STMT_FINISHED:
|
||||
mylog("**** SQLPrepare: STMT_FINISHED, recycle\n");
|
||||
SC_recycle_statement(self); /* recycle the statement, but do not remove parameter bindings */
|
||||
break;
|
||||
|
||||
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;
|
||||
break;
|
||||
|
||||
return SQL_SUCCESS;
|
||||
|
||||
case STMT_READY: /* SQLPrepare has already been called -- Just changed the SQL statement that is assigned to the handle */
|
||||
case STMT_READY:
|
||||
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;
|
||||
|
||||
case STMT_FINISHED:
|
||||
mylog("**** SQLPrepare: STMT_FINISHED\n");
|
||||
/* No BREAK: continue as with STMT_EXECUTING */
|
||||
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;
|
||||
|
@ -165,10 +147,10 @@ StatementClass *stmt = (StatementClass *) hstmt;
|
|||
RETCODE SQL_API SQLExecute(
|
||||
HSTMT hstmt)
|
||||
{
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
ConnectionClass *conn;
|
||||
int i, retval;
|
||||
|
||||
int i, retval;
|
||||
|
||||
|
||||
if ( ! stmt)
|
||||
return SQL_INVALID_HANDLE;
|
||||
|
@ -212,45 +194,45 @@ int i, retval;
|
|||
stmt->errornumber = STMT_STATUS_ERROR;
|
||||
stmt->errormsg = "The handle does not point to a statement that is ready to be executed";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* The bound parameters could have possibly changed since the last execute
|
||||
of this statement? Therefore check for params and re-copy.
|
||||
*/
|
||||
stmt->data_at_exec = -1;
|
||||
for (i = 0; i < stmt->parameters_allocated; i++) {
|
||||
/* Check for data at execution parameters */
|
||||
if ( stmt->parameters[i].data_at_exec == TRUE) {
|
||||
if (stmt->data_at_exec < 0)
|
||||
stmt->data_at_exec = 1;
|
||||
else
|
||||
stmt->data_at_exec++;
|
||||
}
|
||||
}
|
||||
// If there are some data at execution parameters, return need data
|
||||
// SQLParamData and SQLPutData will be used to send params and execute the statement.
|
||||
if (stmt->data_at_exec > 0)
|
||||
return SQL_NEED_DATA;
|
||||
|
||||
|
||||
mylog("SQLExecute: copying statement params: trans_status=%d, len=%d, stmt='%s'\n", conn->transact_status, strlen(stmt->statement), stmt->statement);
|
||||
|
||||
// Create the statement with parameters substituted.
|
||||
retval = copy_statement_with_parameters(stmt);
|
||||
if( retval != SQL_SUCCESS)
|
||||
/* error msg passed from above */
|
||||
return retval;
|
||||
|
||||
mylog(" stmt_with_params = '%s'\n", stmt->stmt_with_params);
|
||||
|
||||
|
||||
return SC_execute(stmt);
|
||||
}
|
||||
|
||||
|
||||
/* The bound parameters could have possibly changed since the last execute
|
||||
of this statement? Therefore check for params and re-copy.
|
||||
*/
|
||||
stmt->data_at_exec = -1;
|
||||
for (i = 0; i < stmt->parameters_allocated; i++) {
|
||||
/* Check for data at execution parameters */
|
||||
if ( stmt->parameters[i].data_at_exec == TRUE) {
|
||||
if (stmt->data_at_exec < 0)
|
||||
stmt->data_at_exec = 1;
|
||||
else
|
||||
stmt->data_at_exec++;
|
||||
}
|
||||
}
|
||||
// If there are some data at execution parameters, return need data
|
||||
// SQLParamData and SQLPutData will be used to send params and execute the statement.
|
||||
if (stmt->data_at_exec > 0)
|
||||
return SQL_NEED_DATA;
|
||||
|
||||
|
||||
mylog("SQLExecute: copying statement params: trans_status=%d, len=%d, stmt='%s'\n", conn->transact_status, strlen(stmt->statement), stmt->statement);
|
||||
|
||||
// Create the statement with parameters substituted.
|
||||
retval = copy_statement_with_parameters(stmt);
|
||||
if( retval != SQL_SUCCESS)
|
||||
/* error msg passed from above */
|
||||
return retval;
|
||||
|
||||
mylog(" stmt_with_params = '%s'\n", stmt->stmt_with_params);
|
||||
|
||||
|
||||
return SC_execute(stmt);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// - - - - - - - - -
|
||||
RETCODE SQL_API SQLTransact(
|
||||
|
@ -325,24 +307,24 @@ mylog("**** SQLTransact: hdbc=%u, henv=%u\n", hdbc, henv);
|
|||
RETCODE SQL_API SQLCancel(
|
||||
HSTMT hstmt) // Statement to cancel.
|
||||
{
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
|
||||
// Check if this can handle canceling in the middle of a SQLPutData?
|
||||
if ( ! stmt)
|
||||
return SQL_INVALID_HANDLE;
|
||||
|
||||
// Not in the middle of SQLParamData/SQLPutData so cancel like a close.
|
||||
if (stmt->data_at_exec < 0)
|
||||
return SQLFreeStmt(hstmt, SQL_CLOSE);
|
||||
|
||||
// In the middle of SQLParamData/SQLPutData, so cancel that.
|
||||
// Note, any previous data-at-exec buffers will be freed in the recycle
|
||||
// if they call SQLExecDirect or SQLExecute again.
|
||||
|
||||
stmt->data_at_exec = -1;
|
||||
stmt->current_exec_param = -1;
|
||||
stmt->put_data = FALSE;
|
||||
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
|
||||
// Check if this can handle canceling in the middle of a SQLPutData?
|
||||
if ( ! stmt)
|
||||
return SQL_INVALID_HANDLE;
|
||||
|
||||
// Not in the middle of SQLParamData/SQLPutData so cancel like a close.
|
||||
if (stmt->data_at_exec < 0)
|
||||
return SQLFreeStmt(hstmt, SQL_CLOSE);
|
||||
|
||||
// In the middle of SQLParamData/SQLPutData, so cancel that.
|
||||
// Note, any previous data-at-exec buffers will be freed in the recycle
|
||||
// if they call SQLExecDirect or SQLExecute again.
|
||||
|
||||
stmt->data_at_exec = -1;
|
||||
stmt->current_exec_param = -1;
|
||||
stmt->put_data = FALSE;
|
||||
|
||||
}
|
||||
|
||||
// - - - - - - - - -
|
||||
|
@ -371,45 +353,63 @@ RETCODE SQL_API SQLNativeSql(
|
|||
RETCODE SQL_API SQLParamData(
|
||||
HSTMT hstmt,
|
||||
PTR FAR *prgbValue)
|
||||
{
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
int i, retval;
|
||||
|
||||
if ( ! stmt)
|
||||
return SQL_INVALID_HANDLE;
|
||||
|
||||
if (stmt->data_at_exec < 0) {
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
stmt->errormsg = "No execution-time parameters for this statement";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
if (stmt->data_at_exec > stmt->parameters_allocated) {
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
stmt->errormsg = "Too many execution-time parameters were present";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
return SC_execute(stmt);
|
||||
}
|
||||
|
||||
/* At least 1 data at execution parameter, so Fill in the token value */
|
||||
for (i = 0; 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 */
|
||||
}
|
||||
}
|
||||
|
||||
return SQL_NEED_DATA;
|
||||
{
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
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";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
if (stmt->data_at_exec > stmt->parameters_allocated) {
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
stmt->errormsg = "Too many execution-time parameters were present";
|
||||
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 < stmt->parameters_allocated; i++) {
|
||||
if (stmt->parameters[i].data_at_exec == TRUE) {
|
||||
stmt->data_at_exec--;
|
||||
stmt->current_exec_param = i;
|
||||
stmt->put_data = FALSE;
|
||||
*prgbValue = stmt->parameters[i].buffer; /* token */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SQL_NEED_DATA;
|
||||
}
|
||||
|
||||
// - - - - - - - - -
|
||||
|
@ -422,114 +422,157 @@ RETCODE SQL_API SQLPutData(
|
|||
PTR rgbValue,
|
||||
SDWORD cbValue)
|
||||
{
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
char *buffer;
|
||||
SDWORD *used;
|
||||
int old_pos;
|
||||
|
||||
|
||||
if ( ! stmt)
|
||||
return SQL_INVALID_HANDLE;
|
||||
|
||||
|
||||
if (stmt->current_exec_param < 0) {
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
stmt->errormsg = "Previous call was not SQLPutData or SQLParamData";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
if ( ! stmt->put_data) { /* first call */
|
||||
|
||||
mylog("SQLPutData: (1) cbValue = %d\n", cbValue);
|
||||
|
||||
stmt->put_data = TRUE;
|
||||
|
||||
used = (SDWORD *) malloc(sizeof(SDWORD));
|
||||
if ( ! 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 (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));
|
||||
|
||||
*used = cbValue;
|
||||
|
||||
}
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return SQL_SUCCESS;
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
int old_pos, retval;
|
||||
ParameterInfoClass *current_param;
|
||||
char *buffer;
|
||||
|
||||
|
||||
if ( ! stmt)
|
||||
return SQL_INVALID_HANDLE;
|
||||
|
||||
|
||||
if (stmt->current_exec_param < 0) {
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
stmt->errormsg = "Previous call was not SQLPutData or SQLParamData";
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
*current_param->EXEC_used = cbValue;
|
||||
|
||||
if (cbValue == SQL_NULL_DATA)
|
||||
return SQL_SUCCESS;
|
||||
|
||||
|
||||
/* Handle Long Var Binary with Large Objects */
|
||||
if ( current_param->SQLType == SQL_LONGVARBINARY) {
|
||||
|
||||
/* 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 *) ¤t_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);
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
else if (cbValue > 0) {
|
||||
|
||||
old_pos = *current_param->EXEC_used;
|
||||
|
||||
*current_param->EXEC_used += cbValue;
|
||||
|
||||
mylog(" cbValue = %d, old_pos = %d, *used = %d\n", cbValue, old_pos, *current_param->EXEC_used);
|
||||
|
||||
/* dont lose the old pointer in case out of memory */
|
||||
buffer = realloc(current_param->EXEC_buffer, *current_param->EXEC_used + 1);
|
||||
if ( ! buffer) {
|
||||
stmt->errornumber = STMT_NO_MEMORY_ERROR;
|
||||
stmt->errormsg = "Out of memory in 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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,28 +1,27 @@
|
|||
|
||||
/* Module: misc.c
|
||||
*
|
||||
* Description: This module contains miscellaneous routines
|
||||
* such as for debugging/logging and string functions.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: misc.c
|
||||
*
|
||||
* Description: This module contains miscellaneous routines
|
||||
* such as for debugging/logging and string functions.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <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,31 +95,23 @@ 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 {
|
||||
if (src_len < dst_len) {
|
||||
memcpy(dst, src, src_len);
|
||||
dst[src_len] = '\0';
|
||||
dst[src_len] = '\0';
|
||||
}
|
||||
else {
|
||||
memcpy(dst, src, dst_len-1);
|
||||
dst[dst_len-1] = '\0'; /* truncated */
|
||||
return STRCPY_TRUNCATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return strlen(dst);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -138,7 +135,7 @@ unsigned int i;
|
|||
else if (len == SQL_NTS)
|
||||
len = strlen(src) + 1;
|
||||
|
||||
for(i = 0; src[i] && i < len - 1; i++) {
|
||||
for(i = 0; src[i] && i < len - 1; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
/* File: misc.h
|
||||
*
|
||||
* Description: See "misc.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: misc.h
|
||||
*
|
||||
* Description: See "misc.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MISC_H__
|
||||
#define __MISC_H__
|
||||
|
@ -23,7 +23,7 @@
|
|||
/* Uncomment Q_LOG to compile in the qlog() statements (Communications log, i.e. CommLog).
|
||||
This logfile contains serious log statements that are intended for an
|
||||
end user to be able to read and understand. It is controlled by the
|
||||
'CommLog' flag in the ODBCINST.INI portion of the registry (see above),
|
||||
'CommLog' flag in the ODBCINST.INI portion of the registry (see above),
|
||||
which is manipulated on the setup/connection dialog boxes.
|
||||
*/
|
||||
#define Q_LOG
|
||||
|
@ -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
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
|
||||
/********************************************************************
|
||||
|
||||
PSQLODBC.DLL - A library to talk to the PostgreSQL DBMS using ODBC.
|
||||
|
||||
|
||||
Copyright (C) 1998; Insight Distribution Systems
|
||||
|
||||
The code contained in this library is based on code written by
|
||||
Christian Czezatke and Dan McGuirk, (C) 1996.
|
||||
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Library General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library (see "license.txt"); if not, write to
|
||||
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
|
||||
02139, USA.
|
||||
|
||||
|
||||
How to contact the author:
|
||||
|
||||
email: byronn@insightdist.com (Byron Nikolaidis)
|
||||
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
|
||||
/********************************************************************
|
||||
|
||||
PSQLODBC.DLL - A library to talk to the PostgreSQL DBMS using ODBC.
|
||||
|
||||
|
||||
Copyright (C) 1998; Insight Distribution Systems
|
||||
|
||||
The code contained in this library is based on code written by
|
||||
Christian Czezatke and Dan McGuirk, (C) 1996.
|
||||
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Library General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library (see "license.txt"); if not, write to
|
||||
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
|
||||
02139, USA.
|
||||
|
||||
|
||||
How to contact the author:
|
||||
|
||||
email: byronn@insightdist.com (Byron Nikolaidis)
|
||||
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
|
|
|
@ -1,25 +1,30 @@
|
|||
|
||||
/* Module: options.c
|
||||
*
|
||||
* Description: This module contains routines for getting/setting
|
||||
* connection and statement options.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: SQLSetConnectOption, SQLSetStmtOption, SQLGetConnectOption,
|
||||
* SQLGetStmtOption
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: options.c
|
||||
*
|
||||
* Description: This module contains routines for getting/setting
|
||||
* connection and statement options.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: SQLSetConnectOption, SQLSetStmtOption, SQLGetConnectOption,
|
||||
* SQLGetStmtOption
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "psqlodbc.h"
|
||||
#include <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:
|
||||
/* 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;
|
||||
return SQL_ERROR;
|
||||
break;
|
||||
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, "");
|
||||
|
||||
return SQL_SUCCESS;
|
||||
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;
|
||||
|
||||
default:
|
||||
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
|
||||
stmt->errormsg = "Driver does not support this statement option";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// - - - - - - - - -
|
||||
|
|
|
@ -1,26 +1,34 @@
|
|||
|
||||
/* Module: pgtypes.c
|
||||
*
|
||||
* Description: This module contains routines for getting information
|
||||
* about the supported Postgres data types. Only the function
|
||||
* pgtype_to_sqltype() returns an unknown condition. All other
|
||||
* functions return a suitable default so that even data types that
|
||||
* are not directly supported can be used (it is handled as char data).
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: pgtypes.c
|
||||
*
|
||||
* Description: This module contains routines for getting information
|
||||
* about the supported Postgres data types. Only the function
|
||||
* pgtype_to_sqltype() returns an unknown condition. All other
|
||||
* functions return a suitable default so that even data types that
|
||||
* are not directly supported can be used (it is handled as char data).
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "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_DATETIME,
|
||||
PG_TYPE_BYTEA,
|
||||
PG_TYPE_LO,
|
||||
0 };
|
||||
|
||||
|
||||
|
@ -57,113 +66,197 @@ 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_BYTEA: return SQL_C_BINARY;
|
||||
case PG_TYPE_BOOL: return globals.bools_as_char ? SQL_C_CHAR : SQL_C_BIT;
|
||||
|
||||
default: return SQL_C_CHAR;
|
||||
}
|
||||
case PG_TYPE_BYTEA: return SQL_C_BINARY;
|
||||
case PG_TYPE_LO: return SQL_C_BINARY;
|
||||
|
||||
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";
|
||||
case PG_TYPE_DATETIME: return "datetime";
|
||||
case PG_TYPE_MONEY: return "money";
|
||||
case PG_TYPE_BOOL: return "bool";
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
/* For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, SQLColumns will
|
||||
override this length with the atttypmod length from pg_attribute
|
||||
*/
|
||||
Int4 pgtype_precision(Int4 type)
|
||||
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 .
|
||||
|
||||
If col >= 0, then will attempt to get the info from the result set.
|
||||
This is used for functions SQLDescribeCol and SQLColAttributes.
|
||||
*/
|
||||
Int4 pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
|
||||
{
|
||||
|
||||
switch(type) {
|
||||
|
||||
case PG_TYPE_CHAR: return 1;
|
||||
case PG_TYPE_CHAR2: return 2;
|
||||
case PG_TYPE_CHAR4: return 4;
|
||||
case PG_TYPE_CHAR8: return 8;
|
||||
case PG_TYPE_CHAR16: return 16;
|
||||
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;
|
||||
|
||||
case PG_TYPE_INT2: return 5;
|
||||
|
||||
case PG_TYPE_OID:
|
||||
case PG_TYPE_INT4: return 10;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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_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(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;
|
||||
|
||||
default:
|
||||
return TEXT_FIELD_SIZE; /* text field types and unknown types */
|
||||
/* Character types use the default precision */
|
||||
default:
|
||||
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:
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
|
||||
/* File: pgtypes.h
|
||||
*
|
||||
* Description: See "pgtypes.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: pgtypes.h
|
||||
*
|
||||
* Description: See "pgtypes.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __PGTYPES_H__
|
||||
#define __PGTYPES_H__
|
||||
|
||||
#include "psqlodbc.h"
|
||||
|
||||
/* the type numbers are defined by the OID's of the types' rows */
|
||||
/* in table pg_type */
|
||||
|
||||
#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);
|
||||
|
||||
|
|
|
@ -1,88 +1,28 @@
|
|||
|
||||
/* Module: psqlodbc.c
|
||||
*
|
||||
* Description: This module contains the main entry point (DllMain) for the library.
|
||||
* It also contains functions to get and set global variables for the
|
||||
* driver in the registry.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: psqlodbc.c
|
||||
*
|
||||
* Description: This module contains the main entry point (DllMain) for the library.
|
||||
* It also contains functions to get and set global variables for the
|
||||
* driver in the registry.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "psqlodbc.h"
|
||||
#include "dlg_specific.h"
|
||||
#include <winsock.h>
|
||||
#include <windows.h>
|
||||
#include <sql.h>
|
||||
#include <odbcinst.h>
|
||||
|
||||
HINSTANCE NEAR s_hModule; /* Saved module handle. */
|
||||
GLOBAL_VALUES globals;
|
||||
|
||||
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)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
|
||||
/* File: psqlodbc.h
|
||||
*
|
||||
* Description: This file contains defines and declarations that are related to
|
||||
* the entire driver.
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: psqlodbc.h
|
||||
*
|
||||
* Description: This file contains defines and declarations that are related to
|
||||
* the entire driver.
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __PSQLODBC_H__
|
||||
#define __PSQLODBC_H__
|
||||
|
@ -21,27 +21,23 @@ typedef UInt4 Oid;
|
|||
|
||||
/* Limits */
|
||||
#define MAX_MESSAGE_LEN 8192
|
||||
#define MAX_CONNECT_STRING 4096
|
||||
#define MAX_CONNECT_STRING 4096
|
||||
#define ERROR_MSG_LENGTH 4096
|
||||
#define FETCH_MAX 100 /* default number of rows to cache for declare/fetch */
|
||||
#define SOCK_BUFFER_SIZE 4096 /* default socket buffer size */
|
||||
#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
|
||||
#define BYTELEN 8
|
||||
#define VARHDRSZ sizeof(Int4)
|
||||
|
||||
|
||||
/* Registry length limits */
|
||||
#define LARGE_REGISTRY_LEN 4096 /* used for special cases */
|
||||
#define MEDIUM_REGISTRY_LEN 128 /* normal size for user,database,etc. */
|
||||
#define SMALL_REGISTRY_LEN 10 /* for 1/0 settings */
|
||||
#define LARGE_REGISTRY_LEN 4096 /* used for special cases */
|
||||
#define MEDIUM_REGISTRY_LEN 256 /* normal size for user,database,etc. */
|
||||
#define SMALL_REGISTRY_LEN 10 /* for 1/0 settings */
|
||||
|
||||
|
||||
/* 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 */
|
||||
|
||||
#define PG62 "6.2" /* "Protocol" key setting to force Postgres 6.2 */
|
||||
|
||||
|
||||
typedef struct ConnectionClass_ ConnectionClass;
|
||||
typedef struct StatementClass_ StatementClass;
|
||||
|
@ -93,27 +69,40 @@ typedef struct TupleListClass_ TupleListClass;
|
|||
typedef struct EnvironmentClass_ EnvironmentClass;
|
||||
typedef struct TupleNode_ TupleNode;
|
||||
typedef struct TupleField_ TupleField;
|
||||
|
||||
|
||||
typedef struct GlobalValues_
|
||||
{
|
||||
int fetch_max;
|
||||
int socket_buffersize;
|
||||
int debug;
|
||||
int commlog;
|
||||
char optimizer[MEDIUM_REGISTRY_LEN];
|
||||
char conn_settings[LARGE_REGISTRY_LEN];
|
||||
} GLOBAL_VALUES;
|
||||
|
||||
|
||||
/* sizes */
|
||||
#define TEXT_FIELD_SIZE 4094 /* size of text fields (not including null term) */
|
||||
#define MAX_VARCHAR_SIZE 254 /* maximum size of a varchar (not including null term) */
|
||||
|
||||
|
||||
/* global prototypes */
|
||||
void updateGlobals(void);
|
||||
|
||||
typedef struct lo_arg LO_ARG;
|
||||
|
||||
typedef struct GlobalValues_
|
||||
{
|
||||
int fetch_max;
|
||||
int socket_buffersize;
|
||||
int unknown_sizes;
|
||||
int max_varchar_size;
|
||||
int max_longvarchar_size;
|
||||
char debug;
|
||||
char commlog;
|
||||
char disable_optimizer;
|
||||
char 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) */
|
||||
|
||||
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
|
|
|
@ -1,205 +1,262 @@
|
|||
//Microsoft Developer Studio generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "afxres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
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
|
||||
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
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO DISCARDABLE
|
||||
BEGIN
|
||||
DRIVERCONNDIALOG, DIALOG
|
||||
BEGIN
|
||||
RIGHTMARGIN, 268
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
#ifndef _MAC
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 6,30,0,0
|
||||
PRODUCTVERSION 6,30,0,0
|
||||
FILEFLAGSMASK 0x3L
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904e4"
|
||||
BEGIN
|
||||
VALUE "Comments", "PostgreSQL ODBC driver for Windows 95\0"
|
||||
VALUE "CompanyName", "Insight Distribution Systems\0"
|
||||
VALUE "FileDescription", "PostgreSQL Driver\0"
|
||||
VALUE "FileVersion", " 6.30.0000\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"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
||||
|
||||
#endif // !_MAC
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
IDS_BADDSN "Invalid DSN entry, please recheck."
|
||||
IDS_MSGTITLE "Invalid DSN"
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
//Microsoft Developer Studio generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "afxres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
DLG_CONFIG DIALOG DISCARDABLE 65, 43, 292, 116
|
||||
STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
|
||||
WS_SYSMENU
|
||||
CAPTION "PostgreSQL Driver Setup"
|
||||
FONT 8, "MS Sans Serif"
|
||||
BEGIN
|
||||
RTEXT "&Data Source:",IDC_DSNAMETEXT,5,10,50,12,NOT WS_GROUP
|
||||
EDITTEXT IDC_DSNAME,57,10,72,12,ES_AUTOHSCROLL | WS_GROUP
|
||||
RTEXT "Des&cription:",IDC_DESCTEXT,135,10,39,12,NOT WS_GROUP
|
||||
EDITTEXT IDC_DESC,175,10,108,12,ES_AUTOHSCROLL
|
||||
RTEXT "Data&base:",IDC_STATIC,17,25,38,12,NOT WS_GROUP
|
||||
EDITTEXT IDC_DATABASE,57,25,72,12,ES_AUTOHSCROLL
|
||||
RTEXT "&Server:",IDC_STATIC,27,40,29,12,NOT WS_GROUP
|
||||
EDITTEXT IDC_SERVER,57,40,72,12,ES_AUTOHSCROLL
|
||||
RTEXT "&Port:",IDC_STATIC,153,40,22,12
|
||||
EDITTEXT IDC_PORT,175,40,37,12,ES_AUTOHSCROLL
|
||||
RTEXT "&User Name:",IDC_STATIC,17,55,39,12
|
||||
EDITTEXT IDC_USER,57,55,72,12,ES_AUTOHSCROLL
|
||||
RTEXT "Pass&word:",IDC_STATIC,141,55,34,12
|
||||
EDITTEXT IDC_PASSWORD,175,55,72,12,ES_PASSWORD | ES_AUTOHSCROLL
|
||||
DEFPUSHBUTTON "OK",IDOK,25,90,40,14,WS_GROUP
|
||||
PUSHBUTTON "Cancel",IDCANCEL,80,90,40,14
|
||||
GROUPBOX "Options (Advanced):",IDC_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
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO DISCARDABLE
|
||||
BEGIN
|
||||
DLG_CONFIG, DIALOG
|
||||
BEGIN
|
||||
BOTTOMMARGIN, 115
|
||||
END
|
||||
|
||||
DLG_OPTIONS_DRV, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 280
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 219
|
||||
END
|
||||
|
||||
DLG_OPTIONS_DS, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 5
|
||||
RIGHTMARGIN, 260
|
||||
VERTGUIDE, 55
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 163
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
#ifndef _MAC
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 6,30,2,44
|
||||
PRODUCTVERSION 6,30,2,44
|
||||
FILEFLAGSMASK 0x3L
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904e4"
|
||||
BEGIN
|
||||
VALUE "Comments", "PostgreSQL ODBC driver for Windows 95\0"
|
||||
VALUE "CompanyName", "Insight Distribution Systems\0"
|
||||
VALUE "FileDescription", "PostgreSQL Driver\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.0244\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
||||
|
||||
#endif // !_MAC
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
IDS_BADDSN "Invalid DSN entry, please recheck."
|
||||
IDS_MSGTITLE "Invalid DSN"
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
|
||||
/* Module: qresult.c
|
||||
*
|
||||
* Description: This module contains functions related to
|
||||
* managing result information (i.e, fetching rows from the backend,
|
||||
* managing the tuple cache, etc.) and retrieving it.
|
||||
* Depending on the situation, a QResultClass will hold either data
|
||||
* from the backend or a manually built result (see "qresult.h" to
|
||||
* see which functions/macros are for manual or backend results.
|
||||
* For manually built results, the QResultClass simply points to
|
||||
* TupleList and ColumnInfo structures, which actually hold the data.
|
||||
*
|
||||
* Classes: QResultClass (Functions prefix: "QR_")
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: qresult.c
|
||||
*
|
||||
* Description: This module contains functions related to
|
||||
* managing result information (i.e, fetching rows from the backend,
|
||||
* managing the tuple cache, etc.) and retrieving it.
|
||||
* Depending on the situation, a QResultClass will hold either data
|
||||
* from the backend or a manually built result (see "qresult.h" to
|
||||
* see which functions/macros are for manual or backend results.
|
||||
* For manually built results, the QResultClass simply points to
|
||||
* TupleList and ColumnInfo structures, which actually hold the data.
|
||||
*
|
||||
* Classes: QResultClass (Functions prefix: "QR_")
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qresult.h"
|
||||
#include "misc.h"
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
extern GLOBAL_VALUES globals;
|
||||
|
||||
/* Used for building a Manual Result only */
|
||||
/* Used for building a Manual Result only */
|
||||
/* All info functions call this function to create the manual result set. */
|
||||
void
|
||||
QR_set_num_fields(QResultClass *self, int new_num_fields)
|
||||
|
@ -84,7 +84,7 @@ QR_Destructor(QResultClass *self)
|
|||
mylog("QResult: in DESTRUCTOR\n");
|
||||
|
||||
/* manual result set tuples */
|
||||
if (self->manual_tuples)
|
||||
if (self->manual_tuples)
|
||||
TL_Destructor(self->manual_tuples);
|
||||
|
||||
// If conn is defined, then we may have used "backend_tuples",
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
self->fcount++;
|
||||
break; // continue reading
|
||||
|
||||
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;
|
||||
case 'C': /* End of tuple list */
|
||||
SOCK_get_string(sock, cmdbuffer, MAX_MESSAGE_LEN);
|
||||
QR_set_command(self, cmdbuffer);
|
||||
|
||||
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;
|
||||
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,7 +453,8 @@ Int4 len;
|
|||
char *buffer;
|
||||
int num_fields = self->num_fields; // speed up access
|
||||
SocketClass *sock = CC_get_socket(self->conn);
|
||||
|
||||
ColumnInfoClass *flds;
|
||||
|
||||
|
||||
/* set the current row to read the fields into */
|
||||
this_tuplefield = self->backend_tuples + (self->fcount * num_fields);
|
||||
|
@ -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
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
/* File: qresult.h
|
||||
*
|
||||
* Description: See "qresult.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: qresult.h
|
||||
*
|
||||
* Description: See "qresult.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __QRESULT_H__
|
||||
#define __QRESULT_H__
|
||||
|
@ -43,7 +43,7 @@ struct QResultClass_ {
|
|||
|
||||
// Stuff for declare/fetch tuples
|
||||
int fetch_count; // logical rows read so far
|
||||
int fcount; // actual rows read in the fetch
|
||||
int fcount; // actual rows read in the fetch
|
||||
|
||||
int num_fields; // number of fields in the result
|
||||
QueryResultCode status;
|
||||
|
@ -63,19 +63,22 @@ 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_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_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_field_type(self, fieldno_) (CI_get_oid(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))
|
||||
#define QR_set_field_info(self, field_num, name, adtid, adtsize) (CI_set_field_info(self->fields, field_num, name, adtid, adtsize))
|
||||
|
||||
/* status macros */
|
||||
#define QR_command_successful(self) ( !(self->status == PGRES_BAD_RESPONSE || self->status == PGRES_NONFATAL_ERROR || self->status == PGRES_FATAL_ERROR))
|
||||
|
@ -98,7 +101,7 @@ int QR_close(QResultClass *self);
|
|||
char QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor);
|
||||
void QR_free_memory(QResultClass *self);
|
||||
void QR_set_command(QResultClass *self, char *msg);
|
||||
void QR_set_notice(QResultClass *self, char *msg);
|
||||
void QR_set_notice(QResultClass *self, char *msg);
|
||||
|
||||
void QR_set_num_fields(QResultClass *self, int new_num_fields); /* manual result only */
|
||||
|
||||
|
|
|
@ -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!
|
||||
|
||||
|
|
@ -1,39 +1,58 @@
|
|||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Developer Studio generated include file.
|
||||
// Used by psqlodbc.rc
|
||||
//
|
||||
#define IDS_BADDSN 1
|
||||
#define IDS_MSGTITLE 2
|
||||
#define DRIVERCONNDIALOG 101
|
||||
#define IDC_DSNAME 400
|
||||
#define IDC_DSNAMETEXT 401
|
||||
#define IDC_DESC 404
|
||||
#define IDC_SERVER 407
|
||||
#define IDC_DATABASE 408
|
||||
#define CONFIGDSN 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
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1018
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Developer Studio generated include file.
|
||||
// Used by psqlodbc.rc
|
||||
//
|
||||
#define IDS_BADDSN 1
|
||||
#define IDS_MSGTITLE 2
|
||||
#define DLG_OPTIONS_DRV 102
|
||||
#define DLG_OPTIONS_DS 103
|
||||
#define IDC_DSNAME 400
|
||||
#define IDC_DSNAMETEXT 401
|
||||
#define IDC_DESC 404
|
||||
#define IDC_SERVER 407
|
||||
#define IDC_DATABASE 408
|
||||
#define DLG_CONFIG 1001
|
||||
#define IDC_PORT 1002
|
||||
#define IDC_USER 1006
|
||||
#define IDC_PASSWORD 1009
|
||||
#define DS_READONLY 1011
|
||||
#define DS_SHOWOIDCOLUMN 1012
|
||||
#define DS_FAKEOIDINDEX 1013
|
||||
#define DRV_COMMLOG 1014
|
||||
#define DS_PG62 1016
|
||||
#define IDC_DATASOURCE 1018
|
||||
#define DRV_OPTIMIZER 1019
|
||||
#define DS_CONNSETTINGS 1020
|
||||
#define IDC_DRIVER 1021
|
||||
#define 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 104
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1054
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
|
||||
/* Module: results.c
|
||||
*
|
||||
* Description: This module contains functions related to
|
||||
* retrieving result information through the ODBC API.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol, SQLColAttributes,
|
||||
* SQLGetData, SQLFetch, SQLExtendedFetch,
|
||||
* SQLMoreResults(NI), SQLSetPos(NI), SQLSetScrollOptions(NI),
|
||||
* SQLSetCursorName(NI), SQLGetCursorName(NI)
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: results.c
|
||||
*
|
||||
* Description: This module contains functions related to
|
||||
* retrieving result information through the ODBC API.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol, SQLColAttributes,
|
||||
* SQLGetData, SQLFetch, SQLExtendedFetch,
|
||||
* SQLMoreResults(NI), SQLSetPos(NI), SQLSetScrollOptions(NI),
|
||||
* SQLSetCursorName, SQLGetCursorName
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#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
|
||||
|
@ -149,7 +155,7 @@ Int4 fieldtype;
|
|||
|
||||
SC_pre_execute(stmt);
|
||||
|
||||
|
||||
|
||||
result = SC_get_Result(stmt);
|
||||
mylog("**** SQLDescribeCol: result = %u, stmt->status = %d, !finished=%d, !premature=%d\n", result, stmt->status, stmt->status != STMT_FINISHED, stmt->status != STMT_PREMATURE);
|
||||
if ( (NULL == result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) {
|
||||
|
@ -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);
|
||||
|
||||
mylog("colAttr: col %d fieldsize = %d\n", icol, *pfDesc);
|
||||
unknown_sizes = globals.unknown_sizes; // atoi(ci->unknown_sizes);
|
||||
if (unknown_sizes == UNKNOWNS_AS_DONTKNOW) // not appropriate for SQLColAttributes()
|
||||
unknown_sizes = UNKNOWNS_AS_MAX;
|
||||
|
||||
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;
|
||||
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_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 ( ! (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->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;
|
||||
}
|
||||
|
||||
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 (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;
|
||||
}
|
||||
|
||||
mylog("manual_result = %d, use_declarefetch = %d\n",
|
||||
stmt->manual_result, globals.use_declarefetch);
|
||||
|
||||
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;
|
||||
if ( stmt->manual_result || ! globals.use_declarefetch) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
mylog("**** SQLFetch: manual_result\n");
|
||||
(stmt->currTuple)++;
|
||||
}
|
||||
else {
|
||||
mylog("**** SQLFetch: manual_result\n");
|
||||
(stmt->currTuple)++;
|
||||
}
|
||||
else {
|
||||
|
||||
// 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
|
||||
// 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;
|
||||
}
|
||||
else {
|
||||
mylog("SQLFetch: error\n");
|
||||
stmt->errornumber = STMT_EXEC_ERROR;
|
||||
stmt->errormsg = "Error fetching next row";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
num_cols = QR_NumResultCols(res);
|
||||
|
||||
num_cols = QR_NumResultCols(res);
|
||||
for (lf=0; lf < num_cols; lf++) {
|
||||
|
||||
for (lf=0; lf < num_cols; lf++) {
|
||||
mylog("fetch: cols=%d, lf=%d, stmt = %u, stmt->bindings = %u, buffer[] = %u\n",
|
||||
num_cols, lf, stmt, stmt->bindings, stmt->bindings[lf].buffer);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,95 +1,28 @@
|
|||
|
||||
/* Module: setup.c
|
||||
*
|
||||
* Description: This module contains the setup functions for
|
||||
* adding/modifying a Data Source in the ODBC.INI portion
|
||||
* of the registry.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: ConfigDSN
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*************************************************************************************/
|
||||
|
||||
/*
|
||||
** 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
|
||||
/* Module: setup.c
|
||||
*
|
||||
* Description: This module contains the setup functions for
|
||||
* adding/modifying a Data Source in the ODBC.INI portion
|
||||
* of the registry.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: ConfigDSN
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*************************************************************************************/
|
||||
|
||||
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);
|
||||
|
||||
|
||||
// CommLog Parameter (this is global)
|
||||
CheckDlgButton(hdlg, IDC_COMMLOG, globals.commlog);
|
||||
// Fill in any defaults
|
||||
getDSNdefaults(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
|
||||
// Initialize dialog fields
|
||||
SetDlgStuff(hdlg, ci);
|
||||
|
||||
|
||||
if (lpsetupdlg->fDefault) {
|
||||
EnableWindow(GetDlgItem(hdlg, IDC_DSNAME), FALSE);
|
||||
EnableWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), FALSE);
|
||||
}
|
||||
else
|
||||
SendDlgItemMessage(hdlg, IDC_DSNAME,
|
||||
EM_LIMITTEXT, (WPARAM)(MAXDSNAME-1), 0L);
|
||||
|
||||
SendDlgItemMessage(hdlg, IDC_DESC,
|
||||
EM_LIMITTEXT, (WPARAM)(MAXDESC-1), 0L);
|
||||
return TRUE; // Focus was not set
|
||||
}
|
||||
|
||||
|
||||
// Process buttons
|
||||
case WM_COMMAND:
|
||||
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
|
||||
// Ensure the OK button is enabled only when a data source name
|
||||
// is entered
|
||||
case IDC_DSNAME:
|
||||
if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
|
||||
{
|
||||
char szItem[MAXDSNAME]; // Edit control text
|
||||
// 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));
|
||||
|
||||
GetDlgItemText(hdlg, IDC_DATABASE,
|
||||
lpsetupdlg->aAttr[KEY_DATABASE].szAttr,
|
||||
sizeof(lpsetupdlg->aAttr[KEY_DATABASE].szAttr));
|
||||
|
||||
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));
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
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';
|
||||
// Accept results
|
||||
case IDOK:
|
||||
{
|
||||
LPSETUPDLG lpsetupdlg;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
writeDSNinfo(&lpsetupdlg->ci);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
|
||||
/* Module: socket.c
|
||||
*
|
||||
* Description: This module contains functions for low level socket
|
||||
* operations (connecting/reading/writing to the backend)
|
||||
*
|
||||
* Classes: SocketClass (Functions prefix: "SOCK_")
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: socket.c
|
||||
*
|
||||
* Description: This module contains functions for low level socket
|
||||
* operations (connecting/reading/writing to the backend)
|
||||
*
|
||||
* Classes: SocketClass (Functions prefix: "SOCK_")
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
|
@ -45,8 +45,8 @@ SocketClass *rv;
|
|||
return NULL;
|
||||
|
||||
rv->errormsg = NULL;
|
||||
rv->errornumber = 0;
|
||||
|
||||
rv->errornumber = 0;
|
||||
|
||||
rv->reverse = FALSE;
|
||||
}
|
||||
return rv;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -185,15 +195,15 @@ char buf[4];
|
|||
switch (len) {
|
||||
case 2:
|
||||
SOCK_get_n_char(self, buf, len);
|
||||
if (self->reverse)
|
||||
return *((unsigned short *) buf);
|
||||
else
|
||||
if (self->reverse)
|
||||
return *((unsigned short *) buf);
|
||||
else
|
||||
return ntohs( *((unsigned short *) buf) );
|
||||
|
||||
case 4:
|
||||
SOCK_get_n_char(self, buf, len);
|
||||
if (self->reverse)
|
||||
return *((unsigned int *) buf);
|
||||
SOCK_get_n_char(self, buf, len);
|
||||
if (self->reverse)
|
||||
return *((unsigned int *) buf);
|
||||
else
|
||||
return ntohl( *((unsigned int *) buf) );
|
||||
|
||||
|
@ -202,7 +212,7 @@ char buf[4];
|
|||
self->errormsg = "Cannot read ints of that length";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
|
@ -211,12 +221,12 @@ SOCK_put_int(SocketClass *self, int value, short len)
|
|||
unsigned int rv;
|
||||
|
||||
switch (len) {
|
||||
case 2:
|
||||
case 2:
|
||||
rv = self->reverse ? value : htons( (unsigned short) value);
|
||||
SOCK_put_n_char(self, (char *) &rv, 2);
|
||||
return;
|
||||
|
||||
case 4:
|
||||
case 4:
|
||||
rv = self->reverse ? value : htonl( (unsigned int) value);
|
||||
SOCK_put_n_char(self, (char *) &rv, 4);
|
||||
return;
|
||||
|
@ -251,7 +261,7 @@ SOCK_get_next_byte(SocketClass *self)
|
|||
// reload the buffer
|
||||
|
||||
self->buffer_read_in = 0;
|
||||
self->buffer_filled_in = recv(self->socket, (char *)self->buffer_in, globals.socket_buffersize, 0);
|
||||
self->buffer_filled_in = recv(self->socket, (char *)self->buffer_in, globals.socket_buffersize, 0);
|
||||
|
||||
mylog("read %d, global_socket_buffersize=%d\n", self->buffer_filled_in, globals.socket_buffersize);
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
/* File: socket.h
|
||||
*
|
||||
* Description: See "socket.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: socket.h
|
||||
*
|
||||
* Description: See "socket.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SOCKET_H__
|
||||
#define __SOCKET_H__
|
||||
|
@ -36,8 +36,8 @@ struct SocketClass_ {
|
|||
SOCKET socket;
|
||||
|
||||
char *errormsg;
|
||||
int errornumber;
|
||||
|
||||
int errornumber;
|
||||
|
||||
char reverse; /* used to handle Postgres 6.2 protocol (reverse byte order) */
|
||||
|
||||
};
|
||||
|
@ -49,9 +49,9 @@ struct SocketClass_ {
|
|||
/* error functions */
|
||||
#define SOCK_get_errcode(self) (self->errornumber)
|
||||
#define SOCK_get_errmsg(self) (self->errormsg)
|
||||
|
||||
|
||||
/* Socket prototypes */
|
||||
|
||||
/* Socket prototypes */
|
||||
SocketClass *SOCK_Constructor();
|
||||
void SOCK_Destructor(SocketClass *self);
|
||||
char SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname);
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
|
||||
/* Module: statement.c
|
||||
*
|
||||
* Description: This module contains functions related to creating
|
||||
* and manipulating a statement.
|
||||
*
|
||||
* Classes: StatementClass (Functions prefix: "SC_")
|
||||
*
|
||||
* API functions: SQLAllocStmt, SQLFreeStmt
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: statement.c
|
||||
*
|
||||
* Description: This module contains functions related to creating
|
||||
* and manipulating a statement.
|
||||
*
|
||||
* Classes: StatementClass (Functions prefix: "SC_")
|
||||
*
|
||||
* API functions: SQLAllocStmt, SQLFreeStmt
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "statement.h"
|
||||
#include "bind.h"
|
||||
|
@ -23,8 +23,25 @@
|
|||
#include <windows.h>
|
||||
#include <sql.h>
|
||||
|
||||
extern GLOBAL_VALUES globals;
|
||||
|
||||
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)
|
||||
|
@ -103,7 +120,7 @@ StatementClass *stmt = (StatementClass *) hstmt;
|
|||
// errormsg passed in above
|
||||
return SQL_ERROR;
|
||||
|
||||
} else if(fOption == SQL_RESET_PARAMS) {
|
||||
} else if(fOption == SQL_RESET_PARAMS) {
|
||||
SC_free_params(stmt, STMT_FREE_PARAMS_ALL);
|
||||
|
||||
} else {
|
||||
|
@ -133,21 +150,28 @@ 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;
|
||||
rv->statement = NULL;
|
||||
rv->stmt_with_params[0] = '\0';
|
||||
rv->statement = NULL;
|
||||
rv->stmt_with_params[0] = '\0';
|
||||
rv->statement_type = STMT_TYPE_UNKNOWN;
|
||||
rv->bindings = NULL;
|
||||
rv->bindings_allocated = 0;
|
||||
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->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;
|
||||
}
|
||||
|
@ -172,9 +196,9 @@ SC_Destructor(StatementClass *self)
|
|||
|
||||
if (self->statement)
|
||||
free(self->statement);
|
||||
|
||||
SC_free_params(self, STMT_FREE_PARAMS_ALL);
|
||||
|
||||
|
||||
SC_free_params(self, STMT_FREE_PARAMS_ALL);
|
||||
|
||||
/* the memory pointed to by the bindings is not deallocated by the driver */
|
||||
/* by by the application that uses that driver, so we don't have to care */
|
||||
/* about that here. */
|
||||
|
@ -183,62 +207,63 @@ SC_Destructor(StatementClass *self)
|
|||
|
||||
free(self);
|
||||
|
||||
mylog("SC_Destructor: EXIT\n");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Free parameters and free the memory from the
|
||||
data-at-execution parameters that was allocated in SQLPutData.
|
||||
*/
|
||||
void
|
||||
SC_free_params(StatementClass *self, char option)
|
||||
{
|
||||
int i;
|
||||
|
||||
if( ! self->parameters)
|
||||
return;
|
||||
|
||||
for (i = 0; i < self->parameters_allocated; i++) {
|
||||
if (self->parameters[i].data_at_exec == TRUE) {
|
||||
|
||||
if (self->parameters[i].EXEC_used) {
|
||||
free(self->parameters[i].EXEC_used);
|
||||
self->parameters[i].EXEC_used = NULL;
|
||||
}
|
||||
|
||||
if (self->parameters[i].EXEC_buffer) {
|
||||
free(self->parameters[i].EXEC_buffer);
|
||||
self->parameters[i].EXEC_buffer = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
self->data_at_exec = -1;
|
||||
self->current_exec_param = -1;
|
||||
self->put_data = FALSE;
|
||||
|
||||
if (option == STMT_FREE_PARAMS_ALL) {
|
||||
free(self->parameters);
|
||||
self->parameters = NULL;
|
||||
self->parameters_allocated = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free parameters and free the memory from the
|
||||
data-at-execution parameters that was allocated in SQLPutData.
|
||||
*/
|
||||
void
|
||||
SC_free_params(StatementClass *self, char option)
|
||||
{
|
||||
int i;
|
||||
|
||||
mylog("SC_free_params: ENTER, self=%d\n", self);
|
||||
|
||||
if( ! self->parameters)
|
||||
return;
|
||||
|
||||
for (i = 0; i < self->parameters_allocated; i++) {
|
||||
if (self->parameters[i].data_at_exec == TRUE) {
|
||||
|
||||
if (self->parameters[i].EXEC_used) {
|
||||
free(self->parameters[i].EXEC_used);
|
||||
self->parameters[i].EXEC_used = NULL;
|
||||
}
|
||||
|
||||
if (self->parameters[i].EXEC_buffer) {
|
||||
free(self->parameters[i].EXEC_buffer);
|
||||
self->parameters[i].EXEC_buffer = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
self->data_at_exec = -1;
|
||||
self->current_exec_param = -1;
|
||||
self->put_data = FALSE;
|
||||
|
||||
if (option == STMT_FREE_PARAMS_ALL) {
|
||||
free(self->parameters);
|
||||
self->parameters = NULL;
|
||||
self->parameters_allocated = 0;
|
||||
}
|
||||
|
||||
mylog("SC_free_params: EXIT\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
statement_type(char *statement)
|
||||
{
|
||||
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,16 +323,21 @@ 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;
|
||||
|
||||
// Free any data at exec params before the statement is executed
|
||||
// again. If not, then there will be a memory leak when
|
||||
// the next SQLParamData/SQLPutData is called.
|
||||
SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
|
||||
|
||||
self->lobj_fd = -1;
|
||||
|
||||
// Free any data at exec params before the statement is executed
|
||||
// again. If not, then there will be a memory leak when
|
||||
// the next SQLParamData/SQLPutData is called.
|
||||
SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -414,132 +444,143 @@ char rv;
|
|||
return rv;
|
||||
}
|
||||
|
||||
RETCODE SC_execute(StatementClass *self)
|
||||
{
|
||||
ConnectionClass *conn;
|
||||
QResultClass *res;
|
||||
char ok, was_ok, was_nonfatal;
|
||||
Int2 oldstatus, numcols;
|
||||
|
||||
|
||||
conn = SC_get_conn(self);
|
||||
|
||||
/* Begin a transaction if one is not already in progress */
|
||||
/* The reason is because we can't use declare/fetch cursors without
|
||||
starting a transaction first.
|
||||
*/
|
||||
|
||||
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) {
|
||||
self->errormsg = "Could not begin a transaction";
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
ok = QR_command_successful(res);
|
||||
|
||||
mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res));
|
||||
|
||||
QR_Destructor(res);
|
||||
|
||||
if (!ok) {
|
||||
self->errormsg = "Could not begin a transaction";
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
else
|
||||
CC_set_in_trans(conn);
|
||||
}
|
||||
|
||||
|
||||
|
||||
oldstatus = conn->status;
|
||||
conn->status = CONN_EXECUTING;
|
||||
self->status = STMT_EXECUTING;
|
||||
|
||||
|
||||
// If its a SELECT statement, use a cursor.
|
||||
// Note that the declare cursor has already been prepended to the statement
|
||||
// in copy_statement...
|
||||
if (self->statement_type == STMT_TYPE_SELECT) {
|
||||
|
||||
char cursor[32];
|
||||
char fetch[64];
|
||||
|
||||
sprintf(cursor, "C%u", self);
|
||||
|
||||
mylog(" Sending SELECT statement on stmt=%u\n", self);
|
||||
|
||||
/* send the declare/select */
|
||||
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
|
||||
if (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);
|
||||
|
||||
// Save the cursor in the result for later use
|
||||
self->result = CC_send_query( conn, fetch, NULL, cursor);
|
||||
}
|
||||
|
||||
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)) {
|
||||
CC_send_query(conn, "COMMIT", NULL, NULL);
|
||||
CC_set_no_trans(conn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
conn->status = oldstatus;
|
||||
self->status = STMT_FINISHED;
|
||||
|
||||
/* Check the status of the result */
|
||||
if (self->result) {
|
||||
|
||||
was_ok = QR_command_successful(self->result);
|
||||
was_nonfatal = QR_command_nonfatal(self->result);
|
||||
|
||||
if ( was_ok)
|
||||
self->errornumber = STMT_OK;
|
||||
else
|
||||
self->errornumber = was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND;
|
||||
|
||||
self->currTuple = -1; /* set cursor before the first tuple in the list */
|
||||
|
||||
/* see if the query did return any result columns */
|
||||
numcols = QR_NumResultCols(self->result);
|
||||
|
||||
/* now allocate the array to hold the binding info */
|
||||
if (numcols > 0) {
|
||||
extend_bindings(self, numcols);
|
||||
if (self->bindings == NULL) {
|
||||
self->errornumber = STMT_NO_MEMORY_ERROR;
|
||||
self->errormsg = "Could not get enough free memory to store the binding information";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
} else { /* Bad Error -- The error message will be in the Connection */
|
||||
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
self->errormsg = "Error while executing the query";
|
||||
|
||||
CC_abort(conn);
|
||||
}
|
||||
|
||||
if (self->errornumber == STMT_OK)
|
||||
return SQL_SUCCESS;
|
||||
|
||||
else if (self->errornumber == STMT_INFO_ONLY)
|
||||
return SQL_SUCCESS_WITH_INFO;
|
||||
|
||||
else
|
||||
return SQL_ERROR;
|
||||
}
|
||||
RETCODE SC_execute(StatementClass *self)
|
||||
{
|
||||
ConnectionClass *conn;
|
||||
QResultClass *res;
|
||||
char ok, was_ok, was_nonfatal;
|
||||
Int2 oldstatus, numcols;
|
||||
|
||||
|
||||
conn = SC_get_conn(self);
|
||||
|
||||
/* Begin a transaction if one is not already in progress */
|
||||
/* 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))) {
|
||||
|
||||
mylog(" about to begin a transaction on statement = %u\n", self);
|
||||
res = CC_send_query(conn, "BEGIN", NULL, NULL);
|
||||
if ( ! res) {
|
||||
self->errormsg = "Could not begin a transaction";
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
ok = QR_command_successful(res);
|
||||
|
||||
mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res));
|
||||
|
||||
QR_Destructor(res);
|
||||
|
||||
if (!ok) {
|
||||
self->errormsg = "Could not begin a transaction";
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
else
|
||||
CC_set_in_trans(conn);
|
||||
}
|
||||
|
||||
|
||||
|
||||
oldstatus = conn->status;
|
||||
conn->status = CONN_EXECUTING;
|
||||
self->status = STMT_EXECUTING;
|
||||
|
||||
|
||||
// If its a SELECT statement, use a cursor.
|
||||
// Note that the declare cursor has already been prepended to the statement
|
||||
// in copy_statement...
|
||||
if (self->statement_type == STMT_TYPE_SELECT) {
|
||||
|
||||
char fetch[128];
|
||||
|
||||
mylog(" Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name);
|
||||
|
||||
/* send the declare/select */
|
||||
self->result = CC_send_query(conn, self->stmt_with_params, NULL, 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, self->cursor_name);
|
||||
|
||||
// Save the cursor in the result for later use
|
||||
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) && STMT_UPDATE(self)) {
|
||||
CC_send_query(conn, "COMMIT", NULL, NULL);
|
||||
CC_set_no_trans(conn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
conn->status = oldstatus;
|
||||
self->status = STMT_FINISHED;
|
||||
|
||||
/* Check the status of the result */
|
||||
if (self->result) {
|
||||
|
||||
was_ok = QR_command_successful(self->result);
|
||||
was_nonfatal = QR_command_nonfatal(self->result);
|
||||
|
||||
if ( was_ok)
|
||||
self->errornumber = STMT_OK;
|
||||
else
|
||||
self->errornumber = was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND;
|
||||
|
||||
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);
|
||||
|
||||
/* now allocate the array to hold the binding info */
|
||||
if (numcols > 0) {
|
||||
extend_bindings(self, numcols);
|
||||
if (self->bindings == NULL) {
|
||||
self->errornumber = STMT_NO_MEMORY_ERROR;
|
||||
self->errormsg = "Could not get enough free memory to store the binding information";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
} else { /* Bad Error -- The error message will be in the Connection */
|
||||
|
||||
if (self->statement_type == STMT_TYPE_CREATE) {
|
||||
self->errornumber = STMT_CREATE_TABLE_ERROR;
|
||||
self->errormsg = "Error creating the table";
|
||||
/* This would allow the table to already exists, thus appending
|
||||
rows to it. BUT, if the table didn't have the same attributes,
|
||||
it would fail.
|
||||
return SQL_SUCCESS_WITH_INFO;
|
||||
*/
|
||||
}
|
||||
else {
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
self->errormsg = "Error while executing the query";
|
||||
}
|
||||
CC_abort(conn);
|
||||
}
|
||||
|
||||
if (self->errornumber == STMT_OK)
|
||||
return SQL_SUCCESS;
|
||||
|
||||
else if (self->errornumber == STMT_INFO_ONLY)
|
||||
return SQL_SUCCESS_WITH_INFO;
|
||||
|
||||
else
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
/* File: statement.h
|
||||
*
|
||||
* Description: See "statement.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: statement.h
|
||||
*
|
||||
* Description: See "statement.c"
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __STATEMENT_H__
|
||||
#define __STATEMENT_H__
|
||||
|
@ -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,30 +89,35 @@ 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 */
|
||||
|
||||
|
||||
int statement_type; /* According to the defines above */
|
||||
int data_at_exec; /* Number of params needing SQLPutData */
|
||||
int current_exec_param; /* The current parameter for SQLPutData */
|
||||
|
||||
char put_data; /* Has SQLPutData been called yet? */
|
||||
int data_at_exec; /* Number of params needing SQLPutData */
|
||||
int current_exec_param; /* The current parameter for SQLPutData */
|
||||
|
||||
char put_data; /* Has SQLPutData been called yet? */
|
||||
|
||||
char errormsg_created; /* has an informative error msg been created? */
|
||||
char manual_result; /* Is the statement result manually built? */
|
||||
char prepare; /* is this statement a prepared statement or direct */
|
||||
|
||||
char stmt_with_params[65536 /* MAX_STATEMENT_LEN */]; /* statement after parameter substitution */
|
||||
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 */
|
||||
|
||||
};
|
||||
|
||||
#define SC_get_conn(a) (a->hdbc)
|
||||
#define SC_get_Result(a) (a->result);
|
||||
|
||||
/* options for SC_free_params() */
|
||||
#define STMT_FREE_PARAMS_ALL 0
|
||||
#define STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY 1
|
||||
|
||||
|
||||
/* options for SC_free_params() */
|
||||
#define STMT_FREE_PARAMS_ALL 0
|
||||
#define STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY 1
|
||||
|
||||
/* Statement prototypes */
|
||||
StatementClass *SC_Constructor();
|
||||
char SC_Destructor(StatementClass *self);
|
||||
|
@ -113,7 +129,7 @@ char SC_recycle_statement(StatementClass *self);
|
|||
void SC_clear_error(StatementClass *self);
|
||||
char SC_get_error(StatementClass *self, int *number, char **message);
|
||||
char *SC_create_errormsg(StatementClass *self);
|
||||
RETCODE SC_execute(StatementClass *stmt);
|
||||
void SC_free_params(StatementClass *self, char option);
|
||||
RETCODE SC_execute(StatementClass *stmt);
|
||||
void SC_free_params(StatementClass *self, char option);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
|
||||
/* Module: tuple.c
|
||||
*
|
||||
* Description: This module contains functions for setting the data for individual
|
||||
* fields (TupleField structure) of a manual result set.
|
||||
*
|
||||
* Important Note: These functions are ONLY used in building manual result sets for
|
||||
* info functions (SQLTables, SQLColumns, etc.)
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: tuple.c
|
||||
*
|
||||
* Description: This module contains functions for setting the data for individual
|
||||
* fields (TupleField structure) of a manual result set.
|
||||
*
|
||||
* Important Note: These functions are ONLY used in building manual result sets for
|
||||
* info functions (SQLTables, SQLColumns, etc.)
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tuple.h"
|
||||
#include <string.h>
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
|
||||
/* File: tuple.h
|
||||
*
|
||||
* Description: See "tuple.c"
|
||||
*
|
||||
* Important NOTE: The TupleField structure is used both to hold backend data and
|
||||
* manual result set data. The "set_" functions and the TupleNode
|
||||
* structure are only used for manual result sets by info routines.
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: tuple.h
|
||||
*
|
||||
* Description: See "tuple.c"
|
||||
*
|
||||
* Important NOTE: The TupleField structure is used both to hold backend data and
|
||||
* manual result set data. The "set_" functions and the TupleNode
|
||||
* structure are only used for manual result sets by info routines.
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TUPLE_H__
|
||||
#define __TUPLE_H__
|
||||
|
||||
#include "psqlodbc.h"
|
||||
|
||||
|
||||
/* Used by backend data AND manual result sets */
|
||||
struct TupleField_ {
|
||||
Int4 len; /* length of the current Tuple */
|
||||
void *value; /* an array representing the value */
|
||||
};
|
||||
|
||||
|
||||
/* Used ONLY for manual result sets */
|
||||
struct TupleNode_ {
|
||||
struct TupleNode_ *prev, *next;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
|
||||
/* Module: tuplelist.c
|
||||
*
|
||||
* Description: This module contains functions for creating a manual result set
|
||||
* (the TupleList) and retrieving data from it for a specific row/column.
|
||||
*
|
||||
* Classes: TupleListClass (Functions prefix: "TL_")
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: tuplelist.c
|
||||
*
|
||||
* Description: This module contains functions for creating a manual result set
|
||||
* (the TupleList) and retrieving data from it for a specific row/column.
|
||||
*
|
||||
* Classes: TupleListClass (Functions prefix: "TL_")
|
||||
*
|
||||
* API functions: none
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
@ -21,8 +21,8 @@ TupleListClass *
|
|||
TL_Constructor(UInt4 fieldcnt)
|
||||
{
|
||||
TupleListClass *rv;
|
||||
|
||||
mylog("in TL_Constructor\n");
|
||||
|
||||
mylog("in TL_Constructor\n");
|
||||
|
||||
rv = (TupleListClass *) malloc(sizeof(TupleListClass));
|
||||
if (rv) {
|
||||
|
@ -35,8 +35,8 @@ TupleListClass *rv;
|
|||
rv->last_indexed = -1;
|
||||
}
|
||||
|
||||
mylog("exit TL_Constructor\n");
|
||||
|
||||
mylog("exit TL_Constructor\n");
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -46,8 +46,8 @@ TL_Destructor(TupleListClass *self)
|
|||
int lf;
|
||||
TupleNode *node, *tp;
|
||||
|
||||
mylog("TupleList: in DESTRUCTOR\n");
|
||||
|
||||
mylog("TupleList: in DESTRUCTOR\n");
|
||||
|
||||
node = self->list_start;
|
||||
while(node != NULL) {
|
||||
for (lf=0; lf < self->num_fields; lf++)
|
||||
|
@ -57,11 +57,11 @@ TupleNode *node, *tp;
|
|||
tp = node->next;
|
||||
free(node);
|
||||
node = tp;
|
||||
}
|
||||
|
||||
free(self);
|
||||
}
|
||||
|
||||
mylog("TupleList: exit DESTRUCTOR\n");
|
||||
free(self);
|
||||
|
||||
mylog("TupleList: exit DESTRUCTOR\n");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
|
||||
/* File: tuplelist.h
|
||||
*
|
||||
* Description: See "tuplelist.c"
|
||||
*
|
||||
* Important Note: This structure and its functions are ONLY used in building manual result
|
||||
* sets for info functions (SQLTables, SQLColumns, etc.)
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* File: tuplelist.h
|
||||
*
|
||||
* Description: See "tuplelist.c"
|
||||
*
|
||||
* Important Note: This structure and its functions are ONLY used in building manual result
|
||||
* sets for info functions (SQLTables, SQLColumns, etc.)
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TUPLELIST_H__
|
||||
#define __TUPLELIST_H__
|
||||
|
|
Loading…
Reference in New Issue