Commit 99d21d5b authored by Byron Nikolaidis's avatar Byron Nikolaidis

Update odbc driver to current version V.0244

parent 85f91d0e
/* Module: bind.c /* Module: bind.c
* *
* Description: This module contains routines related to binding * Description: This module contains routines related to binding
* columns and parameters. * columns and parameters.
* *
* Classes: BindInfoClass, ParameterInfoClass * Classes: BindInfoClass, ParameterInfoClass
* *
* API functions: SQLBindParameter, SQLBindCol, SQLDescribeParam, SQLNumParams, * API functions: SQLBindParameter, SQLBindCol, SQLDescribeParam, SQLNumParams,
* SQLParamOptions(NI) * SQLParamOptions(NI)
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "bind.h" #include "bind.h"
#include "environ.h" #include "environ.h"
#include "statement.h" #include "statement.h"
...@@ -19,8 +19,8 @@ ...@@ -19,8 +19,8 @@
#include "pgtypes.h" #include "pgtypes.h"
#include <stdlib.h> #include <stdlib.h>
#include <malloc.h> #include <malloc.h>
#include <sql.h> #include <sql.h>
#include <sqlext.h> #include <sqlext.h>
// Bind parameters on a statement handle // Bind parameters on a statement handle
...@@ -77,10 +77,11 @@ StatementClass *stmt = (StatementClass *) hstmt; ...@@ -77,10 +77,11 @@ StatementClass *stmt = (StatementClass *) hstmt;
stmt->parameters[i].CType = 0; stmt->parameters[i].CType = 0;
stmt->parameters[i].SQLType = 0; stmt->parameters[i].SQLType = 0;
stmt->parameters[i].precision = 0; stmt->parameters[i].precision = 0;
stmt->parameters[i].scale = 0; stmt->parameters[i].scale = 0;
stmt->parameters[i].data_at_exec = FALSE; stmt->parameters[i].data_at_exec = FALSE;
stmt->parameters[i].EXEC_used = NULL; stmt->parameters[i].lobj_oid = 0;
stmt->parameters[i].EXEC_buffer = NULL; stmt->parameters[i].EXEC_used = NULL;
stmt->parameters[i].EXEC_buffer = NULL;
} }
} }
...@@ -94,26 +95,28 @@ StatementClass *stmt = (StatementClass *) hstmt; ...@@ -94,26 +95,28 @@ StatementClass *stmt = (StatementClass *) hstmt;
stmt->parameters[ipar].CType = fCType; stmt->parameters[ipar].CType = fCType;
stmt->parameters[ipar].SQLType = fSqlType; stmt->parameters[ipar].SQLType = fSqlType;
stmt->parameters[ipar].precision = cbColDef; stmt->parameters[ipar].precision = cbColDef;
stmt->parameters[ipar].scale = ibScale; stmt->parameters[ipar].scale = ibScale;
/* If rebinding a parameter that had data-at-exec stuff in it, /* If rebinding a parameter that had data-at-exec stuff in it,
then free that stuff then free that stuff
*/ */
if (stmt->parameters[ipar].EXEC_used) { if (stmt->parameters[ipar].EXEC_used) {
free(stmt->parameters[ipar].EXEC_used); free(stmt->parameters[ipar].EXEC_used);
stmt->parameters[ipar].EXEC_used = NULL; stmt->parameters[ipar].EXEC_used = NULL;
} }
if (stmt->parameters[ipar].EXEC_buffer) { if (stmt->parameters[ipar].EXEC_buffer) {
free(stmt->parameters[ipar].EXEC_buffer); free(stmt->parameters[ipar].EXEC_buffer);
stmt->parameters[ipar].EXEC_buffer = NULL; stmt->parameters[ipar].EXEC_buffer = NULL;
} }
if (pcbValue && *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET) if (pcbValue && *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)
stmt->parameters[ipar].data_at_exec = TRUE; stmt->parameters[ipar].data_at_exec = TRUE;
else else
stmt->parameters[ipar].data_at_exec = FALSE; 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; return SQL_SUCCESS;
} }
...@@ -188,6 +191,8 @@ mylog("**** SQLBindCol: stmt = %u, icol = %d\n", stmt, icol); ...@@ -188,6 +191,8 @@ mylog("**** SQLBindCol: stmt = %u, icol = %d\n", stmt, icol);
stmt->bindings[icol].buffer = rgbValue; stmt->bindings[icol].buffer = rgbValue;
stmt->bindings[icol].used = pcbValue; stmt->bindings[icol].used = pcbValue;
stmt->bindings[icol].returntype = fCType; stmt->bindings[icol].returntype = fCType;
mylog(" bound buffer[%d] = %u\n", icol, stmt->bindings[icol].buffer);
} }
return SQL_SUCCESS; return SQL_SUCCESS;
...@@ -228,7 +233,7 @@ StatementClass *stmt = (StatementClass *) hstmt; ...@@ -228,7 +233,7 @@ StatementClass *stmt = (StatementClass *) hstmt;
*pibScale = stmt->parameters[ipar].scale; *pibScale = stmt->parameters[ipar].scale;
if(pfNullable) if(pfNullable)
*pfNullable = pgtype_nullable(stmt->parameters[ipar].paramType); *pfNullable = pgtype_nullable(stmt, stmt->parameters[ipar].paramType);
return SQL_SUCCESS; return SQL_SUCCESS;
} }
...@@ -247,37 +252,25 @@ RETCODE SQL_API SQLParamOptions( ...@@ -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( RETCODE SQL_API SQLNumParams(
HSTMT hstmt, HSTMT hstmt,
SWORD FAR *pcpar) SWORD FAR *pcpar)
{ {
StatementClass *stmt = (StatementClass *) hstmt; 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) if(!stmt)
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
if(!stmt->statement) { // If the statement does not have parameters, it should just return 0.
// 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)++;
}
return SQL_SUCCESS; if (pcpar) {
*pcpar = stmt->parameters_allocated;
} }
return SQL_SUCCESS;
} }
/******************************************************************** /********************************************************************
...@@ -309,7 +302,7 @@ extend_bindings(StatementClass *stmt, int num_columns) ...@@ -309,7 +302,7 @@ extend_bindings(StatementClass *stmt, int num_columns)
BindInfoClass *new_bindings; BindInfoClass *new_bindings;
int i; 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 */ /* if we have too few, allocate room for more, and copy the old */
/* entries into the new structure */ /* entries into the new structure */
...@@ -325,6 +318,7 @@ int i; ...@@ -325,6 +318,7 @@ int i;
} }
stmt->bindings = new_bindings; // null indicates error stmt->bindings = new_bindings; // null indicates error
stmt->bindings_allocated = num_columns;
} else { } else {
/* if we have too many, make sure the extra ones are emptied out */ /* if we have too many, make sure the extra ones are emptied out */
......
/* File: bind.h /* File: bind.h
* *
* Description: See "bind.c" * Description: See "bind.c"
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __BIND_H__ #ifndef __BIND_H__
#define __BIND_H__ #define __BIND_H__
#include "psqlodbc.h" #include "psqlodbc.h"
/* /*
* BindInfoClass -- stores information about a bound column * BindInfoClass -- stores information about a bound column
*/ */
struct BindInfoClass_ { struct BindInfoClass_ {
Int4 buflen; /* size of buffer */ Int4 buflen; /* size of buffer */
char *buffer; /* pointer to the buffer */ char *buffer; /* pointer to the buffer */
...@@ -33,10 +33,11 @@ struct ParameterInfoClass_ { ...@@ -33,10 +33,11 @@ struct ParameterInfoClass_ {
Int2 CType; Int2 CType;
Int2 SQLType; Int2 SQLType;
UInt4 precision; UInt4 precision;
Int2 scale; Int2 scale;
Int4 *EXEC_used; Oid lobj_oid;
char *EXEC_buffer; Int4 *EXEC_used; /* amount of data OR the oid of the large object */
char data_at_exec; char *EXEC_buffer; /* the data or the FD of the large object */
char data_at_exec;
}; };
BindInfoClass *create_empty_bindings(int num_columns); BindInfoClass *create_empty_bindings(int num_columns);
......
/* Module: columninfo.c /* Module: columninfo.c
* *
* Description: This module contains routines related to * Description: This module contains routines related to
* reading and storing the field information from a query. * reading and storing the field information from a query.
* *
* Classes: ColumnInfoClass (Functions prefix: "CI_") * Classes: ColumnInfoClass (Functions prefix: "CI_")
* *
* API functions: none * API functions: none
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "columninfo.h" #include "columninfo.h"
#include "socket.h" #include "socket.h"
#include <stdlib.h> #include <stdlib.h>
#include <malloc.h> #include <malloc.h>
...@@ -29,6 +29,7 @@ ColumnInfoClass *rv; ...@@ -29,6 +29,7 @@ ColumnInfoClass *rv;
rv->name = NULL; rv->name = NULL;
rv->adtid = NULL; rv->adtid = NULL;
rv->adtsize = NULL; rv->adtsize = NULL;
rv->display_size = NULL;
} }
return rv; return rv;
...@@ -54,7 +55,7 @@ int new_num_fields; ...@@ -54,7 +55,7 @@ int new_num_fields;
Oid new_adtid; Oid new_adtid;
Int2 new_adtsize; Int2 new_adtsize;
char new_field_name[MAX_MESSAGE_LEN+1]; char new_field_name[MAX_MESSAGE_LEN+1];
/* at first read in the number of fields that are in the query */ /* at first read in the number of fields that are in the query */
new_num_fields = (Int2) SOCK_get_int(sock, sizeof(Int2)); new_num_fields = (Int2) SOCK_get_int(sock, sizeof(Int2));
...@@ -93,11 +94,12 @@ int num_fields = self->num_fields; ...@@ -93,11 +94,12 @@ int num_fields = self->num_fields;
if( self->name[lf]) if( self->name[lf])
free (self->name[lf]); free (self->name[lf]);
} }
/* Safe to call even if null */ /* Safe to call even if null */
free(self->name); free(self->name);
free(self->adtid); free(self->adtid);
free(self->adtsize); free(self->adtsize);
free(self->display_size);
} }
void void
...@@ -110,6 +112,7 @@ CI_set_num_fields(ColumnInfoClass *self, int new_num_fields) ...@@ -110,6 +112,7 @@ CI_set_num_fields(ColumnInfoClass *self, int new_num_fields)
self->name = (char **) malloc (sizeof(char *) * self->num_fields); self->name = (char **) malloc (sizeof(char *) * self->num_fields);
self->adtid = (Oid *) malloc (sizeof(Oid) * self->num_fields); self->adtid = (Oid *) malloc (sizeof(Oid) * self->num_fields);
self->adtsize = (Int2 *) malloc (sizeof(Int2) * self->num_fields); self->adtsize = (Int2 *) malloc (sizeof(Int2) * self->num_fields);
self->display_size = (Int2 *) malloc(sizeof(Int2) * self->num_fields);
} }
void void
...@@ -126,34 +129,7 @@ CI_set_field_info(ColumnInfoClass *self, int field_num, char *new_name, ...@@ -126,34 +129,7 @@ CI_set_field_info(ColumnInfoClass *self, int field_num, char *new_name,
self->name[field_num] = strdup(new_name); self->name[field_num] = strdup(new_name);
self->adtid[field_num] = new_adtid; self->adtid[field_num] = new_adtid;
self->adtsize[field_num] = new_adtsize; self->adtsize[field_num] = new_adtsize;
}
char * self->display_size[field_num] = 0;
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;
} }
/* File: columninfo.h /* File: columninfo.h
* *
* Description: See "columninfo.c" * Description: See "columninfo.c"
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __COLUMNINFO_H__ #ifndef __COLUMNINFO_H__
#define __COLUMNINFO_H__ #define __COLUMNINFO_H__
...@@ -17,14 +17,18 @@ struct ColumnInfoClass_ { ...@@ -17,14 +17,18 @@ struct ColumnInfoClass_ {
char **name; /* list of type names */ char **name; /* list of type names */
Oid *adtid; /* list of type ids */ Oid *adtid; /* list of type ids */
Int2 *adtsize; /* list type sizes */ 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_num_fields(self) (self->num_fields)
#define CI_get_oid(self, col) (self->adtid[col]) #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(); ColumnInfoClass *CI_Constructor();
void CI_Destructor(ColumnInfoClass *self); void CI_Destructor(ColumnInfoClass *self);
void CI_free_memory(ColumnInfoClass *self);
char CI_read_fields(ColumnInfoClass *self, SocketClass *sock); char CI_read_fields(ColumnInfoClass *self, SocketClass *sock);
/* functions for setting up the fields from within the program, */ /* 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); ...@@ -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, void CI_set_field_info(ColumnInfoClass *self, int field_num, char *new_name,
Oid new_adtid, Int2 new_adtsize); 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 #endif
/* Module: connection.c /* Module: connection.c
* *
* Description: This module contains routines related to * Description: This module contains routines related to
* connecting to and disconnecting from the Postgres DBMS. * connecting to and disconnecting from the Postgres DBMS.
* *
* Classes: ConnectionClass (Functions prefix: "CC_") * Classes: ConnectionClass (Functions prefix: "CC_")
* *
* API functions: SQLAllocConnect, SQLConnect, SQLDisconnect, SQLFreeConnect, * API functions: SQLAllocConnect, SQLConnect, SQLDisconnect, SQLFreeConnect,
* SQLBrowseConnect(NI) * SQLBrowseConnect(NI)
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "environ.h" #include "environ.h"
#include "connection.h" #include "connection.h"
#include "socket.h" #include "socket.h"
#include "statement.h" #include "statement.h"
#include "qresult.h" #include "qresult.h"
#include "lobj.h"
#include "dlg_specific.h"
#include <stdio.h> #include <stdio.h>
#include <odbcinst.h> #include <odbcinst.h>
#define STMT_INCREMENT 16 /* how many statement holders to allocate at a time */ #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( RETCODE SQL_API SQLAllocConnect(
HENV henv, HENV henv,
...@@ -70,25 +73,28 @@ RETCODE SQL_API SQLConnect( ...@@ -70,25 +73,28 @@ RETCODE SQL_API SQLConnect(
SWORD cbAuthStr) SWORD cbAuthStr)
{ {
ConnectionClass *conn = (ConnectionClass *) hdbc; ConnectionClass *conn = (ConnectionClass *) hdbc;
ConnInfo *ci;
if ( ! conn) if ( ! conn)
return SQL_INVALID_HANDLE; 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 */ /* 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) /* override values from DSN info with UID and authStr(pwd)
This only occurs if the values are actually there. This only occurs if the values are actually there.
*/ */
make_string(szUID, cbUID, conn->connInfo.username); make_string(szUID, cbUID, ci->username);
make_string(szAuthStr, cbAuthStr, conn->connInfo.password); make_string(szAuthStr, cbAuthStr, ci->password);
/* fill in any defaults */ /* 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) if ( CC_connect(conn, FALSE) <= 0)
// Error messages are filled in // Error messages are filled in
...@@ -124,12 +130,12 @@ ConnectionClass *conn = (ConnectionClass *) hdbc; ...@@ -124,12 +130,12 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
qlog("conn=%u, SQLDisconnect\n", conn); qlog("conn=%u, SQLDisconnect\n", conn);
if (conn->status == CONN_EXECUTING) { if (conn->status == CONN_EXECUTING) {
conn->errornumber = CONN_IN_USE; conn->errornumber = CONN_IN_USE;
conn->errormsg = "A transaction is currently being executed"; conn->errormsg = "A transaction is currently being executed";
return SQL_ERROR; return SQL_ERROR;
} }
mylog("SQLDisconnect: about to CC_cleanup\n"); mylog("SQLDisconnect: about to CC_cleanup\n");
...@@ -155,12 +161,12 @@ ConnectionClass *conn = (ConnectionClass *) hdbc; ...@@ -155,12 +161,12 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
if ( ! conn) if ( ! conn)
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
/* Remove the connection from the environment */ /* Remove the connection from the environment */
if ( ! EN_remove_connection(conn->henv, conn)) { if ( ! EN_remove_connection(conn->henv, conn)) {
conn->errornumber = CONN_IN_USE; conn->errornumber = CONN_IN_USE;
conn->errormsg = "A transaction is currently being executed"; conn->errormsg = "A transaction is currently being executed";
return SQL_ERROR; return SQL_ERROR;
} }
CC_Destructor(conn); CC_Destructor(conn);
...@@ -207,6 +213,7 @@ ConnectionClass *rv; ...@@ -207,6 +213,7 @@ ConnectionClass *rv;
rv->num_stmts = STMT_INCREMENT; rv->num_stmts = STMT_INCREMENT;
rv->lobj_type = PG_TYPE_LO;
} }
return rv; return rv;
} }
...@@ -239,6 +246,26 @@ CC_Destructor(ConnectionClass *self) ...@@ -239,6 +246,26 @@ CC_Destructor(ConnectionClass *self)
return 1; 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 void
CC_clear_error(ConnectionClass *self) CC_clear_error(ConnectionClass *self)
{ {
...@@ -316,75 +343,12 @@ StatementClass *stmt; ...@@ -316,75 +343,12 @@ StatementClass *stmt;
return TRUE; 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 char
CC_connect(ConnectionClass *self, char do_password) CC_connect(ConnectionClass *self, char do_password)
{ {
StartupPacket sp; StartupPacket sp;
StartupPacket6_2 sp62; StartupPacket6_2 sp62;
QResultClass *res; QResultClass *res;
SocketClass *sock; SocketClass *sock;
...@@ -392,7 +356,7 @@ ConnInfo *ci = &(self->connInfo); ...@@ -392,7 +356,7 @@ ConnInfo *ci = &(self->connInfo);
int areq = -1; int areq = -1;
int beresp; int beresp;
char msgbuffer[ERROR_MSG_LENGTH]; char msgbuffer[ERROR_MSG_LENGTH];
char salt[2]; char salt[2];
if ( do_password) if ( do_password)
...@@ -400,6 +364,24 @@ char salt[2]; ...@@ -400,6 +364,24 @@ char salt[2];
else { 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) { if (self->status != CONN_NOT_CONNECTED) {
self->errormsg = "Already connected."; self->errormsg = "Already connected.";
self->errornumber = CONN_OPENDB_ERROR; self->errornumber = CONN_OPENDB_ERROR;
...@@ -439,18 +421,18 @@ char salt[2]; ...@@ -439,18 +421,18 @@ char salt[2];
return 0; return 0;
} }
mylog("connection to the server socket succeeded.\n"); mylog("connection to the server socket succeeded.\n");
if ( PROTOCOL_62(ci)) { if ( PROTOCOL_62(ci)) {
sock->reverse = TRUE; /* make put_int and get_int work for 6.2 */ sock->reverse = TRUE; /* make put_int and get_int work for 6.2 */
memset(&sp62, 0, sizeof(StartupPacket6_2)); memset(&sp62, 0, sizeof(StartupPacket6_2));
SOCK_put_int(sock, htonl(4+sizeof(StartupPacket6_2)), 4); SOCK_put_int(sock, htonl(4+sizeof(StartupPacket6_2)), 4);
sp62.authtype = htonl(NO_AUTHENTICATION); sp62.authtype = htonl(NO_AUTHENTICATION);
strncpy(sp62.database, ci->database, PATH_SIZE); strncpy(sp62.database, ci->database, PATH_SIZE);
strncpy(sp62.user, ci->username, NAMEDATALEN); strncpy(sp62.user, ci->username, NAMEDATALEN);
SOCK_put_n_char(sock, (char *) &sp62, sizeof(StartupPacket6_2)); SOCK_put_n_char(sock, (char *) &sp62, sizeof(StartupPacket6_2));
SOCK_flush_output(sock); SOCK_flush_output(sock);
} }
else { else {
memset(&sp, 0, sizeof(StartupPacket)); memset(&sp, 0, sizeof(StartupPacket));
...@@ -465,7 +447,7 @@ char salt[2]; ...@@ -465,7 +447,7 @@ char salt[2];
SOCK_put_n_char(sock, (char *) &sp, sizeof(StartupPacket)); SOCK_put_n_char(sock, (char *) &sp, sizeof(StartupPacket));
SOCK_flush_output(sock); SOCK_flush_output(sock);
} }
mylog("sent the authentication block.\n"); mylog("sent the authentication block.\n");
...@@ -484,9 +466,9 @@ char salt[2]; ...@@ -484,9 +466,9 @@ char salt[2];
// *************************************************** // ***************************************************
// Now get the authentication request from backend // Now get the authentication request from backend
// *************************************************** // ***************************************************
if ( ! PROTOCOL_62(ci)) do { if ( ! PROTOCOL_62(ci)) do {
if (do_password) if (do_password)
beresp = 'R'; beresp = 'R';
...@@ -590,15 +572,19 @@ char salt[2]; ...@@ -590,15 +572,19 @@ char salt[2];
mylog("empty query seems to be OK.\n"); 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; self->status = CONN_CONNECTED;
return 1; return 1;
...@@ -650,8 +636,8 @@ int i; ...@@ -650,8 +636,8 @@ int i;
return FALSE; return FALSE;
} }
/* Create a more informative error message by concatenating the connection /* Create a more informative error message by concatenating the connection
error message with its socket error message. error message with its socket error message.
*/ */
char * char *
CC_create_errormsg(ConnectionClass *self) CC_create_errormsg(ConnectionClass *self)
...@@ -705,14 +691,14 @@ int rv; ...@@ -705,14 +691,14 @@ int rv;
return rv; return rv;
} }
/* The "result_in" is only used by QR_next_tuple() to fetch another group of rows into /* 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 the same existing QResultClass (this occurs when the tuple cache is depleted and
needs to be re-filled). needs to be re-filled).
The "cursor" is used by SQLExecute to associate a statement handle as the cursor name 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 (i.e., C3326857) for SQL select statements. This cursor is then used in future
'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements. 'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements.
*/ */
QResultClass * QResultClass *
CC_send_query(ConnectionClass *self, char *query, QResultClass *result_in, char *cursor) 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 ...@@ -822,9 +808,16 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
SOCK_put_string(sock, "Q "); SOCK_put_string(sock, "Q ");
SOCK_flush_output(sock); SOCK_flush_output(sock);
while(!clear) { while(!clear) {
SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
mylog("send_query: read command '%s'\n", cmdbuffer); mylog("send_query: read command '%s'\n", cmdbuffer);
clear = (cmdbuffer[0] == 'I'); 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); 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 ...@@ -925,55 +918,270 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
} }
} }
} }
char int
CC_send_settings(ConnectionClass *self) CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *args, int nargs)
{ {
char ini_query[MAX_MESSAGE_LEN]; char id, c, done;
ConnInfo *ci = &(self->connInfo); SocketClass *sock = self->sock;
QResultClass *res; static char msgbuffer[MAX_MESSAGE_LEN+1];
int i;
ini_query[0] = '\0';
mylog("send_function(): conn=%u, fnid=%d, result_is_int=%d, nargs=%d\n", self, fnid, result_is_int, nargs);
/* Turn on/off genetic optimizer based on global flag */ // qlog("conn=%u, func=%d\n", self, fnid);
if (globals.optimizer[0] != '\0')
sprintf(ini_query, "set geqo to '%s'", globals.optimizer); if (SOCK_get_errcode(sock) != 0) {
self->errornumber = CONNECTION_COULD_NOT_SEND;
/* Global settings */ self->errormsg = "Could not send function to backend";
if (globals.conn_settings[0] != '\0') CC_set_no_trans(self);
sprintf(&ini_query[strlen(ini_query)], "%s%s", return FALSE;
ini_query[0] != '\0' ? "; " : "", }
globals.conn_settings);
SOCK_put_string(sock, "F ");
/* Per Datasource settings */ if (SOCK_get_errcode(sock) != 0) {
if (ci->conn_settings[0] != '\0') self->errornumber = CONNECTION_COULD_NOT_SEND;
sprintf(&ini_query[strlen(ini_query)], "%s%s", self->errormsg = "Could not send function to backend";
ini_query[0] != '\0' ? "; " : "", CC_set_no_trans(self);
ci->conn_settings); return FALSE;
}
if (ini_query[0] != '\0') {
mylog("Sending Initial Connection query: '%s'\n", ini_query); SOCK_put_int(sock, fnid, 4);
SOCK_put_int(sock, nargs, 4);
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)); mylog("send_function: done sending function\n");
}
for (i = 0; i < nargs; ++i) {
if ( res == NULL ||
QR_get_status(res) == PGRES_BAD_RESPONSE || mylog(" arg[%d]: len = %d, isint = %d, integer = %d, ptr = %u\n",
QR_get_status(res) == PGRES_FATAL_ERROR || i, args[i].len, args[i].isint, args[i].u.integer, args[i].u.ptr);
QR_get_status(res) == PGRES_INTERNAL_ERROR) {
SOCK_put_int(sock, args[i].len, 4);
self->errornumber = CONNECTION_COULD_NOT_SEND; if (args[i].isint)
self->errormsg = "Error sending ConnSettings"; SOCK_put_int(sock, args[i].u.integer, 4);
if (res) else
QR_Destructor(res); SOCK_put_n_char(sock, (char *) args[i].u.ptr, args[i].len);
return 0;
}
}
if (res)
QR_Destructor(res); mylog(" done sending args\n");
}
return TRUE; 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);
}
*/
/* File: connection.h /* File: connection.h
* *
* Description: See "connection.c" * Description: See "connection.c"
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __CONNECTION_H__ #ifndef __CONNECTION_H__
#define __CONNECTION_H__ #define __CONNECTION_H__
#include <windows.h> #include <windows.h>
#include <sql.h> #include <sql.h>
#include <sqlext.h>
#include "psqlodbc.h" #include "psqlodbc.h"
typedef enum { typedef enum {
...@@ -44,9 +45,9 @@ typedef enum { ...@@ -44,9 +45,9 @@ typedef enum {
/* SetConnectOption: corresponds to ODBC--"S1009" */ /* SetConnectOption: corresponds to ODBC--"S1009" */
#define CONN_TRANSACT_IN_PROGRES 7 #define CONN_TRANSACT_IN_PROGRES 7
#define CONN_NO_MEMORY_ERROR 8 #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_INVALID_AUTHENTICATION 10
#define CONN_AUTH_TYPE_UNSUPPORTED 11 #define CONN_AUTH_TYPE_UNSUPPORTED 11
/* Conn_status defines */ /* Conn_status defines */
...@@ -77,12 +78,12 @@ typedef enum { ...@@ -77,12 +78,12 @@ typedef enum {
#define SM_OPTIONS 64 #define SM_OPTIONS 64
#define SM_UNUSED 64 #define SM_UNUSED 64
#define SM_TTY 64 #define SM_TTY 64
/* Old 6.2 protocol defines */ /* Old 6.2 protocol defines */
#define NO_AUTHENTICATION 7 #define NO_AUTHENTICATION 7
#define PATH_SIZE 64 #define PATH_SIZE 64
#define ARGV_SIZE 64 #define ARGV_SIZE 64
#define NAMEDATALEN 16 #define NAMEDATALEN 16
typedef unsigned int ProtocolVersion; typedef unsigned int ProtocolVersion;
...@@ -90,7 +91,7 @@ typedef unsigned int ProtocolVersion; ...@@ -90,7 +91,7 @@ typedef unsigned int ProtocolVersion;
#define PG_PROTOCOL_LATEST PG_PROTOCOL(1, 0) #define PG_PROTOCOL_LATEST PG_PROTOCOL(1, 0)
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(0, 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 typedef struct _StartupPacket
{ {
ProtocolVersion protoVersion; ProtocolVersion protoVersion;
...@@ -100,41 +101,47 @@ typedef struct _StartupPacket ...@@ -100,41 +101,47 @@ typedef struct _StartupPacket
char unused[SM_UNUSED]; char unused[SM_UNUSED];
char tty[SM_TTY]; char tty[SM_TTY];
} StartupPacket; } StartupPacket;
/* This startup packet is to support pre-Postgres 6.3 protocol */ /* This startup packet is to support pre-Postgres 6.3 protocol */
typedef struct _StartupPacket6_2 typedef struct _StartupPacket6_2
{ {
unsigned int authtype; unsigned int authtype;
char database[PATH_SIZE]; char database[PATH_SIZE];
char user[NAMEDATALEN]; char user[NAMEDATALEN];
char options[ARGV_SIZE]; char options[ARGV_SIZE];
char execfile[ARGV_SIZE]; char execfile[ARGV_SIZE];
char tty[PATH_SIZE]; char tty[PATH_SIZE];
} StartupPacket6_2; } StartupPacket6_2;
/* Structure to hold all the connection attributes for a specific /* Structure to hold all the connection attributes for a specific
connection (used for both registry and file, DSN and DRIVER) connection (used for both registry and file, DSN and DRIVER)
*/ */
typedef struct { typedef struct {
char dsn[MEDIUM_REGISTRY_LEN]; char dsn[MEDIUM_REGISTRY_LEN];
char desc[MEDIUM_REGISTRY_LEN];
char driver[MEDIUM_REGISTRY_LEN]; char driver[MEDIUM_REGISTRY_LEN];
char server[MEDIUM_REGISTRY_LEN]; char server[MEDIUM_REGISTRY_LEN];
char database[MEDIUM_REGISTRY_LEN]; char database[MEDIUM_REGISTRY_LEN];
char username[MEDIUM_REGISTRY_LEN]; char username[MEDIUM_REGISTRY_LEN];
char password[MEDIUM_REGISTRY_LEN]; char password[MEDIUM_REGISTRY_LEN];
char conn_settings[LARGE_REGISTRY_LEN]; char conn_settings[LARGE_REGISTRY_LEN];
char protocol[SMALL_REGISTRY_LEN]; char protocol[SMALL_REGISTRY_LEN];
char port[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; char focus_password;
} ConnInfo; } ConnInfo;
/* Macro to determine is the connection using 6.2 protocol? */ /* 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 ************/ /******* The Connection handle ************/
struct ConnectionClass_ { struct ConnectionClass_ {
HENV henv; /* environment this connection was created on */ HENV henv; /* environment this connection was created on */
...@@ -145,6 +152,7 @@ struct ConnectionClass_ { ...@@ -145,6 +152,7 @@ struct ConnectionClass_ {
StatementClass **stmts; StatementClass **stmts;
int num_stmts; int num_stmts;
SocketClass *sock; SocketClass *sock;
int lobj_type;
char transact_status; /* Is a transaction is currently in progress */ char transact_status; /* Is a transaction is currently in progress */
char errormsg_created; /* has an informative error msg been created? */ char errormsg_created; /* has an informative error msg been created? */
}; };
...@@ -162,15 +170,14 @@ struct ConnectionClass_ { ...@@ -162,15 +170,14 @@ struct ConnectionClass_ {
/* for CC_DSN_info */ /* for CC_DSN_info */
#define CONN_DONT_OVERWRITE 0 #define CONN_DONT_OVERWRITE 0
#define CONN_OVERWRITE 1 #define CONN_OVERWRITE 1
/* prototypes */ /* prototypes */
ConnectionClass *CC_Constructor(); ConnectionClass *CC_Constructor();
char CC_Destructor(ConnectionClass *self); char CC_Destructor(ConnectionClass *self);
int CC_cursor_count(ConnectionClass *self);
char CC_cleanup(ConnectionClass *self); char CC_cleanup(ConnectionClass *self);
char CC_abort(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_connect(ConnectionClass *self, char do_password);
char CC_add_statement(ConnectionClass *self, StatementClass *stmt); char CC_add_statement(ConnectionClass *self, StatementClass *stmt);
char CC_remove_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); ...@@ -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); QResultClass *CC_send_query(ConnectionClass *self, char *query, QResultClass *result_in, char *cursor);
void CC_clear_error(ConnectionClass *self); void CC_clear_error(ConnectionClass *self);
char *CC_create_errormsg(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 #endif
/* Module: convert.c /* Module: convert.c
* *
* Description: This module contains routines related to * Description: This module contains routines related to
* converting parameters and columns into requested data types. * converting parameters and columns into requested data types.
* Parameters are converted from their SQL_C data types into * Parameters are converted from their SQL_C data types into
* the appropriate postgres type. Columns are converted from * the appropriate postgres type. Columns are converted from
* their postgres type (SQL type) into the appropriate SQL_C * their postgres type (SQL type) into the appropriate SQL_C
* data type. * data type.
* *
* Classes: n/a * Classes: n/a
* *
* API functions: none * API functions: none
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <windows.h> #include <windows.h>
#include <sql.h> #include <sql.h>
#include <sqlext.h> #include <sqlext.h>
#include <time.h> #include <time.h>
#include <math.h> #include <math.h>
#include "convert.h" #include "convert.h"
#include "statement.h" #include "statement.h"
#include "bind.h" #include "bind.h"
#include "pgtypes.h" #include "pgtypes.h"
#include "lobj.h"
#include "connection.h"
extern GLOBAL_VALUES globals;
/* How to map ODBC scalar functions {fn func(args)} to Postgres */
/* This is just a simple substitution */
char *mapFuncs[][2] = {
{ "CONCAT", "textcat" },
{ "LCASE", "lower" },
{ "LOCATE", "strpos" },
{ "LENGTH", "textlen" },
{ "LTRIM", "ltrim" },
{ "RTRIM", "rtrim" },
{ "SUBSTRING", "substr" },
{ "UCASE", "upper" },
{ "NOW", "now" },
{ 0, 0 }
};
/******** A Guide for date/time/timestamp conversions ************** /******** A Guide for date/time/timestamp conversions **************
...@@ -47,23 +67,23 @@ ...@@ -47,23 +67,23 @@
/* This is called by SQLFetch() */ /* This is called by SQLFetch() */
int int
copy_and_convert_field_bindinfo(Int4 field_type, void *value, BindInfoClass *bic) copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col)
{ {
return copy_and_convert_field(field_type, value, (Int2)bic->returntype, (PTR)bic->buffer, BindInfoClass *bic = &(stmt->bindings[col]);
(SDWORD)bic->buflen, (SDWORD *)bic->used);
return copy_and_convert_field(stmt, field_type, value, (Int2)bic->returntype, (PTR)bic->buffer,
(SDWORD)bic->buflen, (SDWORD *)bic->used, FALSE);
} }
/* This is called by SQLGetData() */ /* This is called by SQLGetData() */
int int
copy_and_convert_field(Int4 field_type, void *value, Int2 fCType, PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue) copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType,
PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue, char multiple)
{ {
Int4 len = 0, nf; Int4 len = 0;
char day[4], mon[4], tz[4];
SIMPLE_TIME st; SIMPLE_TIME st;
time_t t = time(NULL); time_t t = time(NULL);
struct tm *tim; struct tm *tim;
int bool;
memset(&st, 0, sizeof(SIMPLE_TIME)); memset(&st, 0, sizeof(SIMPLE_TIME));
...@@ -73,8 +93,6 @@ int bool; ...@@ -73,8 +93,6 @@ int bool;
st.d = tim->tm_mday; st.d = tim->tm_mday;
st.y = tim->tm_year + 1900; st.y = tim->tm_year + 1900;
bool = 0;
mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, value, cbValueMax); mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, value, cbValueMax);
if(value) { if(value) {
...@@ -88,7 +106,7 @@ int bool; ...@@ -88,7 +106,7 @@ int bool;
switch(field_type) { switch(field_type) {
/* $$$ need to add parsing for date/time/timestamp strings in PG_TYPE_CHAR,VARCHAR $$$ */ /* $$$ need to add parsing for date/time/timestamp strings in PG_TYPE_CHAR,VARCHAR $$$ */
case PG_TYPE_DATE: case PG_TYPE_DATE:
sscanf(value, "%2d-%2d-%4d", &st.m, &st.d, &st.y); sscanf(value, "%4d-%2d-%2d", &st.y, &st.m, &st.d);
break; break;
case PG_TYPE_TIME: case PG_TYPE_TIME:
...@@ -96,32 +114,28 @@ int bool; ...@@ -96,32 +114,28 @@ int bool;
break; break;
case PG_TYPE_ABSTIME: case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME: case PG_TYPE_DATETIME:
if (strnicmp(value, "invalid", 7) != 0) { if (strnicmp(value, "invalid", 7) != 0) {
nf = sscanf(value, "%3s %3s %2d %2d:%2d:%2d %4d %3s", &day, &mon, &st.d, &st.hh, &st.mm, &st.ss, &st.y, &tz); sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m, &st.d, &st.hh, &st.mm, &st.ss);
if (nf == 7 || nf == 8) { } else { /* The timestamp is invalid so set something conspicuous, like the epoch */
/* convert month name to month number */ t = 0;
st.m = monthToNumber(mon); tim = localtime(&t);
} st.m = tim->tm_mon + 1;
} else { /* The timestamp is invalid so set something conspicuous, like the epoch */ st.d = tim->tm_mday;
t = 0; st.y = tim->tm_year + 1900;
tim = localtime(&t); st.hh = tim->tm_hour;
st.m = tim->tm_mon + 1; st.mm = tim->tm_min;
st.d = tim->tm_mday; st.ss = tim->tm_sec;
st.y = tim->tm_year + 1900;
st.hh = tim->tm_hour;
st.mm = tim->tm_min;
st.ss = tim->tm_sec;
} }
break; break;
case PG_TYPE_BOOL: { /* change T/F to 1/0 */ case PG_TYPE_BOOL: { /* change T/F to 1/0 */
char *s = (char *) value; char *s = (char *) value;
if (s[0] == 'T' || s[0] == 't' || s[0] == '1') if (s[0] == 'T' || s[0] == 't')
bool = 1; s[0] = '1';
else else
bool = 0; s[0] = '0';
} }
break; break;
...@@ -149,11 +163,20 @@ int bool; ...@@ -149,11 +163,20 @@ int bool;
return COPY_OK; /* dont go any further or the data will be trashed */ return COPY_OK; /* dont go any further or the data will be trashed */
} }
/* This is a large object OID, which is used to store LONGVARBINARY objects. */
case PG_TYPE_LO:
return convert_lo( stmt, value, fCType, rgbValue, cbValueMax, pcbValue, multiple);
default:
if (field_type == stmt->hdbc->lobj_type) /* hack until permanent type available */
return convert_lo( stmt, value, fCType, rgbValue, cbValueMax, pcbValue, multiple);
} }
/* Change default into something useable */ /* Change default into something useable */
if (fCType == SQL_C_DEFAULT) { if (fCType == SQL_C_DEFAULT) {
fCType = pgtype_to_ctype(field_type); fCType = pgtype_to_ctype(stmt, field_type);
mylog("copy_and_convert, SQL_C_DEFAULT: fCType = %d\n", fCType); mylog("copy_and_convert, SQL_C_DEFAULT: fCType = %d\n", fCType);
} }
...@@ -165,41 +188,41 @@ int bool; ...@@ -165,41 +188,41 @@ int bool;
switch(field_type) { switch(field_type) {
case PG_TYPE_DATE: case PG_TYPE_DATE:
len = 11; len = 11;
if (cbValueMax > len) if (cbValueMax >= len)
sprintf((char *)rgbValue, "%.4d-%.2d-%.2d", st.y, st.m, st.d); sprintf((char *)rgbValue, "%.4d-%.2d-%.2d", st.y, st.m, st.d);
break; break;
case PG_TYPE_TIME: case PG_TYPE_TIME:
len = 9; len = 9;
if (cbValueMax > len) if (cbValueMax >= len)
sprintf((char *)rgbValue, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss); sprintf((char *)rgbValue, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss);
break; break;
case PG_TYPE_ABSTIME: case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME: case PG_TYPE_DATETIME:
len = 19; len = 19;
if (cbValueMax > len) if (cbValueMax >= len)
sprintf((char *) rgbValue, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", sprintf((char *) rgbValue, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
st.y, st.m, st.d, st.hh, st.mm, st.ss); st.y, st.m, st.d, st.hh, st.mm, st.ss);
break; break;
case PG_TYPE_BOOL: case PG_TYPE_BOOL:
len = 1; len = 1;
if (cbValueMax > len) { if (cbValueMax > len) {
strcpy((char *) rgbValue, bool ? "1" : "0"); strcpy((char *) rgbValue, value);
mylog("PG_TYPE_BOOL: rgbValue = '%s'\n", rgbValue); mylog("PG_TYPE_BOOL: rgbValue = '%s'\n", rgbValue);
} }
break;
case PG_TYPE_BYTEA: // convert binary data to hex strings (i.e, 255 = "FF")
len = convert_pgbinary_to_char(value, rgbValue, cbValueMax);
break; break;
default: case PG_TYPE_BYTEA: // convert binary data to hex strings (i.e, 255 = "FF")
/* convert linefeeds to carriage-return/linefeed */ len = convert_pgbinary_to_char(value, rgbValue, cbValueMax);
convert_linefeeds( (char *) value, rgbValue, cbValueMax); break;
len = strlen(rgbValue);
default:
/* convert linefeeds to carriage-return/linefeed */
convert_linefeeds( (char *) value, rgbValue, cbValueMax);
len = strlen(rgbValue);
mylog(" SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValue = '%s'\n", len, cbValueMax, rgbValue); mylog(" SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValue = '%s'\n", len, cbValueMax, rgbValue);
break; break;
} }
...@@ -233,7 +256,7 @@ int bool; ...@@ -233,7 +256,7 @@ int bool;
} }
break; break;
case SQL_C_TIMESTAMP: case SQL_C_TIMESTAMP:
len = 16; len = 16;
if (cbValueMax >= len) { if (cbValueMax >= len) {
TIMESTAMP_STRUCT *ts = (TIMESTAMP_STRUCT *) rgbValue; TIMESTAMP_STRUCT *ts = (TIMESTAMP_STRUCT *) rgbValue;
...@@ -250,7 +273,7 @@ int bool; ...@@ -250,7 +273,7 @@ int bool;
case SQL_C_BIT: case SQL_C_BIT:
len = 1; len = 1;
if (cbValueMax >= len || field_type == PG_TYPE_BOOL) { if (cbValueMax >= len || field_type == PG_TYPE_BOOL) {
*((UCHAR *)rgbValue) = (UCHAR) bool; *((UCHAR *)rgbValue) = atoi(value);
mylog("SQL_C_BIT: val = %d, cb = %d, rgb=%d\n", atoi(value), cbValueMax, *((UCHAR *)rgbValue)); mylog("SQL_C_BIT: val = %d, cb = %d, rgb=%d\n", atoi(value), cbValueMax, *((UCHAR *)rgbValue));
} }
break; break;
...@@ -306,12 +329,13 @@ int bool; ...@@ -306,12 +329,13 @@ int bool;
*((UDWORD *)rgbValue) = atol(value); *((UDWORD *)rgbValue) = atol(value);
break; break;
case SQL_C_BINARY: case SQL_C_BINARY:
// truncate if necessary
// convert octal escapes to bytes // truncate if necessary
len = convert_from_pgbinary(value, rgbValue, cbValueMax); // convert octal escapes to bytes
mylog("SQL_C_BINARY: len = %d\n", len); len = convert_from_pgbinary(value, rgbValue, cbValueMax);
break; mylog("SQL_C_BINARY: len = %d\n", len);
break;
default: default:
return COPY_UNSUPPORTED_TYPE; return COPY_UNSUPPORTED_TYPE;
...@@ -361,18 +385,19 @@ unsigned int opos, npos; ...@@ -361,18 +385,19 @@ unsigned int opos, npos;
char param_string[128], tmp[256], cbuf[TEXT_FIELD_SIZE+5]; char param_string[128], tmp[256], cbuf[TEXT_FIELD_SIZE+5];
int param_number; int param_number;
Int2 param_ctype, param_sqltype; Int2 param_ctype, param_sqltype;
char *old_statement = stmt->statement; char *old_statement = stmt->statement;
char *new_statement = stmt->stmt_with_params; char *new_statement = stmt->stmt_with_params;
SIMPLE_TIME st; SIMPLE_TIME st;
time_t t = time(NULL); time_t t = time(NULL);
struct tm *tim; struct tm *tim;
SDWORD FAR *used; SDWORD used;
char *buffer, *buf; char *buffer, *buf;
char in_quote = FALSE;
if ( ! old_statement) if ( ! old_statement)
return SQL_ERROR; return SQL_ERROR;
memset(&st, 0, sizeof(SIMPLE_TIME)); memset(&st, 0, sizeof(SIMPLE_TIME));
...@@ -382,11 +407,13 @@ char *buffer, *buf; ...@@ -382,11 +407,13 @@ char *buffer, *buf;
st.d = tim->tm_mday; st.d = tim->tm_mday;
st.y = tim->tm_year + 1900; st.y = tim->tm_year + 1900;
/* If the application hasn't set a cursor name, then generate one */
if ( stmt->cursor_name[0] == '\0')
sprintf(stmt->cursor_name, "SQL_CUR%u", stmt);
// For selects, prepend a declare cursor to the statement // For selects, prepend a declare cursor to the statement
if (stmt->statement_type == STMT_TYPE_SELECT) { if (stmt->statement_type == STMT_TYPE_SELECT && globals.use_declarefetch) {
sprintf(new_statement, "declare C%u cursor for ", stmt); sprintf(new_statement, "declare %s cursor for ", stmt->cursor_name);
npos = strlen(new_statement); npos = strlen(new_statement);
} }
else { else {
...@@ -398,10 +425,10 @@ char *buffer, *buf; ...@@ -398,10 +425,10 @@ char *buffer, *buf;
for (opos = 0; opos < strlen(old_statement); opos++) { for (opos = 0; opos < strlen(old_statement); opos++) {
// Squeeze carriage-returns/linfeed pairs to linefeed only // Squeeze carriage-returns/linfeed pairs to linefeed only
if (old_statement[opos] == '\r' && opos+1<strlen(old_statement) && old_statement[opos+1] == '\n') { if (old_statement[opos] == '\r' && opos+1<strlen(old_statement) && old_statement[opos+1] == '\n') {
continue; continue;
} }
// Handle literals (date, time, timestamp) // Handle literals (date, time, timestamp)
else if (old_statement[opos] == '{') { else if (old_statement[opos] == '{') {
...@@ -419,19 +446,34 @@ char *buffer, *buf; ...@@ -419,19 +446,34 @@ char *buffer, *buf;
memcpy(&new_statement[npos], esc, strlen(esc)); memcpy(&new_statement[npos], esc, strlen(esc));
npos += strlen(esc); npos += strlen(esc);
} }
else { /* its not a valid literal so just copy */
*end = '}';
new_statement[npos++] = old_statement[opos];
continue;
}
opos += end - begin + 2; opos += end - begin + 1;
*end = '}'; *end = '}';
continue; continue;
} }
else if (old_statement[opos] != '?') { // a regular character /* Can you have parameter markers inside of quotes? I dont think so.
All the queries I've seen expect the driver to put quotes if needed.
*/
else if (old_statement[opos] == '?' && !in_quote)
; /* ok */
else {
if (old_statement[opos] == '\'')
in_quote = (in_quote ? FALSE : TRUE);
new_statement[npos++] = old_statement[opos]; new_statement[npos++] = old_statement[opos];
continue; continue;
} }
/****************************************************/ /****************************************************/
/* Its a '?' parameter alright */ /* Its a '?' parameter alright */
/****************************************************/ /****************************************************/
...@@ -440,31 +482,31 @@ char *buffer, *buf; ...@@ -440,31 +482,31 @@ char *buffer, *buf;
if (param_number >= stmt->parameters_allocated) if (param_number >= stmt->parameters_allocated)
break; break;
/* Assign correct buffers based on data at exec param or not */ /* Assign correct buffers based on data at exec param or not */
if ( stmt->parameters[param_number].data_at_exec) { if ( stmt->parameters[param_number].data_at_exec) {
used = stmt->parameters[param_number].EXEC_used; used = stmt->parameters[param_number].EXEC_used ? *stmt->parameters[param_number].EXEC_used : SQL_NTS;
buffer = stmt->parameters[param_number].EXEC_buffer; buffer = stmt->parameters[param_number].EXEC_buffer;
} }
else { else {
used = stmt->parameters[param_number].used; used = stmt->parameters[param_number].used ? *stmt->parameters[param_number].used : SQL_NTS;
buffer = stmt->parameters[param_number].buffer; buffer = stmt->parameters[param_number].buffer;
} }
/* Handle NULL parameter data */ /* Handle NULL parameter data */
if (used && *used == SQL_NULL_DATA) { if (used == SQL_NULL_DATA) {
strcpy(&new_statement[npos], "NULL"); strcpy(&new_statement[npos], "NULL");
npos += 4; npos += 4;
continue; continue;
} }
/* If no buffer, and its not null, then what the hell is it? /* If no buffer, and its not null, then what the hell is it?
Just leave it alone then. Just leave it alone then.
*/ */
if ( ! buffer) { if ( ! buffer) {
new_statement[npos++] = '?'; new_statement[npos++] = '?';
continue; continue;
} }
param_ctype = stmt->parameters[param_number].CType; param_ctype = stmt->parameters[param_number].CType;
param_sqltype = stmt->parameters[param_number].SQLType; param_sqltype = stmt->parameters[param_number].SQLType;
...@@ -483,8 +525,8 @@ char *buffer, *buf; ...@@ -483,8 +525,8 @@ char *buffer, *buf;
/* Convert input C type to a neutral format */ /* Convert input C type to a neutral format */
switch(param_ctype) { switch(param_ctype) {
case SQL_C_BINARY: case SQL_C_BINARY:
case SQL_C_CHAR: case SQL_C_CHAR:
buf = buffer; buf = buffer;
break; break;
...@@ -535,7 +577,7 @@ char *buffer, *buf; ...@@ -535,7 +577,7 @@ char *buffer, *buf;
case SQL_C_BIT: { case SQL_C_BIT: {
int i = *((UCHAR *) buffer); int i = *((UCHAR *) buffer);
sprintf(param_string, "'%s'", i ? "t" : "f"); sprintf(param_string, "%d", i ? 1 : 0);
break; break;
} }
...@@ -587,14 +629,14 @@ char *buffer, *buf; ...@@ -587,14 +629,14 @@ char *buffer, *buf;
switch(param_sqltype) { switch(param_sqltype) {
case SQL_CHAR: case SQL_CHAR:
case SQL_VARCHAR: case SQL_VARCHAR:
case SQL_LONGVARCHAR: case SQL_LONGVARCHAR:
new_statement[npos++] = '\''; /* Open Quote */ new_statement[npos++] = '\''; /* Open Quote */
/* it was a SQL_C_CHAR */ /* it was a SQL_C_CHAR */
if (buf) { if (buf) {
convert_returns(buf, &new_statement[npos], used ? *used : SQL_NTS); convert_special_chars(buf, &new_statement[npos], used);
npos += strlen(&new_statement[npos]); npos += strlen(&new_statement[npos]);
} }
/* it was a numeric type */ /* it was a numeric type */
...@@ -605,9 +647,11 @@ char *buffer, *buf; ...@@ -605,9 +647,11 @@ char *buffer, *buf;
/* it was date,time,timestamp -- use m,d,y,hh,mm,ss */ /* it was date,time,timestamp -- use m,d,y,hh,mm,ss */
else { else {
char *buf = convert_time(&st); sprintf(tmp, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
strcpy(&new_statement[npos], buf); st.y, st.m, st.d, st.hh, st.mm, st.ss);
npos += strlen(buf);
strcpy(&new_statement[npos], tmp);
npos += strlen(tmp);
} }
new_statement[npos++] = '\''; /* Close Quote */ new_statement[npos++] = '\''; /* Close Quote */
...@@ -615,20 +659,20 @@ char *buffer, *buf; ...@@ -615,20 +659,20 @@ char *buffer, *buf;
break; break;
case SQL_DATE: case SQL_DATE:
if (buf && used) { /* copy char data to time */ if (buf) { /* copy char data to time */
my_strcpy(cbuf, sizeof(cbuf), buf, *used); my_strcpy(cbuf, sizeof(cbuf), buf, used);
parse_datetime(cbuf, &st); parse_datetime(cbuf, &st);
} }
sprintf(tmp, "'%.2d-%.2d-%.4d'", st.m, st.d, st.y); sprintf(tmp, "'%.4d-%.2d-%.2d'", st.y, st.m, st.d);
strcpy(&new_statement[npos], tmp); strcpy(&new_statement[npos], tmp);
npos += strlen(tmp); npos += strlen(tmp);
break; break;
case SQL_TIME: case SQL_TIME:
if (buf && used) { /* copy char data to time */ if (buf) { /* copy char data to time */
my_strcpy(cbuf, sizeof(cbuf), buf, *used); my_strcpy(cbuf, sizeof(cbuf), buf, used);
parse_datetime(cbuf, &st); parse_datetime(cbuf, &st);
} }
...@@ -638,39 +682,61 @@ char *buffer, *buf; ...@@ -638,39 +682,61 @@ char *buffer, *buf;
npos += strlen(tmp); npos += strlen(tmp);
break; break;
case SQL_TIMESTAMP: { case SQL_TIMESTAMP:
char *tbuf;
if (buf && used) { if (buf) {
my_strcpy(cbuf, sizeof(cbuf), buf, *used); my_strcpy(cbuf, sizeof(cbuf), buf, used);
parse_datetime(cbuf, &st); parse_datetime(cbuf, &st);
} }
tbuf = convert_time(&st); sprintf(tmp, "'%.4d-%.2d-%.2d %.2d:%.2d:%.2d'",
st.y, st.m, st.d, st.hh, st.mm, st.ss);
sprintf(&new_statement[npos], "'%s'", tbuf); strcpy(&new_statement[npos], tmp);
npos += strlen(tbuf) + 2; npos += strlen(tmp);
break; break;
}
case SQL_BINARY:
case SQL_BINARY: case SQL_VARBINARY: /* non-ascii characters should be converted to octal */
case SQL_VARBINARY: new_statement[npos++] = '\''; /* Open Quote */
case SQL_LONGVARBINARY: /* non-ascii characters should be converted to octal */
mylog("SQL_LONGVARBINARY: about to call convert_to_pgbinary, used = %d\n", used);
new_statement[npos++] = '\''; /* Open Quote */
npos += convert_to_pgbinary(buf, &new_statement[npos], used);
mylog("SQL_LONGVARBINARY: about to call convert_to_pgbinary, *used = %d\n", *used);
new_statement[npos++] = '\''; /* Close Quote */
npos += convert_to_pgbinary(buf, &new_statement[npos], *used);
break;
new_statement[npos++] = '\''; /* Close Quote */ case SQL_LONGVARBINARY:
/* the oid of the large object -- just put that in for the
break; parameter marker -- the data has already been sent to the large object
*/
default: /* a numeric type */ sprintf(param_string, "%d", stmt->parameters[param_number].lobj_oid);
strcpy(&new_statement[npos], param_string); strcpy(&new_statement[npos], param_string);
npos += strlen(param_string); npos += strlen(param_string);
break;
// because of no conversion operator for bool and int4, SQL_BIT
// must be quoted (0 or 1 is ok to use inside the quotes)
default: /* a numeric type or SQL_BIT */
if (param_sqltype == SQL_BIT)
new_statement[npos++] = '\''; /* Open Quote */
if (buf) {
my_strcpy(&new_statement[npos], sizeof(stmt->stmt_with_params) - npos, buf, used);
npos += strlen(&new_statement[npos]);
}
else {
strcpy(&new_statement[npos], param_string);
npos += strlen(param_string);
}
if (param_sqltype == SQL_BIT)
new_statement[npos++] = '\''; /* Close Quote */
break; break;
} }
...@@ -683,29 +749,47 @@ char *buffer, *buf; ...@@ -683,29 +749,47 @@ char *buffer, *buf;
return SQL_SUCCESS; return SQL_SUCCESS;
} }
char *
mapFunction(char *func)
{
int i;
for (i = 0; mapFuncs[i][0]; i++)
if ( ! stricmp(mapFuncs[i][0], func))
return mapFuncs[i][1];
return NULL;
}
// This function returns a pointer to static memory! // This function returns a pointer to static memory!
char * char *
convert_escape(char *value) convert_escape(char *value)
{ {
char key[32], val[256]; char key[32], val[256];
static char escape[256]; static char escape[1024];
SIMPLE_TIME st; char func[32], the_rest[1024];
char *mapFunc;
sscanf(value, "%[^'] '%[^']'", key, val); sscanf(value, "%s %[^\r]", key, val);
mylog("convert_escape: key='%s', val='%s'\n", key, val); mylog("convert_escape: key='%s', val='%s'\n", key, val);
if ( ! strncmp(key, "d", 1)) { if ( ! strcmp(key, "d") ||
sscanf(val, "%4d-%2d-%2d", &st.y, &st.m, &st.d); ! strcmp(key, "t") ||
sprintf(escape, "'%.2d-%.2d-%.4d'", st.m, st.d, st.y); ! strcmp(key, "ts")) {
} else if (! strncmp(key, "t", 1)) { strcpy(escape, val);
sprintf(escape, "'%s'", val); }
else if ( ! strcmp(key, "fn")) {
sscanf(val, "%[^(]%[^\r]", func, the_rest);
mapFunc = mapFunction(func);
if ( ! mapFunc)
return NULL;
else {
strcpy(escape, mapFunc);
strcat(escape, the_rest);
}
} else if (! strncmp(key, "ts", 2)) {
sscanf(val, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m, &st.d, &st.hh, &st.mm, &st.ss);
strcpy(escape, convert_time(&st));
} }
else { else {
return NULL; return NULL;
...@@ -716,40 +800,6 @@ SIMPLE_TIME st; ...@@ -716,40 +800,6 @@ SIMPLE_TIME st;
} }
int
monthToNumber(char *mon)
{
int m = 0;
if ( ! stricmp(mon, "Jan"))
m = 1;
else if ( ! stricmp(mon, "Feb"))
m = 2;
else if ( ! stricmp(mon, "Mar"))
m = 3;
else if ( ! stricmp(mon, "Apr"))
m = 4;
else if ( ! stricmp(mon, "May"))
m = 5;
else if ( ! stricmp(mon, "Jun"))
m = 6;
else if ( ! stricmp(mon, "Jul"))
m = 7;
else if ( ! stricmp(mon, "Aug"))
m = 8;
else if ( ! stricmp(mon, "Sep"))
m = 9;
else if ( ! stricmp(mon, "Oct"))
m = 10;
else if ( ! stricmp(mon, "Nov"))
m = 11;
else if ( ! stricmp(mon, "Dec"))
m = 12;
return m;
}
char * char *
convert_money(char *s) convert_money(char *s)
{ {
...@@ -767,39 +817,7 @@ size_t i = 0, out = 0; ...@@ -767,39 +817,7 @@ size_t i = 0, out = 0;
return s; return s;
} }
/* Convert a discrete time into a localized string */
char *
convert_time(SIMPLE_TIME *st)
{
struct tm tim;
static char buf[1024];
mylog("convert_time: m=%d,d=%d,y=%d,hh=%d,mm=%d,ss=%d\n",
st->m, st->d, st->y, st->hh, st->mm, st->ss);
memset(&tim, 0, sizeof(tim));
tim.tm_mon = st->m - 1;
tim.tm_mday = st->d;
tim.tm_year = st->y - 1900;
tim.tm_hour = st->hh;
tim.tm_min = st->mm;
tim.tm_sec = st->ss;
/* Dont bother trying to figure out the day of week because
postgres will determine it correctly. However, the timezone
should be taken into account. $$$$
*/
// tim.tm_isdst = _daylight;
strftime(buf, sizeof(buf), "%b %d %H:%M:%S %Y",
&tim);
mylog("convert_time: buf = '%s'\n", buf);
return buf;
}
/* This function parses a character string for date/time info and fills in SIMPLE_TIME */ /* This function parses a character string for date/time info and fills in SIMPLE_TIME */
/* It does not zero out SIMPLE_TIME in case it is desired to initialize it with a value */ /* It does not zero out SIMPLE_TIME in case it is desired to initialize it with a value */
...@@ -851,145 +869,214 @@ int nf; ...@@ -851,145 +869,214 @@ int nf;
return FALSE; return FALSE;
} }
/* Change linefeed to carriage-return/linefeed */ /* Change linefeed to carriage-return/linefeed */
char * char *
convert_linefeeds(char *si, char *dst, size_t max) convert_linefeeds(char *si, char *dst, size_t max)
{ {
size_t i = 0, out = 0; size_t i = 0, out = 0;
static char sout[TEXT_FIELD_SIZE+5]; static char sout[TEXT_FIELD_SIZE+5];
char *p; char *p;
if (dst) if (dst)
p = dst; p = dst;
else { else {
p = sout; p = sout;
max = sizeof(sout); max = sizeof(sout);
} }
p[0] = '\0'; p[0] = '\0';
for (i = 0; i < strlen(si) && out < max-2; i++) { for (i = 0; i < strlen(si) && out < max; i++) {
if (si[i] == '\n') { if (si[i] == '\n') {
p[out++] = '\r'; p[out++] = '\r';
p[out++] = '\n'; p[out++] = '\n';
} }
else else
p[out++] = si[i]; p[out++] = si[i];
} }
p[out] = '\0'; p[out] = '\0';
return p; return p;
} }
/* Change carriage-return/linefeed to just linefeed */ /* Change carriage-return/linefeed to just linefeed
char * Plus, escape any special characters.
convert_returns(char *si, char *dst, int used) */
{ char *
size_t i = 0, out = 0, max; convert_special_chars(char *si, char *dst, int used)
static char sout[TEXT_FIELD_SIZE+5]; {
char *p; size_t i = 0, out = 0, max;
static char sout[TEXT_FIELD_SIZE+5];
if (dst) char *p;
p = dst;
else if (dst)
p = sout; p = dst;
else
p[0] = '\0'; p = sout;
if (used == SQL_NTS) p[0] = '\0';
max = strlen(si);
else if (used == SQL_NTS)
max = used; max = strlen(si);
else
for (i = 0; i < max; i++) { max = used;
if (si[i] == '\r' && i+1 < strlen(si) && si[i+1] == '\n')
continue; for (i = 0; i < max; i++) {
else if (si[i] == '\r' && i+1 < strlen(si) && si[i+1] == '\n')
p[out++] = si[i]; continue;
} if (si[i] == '\'')
p[out] = '\0'; p[out++] = '\\';
return p;
} p[out++] = si[i];
}
int p[out] = '\0';
convert_pgbinary_to_char(char *value, char *rgbValue, int cbValueMax) return p;
{ }
return 0;
} /* !!! Need to implement this function !!! */
int
unsigned int convert_pgbinary_to_char(char *value, char *rgbValue, int cbValueMax)
conv_from_octal(unsigned char *s) {
{ return 0;
int i, y=0; }
for (i = 1; i <= 3; i++) { unsigned int
y += (s[i] - 48) * (int) pow(8, 3-i); conv_from_octal(unsigned char *s)
} {
int i, y=0;
return y;
for (i = 1; i <= 3; i++) {
} y += (s[i] - 48) * (int) pow(8, 3-i);
}
// convert octal escapes to bytes
int return y;
convert_from_pgbinary(unsigned char *value, unsigned char *rgbValue, int cbValueMax)
{ }
size_t i;
int o=0; // convert octal escapes to bytes
int
for (i = 0; i < strlen(value); ) { convert_from_pgbinary(unsigned char *value, unsigned char *rgbValue, int cbValueMax)
if (value[i] == '\\') { {
rgbValue[o] = conv_from_octal(&value[i]); size_t i;
i += 4; int o=0;
}
else { for (i = 0; i < strlen(value); ) {
rgbValue[o] = value[i++]; if (value[i] == '\\') {
} rgbValue[o] = conv_from_octal(&value[i]);
mylog("convert_from_pgbinary: i=%d, rgbValue[%d] = %d, %c\n", i, o, rgbValue[o], rgbValue[o]); i += 4;
o++; }
} else {
return o; rgbValue[o] = value[i++];
} }
mylog("convert_from_pgbinary: i=%d, rgbValue[%d] = %d, %c\n", i, o, rgbValue[o], rgbValue[o]);
o++;
char * }
conv_to_octal(unsigned char val) return o;
{ }
int i;
static char x[6];
char *
x[0] = '\\'; conv_to_octal(unsigned char val)
x[1] = '\\'; {
x[5] = '\0'; int i;
static char x[6];
for (i = 4; i > 1; i--) {
x[i] = (val & 7) + 48; x[0] = '\\';
val >>= 3; x[1] = '\\';
} x[5] = '\0';
return x; for (i = 4; i > 1; i--) {
} x[i] = (val & 7) + 48;
val >>= 3;
// convert non-ascii bytes to octal escape sequences }
int
convert_to_pgbinary(unsigned char *in, char *out, int len) return x;
{ }
int i, o=0;
// convert non-ascii bytes to octal escape sequences
int
for (i = 0; i < len; i++) { convert_to_pgbinary(unsigned char *in, char *out, int len)
mylog("convert_to_pgbinary: in[%d] = %d, %c\n", i, in[i], in[i]); {
if (in[i] < 32 || in[i] > 126) { int i, o=0;
strcpy(&out[o], conv_to_octal(in[i]));
o += 5;
} for (i = 0; i < len; i++) {
else mylog("convert_to_pgbinary: in[%d] = %d, %c\n", i, in[i], in[i]);
out[o++] = in[i]; if (in[i] < 32 || in[i] > 126) {
strcpy(&out[o], conv_to_octal(in[i]));
} o += 5;
}
mylog("convert_to_pgbinary: returning %d, out='%.*s'\n", o, o, out); else
out[o++] = in[i];
return o;
} }
mylog("convert_to_pgbinary: returning %d, out='%.*s'\n", o, o, out);
return o;
}
/* 1. get oid (from 'value')
2. open the large object
3. read from the large object (handle multiple GetData)
4. close when read less than requested? -OR-
lseek/read each time
handle case where application receives truncated and
decides not to continue reading.
CURRENTLY, ONLY LONGVARBINARY is handled, since that is the only
data type currently mapped to a PG_TYPE_LO. But, if any other types
are desired to map to a large object (PG_TYPE_LO), then that would
need to be handled here. For example, LONGVARCHAR could possibly be
mapped to PG_TYPE_LO someday, instead of PG_TYPE_TEXT as it is now.
*/
int
convert_lo(StatementClass *stmt, void *value, Int2 fCType, PTR rgbValue,
SDWORD cbValueMax, SDWORD *pcbValue, char multiple)
{
Oid oid;
int retval;
/* if this is the first call for this column,
open the large object for reading
*/
if ( ! multiple) {
oid = atoi(value);
stmt->lobj_fd = lo_open(stmt->hdbc, oid, INV_READ);
if (stmt->lobj_fd < 0) {
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Couldnt open large object for writing.";
return COPY_GENERAL_ERROR;
}
}
if (stmt->lobj_fd < 0)
return COPY_NO_DATA_FOUND;
retval = lo_read(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValueMax);
if (retval < 0) {
lo_close(stmt->hdbc, stmt->lobj_fd);
stmt->lobj_fd = -1;
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Error reading from large object.";
return COPY_GENERAL_ERROR;
}
else if (retval < cbValueMax) { /* success, all done */
lo_close(stmt->hdbc, stmt->lobj_fd);
stmt->lobj_fd = -1; /* prevent further reading */
if (pcbValue)
*pcbValue = retval;
return COPY_OK;
}
else { /* retval == cbVaueMax -- assume truncated */
if (pcbValue)
*pcbValue = SQL_NO_TOTAL;
return COPY_RESULT_TRUNCATED;
}
}
/* File: convert.h /* File: convert.h
* *
* Description: See "convert.c" * Description: See "convert.c"
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __CONVERT_H__ #ifndef __CONVERT_H__
#define __CONVERT_H__ #define __CONVERT_H__
...@@ -13,10 +13,12 @@ ...@@ -13,10 +13,12 @@
#include "psqlodbc.h" #include "psqlodbc.h"
/* copy_and_convert results */ /* copy_and_convert results */
#define COPY_OK 0 #define COPY_OK 0
#define COPY_UNSUPPORTED_TYPE 1 #define COPY_UNSUPPORTED_TYPE 1
#define COPY_UNSUPPORTED_CONVERSION 2 #define COPY_UNSUPPORTED_CONVERSION 2
#define COPY_RESULT_TRUNCATED 3 #define COPY_RESULT_TRUNCATED 3
#define COPY_GENERAL_ERROR 4
#define COPY_NO_DATA_FOUND 5
typedef struct { typedef struct {
int m; int m;
...@@ -27,21 +29,21 @@ typedef struct { ...@@ -27,21 +29,21 @@ typedef struct {
int ss; int ss;
} SIMPLE_TIME; } SIMPLE_TIME;
int copy_and_convert_field_bindinfo(Int4 field_type, void *value, BindInfoClass *bic); int copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col);
int copy_and_convert_field(Int4 field_type, void *value, int copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType,
Int2 fCType, PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue); PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue, char multiple);
int copy_statement_with_parameters(StatementClass *stmt); int copy_statement_with_parameters(StatementClass *stmt);
char *convert_escape(char *value); char *convert_escape(char *value);
char *convert_money(char *s); char *convert_money(char *s);
int monthToNumber(char *mon);
char *convert_time(SIMPLE_TIME *st);
char parse_datetime(char *buf, SIMPLE_TIME *st); char parse_datetime(char *buf, SIMPLE_TIME *st);
char *convert_linefeeds(char *s, char *dst, size_t max); char *convert_linefeeds(char *s, char *dst, size_t max);
char *convert_returns(char *si, char *dst, int used); char *convert_special_chars(char *si, char *dst, int used);
int convert_pgbinary_to_char(char *value, char *rgbValue, int cbValueMax); int convert_pgbinary_to_char(char *value, char *rgbValue, int cbValueMax);
int convert_from_pgbinary(unsigned char *value, unsigned 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_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 #endif
/* Module: dlg_specific.c
*
* Description: This module contains any specific code for handling
* dialog boxes such as driver/datasource options. Both the
* ConfigDSN() and the SQLDriverConnect() functions use
* functions in this module. If you were to add a new option
* to any dialog box, you would most likely only have to change
* things in here rather than in 2 separate places as before.
*
* Classes: none
*
* API functions: none
*
* Comments: See "notice.txt" for copyright and license information.
*
*/
#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);
}
/* 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
/* Module: drvconn.c /* Module: drvconn.c
* *
* Description: This module contains only routines related to * Description: This module contains only routines related to
* implementing SQLDriverConnect. * implementing SQLDriverConnect.
* *
* Classes: n/a * Classes: n/a
* *
* API functions: SQLDriverConnect * API functions: SQLDriverConnect
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -26,15 +26,17 @@ ...@@ -26,15 +26,17 @@
#include <odbcinst.h> #include <odbcinst.h>
#include "resource.h" #include "resource.h"
#include "dlg_specific.h"
/* prototypes */ /* prototypes */
BOOL FAR PASCAL dconn_FDriverConnectProc(HWND hdlg, UINT wMsg, WPARAM wParam, LPARAM lParam); BOOL FAR PASCAL dconn_FDriverConnectProc(HWND hdlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
RETCODE dconn_DoDialog(HWND hwnd, ConnInfo *ci); RETCODE dconn_DoDialog(HWND hwnd, ConnInfo *ci);
void dconn_get_connect_attributes(UCHAR FAR *connect_string, ConnInfo *ci); void dconn_get_connect_attributes(UCHAR FAR *connect_string, ConnInfo *ci);
extern HINSTANCE NEAR s_hModule; /* Saved module handle. */ extern HINSTANCE NEAR s_hModule; /* Saved module handle. */
extern GLOBAL_VALUES globals; extern GLOBAL_VALUES globals;
RETCODE SQL_API SQLDriverConnect( RETCODE SQL_API SQLDriverConnect(
HDBC hdbc, HDBC hdbc,
HWND hwnd, HWND hwnd,
...@@ -67,10 +69,10 @@ char password_required = FALSE; ...@@ -67,10 +69,10 @@ char password_required = FALSE;
// If the ConnInfo in the hdbc is missing anything, // If the ConnInfo in the hdbc is missing anything,
// this function will fill them in from the registry (assuming // this function will fill them in from the registry (assuming
// of course there is a DSN given -- if not, it does nothing!) // 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. // Fill in any default parameters if they are not there.
CC_set_defaults(conn); getDSNdefaults(ci);
dialog: dialog:
ci->focus_password = password_required; ci->focus_password = password_required;
...@@ -102,14 +104,14 @@ dialog: ...@@ -102,14 +104,14 @@ dialog:
break; break;
} }
/* Password is not a required parameter unless authentication asks for it. /* 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 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) a password is entered (the user can always hit Cancel to get out)
*/ */
if( ci->username[0] == '\0' || if( ci->username[0] == '\0' ||
ci->server[0] == '\0' || ci->server[0] == '\0' ||
ci->database[0] == '\0' || ci->database[0] == '\0' ||
ci->port[0] == '\0') { ci->port[0] == '\0') {
// (password_required && ci->password[0] == '\0')) // (password_required && ci->password[0] == '\0'))
return SQL_NO_DATA_FOUND; return SQL_NO_DATA_FOUND;
...@@ -119,19 +121,7 @@ dialog: ...@@ -119,19 +121,7 @@ dialog:
// return the completed string to the caller. // return the completed string to the caller.
char got_dsn = (ci->dsn[0] != '\0'); makeConnectString(connect_string, ci);
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);
if(pcbConnStrOut) { if(pcbConnStrOut) {
*pcbConnStrOut = strlen(connect_string); *pcbConnStrOut = strlen(connect_string);
...@@ -169,7 +159,7 @@ int dialog_result; ...@@ -169,7 +159,7 @@ int dialog_result;
mylog("dconn_DoDialog: ci = %u\n", ci); mylog("dconn_DoDialog: ci = %u\n", ci);
if(hwnd) { if(hwnd) {
dialog_result = DialogBoxParam(s_hModule, MAKEINTRESOURCE(DRIVERCONNDIALOG), dialog_result = DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_CONFIG),
hwnd, dconn_FDriverConnectProc, (LPARAM) ci); hwnd, dconn_FDriverConnectProc, (LPARAM) ci);
if(!dialog_result || (dialog_result == -1)) { if(!dialog_result || (dialog_result == -1)) {
return SQL_NO_DATA_FOUND; return SQL_NO_DATA_FOUND;
...@@ -188,34 +178,37 @@ BOOL FAR PASCAL dconn_FDriverConnectProc( ...@@ -188,34 +178,37 @@ BOOL FAR PASCAL dconn_FDriverConnectProc(
WPARAM wParam, WPARAM wParam,
LPARAM lParam) LPARAM lParam)
{ {
static ConnInfo *ci; ConnInfo *ci;
switch (wMsg) { switch (wMsg) {
case WM_INITDIALOG: case WM_INITDIALOG:
ci = (ConnInfo *) lParam; // Save the ConnInfo for the "OK" ci = (ConnInfo *) lParam;
SetDlgItemText(hdlg, SERVER_EDIT, ci->server); /* Change the caption for the setup dialog */
SetDlgItemText(hdlg, DATABASE_EDIT, ci->database); SetWindowText(hdlg, "PostgreSQL Connection");
SetDlgItemText(hdlg, USERNAME_EDIT, ci->username);
SetDlgItemText(hdlg, PASSWORD_EDIT, ci->password); SetWindowText(GetDlgItem(hdlg, IDC_DATASOURCE), "Connection");
SetDlgItemText(hdlg, PORT_EDIT, ci->port);
CheckDlgButton(hdlg, READONLY_EDIT, atoi(ci->readonly)); /* Hide the DSN and description fields */
ShowWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), SW_HIDE);
CheckDlgButton(hdlg, PG62_EDIT, PROTOCOL_62(ci)); ShowWindow(GetDlgItem(hdlg, IDC_DSNAME), SW_HIDE);
ShowWindow(GetDlgItem(hdlg, IDC_DESCTEXT), SW_HIDE);
/* The driver connect dialog box allows manipulating this global variable */ ShowWindow(GetDlgItem(hdlg, IDC_DESC), SW_HIDE);
CheckDlgButton(hdlg, COMMLOG_EDIT, globals.commlog);
SetWindowLong(hdlg, DWL_USER, lParam);// Save the ConnInfo for the "OK"
SetDlgStuff(hdlg, ci);
if (ci->database[0] == '\0') if (ci->database[0] == '\0')
; /* default focus */ ; /* default focus */
else if (ci->server[0] == '\0') else if (ci->server[0] == '\0')
SetFocus(GetDlgItem(hdlg, SERVER_EDIT)); SetFocus(GetDlgItem(hdlg, IDC_SERVER));
else if (ci->port[0] == '\0') else if (ci->port[0] == '\0')
SetFocus(GetDlgItem(hdlg, PORT_EDIT)); SetFocus(GetDlgItem(hdlg, IDC_PORT));
else if (ci->username[0] == '\0') else if (ci->username[0] == '\0')
SetFocus(GetDlgItem(hdlg, USERNAME_EDIT)); SetFocus(GetDlgItem(hdlg, IDC_USER));
else if (ci->focus_password) else if (ci->focus_password)
SetFocus(GetDlgItem(hdlg, PASSWORD_EDIT)); SetFocus(GetDlgItem(hdlg, IDC_PASSWORD));
break; break;
...@@ -223,26 +216,30 @@ static ConnInfo *ci; ...@@ -223,26 +216,30 @@ static ConnInfo *ci;
switch (GET_WM_COMMAND_ID(wParam, lParam)) { switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDOK: case IDOK:
GetDlgItemText(hdlg, SERVER_EDIT, ci->server, sizeof(ci->server)); ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
GetDlgItemText(hdlg, DATABASE_EDIT, ci->database, sizeof(ci->database));
GetDlgItemText(hdlg, USERNAME_EDIT, ci->username, sizeof(ci->username)); GetDlgStuff(hdlg, ci);
GetDlgItemText(hdlg, PASSWORD_EDIT, ci->password, sizeof(ci->password));
GetDlgItemText(hdlg, PORT_EDIT, ci->port, sizeof(ci->port));
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: case IDCANCEL:
EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK); EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
return TRUE; 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; ...@@ -286,42 +283,12 @@ char *strtok_arg;
if( !attribute || !value) if( !attribute || !value)
continue; continue;
/*********************************************************/ // Copy the appropriate value to the conninfo
/* PARSE ATTRIBUTES */ copyAttributes(ci, attribute, value);
/*********************************************************/
if(stricmp(attribute, "DSN") == 0)
strcpy(ci->dsn, value);
else if(stricmp(attribute, "driver") == 0)
strcpy(ci->driver, value);
else if(stricmp(attribute, "uid") == 0)
strcpy(ci->username, value);
else if(stricmp(attribute, "pwd") == 0)
strcpy(ci->password, value);
else if ((stricmp(attribute, "server") == 0) ||
(stricmp(attribute, "servername") == 0))
strcpy(ci->server, value);
else if(stricmp(attribute, "port") == 0)
strcpy(ci->port, value);
else if(stricmp(attribute, "database") == 0)
strcpy(ci->database, value);
else if (stricmp(attribute, "readonly") == 0)
strcpy(ci->readonly, value);
else if (stricmp(attribute, "protocol") == 0)
strcpy(ci->protocol, value);
else if (stricmp(attribute, "connsettings") == 0)
strcpy(ci->conn_settings, value);
} }
free(our_connect_string); free(our_connect_string);
} }
/* Module: environ.c /* Module: environ.c
* *
* Description: This module contains routines related to * Description: This module contains routines related to
* the environment, such as storing connection handles, * the environment, such as storing connection handles,
* and returning errors. * and returning errors.
* *
* Classes: EnvironmentClass (Functions prefix: "EN_") * Classes: EnvironmentClass (Functions prefix: "EN_")
* *
* API functions: SQLAllocEnv, SQLFreeEnv, SQLError * API functions: SQLAllocEnv, SQLFreeEnv, SQLError
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "environ.h" #include "environ.h"
#include "connection.h" #include "connection.h"
...@@ -113,6 +113,10 @@ int status; ...@@ -113,6 +113,10 @@ int status;
strcpy(szSqlState, "08S01"); strcpy(szSqlState, "08S01");
// communication link failure // communication link failure
break; break;
case STMT_CREATE_TABLE_ERROR:
strcpy(szSqlState, "S0001");
// table already exists
break;
case STMT_STATUS_ERROR: case STMT_STATUS_ERROR:
case STMT_SEQUENCE_ERROR: case STMT_SEQUENCE_ERROR:
strcpy(szSqlState, "S1010"); strcpy(szSqlState, "S1010");
...@@ -158,6 +162,12 @@ int status; ...@@ -158,6 +162,12 @@ int status;
break; break;
case STMT_OPTION_VALUE_CHANGED: case STMT_OPTION_VALUE_CHANGED:
strcpy(szSqlState, "01S02"); strcpy(szSqlState, "01S02");
break;
case STMT_INVALID_CURSOR_NAME:
strcpy(szSqlState, "34000");
break;
case STMT_NO_CURSOR_NAME:
strcpy(szSqlState, "S1015");
break; break;
default: default:
strcpy(szSqlState, "S1000"); strcpy(szSqlState, "S1000");
...@@ -213,10 +223,10 @@ int status; ...@@ -213,10 +223,10 @@ int status;
case CONN_OPENDB_ERROR: case CONN_OPENDB_ERROR:
strcpy(szSqlState, "08001"); strcpy(szSqlState, "08001");
// unable to connect to data source // unable to connect to data source
break; break;
case CONN_INVALID_AUTHENTICATION: case CONN_INVALID_AUTHENTICATION:
case CONN_AUTH_TYPE_UNSUPPORTED: case CONN_AUTH_TYPE_UNSUPPORTED:
strcpy(szSqlState, "28000"); strcpy(szSqlState, "28000");
break; break;
case CONN_STMT_ALLOC_ERROR: case CONN_STMT_ALLOC_ERROR:
strcpy(szSqlState, "S1001"); strcpy(szSqlState, "S1001");
...@@ -339,7 +349,7 @@ EnvironmentClass *rv; ...@@ -339,7 +349,7 @@ EnvironmentClass *rv;
rv->errormsg = 0; rv->errormsg = 0;
rv->errornumber = 0; rv->errornumber = 0;
} }
return rv; return rv;
} }
...@@ -356,7 +366,7 @@ char rv = 1; ...@@ -356,7 +366,7 @@ char rv = 1;
// the source--they should not be freed // the source--they should not be freed
/* Free any connections belonging to this environment */ /* 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) if (conns[lf] && conns[lf]->henv == self)
rv = rv && CC_Destructor(conns[lf]); rv = rv && CC_Destructor(conns[lf]);
} }
...@@ -383,16 +393,16 @@ char ...@@ -383,16 +393,16 @@ char
EN_add_connection(EnvironmentClass *self, ConnectionClass *conn) EN_add_connection(EnvironmentClass *self, ConnectionClass *conn)
{ {
int i; 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++) { for (i = 0; i < MAX_CONNECTIONS; i++) {
if ( ! conns[i]) { if ( ! conns[i]) {
conn->henv = self; conn->henv = self;
conns[i] = conn; conns[i] = conn;
mylog(" added at i =%d, conn->henv = %u, conns[i]->henv = %u\n", mylog(" added at i =%d, conn->henv = %u, conns[i]->henv = %u\n",
i, conn->henv, conns[i]->henv); i, conn->henv, conns[i]->henv);
return TRUE; return TRUE;
} }
......
/* File: environ.h /* File: environ.h
* *
* Description: See "environ.c" * Description: See "environ.c"
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __ENVIRON_H__ #ifndef __ENVIRON_H__
#define __ENVIRON_H__ #define __ENVIRON_H__
...@@ -13,15 +13,16 @@ ...@@ -13,15 +13,16 @@
#include "psqlodbc.h" #include "psqlodbc.h"
#include <windows.h> #include <windows.h>
#include <sql.h> #include <sql.h>
#include <sqlext.h>
#define ENV_ALLOC_ERROR 1 #define ENV_ALLOC_ERROR 1
/********** Environment Handle *************/ /********** Environment Handle *************/
struct EnvironmentClass_ { struct EnvironmentClass_ {
char *errormsg; char *errormsg;
int errornumber; int errornumber;
}; };
/* Environment prototypes */ /* Environment prototypes */
EnvironmentClass *EN_Constructor(void); EnvironmentClass *EN_Constructor(void);
char EN_Destructor(EnvironmentClass *self); char EN_Destructor(EnvironmentClass *self);
......
/* Module: execute.c /* Module: execute.c
* *
* Description: This module contains routines related to * Description: This module contains routines related to
* preparing and executing an SQL statement. * preparing and executing an SQL statement.
* *
* Classes: n/a * Classes: n/a
* *
* API functions: SQLPrepare, SQLExecute, SQLExecDirect, SQLTransact, * API functions: SQLPrepare, SQLExecute, SQLExecDirect, SQLTransact,
* SQLCancel, SQLNativeSql, SQLParamData, SQLPutData * SQLCancel, SQLNativeSql, SQLParamData, SQLPutData
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "psqlodbc.h" #include "psqlodbc.h"
#include <stdio.h> #include <stdio.h>
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "qresult.h" #include "qresult.h"
#include "convert.h" #include "convert.h"
#include "bind.h" #include "bind.h"
#include "lobj.h"
// Perform a Prepare on the SQL statement // Perform a Prepare on the SQL statement
...@@ -36,72 +37,29 @@ StatementClass *self = (StatementClass *) hstmt; ...@@ -36,72 +37,29 @@ StatementClass *self = (StatementClass *) hstmt;
if ( ! self) if ( ! self)
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
/* CC: According to the ODBC specs it is valid to call SQLPrepare mulitple times. In that case, /* According to the ODBC specs it is valid to call SQLPrepare mulitple times.
the bound SQL statement is replaced by the new one */ In that case, the bound SQL statement is replaced by the new one
*/
switch (self->status) { switch(self->status) {
case STMT_PREMATURE: case STMT_PREMATURE:
mylog("**** SQLPrepare: STMT_PREMATURE, recycle\n"); mylog("**** SQLPrepare: STMT_PREMATURE, recycle\n");
SC_recycle_statement(self); /* recycle the statement, but do not remove parameter bindings */ 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: 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"); 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; self->status = STMT_READY;
break;
return SQL_SUCCESS; case STMT_READY:
case STMT_READY: /* SQLPrepare has already been called -- Just changed the SQL statement that is assigned to the handle */
mylog("**** SQLPrepare: STMT_READY, change SQL\n"); mylog("**** SQLPrepare: STMT_READY, change SQL\n");
break;
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 */
case STMT_EXECUTING: case STMT_EXECUTING:
mylog("**** SQLPrepare: STMT_EXECUTING, error!\n"); mylog("**** SQLPrepare: STMT_EXECUTING, error!\n");
...@@ -116,6 +74,30 @@ StatementClass *self = (StatementClass *) hstmt; ...@@ -116,6 +74,30 @@ StatementClass *self = (StatementClass *) hstmt;
self->errormsg = "An Internal Error has occured -- Unknown statement status."; self->errormsg = "An Internal Error has occured -- Unknown statement status.";
return SQL_ERROR; 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; ...@@ -150,7 +132,7 @@ StatementClass *stmt = (StatementClass *) hstmt;
stmt->statement_type = statement_type(stmt->statement); stmt->statement_type = statement_type(stmt->statement);
// Check if connection is readonly (only selects are allowed) // 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->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Connection is readonly, only select statements are allowed."; stmt->errormsg = "Connection is readonly, only select statements are allowed.";
return SQL_ERROR; return SQL_ERROR;
...@@ -165,10 +147,10 @@ StatementClass *stmt = (StatementClass *) hstmt; ...@@ -165,10 +147,10 @@ StatementClass *stmt = (StatementClass *) hstmt;
RETCODE SQL_API SQLExecute( RETCODE SQL_API SQLExecute(
HSTMT hstmt) HSTMT hstmt)
{ {
StatementClass *stmt = (StatementClass *) hstmt; StatementClass *stmt = (StatementClass *) hstmt;
ConnectionClass *conn; ConnectionClass *conn;
int i, retval; int i, retval;
if ( ! stmt) if ( ! stmt)
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
...@@ -212,45 +194,45 @@ int i, retval; ...@@ -212,45 +194,45 @@ int i, retval;
stmt->errornumber = STMT_STATUS_ERROR; stmt->errornumber = STMT_STATUS_ERROR;
stmt->errormsg = "The handle does not point to a statement that is ready to be executed"; stmt->errormsg = "The handle does not point to a statement that is ready to be executed";
return SQL_ERROR; return SQL_ERROR;
} }
/* The bound parameters could have possibly changed since the last execute /* The bound parameters could have possibly changed since the last execute
of this statement? Therefore check for params and re-copy. of this statement? Therefore check for params and re-copy.
*/ */
stmt->data_at_exec = -1; stmt->data_at_exec = -1;
for (i = 0; i < stmt->parameters_allocated; i++) { for (i = 0; i < stmt->parameters_allocated; i++) {
/* Check for data at execution parameters */ /* Check for data at execution parameters */
if ( stmt->parameters[i].data_at_exec == TRUE) { if ( stmt->parameters[i].data_at_exec == TRUE) {
if (stmt->data_at_exec < 0) if (stmt->data_at_exec < 0)
stmt->data_at_exec = 1; stmt->data_at_exec = 1;
else else
stmt->data_at_exec++; stmt->data_at_exec++;
} }
} }
// If there are some data at execution parameters, return need data // If there are some data at execution parameters, return need data
// SQLParamData and SQLPutData will be used to send params and execute the statement. // SQLParamData and SQLPutData will be used to send params and execute the statement.
if (stmt->data_at_exec > 0) if (stmt->data_at_exec > 0)
return SQL_NEED_DATA; 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); 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. // Create the statement with parameters substituted.
retval = copy_statement_with_parameters(stmt); retval = copy_statement_with_parameters(stmt);
if( retval != SQL_SUCCESS) if( retval != SQL_SUCCESS)
/* error msg passed from above */ /* error msg passed from above */
return retval; return retval;
mylog(" stmt_with_params = '%s'\n", stmt->stmt_with_params); mylog(" stmt_with_params = '%s'\n", stmt->stmt_with_params);
return SC_execute(stmt); return SC_execute(stmt);
} }
// - - - - - - - - - // - - - - - - - - -
RETCODE SQL_API SQLTransact( RETCODE SQL_API SQLTransact(
...@@ -325,24 +307,24 @@ mylog("**** SQLTransact: hdbc=%u, henv=%u\n", hdbc, henv); ...@@ -325,24 +307,24 @@ mylog("**** SQLTransact: hdbc=%u, henv=%u\n", hdbc, henv);
RETCODE SQL_API SQLCancel( RETCODE SQL_API SQLCancel(
HSTMT hstmt) // Statement to cancel. HSTMT hstmt) // Statement to cancel.
{ {
StatementClass *stmt = (StatementClass *) hstmt; StatementClass *stmt = (StatementClass *) hstmt;
// Check if this can handle canceling in the middle of a SQLPutData? // Check if this can handle canceling in the middle of a SQLPutData?
if ( ! stmt) if ( ! stmt)
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
// Not in the middle of SQLParamData/SQLPutData so cancel like a close. // Not in the middle of SQLParamData/SQLPutData so cancel like a close.
if (stmt->data_at_exec < 0) if (stmt->data_at_exec < 0)
return SQLFreeStmt(hstmt, SQL_CLOSE); return SQLFreeStmt(hstmt, SQL_CLOSE);
// In the middle of SQLParamData/SQLPutData, so cancel that. // In the middle of SQLParamData/SQLPutData, so cancel that.
// Note, any previous data-at-exec buffers will be freed in the recycle // Note, any previous data-at-exec buffers will be freed in the recycle
// if they call SQLExecDirect or SQLExecute again. // if they call SQLExecDirect or SQLExecute again.
stmt->data_at_exec = -1; stmt->data_at_exec = -1;
stmt->current_exec_param = -1; stmt->current_exec_param = -1;
stmt->put_data = FALSE; stmt->put_data = FALSE;
} }
// - - - - - - - - - // - - - - - - - - -
...@@ -371,45 +353,63 @@ RETCODE SQL_API SQLNativeSql( ...@@ -371,45 +353,63 @@ RETCODE SQL_API SQLNativeSql(
RETCODE SQL_API SQLParamData( RETCODE SQL_API SQLParamData(
HSTMT hstmt, HSTMT hstmt,
PTR FAR *prgbValue) PTR FAR *prgbValue)
{ {
StatementClass *stmt = (StatementClass *) hstmt; StatementClass *stmt = (StatementClass *) hstmt;
int i, retval; int i, retval;
if ( ! stmt) if ( ! stmt)
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
if (stmt->data_at_exec < 0) { mylog("SQLParamData, enter: data_at_exec=%d, params_alloc=%d\n",
stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->data_at_exec, stmt->parameters_allocated);
stmt->errormsg = "No execution-time parameters for this statement";
return SQL_ERROR; if (stmt->data_at_exec < 0) {
} stmt->errornumber = STMT_SEQUENCE_ERROR;
stmt->errormsg = "No execution-time parameters for this statement";
if (stmt->data_at_exec > stmt->parameters_allocated) { return SQL_ERROR;
stmt->errornumber = STMT_SEQUENCE_ERROR; }
stmt->errormsg = "Too many execution-time parameters were present";
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";
/* Done, now copy the params and then execute the statement */ return SQL_ERROR;
if (stmt->data_at_exec == 0) { }
retval = copy_statement_with_parameters(stmt);
if (retval != SQL_SUCCESS) /* close the large object */
return retval; if ( stmt->lobj_fd >= 0) {
lo_close(stmt->hdbc, stmt->lobj_fd);
return SC_execute(stmt); stmt->lobj_fd = -1;
} }
/* At least 1 data at execution parameter, so Fill in the token value */
for (i = 0; i < stmt->parameters_allocated; i++) { /* Done, now copy the params and then execute the statement */
if (stmt->parameters[i].data_at_exec == TRUE) { if (stmt->data_at_exec == 0) {
stmt->data_at_exec--; retval = copy_statement_with_parameters(stmt);
stmt->current_exec_param = i; if (retval != SQL_SUCCESS)
stmt->put_data = FALSE; return retval;
*prgbValue = stmt->parameters[i].buffer; /* token */
} stmt->current_exec_param = -1;
}
return SC_execute(stmt);
return SQL_NEED_DATA; }
/* 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( ...@@ -422,114 +422,157 @@ RETCODE SQL_API SQLPutData(
PTR rgbValue, PTR rgbValue,
SDWORD cbValue) SDWORD cbValue)
{ {
StatementClass *stmt = (StatementClass *) hstmt; StatementClass *stmt = (StatementClass *) hstmt;
char *buffer; int old_pos, retval;
SDWORD *used; ParameterInfoClass *current_param;
int old_pos; char *buffer;
if ( ! stmt) if ( ! stmt)
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
if (stmt->current_exec_param < 0) { if (stmt->current_exec_param < 0) {
stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errornumber = STMT_SEQUENCE_ERROR;
stmt->errormsg = "Previous call was not SQLPutData or SQLParamData"; stmt->errormsg = "Previous call was not SQLPutData or SQLParamData";
return SQL_ERROR; return SQL_ERROR;
} }
if ( ! stmt->put_data) { /* first call */ current_param = &(stmt->parameters[stmt->current_exec_param]);
mylog("SQLPutData: (1) cbValue = %d\n", cbValue); if ( ! stmt->put_data) { /* first call */
stmt->put_data = TRUE; mylog("SQLPutData: (1) cbValue = %d\n", cbValue);
used = (SDWORD *) malloc(sizeof(SDWORD)); stmt->put_data = TRUE;
if ( ! used) {
stmt->errornumber = STMT_NO_MEMORY_ERROR; current_param->EXEC_used = (SDWORD *) malloc(sizeof(SDWORD));
stmt->errormsg = "Out of memory in SQLPutData (1)"; if ( ! current_param->EXEC_used) {
return SQL_ERROR; stmt->errornumber = STMT_NO_MEMORY_ERROR;
} stmt->errormsg = "Out of memory in SQLPutData (1)";
return SQL_ERROR;
*used = cbValue; }
stmt->parameters[stmt->current_exec_param].EXEC_used = used;
*current_param->EXEC_used = cbValue;
if (cbValue == SQL_NULL_DATA)
return SQL_SUCCESS; if (cbValue == SQL_NULL_DATA)
return SQL_SUCCESS;
if (cbValue == SQL_NTS) {
buffer = strdup(rgbValue);
if ( ! buffer) { /* Handle Long Var Binary with Large Objects */
stmt->errornumber = STMT_NO_MEMORY_ERROR; if ( current_param->SQLType == SQL_LONGVARBINARY) {
stmt->errormsg = "Out of memory in SQLPutData (2)";
return SQL_ERROR; /* store the oid */
} current_param->lobj_oid = lo_creat(stmt->hdbc, INV_READ | INV_WRITE);
} if (current_param->lobj_oid == 0) {
else { stmt->errornumber = STMT_EXEC_ERROR;
buffer = malloc(cbValue + 1); stmt->errormsg = "Couldnt create large object.";
if ( ! buffer) { return SQL_ERROR;
stmt->errornumber = STMT_NO_MEMORY_ERROR; }
stmt->errormsg = "Out of memory in SQLPutData (2)";
return SQL_ERROR; /* major hack -- to allow convert to see somethings there */
} /* have to modify convert to handle this better */
memcpy(buffer, rgbValue, cbValue); current_param->EXEC_buffer = (char *) &current_param->lobj_oid;
buffer[cbValue] = '\0';
} /* store the fd */
stmt->lobj_fd = lo_open(stmt->hdbc, current_param->lobj_oid, INV_WRITE);
stmt->parameters[stmt->current_exec_param].EXEC_buffer = buffer; if ( stmt->lobj_fd < 0) {
} stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Couldnt open large object for writing.";
else { /* calling SQLPutData more than once */ return SQL_ERROR;
}
mylog("SQLPutData: (>1) cbValue = %d\n", cbValue);
retval = lo_write(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValue);
used = stmt->parameters[stmt->current_exec_param].EXEC_used; mylog("lo_write: cbValue=%d, wrote %d bytes\n", cbValue, retval);
buffer = stmt->parameters[stmt->current_exec_param].EXEC_buffer;
}
if (cbValue == SQL_NTS) { else { /* for handling text fields and small binaries */
buffer = realloc(buffer, strlen(buffer) + strlen(rgbValue) + 1);
if ( ! buffer) { if (cbValue == SQL_NTS) {
stmt->errornumber = STMT_NO_MEMORY_ERROR; current_param->EXEC_buffer = strdup(rgbValue);
stmt->errormsg = "Out of memory in SQLPutData (3)"; if ( ! current_param->EXEC_buffer) {
return SQL_ERROR; stmt->errornumber = STMT_NO_MEMORY_ERROR;
} stmt->errormsg = "Out of memory in SQLPutData (2)";
strcat(buffer, rgbValue); return SQL_ERROR;
}
mylog(" cbValue = SQL_NTS: strlen(buffer) = %d\n", strlen(buffer)); }
else {
*used = cbValue; current_param->EXEC_buffer = malloc(cbValue + 1);
if ( ! current_param->EXEC_buffer) {
} stmt->errornumber = STMT_NO_MEMORY_ERROR;
else if (cbValue > 0) { stmt->errormsg = "Out of memory in SQLPutData (2)";
return SQL_ERROR;
old_pos = *used; }
memcpy(current_param->EXEC_buffer, rgbValue, cbValue);
*used += cbValue; current_param->EXEC_buffer[cbValue] = '\0';
}
mylog(" cbValue = %d, old_pos = %d, *used = %d\n", cbValue, old_pos, *used); }
}
buffer = realloc(buffer, *used + 1);
if ( ! buffer) { else { /* calling SQLPutData more than once */
stmt->errornumber = STMT_NO_MEMORY_ERROR;
stmt->errormsg = "Out of memory in SQLPutData (3)"; mylog("SQLPutData: (>1) cbValue = %d\n", cbValue);
return SQL_ERROR;
} if (current_param->SQLType == SQL_LONGVARBINARY) {
memcpy(&buffer[old_pos], rgbValue, cbValue); /* the large object fd is in EXEC_buffer */
buffer[*used] = '\0'; retval = lo_write(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValue);
mylog("lo_write(2): cbValue = %d, wrote %d bytes\n", cbValue, retval);
}
else *current_param->EXEC_used += cbValue;
return SQL_ERROR;
} else {
/* reassign buffer incase realloc moved it */ buffer = current_param->EXEC_buffer;
stmt->parameters[stmt->current_exec_param].EXEC_buffer = buffer;
if (cbValue == SQL_NTS) {
} buffer = realloc(buffer, strlen(buffer) + strlen(rgbValue) + 1);
if ( ! buffer) {
stmt->errornumber = STMT_NO_MEMORY_ERROR;
return SQL_SUCCESS; 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;
} }
/* Module: info.c /* Module: info.c
* *
* Description: This module contains routines related to * Description: This module contains routines related to
* ODBC informational functions. * ODBC informational functions.
* *
* Classes: n/a * Classes: n/a
* *
* API functions: SQLGetInfo, SQLGetTypeInfo, SQLGetFunctions, * API functions: SQLGetInfo, SQLGetTypeInfo, SQLGetFunctions,
* SQLTables, SQLColumns, SQLStatistics, SQLSpecialColumns, * SQLTables, SQLColumns, SQLStatistics, SQLSpecialColumns,
* SQLPrimaryKeys, SQLForeignKeys, * SQLPrimaryKeys, SQLForeignKeys,
* SQLProcedureColumns(NI), SQLProcedures(NI), * SQLProcedureColumns(NI), SQLProcedures(NI),
* SQLTablePrivileges(NI), SQLColumnPrivileges(NI) * SQLTablePrivileges(NI), SQLColumnPrivileges(NI)
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
...@@ -33,6 +33,9 @@ ...@@ -33,6 +33,9 @@
#include "misc.h" #include "misc.h"
#include "pgtypes.h" #include "pgtypes.h"
extern GLOBAL_VALUES globals;
// - - - - - - - - - // - - - - - - - - -
RETCODE SQL_API SQLGetInfo( RETCODE SQL_API SQLGetInfo(
...@@ -192,7 +195,10 @@ char *p; ...@@ -192,7 +195,10 @@ char *p;
// this tag doesn't seem to be in ODBC 2.0, and it conflicts // this tag doesn't seem to be in ODBC 2.0, and it conflicts
// with a valid tag (SQL_TIMEDATE_ADD_INTERVALS). // with a valid tag (SQL_TIMEDATE_ADD_INTERVALS).
p = CC_get_database(conn); /* Returning the database name causes problems in MS Query.
It generates query like: "SELECT DISTINCT a FROM byronncrap3 crap3"
*/
p = ""; // CC_get_database(conn);
if (pcbInfoValue) *pcbInfoValue = strlen(p); if (pcbInfoValue) *pcbInfoValue = strlen(p);
strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax); strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax);
break; break;
...@@ -224,8 +230,6 @@ char *p; ...@@ -224,8 +230,6 @@ char *p;
break; break;
case SQL_DRIVER_ODBC_VER: case SQL_DRIVER_ODBC_VER:
/* I think we should return 02.00--at least, that is the version of the */
/* spec I'm currently referring to. */
if (pcbInfoValue) *pcbInfoValue = 5; if (pcbInfoValue) *pcbInfoValue = 5;
strncpy_null((char *)rgbInfoValue, "02.00", (size_t)cbInfoValueMax); strncpy_null((char *)rgbInfoValue, "02.00", (size_t)cbInfoValueMax);
break; break;
...@@ -245,12 +249,11 @@ char *p; ...@@ -245,12 +249,11 @@ char *p;
case SQL_FETCH_DIRECTION: /* ODBC 1.0 */ case SQL_FETCH_DIRECTION: /* ODBC 1.0 */
// which fetch directions are supported? (bitmask) // which fetch directions are supported? (bitmask)
// I guess these apply to SQLExtendedFetch? *((DWORD *)rgbInfoValue) = globals.use_declarefetch ? 0 : (SQL_FD_FETCH_NEXT |
*((DWORD *)rgbInfoValue) = SQL_FETCH_NEXT || SQL_FD_FETCH_FIRST |
SQL_FETCH_FIRST || SQL_FD_FETCH_LAST |
SQL_FETCH_LAST || SQL_FD_FETCH_PRIOR |
SQL_FETCH_PRIOR || SQL_FD_FETCH_ABSOLUTE);
SQL_FETCH_ABSOLUTE;
if(pcbInfoValue) { *pcbInfoValue = 4; } if(pcbInfoValue) { *pcbInfoValue = 4; }
break; break;
...@@ -326,7 +329,7 @@ char *p; ...@@ -326,7 +329,7 @@ char *p;
case SQL_MAX_COLUMN_NAME_LEN: /* ODBC 1.0 */ case SQL_MAX_COLUMN_NAME_LEN: /* ODBC 1.0 */
// maximum length of a column name // maximum length of a column name
*((WORD *)rgbInfoValue) = 0; *((WORD *)rgbInfoValue) = 32;
if(pcbInfoValue) { *pcbInfoValue = 2; } if(pcbInfoValue) { *pcbInfoValue = 2; }
break; break;
...@@ -349,7 +352,6 @@ char *p; ...@@ -349,7 +352,6 @@ char *p;
break; break;
case SQL_MAX_COLUMNS_IN_SELECT: /* ODBC 2.0 */ case SQL_MAX_COLUMNS_IN_SELECT: /* ODBC 2.0 */
// I think you get the idea by now
*((WORD *)rgbInfoValue) = 0; *((WORD *)rgbInfoValue) = 0;
if(pcbInfoValue) { *pcbInfoValue = 2; } if(pcbInfoValue) { *pcbInfoValue = 2; }
break; break;
...@@ -360,7 +362,7 @@ char *p; ...@@ -360,7 +362,7 @@ char *p;
break; break;
case SQL_MAX_CURSOR_NAME_LEN: /* ODBC 1.0 */ case SQL_MAX_CURSOR_NAME_LEN: /* ODBC 1.0 */
*((WORD *)rgbInfoValue) = 0; *((WORD *)rgbInfoValue) = 32;
if(pcbInfoValue) { *pcbInfoValue = 2; } if(pcbInfoValue) { *pcbInfoValue = 2; }
break; break;
...@@ -395,7 +397,7 @@ char *p; ...@@ -395,7 +397,7 @@ char *p;
case SQL_MAX_ROW_SIZE_INCLUDES_LONG: /* ODBC 2.0 */ case SQL_MAX_ROW_SIZE_INCLUDES_LONG: /* ODBC 2.0 */
// does the preceding value include LONGVARCHAR and LONGVARBINARY // does the preceding value include LONGVARCHAR and LONGVARBINARY
// fields? // fields? Well, it does include longvarchar, but not longvarbinary.
*pcbInfoValue = 1; *pcbInfoValue = 1;
strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax); strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax);
break; break;
...@@ -407,7 +409,7 @@ char *p; ...@@ -407,7 +409,7 @@ char *p;
break; break;
case SQL_MAX_TABLE_NAME_LEN: /* ODBC 1.0 */ case SQL_MAX_TABLE_NAME_LEN: /* ODBC 1.0 */
*((WORD *)rgbInfoValue) = 0; *((WORD *)rgbInfoValue) = 32;
if(pcbInfoValue) { *pcbInfoValue = 2; } if(pcbInfoValue) { *pcbInfoValue = 2; }
break; break;
...@@ -424,7 +426,7 @@ char *p; ...@@ -424,7 +426,7 @@ char *p;
case SQL_MULT_RESULT_SETS: /* ODBC 1.0 */ case SQL_MULT_RESULT_SETS: /* ODBC 1.0 */
// do we support multiple result sets? // do we support multiple result sets?
if (pcbInfoValue) *pcbInfoValue = 1; if (pcbInfoValue) *pcbInfoValue = 1;
strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax); strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax);
break; break;
case SQL_MULTIPLE_ACTIVE_TXN: /* ODBC 1.0 */ case SQL_MULTIPLE_ACTIVE_TXN: /* ODBC 1.0 */
...@@ -434,7 +436,7 @@ char *p; ...@@ -434,7 +436,7 @@ char *p;
break; break;
case SQL_NEED_LONG_DATA_LEN: /* ODBC 2.0 */ case SQL_NEED_LONG_DATA_LEN: /* ODBC 2.0 */
if (pcbInfoValue) *pcbInfoValue = 1; if (pcbInfoValue) *pcbInfoValue = 1;
/* Dont need the length, SQLPutData can handle any size and multiple calls */ /* Dont need the length, SQLPutData can handle any size and multiple calls */
strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax);
break; break;
...@@ -460,7 +462,7 @@ char *p; ...@@ -460,7 +462,7 @@ char *p;
break; break;
case SQL_ODBC_API_CONFORMANCE: /* ODBC 1.0 */ case SQL_ODBC_API_CONFORMANCE: /* ODBC 1.0 */
*((WORD *)rgbInfoValue) = SQL_OAC_LEVEL1; /* well, almost */ *((WORD *)rgbInfoValue) = SQL_OAC_LEVEL1;
if(pcbInfoValue) { *pcbInfoValue = 2; } if(pcbInfoValue) { *pcbInfoValue = 2; }
break; break;
...@@ -572,22 +574,23 @@ char *p; ...@@ -572,22 +574,23 @@ char *p;
break; break;
case SQL_ROW_UPDATES: /* ODBC 1.0 */ case SQL_ROW_UPDATES: /* ODBC 1.0 */
// not quite sure what this means // Driver doesn't support keyset-driven or mixed cursors, so
// not much point in saying row updates are supported
if (pcbInfoValue) *pcbInfoValue = 1; if (pcbInfoValue) *pcbInfoValue = 1;
strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax); strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax);
break; break;
case SQL_SCROLL_CONCURRENCY: /* ODBC 1.0 */ case SQL_SCROLL_CONCURRENCY: /* ODBC 1.0 */
// what concurrency options are supported? (bitmask) // what concurrency options are supported BY THE CURSOR? (bitmask)
// taking a guess here *((DWORD *)rgbInfoValue) = (SQL_SCCO_READ_ONLY);
*((DWORD *)rgbInfoValue) = SQL_SCCO_OPT_ROWVER;
if(pcbInfoValue) { *pcbInfoValue = 4; } if(pcbInfoValue) { *pcbInfoValue = 4; }
break; break;
case SQL_SCROLL_OPTIONS: /* ODBC 1.0 */ case SQL_SCROLL_OPTIONS: /* ODBC 1.0 */
// what options are supported for scrollable cursors? (bitmask) // what options are supported for scrollable cursors? (bitmask)
// not too sure about this one, either... // for declare/fetch, only FORWARD scrolling is allowed
*((DWORD *)rgbInfoValue) = SQL_SO_KEYSET_DRIVEN; // otherwise, the result set is STATIC (to SQLExtendedFetch for example)
*((DWORD *)rgbInfoValue) = globals.use_declarefetch ? SQL_SO_FORWARD_ONLY : (SQL_SO_FORWARD_ONLY | SQL_SO_STATIC);
if(pcbInfoValue) { *pcbInfoValue = 4; } if(pcbInfoValue) { *pcbInfoValue = 4; }
break; break;
...@@ -623,8 +626,14 @@ char *p; ...@@ -623,8 +626,14 @@ char *p;
case SQL_STRING_FUNCTIONS: /* ODBC 1.0 */ case SQL_STRING_FUNCTIONS: /* ODBC 1.0 */
// what string functions exist? (bitmask) // what string functions exist? (bitmask)
// not sure if any of these exist, either *((DWORD *)rgbInfoValue) = (SQL_FN_STR_CONCAT |
*((DWORD *)rgbInfoValue) = 0; SQL_FN_STR_LCASE |
SQL_FN_STR_LENGTH |
SQL_FN_STR_LOCATE |
SQL_FN_STR_LTRIM |
SQL_FN_STR_RTRIM |
SQL_FN_STR_SUBSTRING |
SQL_FN_STR_UCASE);
if(pcbInfoValue) { *pcbInfoValue = 4; } if(pcbInfoValue) { *pcbInfoValue = 4; }
break; break;
...@@ -666,7 +675,7 @@ char *p; ...@@ -666,7 +675,7 @@ char *p;
case SQL_TIMEDATE_FUNCTIONS: /* ODBC 1.0 */ case SQL_TIMEDATE_FUNCTIONS: /* ODBC 1.0 */
// what time and date functions are supported? (bitmask) // what time and date functions are supported? (bitmask)
*((DWORD *)rgbInfoValue) = 0; *((DWORD *)rgbInfoValue) = (SQL_FN_TD_NOW);
if(pcbInfoValue) { *pcbInfoValue = 4; } if(pcbInfoValue) { *pcbInfoValue = 4; }
break; break;
...@@ -751,30 +760,30 @@ Int4 type; ...@@ -751,30 +760,30 @@ Int4 type;
// cycle through the types // cycle through the types
for(i=0, type = pgtypes_defined[0]; type; type = pgtypes_defined[++i]) { for(i=0, type = pgtypes_defined[0]; type; type = pgtypes_defined[++i]) {
if(fSqlType == SQL_ALL_TYPES || fSqlType == pgtype_to_sqltype(type)) { if(fSqlType == SQL_ALL_TYPES || fSqlType == pgtype_to_sqltype(stmt, type)) {
row = (TupleNode *)malloc(sizeof(TupleNode) + (15 - 1)*sizeof(TupleField)); row = (TupleNode *)malloc(sizeof(TupleNode) + (15 - 1)*sizeof(TupleField));
/* These values can't be NULL */ /* These values can't be NULL */
set_tuplefield_string(&row->tuple[0], pgtype_to_name(type)); set_tuplefield_string(&row->tuple[0], pgtype_to_name(stmt, type));
set_tuplefield_int2(&row->tuple[1], pgtype_to_sqltype(type)); set_tuplefield_int2(&row->tuple[1], pgtype_to_sqltype(stmt, type));
set_tuplefield_int2(&row->tuple[6], pgtype_nullable(type)); set_tuplefield_int2(&row->tuple[6], pgtype_nullable(stmt, type));
set_tuplefield_int2(&row->tuple[7], pgtype_case_sensitive(type)); set_tuplefield_int2(&row->tuple[7], pgtype_case_sensitive(stmt, type));
set_tuplefield_int2(&row->tuple[8], pgtype_searchable(type)); set_tuplefield_int2(&row->tuple[8], pgtype_searchable(stmt, type));
set_tuplefield_int2(&row->tuple[10], pgtype_money(type)); set_tuplefield_int2(&row->tuple[10], pgtype_money(stmt, type));
/* Localized data-source dependent data type name (always NULL) */ /* Localized data-source dependent data type name (always NULL) */
set_tuplefield_null(&row->tuple[12]); set_tuplefield_null(&row->tuple[12]);
/* These values can be NULL */ /* These values can be NULL */
set_nullfield_int4(&row->tuple[2], pgtype_precision(type)); set_nullfield_int4(&row->tuple[2], pgtype_precision(stmt, type, PG_STATIC, PG_STATIC));
set_nullfield_string(&row->tuple[3], pgtype_literal_prefix(type)); set_nullfield_string(&row->tuple[3], pgtype_literal_prefix(stmt, type));
set_nullfield_string(&row->tuple[4], pgtype_literal_suffix(type)); set_nullfield_string(&row->tuple[4], pgtype_literal_suffix(stmt, type));
set_nullfield_string(&row->tuple[5], pgtype_create_params(type)); set_nullfield_string(&row->tuple[5], pgtype_create_params(stmt, type));
set_nullfield_int2(&row->tuple[9], pgtype_unsigned(type)); set_nullfield_int2(&row->tuple[9], pgtype_unsigned(stmt, type));
set_nullfield_int2(&row->tuple[11], pgtype_auto_increment(type)); set_nullfield_int2(&row->tuple[11], pgtype_auto_increment(stmt, type));
set_nullfield_int2(&row->tuple[13], pgtype_scale(type)); set_nullfield_int2(&row->tuple[13], pgtype_scale(stmt, type));
set_nullfield_int2(&row->tuple[14], pgtype_scale(type)); set_nullfield_int2(&row->tuple[14], pgtype_scale(stmt, type));
QR_add_tuple(stmt->result, row); QR_add_tuple(stmt->result, row);
} }
...@@ -782,6 +791,7 @@ Int4 type; ...@@ -782,6 +791,7 @@ Int4 type;
stmt->status = STMT_FINISHED; stmt->status = STMT_FINISHED;
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->current_col = -1;
return SQL_SUCCESS; return SQL_SUCCESS;
} }
...@@ -826,12 +836,12 @@ RETCODE SQL_API SQLGetFunctions( ...@@ -826,12 +836,12 @@ RETCODE SQL_API SQLGetFunctions(
pfExists[SQL_API_SQLFREECONNECT] = TRUE; pfExists[SQL_API_SQLFREECONNECT] = TRUE;
pfExists[SQL_API_SQLFREEENV] = TRUE; pfExists[SQL_API_SQLFREEENV] = TRUE;
pfExists[SQL_API_SQLFREESTMT] = TRUE; pfExists[SQL_API_SQLFREESTMT] = TRUE;
pfExists[SQL_API_SQLGETCURSORNAME] = FALSE; pfExists[SQL_API_SQLGETCURSORNAME] = TRUE;
pfExists[SQL_API_SQLNUMRESULTCOLS] = TRUE; pfExists[SQL_API_SQLNUMRESULTCOLS] = TRUE;
pfExists[SQL_API_SQLPREPARE] = TRUE; // complete? pfExists[SQL_API_SQLPREPARE] = TRUE; // complete?
pfExists[SQL_API_SQLROWCOUNT] = TRUE; pfExists[SQL_API_SQLROWCOUNT] = TRUE;
pfExists[SQL_API_SQLSETCURSORNAME] = FALSE; pfExists[SQL_API_SQLSETCURSORNAME] = TRUE;
pfExists[SQL_API_SQLSETPARAM] = FALSE; pfExists[SQL_API_SQLSETPARAM] = FALSE; // odbc 1.0
pfExists[SQL_API_SQLTRANSACT] = TRUE; pfExists[SQL_API_SQLTRANSACT] = TRUE;
// ODBC level 1 functions // ODBC level 1 functions
...@@ -840,11 +850,9 @@ RETCODE SQL_API SQLGetFunctions( ...@@ -840,11 +850,9 @@ RETCODE SQL_API SQLGetFunctions(
pfExists[SQL_API_SQLDRIVERCONNECT] = TRUE; pfExists[SQL_API_SQLDRIVERCONNECT] = TRUE;
pfExists[SQL_API_SQLGETCONNECTOPTION] = TRUE; // partial pfExists[SQL_API_SQLGETCONNECTOPTION] = TRUE; // partial
pfExists[SQL_API_SQLGETDATA] = TRUE; pfExists[SQL_API_SQLGETDATA] = TRUE;
pfExists[SQL_API_SQLGETFUNCTIONS] = TRUE; // sadly, I still pfExists[SQL_API_SQLGETFUNCTIONS] = TRUE;
// had to think about
// this one
pfExists[SQL_API_SQLGETINFO] = TRUE; pfExists[SQL_API_SQLGETINFO] = TRUE;
pfExists[SQL_API_SQLGETSTMTOPTION] = TRUE; // very partial pfExists[SQL_API_SQLGETSTMTOPTION] = TRUE; // partial
pfExists[SQL_API_SQLGETTYPEINFO] = TRUE; pfExists[SQL_API_SQLGETTYPEINFO] = TRUE;
pfExists[SQL_API_SQLPARAMDATA] = TRUE; pfExists[SQL_API_SQLPARAMDATA] = TRUE;
pfExists[SQL_API_SQLPUTDATA] = TRUE; pfExists[SQL_API_SQLPUTDATA] = TRUE;
...@@ -858,11 +866,11 @@ RETCODE SQL_API SQLGetFunctions( ...@@ -858,11 +866,11 @@ RETCODE SQL_API SQLGetFunctions(
pfExists[SQL_API_SQLBROWSECONNECT] = FALSE; pfExists[SQL_API_SQLBROWSECONNECT] = FALSE;
pfExists[SQL_API_SQLCOLUMNPRIVILEGES] = FALSE; pfExists[SQL_API_SQLCOLUMNPRIVILEGES] = FALSE;
pfExists[SQL_API_SQLDATASOURCES] = FALSE; // only implemented by DM pfExists[SQL_API_SQLDATASOURCES] = FALSE; // only implemented by DM
pfExists[SQL_API_SQLDESCRIBEPARAM] = FALSE; pfExists[SQL_API_SQLDESCRIBEPARAM] = TRUE;
pfExists[SQL_API_SQLDRIVERS] = FALSE; pfExists[SQL_API_SQLDRIVERS] = FALSE; // only implemented by DM
pfExists[SQL_API_SQLEXTENDEDFETCH] = TRUE; // partial? pfExists[SQL_API_SQLEXTENDEDFETCH] = globals.use_declarefetch ? FALSE : TRUE;
pfExists[SQL_API_SQLFOREIGNKEYS] = TRUE; pfExists[SQL_API_SQLFOREIGNKEYS] = TRUE;
pfExists[SQL_API_SQLMORERESULTS] = TRUE; pfExists[SQL_API_SQLMORERESULTS] = FALSE;
pfExists[SQL_API_SQLNATIVESQL] = TRUE; pfExists[SQL_API_SQLNATIVESQL] = TRUE;
pfExists[SQL_API_SQLNUMPARAMS] = TRUE; pfExists[SQL_API_SQLNUMPARAMS] = TRUE;
pfExists[SQL_API_SQLPARAMOPTIONS] = FALSE; pfExists[SQL_API_SQLPARAMOPTIONS] = FALSE;
...@@ -870,7 +878,7 @@ RETCODE SQL_API SQLGetFunctions( ...@@ -870,7 +878,7 @@ RETCODE SQL_API SQLGetFunctions(
pfExists[SQL_API_SQLPROCEDURECOLUMNS] = FALSE; pfExists[SQL_API_SQLPROCEDURECOLUMNS] = FALSE;
pfExists[SQL_API_SQLPROCEDURES] = FALSE; pfExists[SQL_API_SQLPROCEDURES] = FALSE;
pfExists[SQL_API_SQLSETPOS] = FALSE; pfExists[SQL_API_SQLSETPOS] = FALSE;
pfExists[SQL_API_SQLSETSCROLLOPTIONS] = FALSE; pfExists[SQL_API_SQLSETSCROLLOPTIONS] = FALSE; // odbc 1.0
pfExists[SQL_API_SQLTABLEPRIVILEGES] = FALSE; pfExists[SQL_API_SQLTABLEPRIVILEGES] = FALSE;
#endif #endif
} else { } else {
...@@ -894,12 +902,12 @@ RETCODE SQL_API SQLGetFunctions( ...@@ -894,12 +902,12 @@ RETCODE SQL_API SQLGetFunctions(
case SQL_API_SQLFREECONNECT: *pfExists = TRUE; break; case SQL_API_SQLFREECONNECT: *pfExists = TRUE; break;
case SQL_API_SQLFREEENV: *pfExists = TRUE; break; case SQL_API_SQLFREEENV: *pfExists = TRUE; break;
case SQL_API_SQLFREESTMT: *pfExists = TRUE; break; case SQL_API_SQLFREESTMT: *pfExists = TRUE; break;
case SQL_API_SQLGETCURSORNAME: *pfExists = FALSE; break; case SQL_API_SQLGETCURSORNAME: *pfExists = TRUE; break;
case SQL_API_SQLNUMRESULTCOLS: *pfExists = TRUE; break; case SQL_API_SQLNUMRESULTCOLS: *pfExists = TRUE; break;
case SQL_API_SQLPREPARE: *pfExists = TRUE; break; case SQL_API_SQLPREPARE: *pfExists = TRUE; break;
case SQL_API_SQLROWCOUNT: *pfExists = TRUE; break; case SQL_API_SQLROWCOUNT: *pfExists = TRUE; break;
case SQL_API_SQLSETCURSORNAME: *pfExists = FALSE; break; case SQL_API_SQLSETCURSORNAME: *pfExists = TRUE; break;
case SQL_API_SQLSETPARAM: *pfExists = FALSE; break; case SQL_API_SQLSETPARAM: *pfExists = FALSE; break; // odbc 1.0
case SQL_API_SQLTRANSACT: *pfExists = TRUE; break; case SQL_API_SQLTRANSACT: *pfExists = TRUE; break;
// ODBC level 1 functions // ODBC level 1 functions
...@@ -910,7 +918,7 @@ RETCODE SQL_API SQLGetFunctions( ...@@ -910,7 +918,7 @@ RETCODE SQL_API SQLGetFunctions(
case SQL_API_SQLGETDATA: *pfExists = TRUE; break; case SQL_API_SQLGETDATA: *pfExists = TRUE; break;
case SQL_API_SQLGETFUNCTIONS: *pfExists = TRUE; break; case SQL_API_SQLGETFUNCTIONS: *pfExists = TRUE; break;
case SQL_API_SQLGETINFO: *pfExists = TRUE; break; case SQL_API_SQLGETINFO: *pfExists = TRUE; break;
case SQL_API_SQLGETSTMTOPTION: *pfExists = TRUE; break; // very partial case SQL_API_SQLGETSTMTOPTION: *pfExists = TRUE; break; // partial
case SQL_API_SQLGETTYPEINFO: *pfExists = TRUE; break; case SQL_API_SQLGETTYPEINFO: *pfExists = TRUE; break;
case SQL_API_SQLPARAMDATA: *pfExists = TRUE; break; case SQL_API_SQLPARAMDATA: *pfExists = TRUE; break;
case SQL_API_SQLPUTDATA: *pfExists = TRUE; break; case SQL_API_SQLPUTDATA: *pfExists = TRUE; break;
...@@ -924,11 +932,11 @@ RETCODE SQL_API SQLGetFunctions( ...@@ -924,11 +932,11 @@ RETCODE SQL_API SQLGetFunctions(
case SQL_API_SQLBROWSECONNECT: *pfExists = FALSE; break; case SQL_API_SQLBROWSECONNECT: *pfExists = FALSE; break;
case SQL_API_SQLCOLUMNPRIVILEGES: *pfExists = FALSE; break; case SQL_API_SQLCOLUMNPRIVILEGES: *pfExists = FALSE; break;
case SQL_API_SQLDATASOURCES: *pfExists = FALSE; break; // only implemented by DM case SQL_API_SQLDATASOURCES: *pfExists = FALSE; break; // only implemented by DM
case SQL_API_SQLDESCRIBEPARAM: *pfExists = FALSE; break; case SQL_API_SQLDESCRIBEPARAM: *pfExists = TRUE; break;
case SQL_API_SQLDRIVERS: *pfExists = FALSE; break; case SQL_API_SQLDRIVERS: *pfExists = FALSE; break; // only implemented by DM
case SQL_API_SQLEXTENDEDFETCH: *pfExists = TRUE; break; // partial? case SQL_API_SQLEXTENDEDFETCH: *pfExists = globals.use_declarefetch ? FALSE : TRUE; break;
case SQL_API_SQLFOREIGNKEYS: *pfExists = TRUE; break; case SQL_API_SQLFOREIGNKEYS: *pfExists = TRUE; break;
case SQL_API_SQLMORERESULTS: *pfExists = TRUE; break; case SQL_API_SQLMORERESULTS: *pfExists = FALSE; break;
case SQL_API_SQLNATIVESQL: *pfExists = TRUE; break; case SQL_API_SQLNATIVESQL: *pfExists = TRUE; break;
case SQL_API_SQLNUMPARAMS: *pfExists = TRUE; break; case SQL_API_SQLNUMPARAMS: *pfExists = TRUE; break;
case SQL_API_SQLPARAMOPTIONS: *pfExists = FALSE; break; case SQL_API_SQLPARAMOPTIONS: *pfExists = FALSE; break;
...@@ -936,7 +944,7 @@ RETCODE SQL_API SQLGetFunctions( ...@@ -936,7 +944,7 @@ RETCODE SQL_API SQLGetFunctions(
case SQL_API_SQLPROCEDURECOLUMNS: *pfExists = FALSE; break; case SQL_API_SQLPROCEDURECOLUMNS: *pfExists = FALSE; break;
case SQL_API_SQLPROCEDURES: *pfExists = FALSE; break; case SQL_API_SQLPROCEDURES: *pfExists = FALSE; break;
case SQL_API_SQLSETPOS: *pfExists = FALSE; break; case SQL_API_SQLSETPOS: *pfExists = FALSE; break;
case SQL_API_SQLSETSCROLLOPTIONS: *pfExists = FALSE; break; case SQL_API_SQLSETSCROLLOPTIONS: *pfExists = FALSE; break; // odbc 1.0
case SQL_API_SQLTABLEPRIVILEGES: *pfExists = FALSE; break; case SQL_API_SQLTABLEPRIVILEGES: *pfExists = FALSE; break;
} }
#endif #endif
...@@ -965,8 +973,14 @@ HSTMT htbl_stmt; ...@@ -965,8 +973,14 @@ HSTMT htbl_stmt;
RETCODE result; RETCODE result;
char *tableType; char *tableType;
char tables_query[MAX_STATEMENT_LEN]; char tables_query[MAX_STATEMENT_LEN];
char table_name[MAX_INFO_STRING], table_owner[MAX_INFO_STRING]; char table_name[MAX_INFO_STRING], table_owner[MAX_INFO_STRING], relhasrules[MAX_INFO_STRING];
SDWORD table_name_len, table_owner_len; SDWORD table_name_len, table_owner_len, relhasrules_len;
ConnInfo *ci;
char *prefix[32], prefixes[MEDIUM_REGISTRY_LEN];
char *table_type[32], table_types[MAX_INFO_STRING];
char show_system_tables, show_regular_tables, show_views;
char regular_table, view, systable;
int i;
mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt);
...@@ -976,6 +990,8 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); ...@@ -976,6 +990,8 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt);
stmt->manual_result = TRUE; stmt->manual_result = TRUE;
stmt->errormsg_created = TRUE; stmt->errormsg_created = TRUE;
ci = &stmt->hdbc->connInfo;
result = SQLAllocStmt( stmt->hdbc, &htbl_stmt); result = SQLAllocStmt( stmt->hdbc, &htbl_stmt);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errornumber = STMT_NO_MEMORY_ERROR;
...@@ -988,20 +1004,72 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); ...@@ -988,20 +1004,72 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt);
// Create the query to find out the tables // Create the query to find out the tables
// ********************************************************************** // **********************************************************************
strcpy(tables_query, "select relname, usename from pg_class, pg_user where relkind = 'r' "); strcpy(tables_query, "select relname, usename, relhasrules from pg_class, pg_user where relkind = 'r' ");
my_strcat(tables_query, " and usename like '%.*s'", szTableOwner, cbTableOwner); my_strcat(tables_query, " and usename like '%.*s'", szTableOwner, cbTableOwner);
my_strcat(tables_query, " and relname like '%.*s'", szTableName, cbTableName); my_strcat(tables_query, " and relname like '%.*s'", szTableName, cbTableName);
// Parse the extra systable prefix
strcpy(prefixes, globals.extra_systable_prefixes);
i = 0;
prefix[i] = strtok(prefixes, ";");
while (prefix[i] && i<32) {
prefix[++i] = strtok(NULL, ";");
}
/* Parse the desired table types to return */
show_system_tables = FALSE;
show_regular_tables = FALSE;
show_views = FALSE;
// make_string mallocs memory // make_string mallocs memory
tableType = make_string(szTableType, cbTableType, NULL); tableType = make_string(szTableType, cbTableType, NULL);
if (tableType && ! strstr(tableType, "SYSTEM TABLE")) // is SYSTEM TABLE not present? if (tableType) {
strcat(tables_query, " and relname not like '" POSTGRES_SYS_PREFIX "%' and relname not like '" INSIGHT_SYS_PREFIX "%'"); strcpy(table_types, tableType);
if (tableType)
free(tableType); free(tableType);
i = 0;
table_type[i] = strtok(table_types, ",");
while (table_type[i] && i<32) {
table_type[++i] = strtok(NULL, ",");
}
strcat(tables_query, " and relname !~ '^Inv[0-9]+' and int4out(usesysid) = int4out(relowner) order by relname"); /* Check for desired table types to return */
i = 0;
while (table_type[i]) {
if ( strstr(table_type[i], "SYSTEM TABLE"))
show_system_tables = TRUE;
else if ( strstr(table_type[i], "TABLE"))
show_regular_tables = TRUE;
else if ( strstr(table_type[i], "VIEW"))
show_views = TRUE;
i++;
}
}
/* If not interested in SYSTEM TABLES then filter them out
to save some time on the query. If treating system tables
as regular tables, then dont filter either.
*/
if ( ! atoi(ci->show_system_tables) && ! show_system_tables) {
strcat(tables_query, " and relname !~ '^" POSTGRES_SYS_PREFIX);
/* Also filter out user-defined system table types */
i = 0;
while(prefix[i]) {
strcat(tables_query, "|^");
strcat(tables_query, prefix[i]);
i++;
}
strcat(tables_query, "'");
}
/* filter out large objects unconditionally (they are not system tables) and match users */
strcat(tables_query, " and relname !~ '^xinv[0-9]+' and int4out(usesysid) = int4out(relowner) order by relname");
// ********************************************************************** // **********************************************************************
...@@ -1030,6 +1098,14 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); ...@@ -1030,6 +1098,14 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt);
SQLFreeStmt(htbl_stmt, SQL_DROP); SQLFreeStmt(htbl_stmt, SQL_DROP);
return SQL_ERROR; return SQL_ERROR;
} }
result = SQLBindCol(htbl_stmt, 3, SQL_C_CHAR,
relhasrules, MAX_INFO_STRING, &relhasrules_len);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = tbl_stmt->errormsg;
stmt->errornumber = tbl_stmt->errornumber;
SQLFreeStmt(htbl_stmt, SQL_DROP);
return SQL_ERROR;
}
stmt->result = QR_Constructor(); stmt->result = QR_Constructor();
if(!stmt->result) { if(!stmt->result) {
...@@ -1054,33 +1130,63 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); ...@@ -1054,33 +1130,63 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt);
// add the tuples // add the tuples
result = SQLFetch(htbl_stmt); result = SQLFetch(htbl_stmt);
while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) { while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) {
row = (TupleNode *)malloc(sizeof(TupleNode) + (5 - 1) * sizeof(TupleField));
set_tuplefield_string(&row->tuple[0], ""); /* Determine if this table name is a system table.
If treating system tables as regular tables, then
no need to do this test.
*/
systable = FALSE;
if( ! atoi(ci->show_system_tables)) {
if ( strncmp(table_name, POSTGRES_SYS_PREFIX, strlen(POSTGRES_SYS_PREFIX)) == 0)
systable = TRUE;
else { /* Check extra system table prefixes */
i = 0;
while (prefix[i]) {
mylog("table_name='%s', prefix[%d]='%s'\n", table_name, i, prefix[i]);
if (strncmp(table_name, prefix[i], strlen(prefix[i])) == 0) {
systable = TRUE;
break;
}
i++;
}
}
}
// I have to hide the table owner from Access, otherwise it /* Determine if the table name is a view */
// insists on referring to the table as 'owner.table'. view = (relhasrules[0] == '1');
// (this is valid according to the ODBC SQL grammar, but
// Postgres won't support it.)
// set_tuplefield_string(&row->tuple[1], table_owner);
set_tuplefield_string(&row->tuple[1], ""); /* It must be a regular table */
set_tuplefield_string(&row->tuple[2], table_name); regular_table = ( ! systable && ! view);
mylog("SQLTables: table_name = '%s'\n", table_name);
// careful: this is case-sensitive /* Include the row in the result set if meets all criteria */
if(strncmp(table_name, POSTGRES_SYS_PREFIX, strlen(POSTGRES_SYS_PREFIX)) == 0 || /* NOTE: Unsupported table types (i.e., LOCAL TEMPORARY, ALIAS, etc)
strncmp(table_name, INSIGHT_SYS_PREFIX, strlen(INSIGHT_SYS_PREFIX)) == 0) { will return nothing */
set_tuplefield_string(&row->tuple[3], "SYSTEM TABLE"); if ( (systable && show_system_tables) ||
} else { (view && show_views) ||
set_tuplefield_string(&row->tuple[3], "TABLE"); (regular_table && show_regular_tables)) {
}
set_tuplefield_string(&row->tuple[4], ""); row = (TupleNode *)malloc(sizeof(TupleNode) + (5 - 1) * sizeof(TupleField));
QR_add_tuple(stmt->result, row); set_tuplefield_string(&row->tuple[0], "");
// I have to hide the table owner from Access, otherwise it
// insists on referring to the table as 'owner.table'.
// (this is valid according to the ODBC SQL grammar, but
// Postgres won't support it.)
// set_tuplefield_string(&row->tuple[1], table_owner);
mylog("SQLTables: table_name = '%s'\n", table_name);
set_tuplefield_string(&row->tuple[1], "");
set_tuplefield_string(&row->tuple[2], table_name);
set_tuplefield_string(&row->tuple[3], systable ? "SYSTEM TABLE" : (view ? "VIEW" : "TABLE"));
set_tuplefield_string(&row->tuple[4], "");
QR_add_tuple(stmt->result, row);
}
result = SQLFetch(htbl_stmt); result = SQLFetch(htbl_stmt);
} }
if(result != SQL_NO_DATA_FOUND) { if(result != SQL_NO_DATA_FOUND) {
...@@ -1096,6 +1202,7 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); ...@@ -1096,6 +1202,7 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt);
// set up the current tuple pointer for SQLFetch // set up the current tuple pointer for SQLFetch
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->current_col = -1;
SQLFreeStmt(htbl_stmt, SQL_DROP); SQLFreeStmt(htbl_stmt, SQL_DROP);
mylog("SQLTables(): EXIT, stmt=%u\n", stmt); mylog("SQLTables(): EXIT, stmt=%u\n", stmt);
...@@ -1125,6 +1232,9 @@ Int4 field_type; ...@@ -1125,6 +1232,9 @@ Int4 field_type;
SDWORD table_owner_len, table_name_len, field_name_len, SDWORD table_owner_len, table_name_len, field_name_len,
field_type_len, field_type_name_len, field_number_len, field_type_len, field_type_name_len, field_number_len,
field_length_len, mod_length_len; field_length_len, mod_length_len;
ConnInfo *ci;
mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt);
...@@ -1133,13 +1243,15 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); ...@@ -1133,13 +1243,15 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt);
stmt->manual_result = TRUE; stmt->manual_result = TRUE;
stmt->errormsg_created = TRUE; stmt->errormsg_created = TRUE;
ci = &stmt->hdbc->connInfo;
// ********************************************************************** // **********************************************************************
// Create the query to find out the columns (Note: pre 6.3 did not have the atttypmod field) // Create the query to find out the columns (Note: pre 6.3 did not have the atttypmod field)
// ********************************************************************** // **********************************************************************
sprintf(columns_query, "select u.usename, c.relname, a.attname, a.atttypid,t.typname, a.attnum, a.attlen, %s from pg_user u, pg_class c, pg_attribute a, pg_type t where " sprintf(columns_query, "select u.usename, c.relname, a.attname, a.atttypid,t.typname, a.attnum, a.attlen, %s from pg_user u, pg_class c, pg_attribute a, pg_type t where "
"int4out(u.usesysid) = int4out(c.relowner) and c.oid= a.attrelid and a.atttypid = t.oid and (a.attnum > 0)", "int4out(u.usesysid) = int4out(c.relowner) and c.oid= a.attrelid and a.atttypid = t.oid and (a.attnum > 0)",
PROTOCOL_62(&(stmt->hdbc->connInfo)) ? "a.attlen" : "a.atttypmod"); PROTOCOL_62(ci) ? "a.attlen" : "a.atttypmod");
my_strcat(columns_query, " and c.relname like '%.*s'", szTableName, cbTableName); my_strcat(columns_query, " and c.relname like '%.*s'", szTableName, cbTableName);
my_strcat(columns_query, " and u.usename like '%.*s'", szTableOwner, cbTableOwner); my_strcat(columns_query, " and u.usename like '%.*s'", szTableOwner, cbTableOwner);
...@@ -1266,7 +1378,43 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); ...@@ -1266,7 +1378,43 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt);
QR_set_field_info(stmt->result, 10, "NULLABLE", PG_TYPE_INT2, 2); QR_set_field_info(stmt->result, 10, "NULLABLE", PG_TYPE_INT2, 2);
QR_set_field_info(stmt->result, 11, "REMARKS", PG_TYPE_TEXT, 254); QR_set_field_info(stmt->result, 11, "REMARKS", PG_TYPE_TEXT, 254);
result = SQLFetch(hcol_stmt);
result = SQLFetch(hcol_stmt);
/* Only show oid if option AND there are other columns AND
its not being called by SQLStatistics .
Always show OID if its a system table
*/
if (result != SQL_ERROR && ! stmt->internal &&
(atoi(ci->show_oid_column) || strncmp(table_name, POSTGRES_SYS_PREFIX, strlen(POSTGRES_SYS_PREFIX)) == 0)) {
/* For OID fields */
row = (TupleNode *)malloc(sizeof(TupleNode) +
(12 - 1) * sizeof(TupleField));
set_tuplefield_string(&row->tuple[0], "");
// see note in SQLTables()
// set_tuplefield_string(&row->tuple[1], table_owner);
set_tuplefield_string(&row->tuple[1], "");
set_tuplefield_string(&row->tuple[2], table_name);
set_tuplefield_string(&row->tuple[3], "oid");
set_tuplefield_int2(&row->tuple[4], pgtype_to_sqltype(stmt, PG_TYPE_OID));
set_tuplefield_string(&row->tuple[5], "OID");
set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, PG_TYPE_OID, PG_STATIC,
PG_STATIC));
set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, PG_TYPE_OID, PG_STATIC,
PG_STATIC));
set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, PG_TYPE_OID));
set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, PG_TYPE_OID));
set_tuplefield_int2(&row->tuple[10], SQL_NO_NULLS);
set_tuplefield_string(&row->tuple[11], "");
QR_add_tuple(stmt->result, row);
}
while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) { while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) {
row = (TupleNode *)malloc(sizeof(TupleNode) + row = (TupleNode *)malloc(sizeof(TupleNode) +
(12 - 1) * sizeof(TupleField)); (12 - 1) * sizeof(TupleField));
...@@ -1277,51 +1425,40 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); ...@@ -1277,51 +1425,40 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt);
set_tuplefield_string(&row->tuple[1], ""); set_tuplefield_string(&row->tuple[1], "");
set_tuplefield_string(&row->tuple[2], table_name); set_tuplefield_string(&row->tuple[2], table_name);
set_tuplefield_string(&row->tuple[3], field_name); set_tuplefield_string(&row->tuple[3], field_name);
set_tuplefield_int2(&row->tuple[4], pgtype_to_sqltype(stmt, field_type));
set_tuplefield_string(&row->tuple[5], field_type_name);
/* Replace an unknown postgres type with SQL_CHAR type */
/* Leave the field_type_name with "unknown" */
if (pgtype_to_sqltype(field_type) == PG_UNKNOWN)
set_tuplefield_int2(&row->tuple[4], SQL_CHAR);
else
set_tuplefield_int2(&row->tuple[4], pgtype_to_sqltype(field_type));
set_tuplefield_string(&row->tuple[5], field_type_name); /* Some Notes about Postgres Data Types:
VARCHAR - the length is stored in the pg_attribute.atttypmod field
/* Some Notes about Postgres Data Types: BPCHAR - the length is also stored as varchar is
VARCHAR - the length is stored in the pg_attribute.atttypmod field
BPCHAR - the length is also stored as varchar is
NAME - the length is fixed and stored in pg_attribute.attlen field (32 on my system)
*/ */
if((field_type == PG_TYPE_VARCHAR) || if((field_type == PG_TYPE_VARCHAR) ||
(field_type == PG_TYPE_NAME) ||
(field_type == PG_TYPE_BPCHAR)) { (field_type == PG_TYPE_BPCHAR)) {
if (field_type == PG_TYPE_NAME) if (mod_length >= 4)
mod_length = field_length; // the length is in attlen
else if (mod_length >= 4)
mod_length -= 4; // the length is in atttypmod - 4 mod_length -= 4; // the length is in atttypmod - 4
if (mod_length > MAX_VARCHAR_SIZE || mod_length <= 0) if (mod_length > globals.max_varchar_size || mod_length <= 0)
mod_length = MAX_VARCHAR_SIZE; mod_length = globals.max_varchar_size;
mylog("SQLColumns: field type is VARCHAR,NAME: field_type = %d, mod_length = %d\n", field_type, mod_length); mylog("SQLColumns: field type is VARCHAR,BPCHAR: field_type = %d, mod_length = %d\n", field_type, mod_length);
set_tuplefield_int4(&row->tuple[7], mod_length); set_tuplefield_int4(&row->tuple[7], mod_length);
set_tuplefield_int4(&row->tuple[6], mod_length); set_tuplefield_int4(&row->tuple[6], mod_length);
} else { } else {
mylog("SQLColumns: field type is OTHER: field_type = %d, pgtype_length = %d\n", field_type, pgtype_length(field_type)); mylog("SQLColumns: field type is OTHER: field_type = %d, pgtype_length = %d\n", field_type, pgtype_length(stmt, field_type, PG_STATIC, PG_STATIC));
set_tuplefield_int4(&row->tuple[7], pgtype_length(field_type)); set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, field_type, PG_STATIC, PG_STATIC));
set_tuplefield_int4(&row->tuple[6], pgtype_precision(field_type)); set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, field_type, PG_STATIC, PG_STATIC));
} }
set_nullfield_int2(&row->tuple[8], pgtype_scale(field_type)); set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, field_type));
set_nullfield_int2(&row->tuple[9], pgtype_radix(field_type)); set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, field_type));
set_tuplefield_int2(&row->tuple[10], pgtype_nullable(field_type)); set_tuplefield_int2(&row->tuple[10], pgtype_nullable(stmt, field_type));
set_tuplefield_string(&row->tuple[11], ""); set_tuplefield_string(&row->tuple[11], "");
QR_add_tuple(stmt->result, row); QR_add_tuple(stmt->result, row);
...@@ -1341,6 +1478,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); ...@@ -1341,6 +1478,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt);
// set up the current tuple pointer for SQLFetch // set up the current tuple pointer for SQLFetch
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->current_col = -1;
SQLFreeStmt(hcol_stmt, SQL_DROP); SQLFreeStmt(hcol_stmt, SQL_DROP);
mylog("SQLColumns(): EXIT, stmt=%u\n", stmt); mylog("SQLColumns(): EXIT, stmt=%u\n", stmt);
...@@ -1383,16 +1521,15 @@ mylog("**** SQLSpecialColumns(): ENTER, stmt=%u\n", stmt); ...@@ -1383,16 +1521,15 @@ mylog("**** SQLSpecialColumns(): ENTER, stmt=%u\n", stmt);
/* use the oid value for the rowid */ /* use the oid value for the rowid */
if(fColType == SQL_BEST_ROWID) { if(fColType == SQL_BEST_ROWID) {
row = (TupleNode *)malloc(sizeof(TupleNode) + (8 - 1) * sizeof(TupleField)); row = (TupleNode *)malloc(sizeof(TupleNode) + (8 - 1) * sizeof(TupleField));
set_tuplefield_int2(&row->tuple[0], SQL_SCOPE_SESSION); set_tuplefield_int2(&row->tuple[0], SQL_SCOPE_SESSION);
set_tuplefield_string(&row->tuple[1], "oid"); set_tuplefield_string(&row->tuple[1], "oid");
set_tuplefield_int2(&row->tuple[2], pgtype_to_sqltype(PG_TYPE_OID)); set_tuplefield_int2(&row->tuple[2], pgtype_to_sqltype(stmt, PG_TYPE_OID));
set_tuplefield_string(&row->tuple[3], "OID"); set_tuplefield_string(&row->tuple[3], "OID");
set_tuplefield_int4(&row->tuple[4], pgtype_precision(PG_TYPE_OID)); set_tuplefield_int4(&row->tuple[4], pgtype_precision(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC));
set_tuplefield_int4(&row->tuple[5], pgtype_length(PG_TYPE_OID)); set_tuplefield_int4(&row->tuple[5], pgtype_length(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC));
set_tuplefield_int2(&row->tuple[6], pgtype_scale(PG_TYPE_OID)); set_tuplefield_int2(&row->tuple[6], pgtype_scale(stmt, PG_TYPE_OID));
set_tuplefield_int2(&row->tuple[7], SQL_PC_PSEUDO); set_tuplefield_int2(&row->tuple[7], SQL_PC_PSEUDO);
QR_add_tuple(stmt->result, row); QR_add_tuple(stmt->result, row);
...@@ -1405,6 +1542,7 @@ mylog("**** SQLSpecialColumns(): ENTER, stmt=%u\n", stmt); ...@@ -1405,6 +1542,7 @@ mylog("**** SQLSpecialColumns(): ENTER, stmt=%u\n", stmt);
stmt->status = STMT_FINISHED; stmt->status = STMT_FINISHED;
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->current_col = -1;
mylog("SQLSpecialColumns(): EXIT, stmt=%u\n", stmt); mylog("SQLSpecialColumns(): EXIT, stmt=%u\n", stmt);
return SQL_SUCCESS; return SQL_SUCCESS;
...@@ -1428,6 +1566,7 @@ RETCODE result; ...@@ -1428,6 +1566,7 @@ RETCODE result;
char *table_name; char *table_name;
char index_name[MAX_INFO_STRING]; char index_name[MAX_INFO_STRING];
short fields_vector[8]; short fields_vector[8];
char isunique[10], isclustered[10];
SDWORD index_name_len, fields_vector_len; SDWORD index_name_len, fields_vector_len;
TupleNode *row; TupleNode *row;
int i; int i;
...@@ -1438,6 +1577,8 @@ char **column_names = 0; ...@@ -1438,6 +1577,8 @@ char **column_names = 0;
Int4 column_name_len; Int4 column_name_len;
int total_columns = 0; int total_columns = 0;
char error = TRUE; char error = TRUE;
ConnInfo *ci;
char buf[256];
mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt); mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt);
...@@ -1448,6 +1589,8 @@ mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt); ...@@ -1448,6 +1589,8 @@ mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt);
stmt->manual_result = TRUE; stmt->manual_result = TRUE;
stmt->errormsg_created = TRUE; stmt->errormsg_created = TRUE;
ci = &stmt->hdbc->connInfo;
stmt->result = QR_Constructor(); stmt->result = QR_Constructor();
if(!stmt->result) { if(!stmt->result) {
stmt->errormsg = "Couldn't allocate memory for SQLStatistics result."; stmt->errormsg = "Couldn't allocate memory for SQLStatistics result.";
...@@ -1475,114 +1618,178 @@ mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt); ...@@ -1475,114 +1618,178 @@ mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt);
QR_set_field_info(stmt->result, 11, "PAGES", PG_TYPE_INT4, 4); QR_set_field_info(stmt->result, 11, "PAGES", PG_TYPE_INT4, 4);
QR_set_field_info(stmt->result, 12, "FILTER_CONDITION", PG_TYPE_TEXT, MAX_INFO_STRING); QR_set_field_info(stmt->result, 12, "FILTER_CONDITION", PG_TYPE_TEXT, MAX_INFO_STRING);
// there are no unique indexes in postgres, so return nothing
// if those are requested
if(fUnique != SQL_INDEX_UNIQUE) {
// only use the table name... the owner should be redundant, and
// we never use qualifiers.
table_name = make_string(szTableName, cbTableName, NULL);
if ( ! table_name) {
stmt->errormsg = "No table name passed to SQLStatistics.";
stmt->errornumber = STMT_INTERNAL_ERROR;
return SQL_ERROR;
}
// we need to get a list of the field names first, // only use the table name... the owner should be redundant, and
// so we can return them later. // we never use qualifiers.
result = SQLAllocStmt( stmt->hdbc, &hcol_stmt); table_name = make_string(szTableName, cbTableName, NULL);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { if ( ! table_name) {
stmt->errormsg = "SQLAllocStmt failed in SQLStatistics for columns."; stmt->errormsg = "No table name passed to SQLStatistics.";
stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errornumber = STMT_INTERNAL_ERROR;
goto SEEYA; return SQL_ERROR;
} }
col_stmt = (StatementClass *) hcol_stmt; // we need to get a list of the field names first,
// so we can return them later.
result = SQLAllocStmt( stmt->hdbc, &hcol_stmt);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = "SQLAllocStmt failed in SQLStatistics for columns.";
stmt->errornumber = STMT_NO_MEMORY_ERROR;
goto SEEYA;
}
result = SQLColumns(hcol_stmt, "", 0, "", 0, col_stmt = (StatementClass *) hcol_stmt;
table_name, (SWORD) strlen(table_name), "", 0);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { /* "internal" prevents SQLColumns from returning the oid if it is being shown.
stmt->errormsg = col_stmt->errormsg; // "SQLColumns failed in SQLStatistics."; This would throw everything off.
stmt->errornumber = col_stmt->errornumber; // STMT_EXEC_ERROR; */
SQLFreeStmt(hcol_stmt, SQL_DROP); col_stmt->internal = TRUE;
goto SEEYA; result = SQLColumns(hcol_stmt, "", 0, "", 0,
} table_name, (SWORD) strlen(table_name), "", 0);
result = SQLBindCol(hcol_stmt, 4, SQL_C_CHAR, col_stmt->internal = FALSE;
column_name, MAX_INFO_STRING, &column_name_len);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = col_stmt->errormsg; stmt->errormsg = col_stmt->errormsg; // "SQLColumns failed in SQLStatistics.";
stmt->errornumber = col_stmt->errornumber; stmt->errornumber = col_stmt->errornumber; // STMT_EXEC_ERROR;
SQLFreeStmt(hcol_stmt, SQL_DROP); SQLFreeStmt(hcol_stmt, SQL_DROP);
goto SEEYA; goto SEEYA;
}
result = SQLBindCol(hcol_stmt, 4, SQL_C_CHAR,
column_name, MAX_INFO_STRING, &column_name_len);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = col_stmt->errormsg;
stmt->errornumber = col_stmt->errornumber;
SQLFreeStmt(hcol_stmt, SQL_DROP);
goto SEEYA;
} }
result = SQLFetch(hcol_stmt);
while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) {
total_columns++;
column_names =
(char **)realloc(column_names,
total_columns * sizeof(char *));
column_names[total_columns-1] =
(char *)malloc(strlen(column_name)+1);
strcpy(column_names[total_columns-1], column_name);
mylog("SQLStatistics: column_name = '%s'\n", column_name);
result = SQLFetch(hcol_stmt); result = SQLFetch(hcol_stmt);
while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) { }
total_columns++; if(result != SQL_NO_DATA_FOUND || total_columns == 0) {
stmt->errormsg = SC_create_errormsg(hcol_stmt); // "Couldn't get column names in SQLStatistics.";
stmt->errornumber = col_stmt->errornumber;
SQLFreeStmt(hcol_stmt, SQL_DROP);
goto SEEYA;
column_names = }
(char **)realloc(column_names,
total_columns * sizeof(char *)); SQLFreeStmt(hcol_stmt, SQL_DROP);
column_names[total_columns-1] =
(char *)malloc(strlen(column_name)+1);
strcpy(column_names[total_columns-1], column_name);
result = SQLFetch(hcol_stmt); // get a list of indexes on this table
} result = SQLAllocStmt( stmt->hdbc, &hindx_stmt);
if(result != SQL_NO_DATA_FOUND || total_columns == 0) { if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = SC_create_errormsg(hcol_stmt); // "Couldn't get column names in SQLStatistics."; stmt->errormsg = "SQLAllocStmt failed in SQLStatistics for indices.";
stmt->errornumber = col_stmt->errornumber; stmt->errornumber = STMT_NO_MEMORY_ERROR;
SQLFreeStmt(hcol_stmt, SQL_DROP); goto SEEYA;
goto SEEYA;
} }
indx_stmt = (StatementClass *) hindx_stmt;
SQLFreeStmt(hcol_stmt, SQL_DROP);
// get a list of indexes on this table sprintf(index_query, "select c.relname, i.indkey, i.indisunique, i.indisclustered from pg_index i, pg_class c, pg_class d where c.oid = i.indexrelid and d.relname = '%s' and d.oid = i.indrelid",
result = SQLAllocStmt( stmt->hdbc, &hindx_stmt); table_name);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = "SQLAllocStmt failed in SQLStatistics for indices.";
stmt->errornumber = STMT_NO_MEMORY_ERROR;
goto SEEYA;
} result = SQLExecDirect(hindx_stmt, index_query, strlen(index_query));
indx_stmt = (StatementClass *) hindx_stmt; if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = SC_create_errormsg(hindx_stmt); // "Couldn't execute index query (w/SQLExecDirect) in SQLStatistics.";
stmt->errornumber = indx_stmt->errornumber;
SQLFreeStmt(hindx_stmt, SQL_DROP);
goto SEEYA;
}
// bind the index name column
result = SQLBindCol(hindx_stmt, 1, SQL_C_CHAR,
index_name, MAX_INFO_STRING, &index_name_len);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = indx_stmt->errormsg; // "Couldn't bind column in SQLStatistics.";
stmt->errornumber = indx_stmt->errornumber;
SQLFreeStmt(hindx_stmt, SQL_DROP);
goto SEEYA;
sprintf(index_query, "select c.relname, i.indkey from pg_index i, pg_class c, pg_class d where c.oid = i.indexrelid and d.relname = '%s' and d.oid = i.indrelid", }
table_name); // bind the vector column
result = SQLBindCol(hindx_stmt, 2, SQL_C_DEFAULT,
fields_vector, 16, &fields_vector_len);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = indx_stmt->errormsg; // "Couldn't bind column in SQLStatistics.";
stmt->errornumber = indx_stmt->errornumber;
SQLFreeStmt(hindx_stmt, SQL_DROP);
goto SEEYA;
result = SQLExecDirect(hindx_stmt, index_query, strlen(index_query)); }
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { // bind the "is unique" column
stmt->errormsg = SC_create_errormsg(hindx_stmt); // "Couldn't execute index query (w/SQLExecDirect) in SQLStatistics."; result = SQLBindCol(hindx_stmt, 3, SQL_C_CHAR,
stmt->errornumber = indx_stmt->errornumber; isunique, sizeof(isunique), NULL);
SQLFreeStmt(hindx_stmt, SQL_DROP); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
goto SEEYA; stmt->errormsg = indx_stmt->errormsg; // "Couldn't bind column in SQLStatistics.";
stmt->errornumber = indx_stmt->errornumber;
SQLFreeStmt(hindx_stmt, SQL_DROP);
goto SEEYA;
}
} // bind the "is clustered" column
result = SQLBindCol(hindx_stmt, 4, SQL_C_CHAR,
isclustered, sizeof(isclustered), NULL);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = indx_stmt->errormsg; // "Couldn't bind column in SQLStatistics.";
stmt->errornumber = indx_stmt->errornumber;
SQLFreeStmt(hindx_stmt, SQL_DROP);
goto SEEYA;
result = SQLBindCol(hindx_stmt, 1, SQL_C_CHAR, }
index_name, MAX_INFO_STRING, &index_name_len);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = indx_stmt->errormsg; // "Couldn't bind column in SQLStatistics.";
stmt->errornumber = indx_stmt->errornumber;
SQLFreeStmt(hindx_stmt, SQL_DROP);
goto SEEYA;
} /* fake index of OID */
// bind the vector column if (atoi(ci->show_oid_column) && atoi(ci->fake_oid_index)) {
result = SQLBindCol(hindx_stmt, 2, SQL_C_DEFAULT, row = (TupleNode *)malloc(sizeof(TupleNode) +
fields_vector, 16, &fields_vector_len); (13 - 1) * sizeof(TupleField));
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
stmt->errormsg = indx_stmt->errormsg; // "Couldn't bind column in SQLStatistics.";
stmt->errornumber = indx_stmt->errornumber;
SQLFreeStmt(hindx_stmt, SQL_DROP);
goto SEEYA;
} // no table qualifier
set_tuplefield_string(&row->tuple[0], "");
// don't set the table owner, else Access tries to use it
set_tuplefield_string(&row->tuple[1], "");
set_tuplefield_string(&row->tuple[2], table_name);
result = SQLFetch(hindx_stmt); // non-unique index?
while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) { set_tuplefield_int2(&row->tuple[3], (Int2) (globals.unique_index ? FALSE : TRUE));
// no index qualifier
set_tuplefield_string(&row->tuple[4], "");
sprintf(buf, "%s_idx_fake_oid", table_name);
set_tuplefield_string(&row->tuple[5], buf);
// Clustered index? I think non-clustered should be type OTHER not HASHED
set_tuplefield_int2(&row->tuple[6], (Int2) SQL_INDEX_OTHER);
set_tuplefield_int2(&row->tuple[7], (Int2) 1);
set_tuplefield_string(&row->tuple[8], "oid");
set_tuplefield_string(&row->tuple[9], "A");
set_tuplefield_null(&row->tuple[10]);
set_tuplefield_null(&row->tuple[11]);
set_tuplefield_null(&row->tuple[12]);
QR_add_tuple(stmt->result, row);
}
result = SQLFetch(hindx_stmt);
while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) {
// If only requesting unique indexs, then just return those.
if (fUnique == SQL_INDEX_ALL ||
(fUnique == SQL_INDEX_UNIQUE && atoi(isunique))) {
i = 0; i = 0;
// add a row in this table for each field in the index // add a row in this table for each field in the index
while(i < 8 && fields_vector[i] != 0) { while(i < 8 && fields_vector[i] != 0) {
...@@ -1596,24 +1803,32 @@ mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt); ...@@ -1596,24 +1803,32 @@ mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt);
set_tuplefield_string(&row->tuple[1], ""); set_tuplefield_string(&row->tuple[1], "");
set_tuplefield_string(&row->tuple[2], table_name); set_tuplefield_string(&row->tuple[2], table_name);
// Postgres95 indices always allow non-unique values. // non-unique index?
set_tuplefield_int2(&row->tuple[3], TRUE); if (globals.unique_index)
set_tuplefield_int2(&row->tuple[3], (Int2) (atoi(isunique) ? FALSE : TRUE));
else
set_tuplefield_int2(&row->tuple[3], TRUE);
// no index qualifier // no index qualifier
set_tuplefield_string(&row->tuple[4], ""); set_tuplefield_string(&row->tuple[4], "");
set_tuplefield_string(&row->tuple[5], index_name); set_tuplefield_string(&row->tuple[5], index_name);
// check this--what does it mean for an index // Clustered index? I think non-clustered should be type OTHER not HASHED
// to be clustered? (none of mine seem to be-- set_tuplefield_int2(&row->tuple[6], (Int2) (atoi(isclustered) ? SQL_INDEX_CLUSTERED : SQL_INDEX_OTHER));
// we can and probably should find this out from
// the pg_index table)
set_tuplefield_int2(&row->tuple[6], SQL_INDEX_HASHED);
set_tuplefield_int2(&row->tuple[7], (Int2) (i+1)); set_tuplefield_int2(&row->tuple[7], (Int2) (i+1));
if(fields_vector[i] < 0 || fields_vector[i] > total_columns) if(fields_vector[i] == OID_ATTNUM) {
set_tuplefield_string(&row->tuple[8], "oid");
mylog("SQLStatistics: column name = oid\n");
}
else if(fields_vector[i] < 0 || fields_vector[i] > total_columns) {
set_tuplefield_string(&row->tuple[8], "UNKNOWN"); set_tuplefield_string(&row->tuple[8], "UNKNOWN");
else mylog("SQLStatistics: column name = UNKNOWN\n");
}
else {
set_tuplefield_string(&row->tuple[8], column_names[fields_vector[i]-1]); set_tuplefield_string(&row->tuple[8], column_names[fields_vector[i]-1]);
mylog("SQLStatistics: column name = '%s'\n", column_names[fields_vector[i]-1]);
}
set_tuplefield_string(&row->tuple[9], "A"); set_tuplefield_string(&row->tuple[9], "A");
set_tuplefield_null(&row->tuple[10]); set_tuplefield_null(&row->tuple[10]);
...@@ -1622,26 +1837,27 @@ mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt); ...@@ -1622,26 +1837,27 @@ mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt);
QR_add_tuple(stmt->result, row); QR_add_tuple(stmt->result, row);
i++; i++;
} }
}
result = SQLFetch(hindx_stmt);
}
if(result != SQL_NO_DATA_FOUND) {
stmt->errormsg = SC_create_errormsg(hindx_stmt); // "SQLFetch failed in SQLStatistics.";
stmt->errornumber = indx_stmt->errornumber;
SQLFreeStmt(hindx_stmt, SQL_DROP);
goto SEEYA;
}
result = SQLFetch(hindx_stmt);
}
if(result != SQL_NO_DATA_FOUND) {
stmt->errormsg = SC_create_errormsg(hindx_stmt); // "SQLFetch failed in SQLStatistics.";
stmt->errornumber = indx_stmt->errornumber;
SQLFreeStmt(hindx_stmt, SQL_DROP); SQLFreeStmt(hindx_stmt, SQL_DROP);
goto SEEYA;
} }
SQLFreeStmt(hindx_stmt, SQL_DROP);
// also, things need to think that this statement is finished so // also, things need to think that this statement is finished so
// the results can be retrieved. // the results can be retrieved.
stmt->status = STMT_FINISHED; stmt->status = STMT_FINISHED;
// set up the current tuple pointer for SQLFetch // set up the current tuple pointer for SQLFetch
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->current_col = -1;
error = FALSE; error = FALSE;
...@@ -1672,6 +1888,7 @@ RETCODE SQL_API SQLColumnPrivileges( ...@@ -1672,6 +1888,7 @@ RETCODE SQL_API SQLColumnPrivileges(
UCHAR FAR * szColumnName, UCHAR FAR * szColumnName,
SWORD cbColumnName) SWORD cbColumnName)
{ {
/* Neither Access or Borland care about this. */
return SQL_ERROR; return SQL_ERROR;
} }
...@@ -1702,7 +1919,7 @@ int nk = 0; ...@@ -1702,7 +1919,7 @@ int nk = 0;
tbl_stmt = (StatementClass *) htbl_stmt; tbl_stmt = (StatementClass *) htbl_stmt;
tables_query[0] = '\0'; tables_query[0] = '\0';
if ( ! my_strcat(tables_query, "select distinct on attnum a2.attname, a2.attnum from pg_attribute a1, pg_attribute a2, pg_class c, pg_index i where c.relname = '%.*s_key' AND c.oid = i.indexrelid AND a1.attrelid = c.oid AND a2.attrelid = c.oid AND (i.indkey[0] = a1.attnum OR i.indkey[1] = a1.attnum OR i.indkey[2] = a1.attnum OR i.indkey[3] = a1.attnum OR i.indkey[4] = a1.attnum OR i.indkey[5] = a1.attnum OR i.indkey[6] = a1.attnum OR i.indkey[7] = a1.attnum) order by a2.attnum", if ( ! my_strcat(tables_query, "select distinct on attnum a2.attname, a2.attnum from pg_attribute a1, pg_attribute a2, pg_class c, pg_index i where c.relname = '%.*s_pkey' AND c.oid = i.indexrelid AND a1.attrelid = c.oid AND a2.attrelid = c.oid AND (i.indkey[0] = a1.attnum OR i.indkey[1] = a1.attnum OR i.indkey[2] = a1.attnum OR i.indkey[3] = a1.attnum OR i.indkey[4] = a1.attnum OR i.indkey[5] = a1.attnum OR i.indkey[6] = a1.attnum OR i.indkey[7] = a1.attnum) order by a2.attnum",
szTableName, cbTableName)) { szTableName, cbTableName)) {
stmt->errormsg = "No Table specified to getPrimaryKeyString."; stmt->errormsg = "No Table specified to getPrimaryKeyString.";
...@@ -1883,6 +2100,7 @@ mylog("**** SQLPrimaryKeys(): ENTER, stmt=%u\n", stmt); ...@@ -1883,6 +2100,7 @@ mylog("**** SQLPrimaryKeys(): ENTER, stmt=%u\n", stmt);
// set up the current tuple pointer for SQLFetch // set up the current tuple pointer for SQLFetch
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->current_col = -1;
mylog("SQLPrimaryKeys(): EXIT, stmt=%u\n", stmt); mylog("SQLPrimaryKeys(): EXIT, stmt=%u\n", stmt);
return SQL_SUCCESS; return SQL_SUCCESS;
...@@ -2134,6 +2352,7 @@ mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt); ...@@ -2134,6 +2352,7 @@ mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt);
// set up the current tuple pointer for SQLFetch // set up the current tuple pointer for SQLFetch
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->current_col = -1;
mylog("SQLForeignKeys(): EXIT, stmt=%u\n", stmt); mylog("SQLForeignKeys(): EXIT, stmt=%u\n", stmt);
return SQL_SUCCESS; return SQL_SUCCESS;
......
GNU LIBRARY GENERAL PUBLIC LICENSE GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991 Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc. Copyright (C) 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA 675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is [This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.] numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble Preamble
The licenses for most software are designed to take away your The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users. free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for other libraries whose authors decide to use it. You can use it for
your libraries, too. your libraries, too.
When we speak of free software, we are referring to freedom, not When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things. in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights. anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it. you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights. it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library. permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on version, so that any problems introduced by others will not reflect on
the original authors' reputations. the original authors' reputations.
Finally, any free program is threatened constantly by software Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this, transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all. free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license. the same as in the ordinary license.
The reason we have a separate public license for some libraries is that The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License derivative of the original library, and the ordinary General Public License
treats it as such. treats it as such.
Because of this blurred distinction, using the ordinary General Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better. concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries. will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The "work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only former contains code derived from the library, while the latter only
works together with the library. works together with the library.
Note that it is possible for a library to be covered by the ordinary Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one. General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which 0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is General Public License (also called "this License"). Each licensee is
addressed as "you". addressed as "you".
A "library" means a collection of software functions and/or data A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables. (which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".) included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for "Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation interface definition files, plus the scripts used to control compilation
and installation of the library. and installation of the library.
Activities other than copying, distribution and modification are not Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does. and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's 1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the warranty; and distribute a copy of this License along with the
Library. Library.
You may charge a fee for the physical act of transferring a copy, You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a and you may at your option offer warranty protection in exchange for a
fee. fee.
2. You may modify your copy or copies of the Library or any portion 2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1 distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions: above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library. a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change. stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License. charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that, is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of table, the facility still operates, and performs whatever part of
its purpose remains meaningful. its purpose remains meaningful.
(For example, a function in a library to compute square roots has (For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must application-supplied function or table used by this function must
be optional: if the application does not supply it, the square be optional: if the application does not supply it, the square
root function must still compute square roots.) root function must still compute square roots.)
These requirements apply to the modified work as a whole. If These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library, identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote entire whole, and thus to each and every part regardless of who wrote
it. it.
Thus, it is not the intent of this section to claim rights or contest Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or exercise the right to control the distribution of derivative or
collective works based on the Library. collective works based on the Library.
In addition, mere aggregation of another work not based on the Library In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under a storage or distribution medium does not bring the other work under
the scope of this License. the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public 3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2, that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in that version instead if you wish.) Do not make any other change in
these notices. these notices.
Once this change is made in a given copy, it is irreversible for Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy. subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of This option is useful when you wish to copy part of the code of
the Library into a program that is not a library. the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or 4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange. medium customarily used for software interchange.
If distribution of object code is made by offering access to copy If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not distribute the source code, even though third parties are not
compelled to copy the source along with the object code. compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the 5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License. therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License. library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables. Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not. derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law. threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.) Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6. distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6, Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself. whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or 6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse modification of the work for the customer's own use and reverse
engineering for debugging such modifications. engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one directing the user to the copy of this License. Also, you must do one
of these things: of these things:
a) Accompany the work with the complete corresponding a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application Library will not necessarily be able to recompile the application
to use the modified definitions.) to use the modified definitions.)
b) Accompany the work with a written offer, valid for at b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution. than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above from a designated place, offer equivalent access to copy the above
specified materials from the same place. specified materials from the same place.
d) Verify that the user has already received a copy of these d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy. materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception, reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies which the executable runs, unless that component itself accompanies
the executable. the executable.
It may happen that this requirement contradicts the license It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you use both them and the Library together in an executable that you
distribute. distribute.
7. You may place library facilities that are a work based on the 7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things: permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the facilities. This must be distributed under the terms of the
Sections above. Sections above.
b) Give prominent notice with the combined library of the fact b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work. where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute 8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies, rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance. terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not 9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying all its terms and conditions for copying, distributing or modifying
the Library or works based on it. the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the 10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein. restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to You are not responsible for enforcing compliance by third parties to
this License. this License.
11. If, as a consequence of a court judgment or allegation of patent 11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues), infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library. refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply, particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances. and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is integrity of the free software distribution system which is
implemented by public license practices. Many people have made implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot to distribute software through any other system and a licensee cannot
impose that choice. impose that choice.
This section is intended to make thoroughly clear what is believed to This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License. be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in 12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries, an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if excluded. In such case, this License incorporates the limitation as if
written in the body of this License. written in the body of this License.
13. The Free Software Foundation may publish revised and/or new 13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time. versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version, Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns. but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and "any later version", you have the option of following the terms and
conditions either of that version or of any later version published by conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by license version number, you may choose any version ever published by
the Free Software Foundation. the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free 14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these, programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing of all derivatives of our free software and of promoting the sharing
and reuse of software generally. and reuse of software generally.
NO WARRANTY NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. DAMAGES.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Libraries Appendix: How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License). ordinary General Public License).
To apply these terms, attach the following notices to the library. It is To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found. "copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.> <one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author> Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version. version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details. Library General Public License for more details.
You should have received a copy of the GNU Library General Public You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names: necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker. library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990 <signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice Ty Coon, President of Vice
That's all there is to it! That's all there is to it!
/* Module: misc.c /* Module: misc.c
* *
* Description: This module contains miscellaneous routines * Description: This module contains miscellaneous routines
* such as for debugging/logging and string functions. * such as for debugging/logging and string functions.
* *
* Classes: n/a * Classes: n/a
* *
* API functions: none * API functions: none
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include <stdio.h> #include <stdio.h>
#include <windows.h> #include <varargs.h>
#include <sql.h>
#include "psqlodbc.h" #include "psqlodbc.h"
extern GLOBAL_VALUES globals; extern GLOBAL_VALUES globals;
#ifdef MY_LOG #ifdef MY_LOG
#include <varargs.h>
void void
mylog(va_alist) mylog(va_alist)
...@@ -52,7 +51,6 @@ static FILE *LOGFP = 0; ...@@ -52,7 +51,6 @@ static FILE *LOGFP = 0;
#ifdef Q_LOG #ifdef Q_LOG
#include <varargs.h>
void qlog(va_alist) void qlog(va_alist)
va_dcl va_dcl
...@@ -78,10 +76,17 @@ static FILE *LOGFP = 0; ...@@ -78,10 +76,17 @@ static FILE *LOGFP = 0;
} }
#endif #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) */ /* returns STRCPY_FAIL, STRCPY_TRUNCATED, or #bytes copied (not including null term) */
int 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) if (dst_len <= 0)
return STRCPY_FAIL; return STRCPY_FAIL;
...@@ -90,31 +95,23 @@ my_strcpy(char *dst, size_t dst_len, char *src, size_t src_len) ...@@ -90,31 +95,23 @@ my_strcpy(char *dst, size_t dst_len, char *src, size_t src_len)
dst[0] = '\0'; dst[0] = '\0';
return STRCPY_NULL; return STRCPY_NULL;
} }
else if (src_len == SQL_NTS)
src_len = strlen(src);
else if (src_len == SQL_NTS) { if (src_len <= 0)
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)
return STRCPY_FAIL; return STRCPY_FAIL;
else { else {
if (src_len < dst_len) { if (src_len < dst_len) {
memcpy(dst, src, src_len); memcpy(dst, src, src_len);
dst[src_len] = '\0'; dst[src_len] = '\0';
} }
else { else {
memcpy(dst, src, dst_len-1); memcpy(dst, src, dst_len-1);
dst[dst_len-1] = '\0'; /* truncated */ dst[dst_len-1] = '\0'; /* truncated */
return STRCPY_TRUNCATED; return STRCPY_TRUNCATED;
} }
} }
return strlen(dst); return strlen(dst);
} }
...@@ -123,9 +120,9 @@ my_strcpy(char *dst, size_t dst_len, char *src, size_t src_len) ...@@ -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. // the destination string if src has len characters or more.
// instead, I want it to copy up to len-1 characters and always // instead, I want it to copy up to len-1 characters and always
// terminate the destination string. // 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) { if (NULL != dst) {
...@@ -138,7 +135,7 @@ unsigned int i; ...@@ -138,7 +135,7 @@ unsigned int i;
else if (len == SQL_NTS) else if (len == SQL_NTS)
len = strlen(src) + 1; 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]; dst[i] = src[i];
} }
......
/* File: misc.h /* File: misc.h
* *
* Description: See "misc.c" * Description: See "misc.c"
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __MISC_H__ #ifndef __MISC_H__
#define __MISC_H__ #define __MISC_H__
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
/* Uncomment Q_LOG to compile in the qlog() statements (Communications log, i.e. CommLog). /* 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 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 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. which is manipulated on the setup/connection dialog boxes.
*/ */
#define Q_LOG #define Q_LOG
...@@ -42,7 +42,7 @@ void qlog(); /* prototype */ ...@@ -42,7 +42,7 @@ void qlog(); /* prototype */
#endif #endif
void remove_newlines(char *string); 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 *trim(char *string);
char *make_string(char *s, int len, char *buf); char *make_string(char *s, int len, char *buf);
char *my_strcat(char *buf, char *fmt, char *s, int len); 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); ...@@ -53,6 +53,6 @@ char *my_strcat(char *buf, char *fmt, char *s, int len);
#define STRCPY_TRUNCATED -1 #define STRCPY_TRUNCATED -1
#define STRCPY_NULL -2 #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 #endif
/******************************************************************** /********************************************************************
PSQLODBC.DLL - A library to talk to the PostgreSQL DBMS using ODBC. PSQLODBC.DLL - A library to talk to the PostgreSQL DBMS using ODBC.
Copyright (C) 1998; Insight Distribution Systems Copyright (C) 1998; Insight Distribution Systems
The code contained in this library is based on code written by The code contained in this library is based on code written by
Christian Czezatke and Dan McGuirk, (C) 1996. Christian Czezatke and Dan McGuirk, (C) 1996.
This library is free software; you can redistribute it and/or modify This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details. Library General Public License for more details.
You should have received a copy of the GNU Library General Public You should have received a copy of the GNU Library General Public
License along with this library (see "license.txt"); if not, write to License along with this library (see "license.txt"); if not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
02139, USA. 02139, USA.
How to contact the author: How to contact the author:
email: byronn@insightdist.com (Byron Nikolaidis) email: byronn@insightdist.com (Byron Nikolaidis)
***********************************************************************/ ***********************************************************************/
/* Module: options.c /* Module: options.c
* *
* Description: This module contains routines for getting/setting * Description: This module contains routines for getting/setting
* connection and statement options. * connection and statement options.
* *
* Classes: n/a * Classes: n/a
* *
* API functions: SQLSetConnectOption, SQLSetStmtOption, SQLGetConnectOption, * API functions: SQLSetConnectOption, SQLSetStmtOption, SQLGetConnectOption,
* SQLGetStmtOption * SQLGetStmtOption
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "psqlodbc.h" #include "psqlodbc.h"
#include <windows.h> #include <windows.h>
#include <sql.h> #include <sql.h>
#include <sqlext.h>
#include "environ.h" #include "environ.h"
#include "connection.h" #include "connection.h"
#include "statement.h" #include "statement.h"
extern GLOBAL_VALUES globals;
/* Implements only SQL_AUTOCOMMIT */ /* Implements only SQL_AUTOCOMMIT */
RETCODE SQL_API SQLSetConnectOption( RETCODE SQL_API SQLSetConnectOption(
HDBC hdbc, HDBC hdbc,
...@@ -81,92 +86,135 @@ ConnectionClass *conn = (ConnectionClass *) hdbc; ...@@ -81,92 +86,135 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
// - - - - - - - - - // - - - - - - - - -
/* This function just can tell you whether you are in Autcommit mode or not */
RETCODE SQL_API SQLGetConnectOption(
HDBC hdbc,
UWORD fOption,
PTR pvParam)
{
ConnectionClass *conn = (ConnectionClass *) hdbc;
if (! conn)
return SQL_INVALID_HANDLE;
switch (fOption) {
case SQL_AUTOCOMMIT:
*((UDWORD *)pvParam) = (UDWORD)( CC_is_in_autocommit(conn) ?
SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
break;
/* don't use qualifiers */
case SQL_CURRENT_QUALIFIER:
if(pvParam)
strcpy(pvParam, "");
break;
default:
conn->errormsg = "This option is currently unsupported by the driver";
conn->errornumber = CONN_UNSUPPORTED_OPTION;
return SQL_ERROR;
break;
}
return SQL_SUCCESS;
}
// - - - - - - - - -
RETCODE SQL_API SQLSetStmtOption( RETCODE SQL_API SQLSetStmtOption(
HSTMT hstmt, HSTMT hstmt,
UWORD fOption, UWORD fOption,
UDWORD vParam) UDWORD vParam)
{ {
StatementClass *stmt = (StatementClass *) hstmt; StatementClass *stmt = (StatementClass *) hstmt;
char changed = FALSE;
// thought we could fake Access out by just returning SQL_SUCCESS // 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 // 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 // and expects the driver to reduce it to the real value
if( ! stmt) { if( ! stmt)
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
}
switch(fOption) { switch(fOption) {
case SQL_QUERY_TIMEOUT: case SQL_QUERY_TIMEOUT:
mylog("SetStmtOption: vParam = %d\n", vParam); mylog("SetStmtOption: vParam = %d\n", vParam);
/* // "0" returned in SQLGetStmtOption
stmt->errornumber = STMT_OPTION_VALUE_CHANGED; break;
stmt->errormsg = "Query Timeout: value changed to 0";
return SQL_SUCCESS_WITH_INFO; case SQL_MAX_LENGTH:
*/ // "4096" returned in SQLGetStmtOption
return SQL_SUCCESS; break;
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: case SQL_MAX_ROWS:
mylog("SetStmtOption(): SQL_MAX_ROWS = %d, returning success\n", vParam); mylog("SetStmtOption(): SQL_MAX_ROWS = %d, returning success\n", vParam);
stmt->maxRows = vParam; stmt->maxRows = vParam;
return SQL_SUCCESS; return SQL_SUCCESS;
break; break;
default:
return SQL_ERROR;
}
return SQL_SUCCESS; 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;
/* This function just can tell you whether you are in Autcommit mode or not */ break;
RETCODE SQL_API SQLGetConnectOption(
HDBC hdbc, case SQL_CONCURRENCY:
UWORD fOption, // positioned update isn't supported so cursor concurrency is read-only
PTR pvParam) mylog("SetStmtOption(): SQL_CONCURRENCY = %d\n", vParam);
{
ConnectionClass *conn = (ConnectionClass *) hdbc; 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;
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: default:
conn->errormsg = "This option is currently unsupported by the driver"; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
conn->errornumber = CONN_UNSUPPORTED_OPTION; stmt->errormsg = "Driver does not support this statement option";
return SQL_ERROR; return SQL_ERROR;
break; }
}
return SQL_SUCCESS; if (changed) {
stmt->errormsg = "Requested value changed.";
stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
return SQL_SUCCESS_WITH_INFO;
}
else
return SQL_SUCCESS;
} }
// - - - - - - - - - // - - - - - - - - -
RETCODE SQL_API SQLGetStmtOption( RETCODE SQL_API SQLGetStmtOption(
...@@ -180,31 +228,54 @@ StatementClass *stmt = (StatementClass *) hstmt; ...@@ -180,31 +228,54 @@ StatementClass *stmt = (StatementClass *) hstmt;
// all the time, but it tries to set a huge value for SQL_MAX_LENGTH // 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 // and expects the driver to reduce it to the real value
if( ! stmt) { if( ! stmt)
return SQL_INVALID_HANDLE; 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: case SQL_MAX_ROWS:
*((SDWORD *)pvParam) = stmt->maxRows; *((SDWORD *)pvParam) = stmt->maxRows;
mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->maxRows); mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->maxRows);
break;
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; break;
default:
return SQL_ERROR;
}
return SQL_SUCCESS; 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;
} }
// - - - - - - - - - // - - - - - - - - -
/* Module: pgtypes.c /* Module: pgtypes.c
* *
* Description: This module contains routines for getting information * Description: This module contains routines for getting information
* about the supported Postgres data types. Only the function * about the supported Postgres data types. Only the function
* pgtype_to_sqltype() returns an unknown condition. All other * pgtype_to_sqltype() returns an unknown condition. All other
* functions return a suitable default so that even data types that * functions return a suitable default so that even data types that
* are not directly supported can be used (it is handled as char data). * are not directly supported can be used (it is handled as char data).
* *
* Classes: n/a * Classes: n/a
* *
* API functions: none * API functions: none
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "psqlodbc.h" #include "psqlodbc.h"
#include "dlg_specific.h"
#include "pgtypes.h" #include "pgtypes.h"
#include "statement.h"
#include "connection.h"
#include "qresult.h"
#include <windows.h> #include <windows.h>
#include <sql.h> #include <sql.h>
#include <sqlext.h> #include <sqlext.h>
extern GLOBAL_VALUES globals;
/* these are the types we support. all of the pgtype_ functions should */ /* these are the types we support. all of the pgtype_ functions should */
/* return values for each one of these. */ /* return values for each one of these. */
...@@ -32,13 +40,14 @@ Int4 pgtypes_defined[] = { ...@@ -32,13 +40,14 @@ Int4 pgtypes_defined[] = {
PG_TYPE_CHAR2, PG_TYPE_CHAR2,
PG_TYPE_CHAR4, PG_TYPE_CHAR4,
PG_TYPE_CHAR8, PG_TYPE_CHAR8,
PG_TYPE_BPCHAR, PG_TYPE_CHAR16,
PG_TYPE_NAME,
PG_TYPE_VARCHAR, PG_TYPE_VARCHAR,
PG_TYPE_BPCHAR,
PG_TYPE_DATE, PG_TYPE_DATE,
PG_TYPE_TIME, PG_TYPE_TIME,
PG_TYPE_ABSTIME, /* a timestamp, sort of */ PG_TYPE_ABSTIME, /* a timestamp, sort of */
PG_TYPE_TEXT, PG_TYPE_TEXT,
PG_TYPE_NAME,
PG_TYPE_INT2, PG_TYPE_INT2,
PG_TYPE_INT4, PG_TYPE_INT4,
PG_TYPE_FLOAT4, PG_TYPE_FLOAT4,
...@@ -46,9 +55,9 @@ Int4 pgtypes_defined[] = { ...@@ -46,9 +55,9 @@ Int4 pgtypes_defined[] = {
PG_TYPE_OID, PG_TYPE_OID,
PG_TYPE_MONEY, PG_TYPE_MONEY,
PG_TYPE_BOOL, PG_TYPE_BOOL,
PG_TYPE_CHAR16, PG_TYPE_DATETIME,
PG_TYPE_DATETIME, PG_TYPE_BYTEA,
PG_TYPE_BYTEA, PG_TYPE_LO,
0 }; 0 };
...@@ -57,113 +66,197 @@ Int4 pgtypes_defined[] = { ...@@ -57,113 +66,197 @@ Int4 pgtypes_defined[] = {
2. When taking any type id (SQLColumns, SQLGetData) 2. When taking any type id (SQLColumns, SQLGetData)
The first type will always work because all the types defined are returned here. 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 The second type will return a default based on global parameter when it does not
routine checks for this and changes it to a char type. This allows for supporting know. This allows for supporting
types that are unknown. All other pg routines in here return a suitable default. 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) { switch(type) {
case PG_TYPE_CHAR: case PG_TYPE_CHAR:
case PG_TYPE_CHAR2: case PG_TYPE_CHAR2:
case PG_TYPE_CHAR4: case PG_TYPE_CHAR4:
case PG_TYPE_CHAR8: case PG_TYPE_CHAR8:
case PG_TYPE_CHAR16: return SQL_CHAR; case PG_TYPE_CHAR16:
case PG_TYPE_NAME: return SQL_CHAR;
case PG_TYPE_BPCHAR: case PG_TYPE_BPCHAR: return SQL_CHAR; // temporary?
case PG_TYPE_NAME:
case PG_TYPE_VARCHAR: return SQL_VARCHAR;
case PG_TYPE_TEXT: return SQL_LONGVARCHAR; case PG_TYPE_VARCHAR: return SQL_VARCHAR;
case PG_TYPE_BYTEA: return SQL_LONGVARBINARY;
case PG_TYPE_INT2: return SQL_SMALLINT; case PG_TYPE_TEXT: return globals.text_as_longvarchar ? SQL_LONGVARCHAR : SQL_VARCHAR;
case PG_TYPE_OID:
case PG_TYPE_INT4: return SQL_INTEGER; case PG_TYPE_BYTEA: return SQL_VARBINARY;
case PG_TYPE_FLOAT4: return SQL_REAL; case PG_TYPE_LO: return SQL_LONGVARBINARY;
case PG_TYPE_FLOAT8: return SQL_FLOAT;
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_DATE: return SQL_DATE;
case PG_TYPE_TIME: return SQL_TIME; case PG_TYPE_TIME: return SQL_TIME;
case PG_TYPE_ABSTIME: case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME: return SQL_TIMESTAMP; case PG_TYPE_DATETIME: return SQL_TIMESTAMP;
case PG_TYPE_MONEY: return SQL_FLOAT; 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) { switch(type) {
case PG_TYPE_INT2: return SQL_C_SSHORT; case PG_TYPE_INT2: return SQL_C_SSHORT;
case PG_TYPE_OID: case PG_TYPE_OID:
case PG_TYPE_INT4: return SQL_C_SLONG; case PG_TYPE_INT4: return SQL_C_SLONG;
case PG_TYPE_FLOAT4: return SQL_C_FLOAT; case PG_TYPE_FLOAT4: return SQL_C_FLOAT;
case PG_TYPE_FLOAT8: return SQL_C_DOUBLE; case PG_TYPE_FLOAT8: return SQL_C_DOUBLE;
case PG_TYPE_DATE: return SQL_C_DATE; case PG_TYPE_DATE: return SQL_C_DATE;
case PG_TYPE_TIME: return SQL_C_TIME; case PG_TYPE_TIME: return SQL_C_TIME;
case PG_TYPE_ABSTIME: case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME: return SQL_C_TIMESTAMP; case PG_TYPE_DATETIME: return SQL_C_TIMESTAMP;
case PG_TYPE_MONEY: return SQL_C_FLOAT; case PG_TYPE_MONEY: return SQL_C_FLOAT;
case PG_TYPE_BOOL: return SQL_C_CHAR; case PG_TYPE_BOOL: return globals.bools_as_char ? SQL_C_CHAR : SQL_C_BIT;
case PG_TYPE_BYTEA: return SQL_C_BINARY;
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) { switch(type) {
case PG_TYPE_CHAR: return "char"; case PG_TYPE_CHAR: return "char";
case PG_TYPE_CHAR2: return "char2"; case PG_TYPE_CHAR2: return "char2";
case PG_TYPE_CHAR4: return "char4"; 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_CHAR16: return "char16";
case PG_TYPE_VARCHAR: return "varchar"; case PG_TYPE_VARCHAR: return "varchar";
case PG_TYPE_BPCHAR: return "bpchar"; case PG_TYPE_BPCHAR: return "bpchar";
case PG_TYPE_TEXT: return "text"; case PG_TYPE_TEXT: return "text";
case PG_TYPE_NAME: return "name"; case PG_TYPE_NAME: return "name";
case PG_TYPE_INT2: return "int2"; case PG_TYPE_INT2: return "int2";
case PG_TYPE_OID: return "oid"; case PG_TYPE_OID: return "oid";
case PG_TYPE_INT4: return "int4"; case PG_TYPE_INT4: return "int4";
case PG_TYPE_FLOAT4: return "float4"; case PG_TYPE_FLOAT4: return "float4";
case PG_TYPE_FLOAT8: return "float8"; case PG_TYPE_FLOAT8: return "float8";
case PG_TYPE_DATE: return "date"; case PG_TYPE_DATE: return "date";
case PG_TYPE_TIME: return "time"; case PG_TYPE_TIME: return "time";
case PG_TYPE_ABSTIME: return "abstime"; case PG_TYPE_ABSTIME: return "abstime";
case PG_TYPE_DATETIME: return "datetime"; case PG_TYPE_DATETIME: return "datetime";
case PG_TYPE_MONEY: return "money"; case PG_TYPE_MONEY: return "money";
case PG_TYPE_BOOL: return "bool"; case PG_TYPE_BOOL: return "bool";
case PG_TYPE_BYTEA: return "bytea"; case PG_TYPE_BYTEA: return "bytea";
/* "unknown" can actually be used in alter table because it is a real PG type! */ case PG_TYPE_LO: return PG_TYPE_LO_NAME;
default: return "unknown";
} default:
if (type == stmt->hdbc->lobj_type) /* hack until permanent type is available */
return PG_TYPE_LO_NAME;
/* "unknown" can actually be used in alter table because it is a real PG type! */
return "unknown";
}
}
Int4
getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
{
int p = -1, maxsize;
QResultClass *result;
ColumnInfoClass *flds;
mylog("getCharPrecision: type=%d, col=%d, unknown = %d\n", type,col,handle_unknown_size_as);
/* Assign Maximum size based on parameters */
switch(type) {
case PG_TYPE_TEXT:
if (globals.text_as_longvarchar)
maxsize = globals.max_longvarchar_size;
else
maxsize = globals.max_varchar_size;
break;
case PG_TYPE_VARCHAR:
case PG_TYPE_BPCHAR:
maxsize = globals.max_varchar_size;
break;
default:
if (globals.unknowns_as_longvarchar)
maxsize = globals.max_longvarchar_size;
else
maxsize = globals.max_varchar_size;
break;
}
/* Static Precision (i.e., the Maximum Precision of the datatype)
This has nothing to do with a result set.
*/
if (col < 0)
return maxsize;
result = SC_get_Result(stmt);
/* Manual Result Sets -- use assigned column width (i.e., from set_tuplefield_string) */
if (stmt->manual_result) {
flds = result->fields;
if (flds)
return flds->adtsize[col];
else
return maxsize;
}
/* Size is unknown -- handle according to parameter */
if (type == PG_TYPE_BPCHAR || handle_unknown_size_as == UNKNOWNS_AS_LONGEST) {
p = QR_get_display_size(result, col);
mylog("getCharPrecision: LONGEST: p = %d\n", p);
}
if (p < 0 && handle_unknown_size_as == UNKNOWNS_AS_MAX)
return maxsize;
else
return p;
} }
/* For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, SQLColumns will /* For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, SQLColumns will
override this length with the atttypmod length from pg_attribute override this length with the atttypmod length from pg_attribute .
If col >= 0, then will attempt to get the info from the result set.
This is used for functions SQLDescribeCol and SQLColAttributes.
*/ */
Int4 pgtype_precision(Int4 type) Int4 pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
{ {
switch(type) { switch(type) {
case PG_TYPE_CHAR: return 1; case PG_TYPE_CHAR: return 1;
case PG_TYPE_CHAR2: return 2; case PG_TYPE_CHAR2: return 2;
case PG_TYPE_CHAR4: return 4; case PG_TYPE_CHAR4: return 4;
case PG_TYPE_CHAR8: return 8; case PG_TYPE_CHAR8: return 8;
case PG_TYPE_CHAR16: return 16; case PG_TYPE_CHAR16: return 16;
case PG_TYPE_NAME: return NAME_FIELD_SIZE;
case PG_TYPE_NAME: return 32; case PG_TYPE_INT2: return 5;
case PG_TYPE_VARCHAR:
case PG_TYPE_BPCHAR: return MAX_VARCHAR_SIZE;
case PG_TYPE_INT2: return 5;
case PG_TYPE_OID: case PG_TYPE_OID:
case PG_TYPE_INT4: return 10; case PG_TYPE_INT4: return 10;
...@@ -180,29 +273,46 @@ Int4 pgtype_precision(Int4 type) ...@@ -180,29 +273,46 @@ Int4 pgtype_precision(Int4 type)
case PG_TYPE_BOOL: return 1; case PG_TYPE_BOOL: return 1;
case PG_TYPE_LO: return SQL_NO_TOTAL;
default: 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 Int4 pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
override this length with the atttypmod length from pg_attribute {
*/ switch(type) {
Int4 pgtype_length(Int4 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) { 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_INT2: return 2;
case PG_TYPE_OID: case PG_TYPE_OID:
...@@ -219,14 +329,14 @@ Int4 pgtype_length(Int4 type) ...@@ -219,14 +329,14 @@ Int4 pgtype_length(Int4 type)
case PG_TYPE_ABSTIME: case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME: return 16; case PG_TYPE_DATETIME: return 16;
case PG_TYPE_BOOL: return 1;
default: /* Character types use the default precision */
return TEXT_FIELD_SIZE; /* text field types and unknown types */ 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) { switch(type) {
...@@ -247,7 +357,7 @@ Int2 pgtype_scale(Int4 type) ...@@ -247,7 +357,7 @@ Int2 pgtype_scale(Int4 type)
} }
Int2 pgtype_radix(Int4 type) Int2 pgtype_radix(StatementClass *stmt, Int4 type)
{ {
switch(type) { switch(type) {
case PG_TYPE_INT2: case PG_TYPE_INT2:
...@@ -261,12 +371,12 @@ Int2 pgtype_radix(Int4 type) ...@@ -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 */ return SQL_NULLABLE; /* everything should be nullable */
} }
Int2 pgtype_auto_increment(Int4 type) Int2 pgtype_auto_increment(StatementClass *stmt, Int4 type)
{ {
switch(type) { switch(type) {
...@@ -287,7 +397,7 @@ Int2 pgtype_auto_increment(Int4 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) { switch(type) {
case PG_TYPE_CHAR: case PG_TYPE_CHAR:
...@@ -306,7 +416,7 @@ Int2 pgtype_case_sensitive(Int4 type) ...@@ -306,7 +416,7 @@ Int2 pgtype_case_sensitive(Int4 type)
} }
} }
Int2 pgtype_money(Int4 type) Int2 pgtype_money(StatementClass *stmt, Int4 type)
{ {
switch(type) { switch(type) {
case PG_TYPE_MONEY: return TRUE; case PG_TYPE_MONEY: return TRUE;
...@@ -314,7 +424,7 @@ Int2 pgtype_money(Int4 type) ...@@ -314,7 +424,7 @@ Int2 pgtype_money(Int4 type)
} }
} }
Int2 pgtype_searchable(Int4 type) Int2 pgtype_searchable(StatementClass *stmt, Int4 type)
{ {
switch(type) { switch(type) {
case PG_TYPE_CHAR: case PG_TYPE_CHAR:
...@@ -329,11 +439,10 @@ Int2 pgtype_searchable(Int4 type) ...@@ -329,11 +439,10 @@ Int2 pgtype_searchable(Int4 type)
case PG_TYPE_NAME: return SQL_SEARCHABLE; case PG_TYPE_NAME: return SQL_SEARCHABLE;
default: return SQL_ALL_EXCEPT_LIKE; default: return SQL_ALL_EXCEPT_LIKE;
} }
} }
Int2 pgtype_unsigned(Int4 type) Int2 pgtype_unsigned(StatementClass *stmt, Int4 type)
{ {
switch(type) { switch(type) {
case PG_TYPE_OID: return TRUE; case PG_TYPE_OID: return TRUE;
...@@ -348,7 +457,7 @@ Int2 pgtype_unsigned(Int4 type) ...@@ -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) { switch(type) {
...@@ -363,7 +472,7 @@ char *pgtype_literal_prefix(Int4 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) { switch(type) {
...@@ -378,7 +487,7 @@ char *pgtype_literal_suffix(Int4 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) { switch(type) {
case PG_TYPE_CHAR: case PG_TYPE_CHAR:
......
/* File: pgtypes.h /* File: pgtypes.h
* *
* Description: See "pgtypes.c" * Description: See "pgtypes.c"
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __PGTYPES_H__ #ifndef __PGTYPES_H__
#define __PGTYPES_H__ #define __PGTYPES_H__
#include "psqlodbc.h"
/* the type numbers are defined by the OID's of the types' rows */ /* the type numbers are defined by the OID's of the types' rows */
/* in table pg_type */ /* 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_BOOL 16
#define PG_TYPE_BYTEA 17 #define PG_TYPE_BYTEA 17
...@@ -58,22 +61,30 @@ ...@@ -58,22 +61,30 @@
extern Int4 pgtypes_defined[]; extern Int4 pgtypes_defined[];
Int2 pgtype_to_sqltype(Int4 type); /* Defines for pgtype_precision */
Int2 pgtype_to_ctype(Int4 type); #define PG_STATIC -1
char *pgtype_to_name(Int4 type);
Int4 pgtype_precision(Int4 type);
Int4 pgtype_length(Int4 type); Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type);
Int2 pgtype_scale(Int4 type); Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type);
Int2 pgtype_radix(Int4 type); char *pgtype_to_name(StatementClass *stmt, Int4 type);
Int2 pgtype_nullable(Int4 type);
Int2 pgtype_auto_increment(Int4 type); /* These functions can use static numbers or result sets(col parameter) */
Int2 pgtype_case_sensitive(Int4 type); Int4 pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as);
Int2 pgtype_money(Int4 type); Int4 pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as);
Int2 pgtype_searchable(Int4 type); Int4 pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as);
Int2 pgtype_unsigned(Int4 type);
char *pgtype_literal_prefix(Int4 type); Int2 pgtype_scale(StatementClass *stmt, Int4 type);
char *pgtype_literal_suffix(Int4 type); Int2 pgtype_radix(StatementClass *stmt, Int4 type);
char *pgtype_create_params(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); Int2 sqltype_to_default_ctype(Int2 sqltype);
......
/* Module: psqlodbc.c /* Module: psqlodbc.c
* *
* Description: This module contains the main entry point (DllMain) for the library. * Description: This module contains the main entry point (DllMain) for the library.
* It also contains functions to get and set global variables for the * It also contains functions to get and set global variables for the
* driver in the registry. * driver in the registry.
* *
* Classes: n/a * Classes: n/a
* *
* API functions: none * API functions: none
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "psqlodbc.h" #include "psqlodbc.h"
#include "dlg_specific.h"
#include <winsock.h> #include <winsock.h>
#include <windows.h> #include <windows.h>
#include <sql.h> #include <sql.h>
#include <odbcinst.h> #include <odbcinst.h>
HINSTANCE NEAR s_hModule; /* Saved module handle. */ 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 */ /* This is where the Driver Manager attaches to this Driver */
BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
......
/* File: psqlodbc.h /* File: psqlodbc.h
* *
* Description: This file contains defines and declarations that are related to * Description: This file contains defines and declarations that are related to
* the entire driver. * the entire driver.
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __PSQLODBC_H__ #ifndef __PSQLODBC_H__
#define __PSQLODBC_H__ #define __PSQLODBC_H__
...@@ -21,27 +21,23 @@ typedef UInt4 Oid; ...@@ -21,27 +21,23 @@ typedef UInt4 Oid;
/* Limits */ /* Limits */
#define MAX_MESSAGE_LEN 8192 #define MAX_MESSAGE_LEN 8192
#define MAX_CONNECT_STRING 4096 #define MAX_CONNECT_STRING 4096
#define ERROR_MSG_LENGTH 4096 #define ERROR_MSG_LENGTH 4096
#define FETCH_MAX 100 /* default number of rows to cache for declare/fetch */ #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_CONNECTIONS 128 /* conns per environment (arbitrary) */
#define MAX_FIELDS 512 #define MAX_FIELDS 512
#define BYTELEN 8 #define BYTELEN 8
#define VARHDRSZ sizeof(Int4) #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 */
/* Registry length limits */
#define LARGE_REGISTRY_LEN 4096 /* used for special cases */
#define MEDIUM_REGISTRY_LEN 256 /* normal size for user,database,etc. */
#define SMALL_REGISTRY_LEN 10 /* for 1/0 settings */
/* Connection Defaults */
#define DEFAULT_PORT "5432"
#define DEFAULT_READONLY "1"
/* These prefixes denote system tables */ /* These prefixes denote system tables */
#define INSIGHT_SYS_PREFIX "dd_"
#define POSTGRES_SYS_PREFIX "pg_" #define POSTGRES_SYS_PREFIX "pg_"
#define KEYS_TABLE "dd_fkey" #define KEYS_TABLE "dd_fkey"
...@@ -54,33 +50,13 @@ typedef UInt4 Oid; ...@@ -54,33 +50,13 @@ typedef UInt4 Oid;
/* Driver stuff */ /* Driver stuff */
#define DRIVERNAME "PostgreSQL ODBC" #define DRIVERNAME "PostgreSQL ODBC"
#define DBMS_NAME "PostgreSQL" #define DBMS_NAME "PostgreSQL"
#define DBMS_VERSION "06.30.0000 PostgreSQL 6.3" #define DBMS_VERSION "06.30.0244 PostgreSQL 6.3"
#define POSTGRESDRIVERVERSION "06.30.0000" #define POSTGRESDRIVERVERSION "06.30.0244"
#define DRIVER_FILE_NAME "PSQLODBC.DLL" #define DRIVER_FILE_NAME "PSQLODBC.DLL"
#define PG62 "6.2" /* "Protocol" key setting to force Postgres 6.2 */ #define PG62 "6.2" /* "Protocol" key setting to force Postgres 6.2 */
/* INI File Stuff */
#define ODBC_INI "ODBC.INI" /* ODBC initialization file */
#define ODBCINST_INI "ODBCINST.INI" /* ODBC Installation file */
#define INI_DSN DBMS_NAME /* Name of default Datasource in ini file (not used?) */
#define INI_KDESC "Description" /* Data source description */
#define INI_SERVER "Servername" /* Name of Server running the Postgres service */
#define INI_PORT "Port" /* Port on which the Postmaster is listening */
#define INI_DATABASE "Database" /* Database Name */
#define INI_USER "Username" /* Default User Name */
#define INI_PASSWORD "Password" /* Default Password */
#define INI_DEBUG "Debug" /* Debug flag */
#define INI_FETCH "Fetch" /* Fetch Max Count */
#define INI_SOCKET "Socket" /* Socket buffer size */
#define INI_READONLY "ReadOnly" /* Database is read only */
#define INI_COMMLOG "CommLog" /* Communication to backend logging */
#define INI_PROTOCOL "Protocol" /* What protocol (6.2) */
#define INI_OPTIMIZER "Optimizer" /* Use backend genetic optimizer */
#define INI_CONNSETTINGS "ConnSettings" /* Anything to send to backend on successful connection */
typedef struct ConnectionClass_ ConnectionClass; typedef struct ConnectionClass_ ConnectionClass;
typedef struct StatementClass_ StatementClass; typedef struct StatementClass_ StatementClass;
...@@ -93,27 +69,40 @@ typedef struct TupleListClass_ TupleListClass; ...@@ -93,27 +69,40 @@ typedef struct TupleListClass_ TupleListClass;
typedef struct EnvironmentClass_ EnvironmentClass; typedef struct EnvironmentClass_ EnvironmentClass;
typedef struct TupleNode_ TupleNode; typedef struct TupleNode_ TupleNode;
typedef struct TupleField_ TupleField; typedef struct TupleField_ TupleField;
typedef struct lo_arg LO_ARG;
typedef struct GlobalValues_
{ typedef struct GlobalValues_
int fetch_max; {
int socket_buffersize; int fetch_max;
int debug; int socket_buffersize;
int commlog; int unknown_sizes;
char optimizer[MEDIUM_REGISTRY_LEN]; int max_varchar_size;
char conn_settings[LARGE_REGISTRY_LEN]; int max_longvarchar_size;
} GLOBAL_VALUES; 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 */ /* sizes */
#define TEXT_FIELD_SIZE 4094 /* size of text fields (not including null term) */ #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) */ #define NAME_FIELD_SIZE 32 /* size of name fields */
#define MAX_VARCHAR_SIZE 254 /* maximum size of a varchar (not including null term) */
/* global prototypes */
void updateGlobals(void);
#include "misc.h" #include "misc.h"
......
//Microsoft Developer Studio generated resource script. //Microsoft Developer Studio generated resource script.
// //
#include "resource.h" #include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS #define APSTUDIO_READONLY_SYMBOLS
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //
// Generated from the TEXTINCLUDE 2 resource. // Generated from the TEXTINCLUDE 2 resource.
// //
#include "afxres.h" #include "afxres.h"
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS #undef APSTUDIO_READONLY_SYMBOLS
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources // English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32 #ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252) #pragma code_page(1252)
#endif //_WIN32 #endif //_WIN32
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //
// TEXTINCLUDE // TEXTINCLUDE
// //
1 TEXTINCLUDE DISCARDABLE 1 TEXTINCLUDE DISCARDABLE
BEGIN BEGIN
"resource.h\0" "resource.h\0"
END END
2 TEXTINCLUDE DISCARDABLE 2 TEXTINCLUDE DISCARDABLE
BEGIN BEGIN
"#include ""afxres.h""\r\n" "#include ""afxres.h""\r\n"
"\0" "\0"
END END
3 TEXTINCLUDE DISCARDABLE 3 TEXTINCLUDE DISCARDABLE
BEGIN BEGIN
"\r\n" "\r\n"
"\0" "\0"
END END
#endif // APSTUDIO_INVOKED #endif // APSTUDIO_INVOKED
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //
// Dialog // Dialog
// //
DRIVERCONNDIALOG DIALOG DISCARDABLE 0, 0, 269, 133 DLG_CONFIG DIALOG DISCARDABLE 65, 43, 292, 116
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
CAPTION "PostgreSQL Connection" WS_SYSMENU
FONT 8, "MS Sans Serif" CAPTION "PostgreSQL Driver Setup"
BEGIN FONT 8, "MS Sans Serif"
RTEXT "&Database:",IDC_STATIC,16,25,37,8 BEGIN
EDITTEXT DATABASE_EDIT,55,25,72,12,ES_AUTOHSCROLL RTEXT "&Data Source:",IDC_DSNAMETEXT,5,10,50,12,NOT WS_GROUP
RTEXT "&Server:",IDC_STATIC,26,40,27,8 EDITTEXT IDC_DSNAME,57,10,72,12,ES_AUTOHSCROLL | WS_GROUP
EDITTEXT SERVER_EDIT,55,40,72,12,ES_AUTOHSCROLL RTEXT "Des&cription:",IDC_DESCTEXT,135,10,39,12,NOT WS_GROUP
RTEXT "&Port:",IDC_STATIC,150,40,20,8 EDITTEXT IDC_DESC,175,10,108,12,ES_AUTOHSCROLL
EDITTEXT PORT_EDIT,172,40,72,12,ES_AUTOHSCROLL RTEXT "Data&base:",IDC_STATIC,17,25,38,12,NOT WS_GROUP
RTEXT "&User Name:",IDC_STATIC,16,56,37,8 EDITTEXT IDC_DATABASE,57,25,72,12,ES_AUTOHSCROLL
EDITTEXT USERNAME_EDIT,55,56,72,12,ES_AUTOHSCROLL RTEXT "&Server:",IDC_STATIC,27,40,29,12,NOT WS_GROUP
RTEXT "Pass&word:",IDC_STATIC,137,56,33,8 EDITTEXT IDC_SERVER,57,40,72,12,ES_AUTOHSCROLL
EDITTEXT PASSWORD_EDIT,172,56,72,12,ES_PASSWORD | ES_AUTOHSCROLL RTEXT "&Port:",IDC_STATIC,153,40,22,12
GROUPBOX "Options:",IDC_STATIC,25,71,200,25 EDITTEXT IDC_PORT,175,40,37,12,ES_AUTOHSCROLL
CONTROL "&ReadOnly:",READONLY_EDIT,"Button",BS_AUTOCHECKBOX | RTEXT "&User Name:",IDC_STATIC,17,55,39,12
BS_LEFTTEXT | BS_RIGHT | WS_GROUP | WS_TABSTOP,45,80,45, EDITTEXT IDC_USER,57,55,72,12,ES_AUTOHSCROLL
14 RTEXT "Pass&word:",IDC_STATIC,141,55,34,12
CONTROL "&CommLog (Global):",COMMLOG_EDIT,"Button", EDITTEXT IDC_PASSWORD,175,55,72,12,ES_PASSWORD | ES_AUTOHSCROLL
BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_RIGHT | WS_TABSTOP, DEFPUSHBUTTON "OK",IDOK,25,90,40,14,WS_GROUP
100,80,75,14 PUSHBUTTON "Cancel",IDCANCEL,80,90,40,14
CONTROL "6.2",PG62_EDIT,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | GROUPBOX "Options (Advanced):",IDC_STATIC,140,74,140,35,BS_CENTER
BS_RIGHT | WS_TABSTOP,185,80,25,14 PUSHBUTTON "Driver",IDC_DRIVER,160,90,50,14
DEFPUSHBUTTON "OK",IDOK,84,108,40,14,WS_GROUP PUSHBUTTON "DataSource",IDC_DATASOURCE,220,90,50,14
PUSHBUTTON "Cancel",IDCANCEL,146,108,40,14 CTEXT "Please supply any missing information needed to connect.",
CTEXT "Please supply any missing information needed to connect.", DRV_MSG_LABEL,36,5,220,15
IDC_STATIC,40,7,188,11 END
END
DLG_OPTIONS_DRV DIALOG DISCARDABLE 0, 0, 287, 226
CONFIGDSN DIALOG DISCARDABLE 65, 43, 292, 151 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | CAPTION "Advanced Options (Driver)"
WS_SYSMENU FONT 8, "MS Sans Serif"
CAPTION "PostgreSQL Driver Setup" BEGIN
FONT 8, "MS Sans Serif" CONTROL "Disable Genetic &Optimizer",DRV_OPTIMIZER,"Button",
BEGIN BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,15,10,97,10
RTEXT "&Data Source:",IDC_DSNAMETEXT,5,30,50,12,NOT WS_GROUP CONTROL "Comm&Log (C:\\psqlodbc.log)",DRV_COMMLOG,"Button",
EDITTEXT IDC_DSNAME,57,30,72,12,ES_AUTOHSCROLL | WS_GROUP BS_AUTOCHECKBOX | WS_TABSTOP,140,10,113,10
RTEXT "Des&cription:",IDC_STATIC,135,30,39,12,NOT WS_GROUP CONTROL "Recognize Unique &Indexes",DRV_UNIQUEINDEX,"Button",
EDITTEXT IDC_DESC,175,30,108,12,ES_AUTOHSCROLL BS_AUTOCHECKBOX | WS_TABSTOP,15,25,101,10
RTEXT "Data&base:",IDC_STATIC,17,45,38,12,NOT WS_GROUP CONTROL "&ReadOnly (Default)",DRV_READONLY,"Button",
EDITTEXT IDC_DATABASE,57,45,72,12,ES_AUTOHSCROLL BS_AUTOCHECKBOX | WS_TABSTOP,140,25,80,10
RTEXT "&Server:",IDC_STATIC,27,60,29,12,NOT WS_GROUP CONTROL "&Use Declare/Fetch",DRV_USEDECLAREFETCH,"Button",
EDITTEXT IDC_SERVER,57,60,72,12,ES_AUTOHSCROLL BS_AUTOCHECKBOX | WS_TABSTOP,15,40,80,10
RTEXT "&Port:",IDC_STATIC,153,60,22,12 GROUPBOX "Unknown Sizes",IDC_STATIC,10,55,175,25
EDITTEXT IDC_PORT,175,60,37,12,ES_AUTOHSCROLL CONTROL "Maximum",DRV_UNKNOWN_MAX,"Button",BS_AUTORADIOBUTTON |
RTEXT "&User Name:",IDC_STATIC,17,75,39,12 WS_GROUP | WS_TABSTOP,15,65,45,10
EDITTEXT IDC_USER,57,75,72,12,ES_AUTOHSCROLL CONTROL "Don't Know",DRV_UNKNOWN_DONTKNOW,"Button",
RTEXT "Pass&word:",IDC_STATIC,141,75,34,12 BS_AUTORADIOBUTTON | WS_TABSTOP,70,65,53,10
EDITTEXT IDC_PASSWORD,175,75,72,12,ES_PASSWORD | ES_AUTOHSCROLL CONTROL "Longest",DRV_UNKNOWN_LONGEST,"Button",
GROUPBOX "Options:",IDC_STATIC,35,92,205,25 BS_AUTORADIOBUTTON | WS_TABSTOP,130,65,50,10
CONTROL "&ReadOnly:",IDC_READONLY,"Button",BS_AUTOCHECKBOX | GROUPBOX "Data Type Options",IDC_STATIC,10,85,270,25
BS_LEFTTEXT | BS_RIGHT | WS_GROUP | WS_TABSTOP,50,100,45, CONTROL "Text as LongVarChar",DRV_TEXT_LONGVARCHAR,"Button",
14 BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,15,95,80,10
CONTROL "&CommLog (Global):",IDC_COMMLOG,"Button", CONTROL "Unknowns as LongVarChar",DRV_UNKNOWNS_LONGVARCHAR,
BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_RIGHT | WS_TABSTOP, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,105,95,100,10
105,100,75,14 CONTROL "Bools as Char",DRV_BOOLS_CHAR,"Button",BS_AUTOCHECKBOX |
CONTROL "6.2",IDC_PG62,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,215,95,60,10
BS_RIGHT | WS_TABSTOP,195,100,25,14 LTEXT "&Cache Size:",IDC_STATIC,10,120,40,10
DEFPUSHBUTTON "OK",IDOK,85,129,40,14,WS_GROUP EDITTEXT DRV_CACHE_SIZE,50,120,35,12,ES_AUTOHSCROLL
PUSHBUTTON "Cancel",IDCANCEL,145,129,40,14 LTEXT "Max &Varchar:",IDC_STATIC,90,120,45,10
CTEXT "Change data source name, description, or options. Then choose OK.", EDITTEXT DRV_VARCHAR_SIZE,135,120,35,12,ES_AUTOHSCROLL
IDC_STATIC,44,5,180,17 LTEXT "Max Lon&gVarChar:",IDC_STATIC,180,120,60,10
END 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 |
// DESIGNINFO ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
// DEFPUSHBUTTON "OK",IDOK,45,205,50,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,115,205,50,14
#ifdef APSTUDIO_INVOKED PUSHBUTTON "Defaults",IDDEFAULTS,185,205,50,15
GUIDELINES DESIGNINFO DISCARDABLE END
BEGIN
DRIVERCONNDIALOG, DIALOG DLG_OPTIONS_DS DIALOG DISCARDABLE 0, 0, 267, 170
BEGIN STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
RIGHTMARGIN, 268 CAPTION "Advanced Options (DataSource)"
END FONT 8, "MS Sans Serif"
END BEGIN
#endif // APSTUDIO_INVOKED CONTROL "&ReadOnly",DS_READONLY,"Button",BS_AUTOCHECKBOX |
WS_GROUP | WS_TABSTOP,25,10,45,15
CONTROL "&6.2 Protocol",DS_PG62,"Button",BS_AUTOCHECKBOX |
#ifndef _MAC WS_TABSTOP,130,10,60,14
///////////////////////////////////////////////////////////////////////////// CONTROL "Show System &Tables",DS_SHOWSYSTEMTABLES,"Button",
// BS_AUTOCHECKBOX | WS_TABSTOP,25,30,85,10
// Version 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
VS_VERSION_INFO VERSIONINFO CONTROL "Fake &Index",DS_FAKEOIDINDEX,"Button",BS_AUTOCHECKBOX |
FILEVERSION 6,30,0,0 WS_GROUP | WS_TABSTOP,115,60,51,10
PRODUCTVERSION 6,30,0,0 RTEXT "Connect &Settings:",IDC_STATIC,10,90,35,25
FILEFLAGSMASK 0x3L EDITTEXT DS_CONNSETTINGS,50,85,200,35,ES_MULTILINE |
#ifdef _DEBUG ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
FILEFLAGS 0x1L DEFPUSHBUTTON "OK",IDOK,65,130,50,14,WS_GROUP
#else PUSHBUTTON "Cancel",IDCANCEL,140,130,50,14
FILEFLAGS 0x0L GROUPBOX "Unknown Sizes",IDC_STATIC,10,145,175,25,NOT WS_VISIBLE
#endif CONTROL "Maximum",DS_UNKNOWN_MAX,"Button",BS_AUTORADIOBUTTON |
FILEOS 0x4L NOT WS_VISIBLE | WS_GROUP | WS_TABSTOP,15,155,45,10
FILETYPE 0x2L CONTROL "Don't Know",DS_UNKNOWN_DONTKNOW,"Button",
FILESUBTYPE 0x0L BS_AUTORADIOBUTTON | NOT WS_VISIBLE | WS_TABSTOP,70,155,
BEGIN 53,10
BLOCK "StringFileInfo" CONTROL "Longest",DS_UNKNOWN_LONGEST,"Button",BS_AUTORADIOBUTTON |
BEGIN NOT WS_VISIBLE | WS_TABSTOP,130,155,50,10
BLOCK "040904e4" END
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" // DESIGNINFO
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" #ifdef APSTUDIO_INVOKED
VALUE "ProductName", "Microsoft Open Database Connectivity\0" GUIDELINES DESIGNINFO DISCARDABLE
VALUE "ProductVersion", " 6.30.0000\0" BEGIN
END DLG_CONFIG, DIALOG
END BEGIN
BLOCK "VarFileInfo" BOTTOMMARGIN, 115
BEGIN END
VALUE "Translation", 0x409, 1252
END DLG_OPTIONS_DRV, DIALOG
END BEGIN
LEFTMARGIN, 7
#endif // !_MAC RIGHTMARGIN, 280
TOPMARGIN, 7
BOTTOMMARGIN, 219
///////////////////////////////////////////////////////////////////////////// END
//
// String Table DLG_OPTIONS_DS, DIALOG
// BEGIN
LEFTMARGIN, 5
STRINGTABLE DISCARDABLE RIGHTMARGIN, 260
BEGIN VERTGUIDE, 55
IDS_BADDSN "Invalid DSN entry, please recheck." TOPMARGIN, 7
IDS_MSGTITLE "Invalid DSN" BOTTOMMARGIN, 163
END END
END
#endif // English (U.S.) resources #endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
#ifndef _MAC
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED //
///////////////////////////////////////////////////////////////////////////// // Version
// //
// Generated from the TEXTINCLUDE 3 resource.
// VS_VERSION_INFO VERSIONINFO
FILEVERSION 6,30,2,44
PRODUCTVERSION 6,30,2,44
///////////////////////////////////////////////////////////////////////////// FILEFLAGSMASK 0x3L
#endif // not APSTUDIO_INVOKED #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
/* Module: qresult.c /* Module: qresult.c
* *
* Description: This module contains functions related to * Description: This module contains functions related to
* managing result information (i.e, fetching rows from the backend, * managing result information (i.e, fetching rows from the backend,
* managing the tuple cache, etc.) and retrieving it. * managing the tuple cache, etc.) and retrieving it.
* Depending on the situation, a QResultClass will hold either data * Depending on the situation, a QResultClass will hold either data
* from the backend or a manually built result (see "qresult.h" to * from the backend or a manually built result (see "qresult.h" to
* see which functions/macros are for manual or backend results. * see which functions/macros are for manual or backend results.
* For manually built results, the QResultClass simply points to * For manually built results, the QResultClass simply points to
* TupleList and ColumnInfo structures, which actually hold the data. * TupleList and ColumnInfo structures, which actually hold the data.
* *
* Classes: QResultClass (Functions prefix: "QR_") * Classes: QResultClass (Functions prefix: "QR_")
* *
* API functions: none * API functions: none
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "qresult.h" #include "qresult.h"
#include "misc.h" #include "misc.h"
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
extern GLOBAL_VALUES globals; 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. */ /* All info functions call this function to create the manual result set. */
void void
QR_set_num_fields(QResultClass *self, int new_num_fields) QR_set_num_fields(QResultClass *self, int new_num_fields)
...@@ -84,7 +84,7 @@ QR_Destructor(QResultClass *self) ...@@ -84,7 +84,7 @@ QR_Destructor(QResultClass *self)
mylog("QResult: in DESTRUCTOR\n"); mylog("QResult: in DESTRUCTOR\n");
/* manual result set tuples */ /* manual result set tuples */
if (self->manual_tuples) if (self->manual_tuples)
TL_Destructor(self->manual_tuples); TL_Destructor(self->manual_tuples);
// If conn is defined, then we may have used "backend_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) ...@@ -183,12 +183,14 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
if (self->cursor) if (self->cursor)
free(self->cursor); free(self->cursor);
if ( ! cursor || cursor[0] == '\0') { if ( globals.use_declarefetch) {
self->status = PGRES_INTERNAL_ERROR; if (! cursor || cursor[0] == '\0') {
QR_set_message(self, "Internal Error -- no cursor for fetch"); self->status = PGRES_INTERNAL_ERROR;
return FALSE; QR_set_message(self, "Internal Error -- no cursor for fetch");
return FALSE;
}
self->cursor = strdup(cursor);
} }
self->cursor = strdup(cursor);
// Read the field attributes. // Read the field attributes.
// $$$$ Should do some error control HERE! $$$$ // $$$$ Should do some error control HERE! $$$$
...@@ -205,6 +207,7 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor) ...@@ -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); mylog("QR_fetch_tuples: past CI_read_fields: num_fields = %d\n", self->num_fields);
/* allocate memory for the tuple cache */ /* 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); self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * globals.fetch_max);
if ( ! self->backend_tuples) { if ( ! self->backend_tuples) {
self->status = PGRES_FATAL_ERROR; self->status = PGRES_FATAL_ERROR;
...@@ -232,24 +235,23 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor) ...@@ -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. // We only close cursor/end the transaction if a cursor was used.
int int
QR_close(QResultClass *self) QR_close(QResultClass *self)
{ {
QResultClass *res; QResultClass *res;
if (self->conn && self->cursor) { if (globals.use_declarefetch && self->conn && self->cursor) {
char buf[64]; char buf[64];
sprintf(buf, "close %s; END", self->cursor); sprintf(buf, "close %s", self->cursor);
mylog("QResult: closing cursor: '%s'\n", buf); mylog("QResult: closing cursor: '%s'\n", buf);
res = CC_send_query(self->conn, buf, NULL, NULL); res = CC_send_query(self->conn, buf, NULL, NULL);
CC_set_no_trans(self->conn);
self->inTuples = FALSE; self->inTuples = FALSE;
free(self->cursor); free(self->cursor);
self->cursor = NULL; self->cursor = NULL;
...@@ -259,6 +261,21 @@ QResultClass *res; ...@@ -259,6 +261,21 @@ QResultClass *res;
return FALSE; 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; return TRUE;
...@@ -301,6 +318,13 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont ...@@ -301,6 +318,13 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
if ( ! self->inTuples) { if ( ! self->inTuples) {
char fetch[128]; 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); 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); 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 ...@@ -336,65 +360,81 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
id = SOCK_get_char(sock); id = SOCK_get_char(sock);
switch (id) { switch (id) {
case 'T': /* Tuples within tuples cannot be handled */ case 'T': /* Tuples within tuples cannot be handled */
self->status = PGRES_BAD_RESPONSE; self->status = PGRES_BAD_RESPONSE;
QR_set_message(self, "Tuples within tuples cannot be handled"); QR_set_message(self, "Tuples within tuples cannot be handled");
return FALSE; return FALSE;
case 'B': /* Tuples in binary format */ case 'B': /* Tuples in binary format */
case 'D': /* Tuples in ASCII format */ case 'D': /* Tuples in ASCII format */
if ( ! QR_read_tuple(self, (char) (id == 0))) {
self->status = PGRES_BAD_RESPONSE; if ( ! globals.use_declarefetch && self->fcount > 0 && ! (self->fcount % globals.fetch_max)) {
QR_set_message(self, "Error reading the tuple"); size_t old_size = self->fcount * self->num_fields * sizeof(TupleField);
return FALSE; mylog("REALLOC: old_size = %d\n", old_size);
}
self->backend_tuples = (TupleField *) realloc(self->backend_tuples, old_size + (self->num_fields * sizeof(TupleField) * globals.fetch_max));
self->fcount++; if ( ! self->backend_tuples) {
break; // continue reading self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Out of memory while reading tuples.");
return FALSE;
case 'C': /* End of tuple list */
SOCK_get_string(sock, cmdbuffer, MAX_MESSAGE_LEN);
QR_set_command(self, cmdbuffer);
mylog("end of tuple list -- setting inUse to false: this = %u\n", self);
self->inTuples = FALSE;
if (self->fcount > 0) {
qlog(" [ fetched %d rows ]\n", self->fcount);
mylog("_next_tuple: 'C' fetch_max && fcount = %d\n", self->fcount);
/* set to first row */
self->tupleField = 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 */ if ( ! QR_read_tuple(self, (char) (id == 0))) {
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); self->status = PGRES_BAD_RESPONSE;
QR_set_message(self, msgbuffer); QR_set_message(self, "Error reading the tuple");
self->status = PGRES_FATAL_ERROR; return FALSE;
CC_set_no_trans(self->conn); }
qlog("ERROR from backend in next_tuple: '%s'\n", msgbuffer);
self->fcount++;
return FALSE; break; // continue reading
case 'N': /* Notice */
SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); case 'C': /* End of tuple list */
QR_set_message(self, msgbuffer); SOCK_get_string(sock, cmdbuffer, MAX_MESSAGE_LEN);
self->status = PGRES_NONFATAL_ERROR; QR_set_command(self, cmdbuffer);
qlog("NOTICE from backend in next_tuple: '%s'\n", msgbuffer);
continue; mylog("end of tuple list -- setting inUse to false: this = %u\n", self);
default: /* this should only happen if the backend dumped core */ self->inTuples = FALSE;
QR_set_message(self, "Unexpected result from backend. It probably crashed"); if (self->fcount > 0) {
self->status = PGRES_FATAL_ERROR;
CC_set_no_trans(self->conn); qlog(" [ fetched %d rows ]\n", self->fcount);
return FALSE; 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; return TRUE;
...@@ -413,7 +453,8 @@ Int4 len; ...@@ -413,7 +453,8 @@ Int4 len;
char *buffer; char *buffer;
int num_fields = self->num_fields; // speed up access int num_fields = self->num_fields; // speed up access
SocketClass *sock = CC_get_socket(self->conn); SocketClass *sock = CC_get_socket(self->conn);
ColumnInfoClass *flds;
/* set the current row to read the fields into */ /* set the current row to read the fields into */
this_tuplefield = self->backend_tuples + (self->fcount * num_fields); this_tuplefield = self->backend_tuples + (self->fcount * num_fields);
...@@ -455,6 +496,17 @@ SocketClass *sock = CC_get_socket(self->conn); ...@@ -455,6 +496,17 @@ SocketClass *sock = CC_get_socket(self->conn);
this_tuplefield[field_lf].len = len; this_tuplefield[field_lf].len = len;
this_tuplefield[field_lf].value = buffer; 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 Now adjust for the next bit to be scanned in the
......
/* File: qresult.h /* File: qresult.h
* *
* Description: See "qresult.c" * Description: See "qresult.c"
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __QRESULT_H__ #ifndef __QRESULT_H__
#define __QRESULT_H__ #define __QRESULT_H__
...@@ -43,7 +43,7 @@ struct QResultClass_ { ...@@ -43,7 +43,7 @@ struct QResultClass_ {
// Stuff for declare/fetch tuples // Stuff for declare/fetch tuples
int fetch_count; // logical rows read so far 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 int num_fields; // number of fields in the result
QueryResultCode status; QueryResultCode status;
...@@ -63,19 +63,22 @@ struct QResultClass_ { ...@@ -63,19 +63,22 @@ struct QResultClass_ {
/* These functions are for retrieving data from the qresult */ /* 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_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(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 */ /* 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_fieldname(self, fieldno_) (CI_get_fieldname(self->fields, fieldno_))
#define QR_get_fieldsize(self, fieldno_) (CI_get_fieldsize(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 */ /* 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_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 */ /* status macros */
#define QR_command_successful(self) ( !(self->status == PGRES_BAD_RESPONSE || self->status == PGRES_NONFATAL_ERROR || self->status == PGRES_FATAL_ERROR)) #define QR_command_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); ...@@ -98,7 +101,7 @@ int QR_close(QResultClass *self);
char QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor); char QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor);
void QR_free_memory(QResultClass *self); void QR_free_memory(QResultClass *self);
void QR_set_command(QResultClass *self, char *msg); 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 */ void QR_set_num_fields(QResultClass *self, int new_num_fields); /* manual result only */
......
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!
//{{NO_DEPENDENCIES}} //{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file. // Microsoft Developer Studio generated include file.
// Used by psqlodbc.rc // Used by psqlodbc.rc
// //
#define IDS_BADDSN 1 #define IDS_BADDSN 1
#define IDS_MSGTITLE 2 #define IDS_MSGTITLE 2
#define DRIVERCONNDIALOG 101 #define DLG_OPTIONS_DRV 102
#define IDC_DSNAME 400 #define DLG_OPTIONS_DS 103
#define IDC_DSNAMETEXT 401 #define IDC_DSNAME 400
#define IDC_DESC 404 #define IDC_DSNAMETEXT 401
#define IDC_SERVER 407 #define IDC_DESC 404
#define IDC_DATABASE 408 #define IDC_SERVER 407
#define CONFIGDSN 1001 #define IDC_DATABASE 408
#define IDC_PORT 1002 #define DLG_CONFIG 1001
#define IDC_USER 1006 #define IDC_PORT 1002
#define IDC_PASSWORD 1009 #define IDC_USER 1006
#define IDC_READONLY 1011 #define IDC_PASSWORD 1009
#define READONLY_EDIT 1012 #define DS_READONLY 1011
#define SAVEPASSWORD_EDIT 1013 #define DS_SHOWOIDCOLUMN 1012
#define IDC_COMMLOG 1014 #define DS_FAKEOIDINDEX 1013
#define COMMLOG_EDIT 1015 #define DRV_COMMLOG 1014
#define IDC_PG62 1016 #define DS_PG62 1016
#define PG62_EDIT 1017 #define IDC_DATASOURCE 1018
#define SERVER_EDIT 1501 #define DRV_OPTIMIZER 1019
#define PORT_EDIT 1502 #define DS_CONNSETTINGS 1020
#define DATABASE_EDIT 1503 #define IDC_DRIVER 1021
#define USERNAME_EDIT 1504 #define DS_UNKNOWN_MAX 1023
#define PASSWORD_EDIT 1505 #define DS_UNKNOWN_DONTKNOW 1024
#define DRV_CONNSETTINGS 1031
// Next default values for new objects #define DRV_UNIQUEINDEX 1032
// #define DRV_UNKNOWN_MAX 1035
#ifdef APSTUDIO_INVOKED #define DRV_UNKNOWN_DONTKNOW 1036
#ifndef APSTUDIO_READONLY_SYMBOLS #define DRV_READONLY 1037
#define _APS_NEXT_RESOURCE_VALUE 102 #define IDC_DESCTEXT 1039
#define _APS_NEXT_COMMAND_VALUE 40001 #define DRV_MSG_LABEL 1040
#define _APS_NEXT_CONTROL_VALUE 1018 #define DRV_UNKNOWN_LONGEST 1041
#define _APS_NEXT_SYMED_VALUE 101 #define DS_UNKNOWN_LONGEST 1042
#endif #define DRV_TEXT_LONGVARCHAR 1043
#endif #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
/* Module: results.c /* Module: results.c
* *
* Description: This module contains functions related to * Description: This module contains functions related to
* retrieving result information through the ODBC API. * retrieving result information through the ODBC API.
* *
* Classes: n/a * Classes: n/a
* *
* API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol, SQLColAttributes, * API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol, SQLColAttributes,
* SQLGetData, SQLFetch, SQLExtendedFetch, * SQLGetData, SQLFetch, SQLExtendedFetch,
* SQLMoreResults(NI), SQLSetPos(NI), SQLSetScrollOptions(NI), * SQLMoreResults(NI), SQLSetPos(NI), SQLSetScrollOptions(NI),
* SQLSetCursorName(NI), SQLGetCursorName(NI) * SQLSetCursorName, SQLGetCursorName
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include <string.h> #include <string.h>
#include "psqlodbc.h" #include "psqlodbc.h"
#include "dlg_specific.h"
#include "environ.h" #include "environ.h"
#include "connection.h" #include "connection.h"
#include "statement.h" #include "statement.h"
...@@ -29,6 +30,7 @@ ...@@ -29,6 +30,7 @@
#include <windows.h> #include <windows.h>
#include <sqlext.h> #include <sqlext.h>
extern GLOBAL_VALUES globals;
RETCODE SQL_API SQLRowCount( RETCODE SQL_API SQLRowCount(
HSTMT hstmt, HSTMT hstmt,
...@@ -46,7 +48,7 @@ char *msg, *ptr; ...@@ -46,7 +48,7 @@ char *msg, *ptr;
res = SC_get_Result(stmt); res = SC_get_Result(stmt);
if(res && pcrow) { if(res && pcrow) {
*pcrow = QR_get_num_tuples(res); *pcrow = globals.use_declarefetch ? 0 : QR_get_num_tuples(res);
return SQL_SUCCESS; return SQL_SUCCESS;
} }
} }
...@@ -115,11 +117,11 @@ QResultClass *result; ...@@ -115,11 +117,11 @@ QResultClass *result;
return SQL_SUCCESS; return SQL_SUCCESS;
} }
// - - - - - - - - - // - - - - - - - - -
// Return information about the database column the user wants // Return information about the database column the user wants
// information about. // information about.
/* CC: preliminary implementation */
RETCODE SQL_API SQLDescribeCol( RETCODE SQL_API SQLDescribeCol(
HSTMT hstmt, HSTMT hstmt,
UWORD icol, UWORD icol,
...@@ -136,10 +138,14 @@ StatementClass *stmt = (StatementClass *) hstmt; ...@@ -136,10 +138,14 @@ StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *result; QResultClass *result;
char *name; char *name;
Int4 fieldtype; Int4 fieldtype;
int p;
ConnInfo *ci;
if ( ! stmt) if ( ! stmt)
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
ci = &(stmt->hdbc->connInfo);
SC_clear_error(stmt); SC_clear_error(stmt);
/* CC: Now check for the "prepared, but not executed" situation, that enables us to /* CC: Now check for the "prepared, but not executed" situation, that enables us to
...@@ -149,7 +155,7 @@ Int4 fieldtype; ...@@ -149,7 +155,7 @@ Int4 fieldtype;
SC_pre_execute(stmt); SC_pre_execute(stmt);
result = SC_get_Result(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); 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))) { if ( (NULL == result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) {
...@@ -159,9 +165,18 @@ Int4 fieldtype; ...@@ -159,9 +165,18 @@ Int4 fieldtype;
return SQL_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--; /* use zero based column numbers */
if (cbColNameMax >= 1) { if (cbColNameMax >= 1) {
name = QR_get_fieldname(result, (Int2) (icol-1)); name = QR_get_fieldname(result, icol);
mylog("describeCol: col %d fieldname = '%s'\n", icol - 1, name); mylog("describeCol: col %d fieldname = '%s'\n", icol, name);
/* our indices start from 0 whereas ODBC defines indices starting from 1 */ /* our indices start from 0 whereas ODBC defines indices starting from 1 */
if (NULL != pcbColName) { if (NULL != pcbColName) {
// we want to get the total number of bytes in the column name // we want to get the total number of bytes in the column name
...@@ -179,27 +194,51 @@ Int4 fieldtype; ...@@ -179,27 +194,51 @@ Int4 fieldtype;
} }
} }
fieldtype = QR_get_field_type(result, (Int2) (icol-1)); fieldtype = QR_get_field_type(result, icol);
mylog("describeCol: col %d fieldtype = %d\n", icol - 1, fieldtype); mylog("describeCol: col %d fieldtype = %d\n", icol, fieldtype);
if (NULL != pfSqlType) { if (NULL != pfSqlType) {
*pfSqlType = pgtype_to_sqltype(fieldtype); *pfSqlType = pgtype_to_sqltype(stmt, fieldtype);
if (*pfSqlType == PG_UNKNOWN)
*pfSqlType = SQL_CHAR; mylog("describeCol: col %d *pfSqlType = %d\n", icol, *pfSqlType);
} }
if (NULL != pcbColDef) if (NULL != pcbColDef) {
*pcbColDef = pgtype_precision(fieldtype);
/* 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) { if (NULL != pibScale) {
Int2 scale; Int2 scale;
scale = pgtype_scale(fieldtype); scale = pgtype_scale(stmt, fieldtype);
if(scale == -1) { scale = 0; } if(scale == -1) { scale = 0; }
*pibScale = scale; *pibScale = scale;
mylog("describeCol: col %d *pibScale = %d\n", icol, *pibScale);
} }
if (NULL != pfNullable) { if (NULL != pfNullable) {
*pfNullable = pgtype_nullable(fieldtype); *pfNullable = pgtype_nullable(stmt, fieldtype);
mylog("describeCol: col %d *pfNullable = %d\n", icol, *pfNullable);
} }
return SQL_SUCCESS; return SQL_SUCCESS;
...@@ -219,132 +258,169 @@ RETCODE SQL_API SQLColAttributes( ...@@ -219,132 +258,169 @@ RETCODE SQL_API SQLColAttributes(
StatementClass *stmt = (StatementClass *) hstmt; StatementClass *stmt = (StatementClass *) hstmt;
char *value; char *value;
Int4 field_type; Int4 field_type;
ConnInfo *ci;
int unknown_sizes;
if( ! stmt) { if( ! stmt)
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
}
ci = &(stmt->hdbc->connInfo);
/* CC: Now check for the "prepared, but not executed" situation, that enables us to /* CC: Now check for the "prepared, but not executed" situation, that enables us to
deal with "SQLPrepare -- SQLDescribeCol -- ... -- SQLExecute" situations. deal with "SQLPrepare -- SQLDescribeCol -- ... -- SQLExecute" situations.
(AutoCAD 13 ASE/ASI just _loves_ that ;-) ) (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); 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)) ) { if ( (NULL == stmt->result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)) ) {
stmt->errormsg = "Can't get column attributes: no result found."; stmt->errormsg = "Can't get column attributes: no result found.";
stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errornumber = STMT_SEQUENCE_ERROR;
return SQL_ERROR; return SQL_ERROR;
} }
if(icol < 1) { if(icol < 1) {
// we do not support bookmarks // we do not support bookmarks
stmt->errormsg = "Bookmarks are not currently supported."; stmt->errormsg = "Bookmarks are not currently supported.";
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
return SQL_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); mylog("colAttr: col %d field_type = %d\n", icol, field_type);
switch(fDescType) {
case SQL_COLUMN_AUTO_INCREMENT: unknown_sizes = globals.unknown_sizes; // atoi(ci->unknown_sizes);
if (NULL != pfDesc) { if (unknown_sizes == UNKNOWNS_AS_DONTKNOW) // not appropriate for SQLColAttributes()
*pfDesc = pgtype_auto_increment(field_type); unknown_sizes = UNKNOWNS_AS_MAX;
if(*pfDesc == -1) { /* "not applicable" becomes false */ switch(fDescType) {
*pfDesc = FALSE; case SQL_COLUMN_AUTO_INCREMENT:
} if (NULL != pfDesc) {
} *pfDesc = pgtype_auto_increment(stmt, field_type);
break; if (*pfDesc == -1) /* non-numeric becomes FALSE (ODBC Doc) */
case SQL_COLUMN_CASE_SENSITIVE: *pfDesc = FALSE;
if (NULL != pfDesc)
*pfDesc = pgtype_case_sensitive(field_type); }
break; break;
case SQL_COLUMN_COUNT:
if (NULL != pfDesc) case SQL_COLUMN_CASE_SENSITIVE:
*pfDesc = QR_NumResultCols(stmt->result); if (NULL != pfDesc)
break; *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: case SQL_COLUMN_DISPLAY_SIZE:
if (NULL != pfDesc) if (NULL != pfDesc) {
*pfDesc = pgtype_precision(field_type); *pfDesc = pgtype_display_size(stmt, field_type, icol, unknown_sizes);
}
mylog("colAttr: col %d fieldsize = %d\n", icol, *pfDesc);
mylog("SQLColAttributes: col %d, display_size= %d\n", icol, *pfDesc);
break; break;
case SQL_COLUMN_LABEL:
case SQL_COLUMN_NAME: case SQL_COLUMN_LABEL:
value = QR_get_fieldname(stmt->result, icol); case SQL_COLUMN_NAME:
strncpy_null((char *)rgbDesc, value, cbDescMax); value = QR_get_fieldname(stmt->result, icol);
/* CC: Check for Nullpointesr */ strncpy_null((char *)rgbDesc, value, cbDescMax);
if (NULL != pcbDesc)
*pcbDesc = strlen(value); if (NULL != pcbDesc)
break; *pcbDesc = strlen(value);
case SQL_COLUMN_LENGTH: break;
if (NULL != pfDesc)
*pfDesc = pgtype_precision(field_type); case SQL_COLUMN_LENGTH:
return SQL_SUCCESS; if (NULL != pfDesc) {
break; *pfDesc = pgtype_length(stmt, field_type, icol, unknown_sizes);
case SQL_COLUMN_MONEY: }
if (NULL != pfDesc) mylog("SQLColAttributes: col %d, length = %d\n", icol, *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; break;
case SQL_COLUMN_SEARCHABLE:
if (NULL != pfDesc) case SQL_COLUMN_MONEY:
*pfDesc = pgtype_searchable(field_type); 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; break;
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: case SQL_COLUMN_TABLE_NAME:
return SQL_ERROR; strncpy_null((char *)rgbDesc, "", cbDescMax);
if (NULL != pcbDesc)
*pcbDesc = 0;
break; break;
case SQL_COLUMN_TYPE:
if (NULL != pfDesc) { case SQL_COLUMN_TYPE:
*pfDesc = pgtype_to_sqltype(field_type); if (NULL != pfDesc) {
if (*pfDesc == PG_UNKNOWN) *pfDesc = pgtype_to_sqltype(stmt, field_type);
*pfDesc = SQL_CHAR;
} }
break; break;
case SQL_COLUMN_TYPE_NAME:
value = pgtype_to_name(field_type); case SQL_COLUMN_TYPE_NAME:
strncpy_null((char *)rgbDesc, value, cbDescMax); value = pgtype_to_name(stmt, field_type);
if (NULL != pcbDesc) strncpy_null((char *)rgbDesc, value, cbDescMax);
*pcbDesc = strlen(value); if (NULL != pcbDesc)
break; *pcbDesc = strlen(value);
case SQL_COLUMN_UNSIGNED: break;
if (NULL != pfDesc) {
*pfDesc = pgtype_unsigned(field_type); case SQL_COLUMN_UNSIGNED:
if(*pfDesc == -1) { if (NULL != pfDesc) {
*pfDesc = FALSE; *pfDesc = pgtype_unsigned(stmt, field_type);
} if(*pfDesc == -1) /* non-numeric becomes TRUE (ODBC Doc) */
} *pfDesc = TRUE;
break; }
case SQL_COLUMN_UPDATABLE: break;
// everything should be updatable, I guess, unless access permissions
// prevent it--are we supposed to check for that here? seems kind case SQL_COLUMN_UPDATABLE:
// of complicated. hmm... // everything should be updatable, I guess, unless access permissions
if (NULL != pfDesc) // prevent it--are we supposed to check for that here? seems kind
*pfDesc = SQL_ATTR_WRITE; // of complicated. hmm...
break; 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; return SQL_SUCCESS;
...@@ -366,6 +442,10 @@ int num_cols, num_rows; ...@@ -366,6 +442,10 @@ int num_cols, num_rows;
Int4 field_type; Int4 field_type;
void *value; void *value;
int result; int result;
char multiple;
mylog("SQLGetData: enter, stmt=%u\n", stmt);
if( ! stmt) { if( ! stmt) {
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
...@@ -401,7 +481,7 @@ int result; ...@@ -401,7 +481,7 @@ int result;
return SQL_ERROR; return SQL_ERROR;
} }
if ( stmt->manual_result) { if ( stmt->manual_result || ! globals.use_declarefetch) {
// make sure we're positioned on a valid row // make sure we're positioned on a valid row
num_rows = QR_get_num_tuples(res); num_rows = QR_get_num_tuples(res);
if((stmt->currTuple < 0) || if((stmt->currTuple < 0) ||
...@@ -410,7 +490,14 @@ int result; ...@@ -410,7 +490,14 @@ int result;
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR; stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
return SQL_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) */ else { /* its a SOCKET result (backend data) */
if (stmt->currTuple == -1 || ! res || QR_end_tuples(res)) { if (stmt->currTuple == -1 || ! res || QR_end_tuples(res)) {
...@@ -421,35 +508,52 @@ int result; ...@@ -421,35 +508,52 @@ int result;
value = QR_get_value_backend(res, icol); value = QR_get_value_backend(res, icol);
mylog(" socket: value = '%s'\n", value);
} }
field_type = QR_get_field_type(res, icol); 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); 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, /* Is this another call for the same column to retrieve more data? */
fCType, rgbValue, cbValueMax, pcbValue); 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->current_col = icol;
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
return SQL_ERROR; switch(result) {
} else if(result == COPY_UNSUPPORTED_CONVERSION) { case COPY_OK:
stmt->errormsg = "Couldn't handle the necessary data type conversion."; return SQL_SUCCESS;
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
return SQL_ERROR; case COPY_UNSUPPORTED_TYPE:
} else if(result == COPY_RESULT_TRUNCATED) { stmt->errormsg = "Received an unsupported type from Postgres.";
stmt->errornumber = STMT_TRUNCATED; stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
stmt->errormsg = "The buffer was too small for the result."; return SQL_ERROR;
return SQL_SUCCESS_WITH_INFO;
} else if(result != COPY_OK) { 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->errormsg = "Unrecognized return value from copy_and_convert_field.";
stmt->errornumber = STMT_INTERNAL_ERROR; stmt->errornumber = STMT_INTERNAL_ERROR;
return SQL_ERROR; return SQL_ERROR;
} }
return SQL_SUCCESS;
} }
// Returns data for bound columns in the current row ("hstmt->iCursor"), // Returns data for bound columns in the current row ("hstmt->iCursor"),
...@@ -465,120 +569,136 @@ Int2 num_cols, lf; ...@@ -465,120 +569,136 @@ Int2 num_cols, lf;
Oid type; Oid type;
char *value; char *value;
ColumnInfoClass *ci; ColumnInfoClass *ci;
// TupleField *tupleField;
if ( ! stmt)
return SQL_INVALID_HANDLE;
if ( ! stmt) SC_clear_error(stmt);
return SQL_INVALID_HANDLE;
SC_clear_error(stmt);
if ( ! (res = stmt->result)) { if ( ! (res = stmt->result)) {
stmt->errormsg = "Null statement result in SQLFetch."; stmt->errormsg = "Null statement result in SQLFetch.";
stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errornumber = STMT_SEQUENCE_ERROR;
return SQL_ERROR; return SQL_ERROR;
} }
ci = QR_get_fields(res); /* the column info */ ci = QR_get_fields(res); /* the column info */
if (stmt->status == STMT_EXECUTING) { if (stmt->status == STMT_EXECUTING) {
stmt->errormsg = "Can't fetch while statement is still executing."; stmt->errormsg = "Can't fetch while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errornumber = STMT_SEQUENCE_ERROR;
return SQL_ERROR; return SQL_ERROR;
} }
if (stmt->status != STMT_FINISHED) { if (stmt->status != STMT_FINISHED) {
stmt->errornumber = STMT_STATUS_ERROR; stmt->errornumber = STMT_STATUS_ERROR;
stmt->errormsg = "Fetch can only be called after the successful execution on a SQL statement"; stmt->errormsg = "Fetch can only be called after the successful execution on a SQL statement";
return SQL_ERROR; return SQL_ERROR;
} }
if (stmt->bindings == NULL) { if (stmt->bindings == NULL) {
// just to avoid a crash if the user insists on calling this // just to avoid a crash if the user insists on calling this
// function even if SQL_ExecDirect has reported an Error // function even if SQL_ExecDirect has reported an Error
stmt->errormsg = "Bindings were not allocated properly."; stmt->errormsg = "Bindings were not allocated properly.";
stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errornumber = STMT_SEQUENCE_ERROR;
return SQL_ERROR; return SQL_ERROR;
} }
mylog("manual_result = %d, use_declarefetch = %d\n",
stmt->manual_result, globals.use_declarefetch);
if ( stmt->manual_result) { if ( stmt->manual_result || ! globals.use_declarefetch) {
if (QR_get_num_tuples(res) -1 == stmt->currTuple ||
(stmt->maxRows > 0 && stmt->currTuple == stmt->maxRows - 1)) if (stmt->currTuple >= QR_get_num_tuples(res) -1 ||
/* if we are at the end of a tuple list, we return a "no data found" */ (stmt->maxRows > 0 && stmt->currTuple == stmt->maxRows - 1)) {
return SQL_NO_DATA_FOUND;
/* 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"); mylog("**** SQLFetch: manual_result\n");
(stmt->currTuple)++; (stmt->currTuple)++;
} }
else { 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
else { // read from the cache or the physical next tuple
mylog("SQLFetch: error\n"); retval = QR_next_tuple(res);
stmt->errornumber = STMT_EXEC_ERROR; if (retval < 0) {
stmt->errormsg = "Error fetching next row"; mylog("**** SQLFetch: end_tuples\n");
return SQL_ERROR; 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;
}
}
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, buffer[] = %u\n", mylog("fetch: cols=%d, lf=%d, stmt = %u, stmt->bindings = %u, buffer[] = %u\n",
num_cols, lf, stmt->bindings[lf].buffer); num_cols, lf, stmt, stmt->bindings, stmt->bindings[lf].buffer);
if (stmt->bindings[lf].buffer != NULL) { if (stmt->bindings[lf].buffer != NULL) {
// this column has a binding // this column has a binding
// type = QR_get_field_type(res, lf); // type = QR_get_field_type(res, lf);
type = CI_get_oid(ci, lf); /* speed things up */ type = CI_get_oid(ci, lf); /* speed things up */
mylog("type = %d\n", type);
if (stmt->manual_result) if (stmt->manual_result)
value = QR_get_value_manual(res, stmt->currTuple, lf); value = QR_get_value_manual(res, stmt->currTuple, lf);
else else if (globals.use_declarefetch)
value = QR_get_value_backend(res, lf); 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 retval = copy_and_convert_field_bindinfo(stmt, type, value, lf);
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) { mylog("copy_and_convert: retval = %d\n", retval);
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) { // check whether the complete result was copied
/* The result has been truncated during the copy */ if(retval == COPY_UNSUPPORTED_TYPE) {
/* this will generate a SQL_SUCCESS_WITH_INFO result */ stmt->errormsg = "Received an unsupported type from Postgres.";
stmt->errornumber = STMT_TRUNCATED; stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
stmt->errormsg = "A buffer was too small for the return value to fit in"; 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; return SQL_SUCCESS_WITH_INFO;
} else if(retval != COPY_OK) { } else if(retval != COPY_OK) {
stmt->errormsg = "Unrecognized return value from copy_and_convert_field."; stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
stmt->errornumber = STMT_INTERNAL_ERROR; stmt->errornumber = STMT_INTERNAL_ERROR;
return SQL_ERROR; return SQL_ERROR;
} }
} }
} }
return SQL_SUCCESS; return SQL_SUCCESS;
} }
// This fetchs a block of data (rowset). // This fetchs a block of data (rowset).
...@@ -591,52 +711,91 @@ RETCODE SQL_API SQLExtendedFetch( ...@@ -591,52 +711,91 @@ RETCODE SQL_API SQLExtendedFetch(
UWORD FAR *rgfRowStatus) UWORD FAR *rgfRowStatus)
{ {
StatementClass *stmt = (StatementClass *) hstmt; StatementClass *stmt = (StatementClass *) hstmt;
int num_tuples;
RETCODE result;
mylog("SQLExtendedFetch: stmt=%u\n", stmt);
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;
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;
// CC: we currently only support fetches in one row bits
if (NULL != pcrow)
*pcrow = 1;
if (NULL != rgfRowStatus)
*rgfRowStatus = SQL_ROW_SUCCESS;
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 // This determines whether there are more results sets available for
// the "hstmt". // the "hstmt".
...@@ -677,7 +836,24 @@ RETCODE SQL_API SQLSetCursorName( ...@@ -677,7 +836,24 @@ RETCODE SQL_API SQLSetCursorName(
UCHAR FAR *szCursor, UCHAR FAR *szCursor,
SWORD cbCursor) 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 // Return the cursor name for a statement handle
...@@ -688,7 +864,27 @@ RETCODE SQL_API SQLGetCursorName( ...@@ -688,7 +864,27 @@ RETCODE SQL_API SQLGetCursorName(
SWORD cbCursorMax, SWORD cbCursorMax,
SWORD FAR *pcbCursor) 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;
} }
/* 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
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).
--------------------------------------------------------------------------*/ /* Module: setup.c
*
* Description: This module contains the setup functions for
// Includes ---------------------------------------------------------------- * adding/modifying a Data Source in the ODBC.INI portion
#include "psqlodbc.h" // Local include files * of the registry.
*
* Classes: n/a
*
* API functions: ConfigDSN
*
* Comments: See "notice.txt" for copyright and license information.
*
*************************************************************************************/
#include "psqlodbc.h"
#include "connection.h"
#include <windows.h> #include <windows.h>
#include <windowsx.h> #include <windowsx.h>
#include <odbcinst.h> // ODBC installer prototypes #include <odbcinst.h>
#include <string.h> // C include files #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "resource.h" #include "resource.h"
#include "dlg_specific.h"
#define INTFUNC __stdcall #define INTFUNC __stdcall
...@@ -104,81 +37,27 @@ extern GLOBAL_VALUES globals; ...@@ -104,81 +37,27 @@ extern GLOBAL_VALUES globals;
#define MAXDESC (255+1) // Max description length #define MAXDESC (255+1) // Max description length
#define MAXDSNAME (32+1) // Max data source name 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 ----------------------------------------------------------------- // Globals -----------------------------------------------------------------
// NOTE: All these are used by the dialog procedures // NOTE: All these are used by the dialog procedures
typedef struct tagSETUPDLG { typedef struct tagSETUPDLG {
HWND hwndParent; // Parent window handle HWND hwndParent; // Parent window handle
LPCSTR lpszDrvr; // Driver description LPCSTR lpszDrvr; // Driver description
Attr aAttr[NUMOFKEYS]; // Attribute array ConnInfo ci;
char szDSN[MAXDSNAME]; // Original data source name char szDSN[MAXDSNAME]; // Original data source name
BOOL fNewDSN; // New data source flag BOOL fNewDSN; // New data source flag
BOOL fDefault; // Default data source flag BOOL fDefault; // Default data source flag
} SETUPDLG, FAR *LPSETUPDLG; } SETUPDLG, FAR *LPSETUPDLG;
// Prototypes -------------------------------------------------------------- // Prototypes --------------------------------------------------------------
void INTFUNC CenterDialog (HWND hdlg); void INTFUNC CenterDialog(HWND hdlg);
int CALLBACK ConfigDlgProc(HWND hdlg, WORD wMsg, WPARAM wParam, LPARAM lParam);
int CALLBACK ConfigDlgProc (HWND hdlg, void INTFUNC ParseAttributes (LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg);
WORD wMsg, BOOL INTFUNC SetDSNAttributes(HWND hwnd, LPSETUPDLG lpsetupdlg);
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 --------------------------------------------------------------- /* ConfigDSN ---------------------------------------------------------------
Description: ODBC Setup entry point Description: ODBC Setup entry point
...@@ -196,9 +75,9 @@ BOOL CALLBACK ConfigDSN (HWND hwnd, ...@@ -196,9 +75,9 @@ BOOL CALLBACK ConfigDSN (HWND hwnd,
LPCSTR lpszDriver, LPCSTR lpszDriver,
LPCSTR lpszAttributes) LPCSTR lpszAttributes)
{ {
BOOL fSuccess; // Success/fail flag BOOL fSuccess; // Success/fail flag
GLOBALHANDLE hglbAttr; GLOBALHANDLE hglbAttr;
LPSETUPDLG lpsetupdlg; LPSETUPDLG lpsetupdlg;
// Allocate attribute array // Allocate attribute array
...@@ -212,20 +91,20 @@ BOOL CALLBACK ConfigDSN (HWND hwnd, ...@@ -212,20 +91,20 @@ BOOL CALLBACK ConfigDSN (HWND hwnd,
ParseAttributes(lpszAttributes, lpsetupdlg); ParseAttributes(lpszAttributes, lpsetupdlg);
// Save original data source name // Save original data source name
if (lpsetupdlg->aAttr[KEY_DSN].fSupplied) if (lpsetupdlg->ci.dsn[0])
lstrcpy(lpsetupdlg->szDSN, lpsetupdlg->aAttr[KEY_DSN].szAttr); lstrcpy(lpsetupdlg->szDSN, lpsetupdlg->ci.dsn);
else else
lpsetupdlg->szDSN[0] = '\0'; lpsetupdlg->szDSN[0] = '\0';
// Remove data source // Remove data source
if (ODBC_REMOVE_DSN == fRequest) { if (ODBC_REMOVE_DSN == fRequest) {
// Fail if no data source name was supplied // Fail if no data source name was supplied
if (!lpsetupdlg->aAttr[KEY_DSN].fSupplied) if (!lpsetupdlg->ci.dsn[0])
fSuccess = FALSE; fSuccess = FALSE;
// Otherwise remove data source from ODBC.INI // Otherwise remove data source from ODBC.INI
else else
fSuccess = SQLRemoveDSNFromIni(lpsetupdlg->aAttr[KEY_DSN].szAttr); fSuccess = SQLRemoveDSNFromIni(lpsetupdlg->ci.dsn);
} }
// Add or Configure data source // Add or Configure data source
...@@ -235,19 +114,19 @@ BOOL CALLBACK ConfigDSN (HWND hwnd, ...@@ -235,19 +114,19 @@ BOOL CALLBACK ConfigDSN (HWND hwnd,
lpsetupdlg->lpszDrvr = lpszDriver; lpsetupdlg->lpszDrvr = lpszDriver;
lpsetupdlg->fNewDSN = (ODBC_ADD_DSN == fRequest); lpsetupdlg->fNewDSN = (ODBC_ADD_DSN == fRequest);
lpsetupdlg->fDefault = 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) // Display the appropriate dialog (if parent window handle supplied)
if (hwnd) { if (hwnd) {
// Display dialog(s) // Display dialog(s)
fSuccess = (IDOK == DialogBoxParam(s_hModule, fSuccess = (IDOK == DialogBoxParam(s_hModule,
MAKEINTRESOURCE(CONFIGDSN), MAKEINTRESOURCE(DLG_CONFIG),
hwnd, hwnd,
ConfigDlgProc, ConfigDlgProc,
(LONG)(LPSTR)lpsetupdlg)); (LONG)(LPSTR)lpsetupdlg));
} }
else if (lpsetupdlg->aAttr[KEY_DSN].fSupplied) else if (lpsetupdlg->ci.dsn[0])
fSuccess = SetDSNAttributes(hwnd, lpsetupdlg); fSuccess = SetDSNAttributes(hwnd, lpsetupdlg);
else else
fSuccess = FALSE; fSuccess = FALSE;
...@@ -314,208 +193,123 @@ void INTFUNC CenterDialog(HWND hdlg) ...@@ -314,208 +193,123 @@ void INTFUNC CenterDialog(HWND hdlg)
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
int CALLBACK ConfigDlgProc(HWND hdlg,
int CALLBACK ConfigDlgProc WORD wMsg,
(HWND hdlg, WPARAM wParam,
WORD wMsg, LPARAM lParam)
WPARAM wParam,
LPARAM lParam)
{ {
switch (wMsg) { switch (wMsg) {
// Initialize the dialog // Initialize the dialog
case WM_INITDIALOG: case WM_INITDIALOG:
{ {
LPSETUPDLG lpsetupdlg; LPSETUPDLG lpsetupdlg = (LPSETUPDLG) lParam;
LPCSTR lpszDSN; ConnInfo *ci = &lpsetupdlg->ci;
SetWindowLong(hdlg, DWL_USER, lParam); /* Hide the driver connect message */
CenterDialog(hdlg); // Center dialog ShowWindow(GetDlgItem(hdlg, DRV_MSG_LABEL), SW_HIDE);
lpsetupdlg = (LPSETUPDLG) lParam; SetWindowLong(hdlg, DWL_USER, lParam);
lpszDSN = lpsetupdlg->aAttr[KEY_DSN].szAttr; CenterDialog(hdlg); // Center dialog
// Initialize dialog fields
// NOTE: Values supplied in the attribute string will always // NOTE: Values supplied in the attribute string will always
// override settings in ODBC.INI // override settings in ODBC.INI
SetDlgItemText(hdlg, IDC_DSNAME, lpszDSN);
// Get the rest of the common attributes
// Description getDSNinfo(ci, CONN_DONT_OVERWRITE);
if (!lpsetupdlg->aAttr[KEY_DESC].fSupplied)
SQLGetPrivateProfileString(lpszDSN, INI_KDESC, // Fill in any defaults
EMPTYSTR, getDSNdefaults(ci);
lpsetupdlg->aAttr[KEY_DESC].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_DESC].szAttr),
ODBC_INI); // Initialize dialog fields
SetDlgItemText(hdlg, IDC_DESC, lpsetupdlg->aAttr[KEY_DESC].szAttr); SetDlgStuff(hdlg, ci);
// Database
if (!lpsetupdlg->aAttr[KEY_DATABASE].fSupplied) if (lpsetupdlg->fDefault) {
SQLGetPrivateProfileString(lpszDSN, INI_DATABASE, EnableWindow(GetDlgItem(hdlg, IDC_DSNAME), FALSE);
EMPTYSTR, EnableWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), FALSE);
lpsetupdlg->aAttr[KEY_DATABASE].szAttr, }
sizeof(lpsetupdlg->aAttr[KEY_DATABASE].szAttr), else
ODBC_INI); SendDlgItemMessage(hdlg, IDC_DSNAME,
SetDlgItemText(hdlg, IDC_DATABASE, lpsetupdlg->aAttr[KEY_DATABASE].szAttr); EM_LIMITTEXT, (WPARAM)(MAXDSNAME-1), 0L);
// Server SendDlgItemMessage(hdlg, IDC_DESC,
if (!lpsetupdlg->aAttr[KEY_SERVER].fSupplied) EM_LIMITTEXT, (WPARAM)(MAXDESC-1), 0L);
SQLGetPrivateProfileString(lpszDSN, INI_SERVER, return TRUE; // Focus was not set
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);
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 // Process buttons
case WM_COMMAND: case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
// Ensure the OK button is enabled only when a data source name
// is entered
case IDC_DSNAME:
if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
{
char szItem[MAXDSNAME]; // Edit control text
// Enable/disable the OK button
EnableWindow(GetDlgItem(hdlg, IDOK),
GetDlgItemText(hdlg, IDC_DSNAME,
szItem, sizeof(szItem)));
return TRUE;
}
break;
// Accept results
case IDOK:
{
LPSETUPDLG lpsetupdlg;
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));
GetDlgItemText(hdlg, IDC_PASSWORD,
lpsetupdlg->aAttr[KEY_PASSWORD].szAttr,
sizeof(lpsetupdlg->aAttr[KEY_PASSWORD].szAttr));
if ( IsDlgButtonChecked(hdlg, IDC_PG62))
strcpy(lpsetupdlg->aAttr[KEY_PROTOCOL].szAttr, PG62);
else
lpsetupdlg->aAttr[KEY_PROTOCOL].szAttr[0] = '\0';
sprintf(lpsetupdlg->aAttr[KEY_READONLY].szAttr, "%d", IsDlgButtonChecked(hdlg, IDC_READONLY));
globals.commlog = IsDlgButtonChecked(hdlg, IDC_COMMLOG);
// Update ODBC.INI
SetDSNAttributes(hdlg, lpsetupdlg);
}
// Return to caller switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDCANCEL: // Ensure the OK button is enabled only when a data source name
EndDialog(hdlg, wParam); // is entered
return TRUE; case IDC_DSNAME:
} if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
break; {
char szItem[MAXDSNAME]; // Edit control text
// Enable/disable the OK button
EnableWindow(GetDlgItem(hdlg, IDOK),
GetDlgItemText(hdlg, IDC_DSNAME,
szItem, sizeof(szItem)));
return TRUE;
}
break;
// Accept results
case IDOK:
{
LPSETUPDLG lpsetupdlg;
lpsetupdlg = (LPSETUPDLG)GetWindowLong(hdlg, DWL_USER);
// Retrieve dialog values
if (!lpsetupdlg->fDefault)
GetDlgItemText(hdlg, IDC_DSNAME,
lpsetupdlg->ci.dsn,
sizeof(lpsetupdlg->ci.dsn));
// Get Dialog Values
GetDlgStuff(hdlg, &lpsetupdlg->ci);
// Update ODBC.INI
SetDSNAttributes(hdlg, lpsetupdlg);
} }
// Message not processed // Return to caller
return FALSE; case IDCANCEL:
EndDialog(hdlg, wParam);
return TRUE;
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 ...@@ -526,11 +320,13 @@ int CALLBACK ConfigDlgProc
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg) void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
{ {
LPCSTR lpsz; LPCSTR lpsz;
LPCSTR lpszStart; LPCSTR lpszStart;
char aszKey[MAXKEYLEN]; char aszKey[MAXKEYLEN];
int iElement; int cbKey;
int cbKey; char value[MAXPATHLEN];
memset(&lpsetupdlg->ci, 0, sizeof(ConnInfo));
for (lpsz=lpszAttributes; *lpsz; lpsz++) for (lpsz=lpszAttributes; *lpsz; lpsz++)
{ // Extract key name (e.g., DSN), it must be terminated by an equals { // Extract key name (e.g., DSN), it must be terminated by an equals
...@@ -543,38 +339,26 @@ void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg) ...@@ -543,38 +339,26 @@ void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
break; // Valid key found break; // Valid key found
} }
// Determine the key's index in the key table (-1 if not found) // Determine the key's index in the key table (-1 if not found)
iElement = -1;
cbKey = lpsz - lpszStart; cbKey = lpsz - lpszStart;
if (cbKey < sizeof(aszKey)) if (cbKey < sizeof(aszKey))
{ {
register int j;
_fmemcpy(aszKey, lpszStart, cbKey); _fmemcpy(aszKey, lpszStart, cbKey);
aszKey[cbKey] = '\0'; 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 // Locate end of key value
lpszStart = ++lpsz; lpszStart = ++lpsz;
for (; *lpsz; lpsz++); for (; *lpsz; lpsz++);
// Save value if key is known
// NOTE: This code assumes the szAttr buffers in aAttr have been // lpsetupdlg->aAttr[iElement].fSupplied = TRUE;
// zero initialized _fmemcpy(value, lpszStart, MIN(lpsz-lpszStart+1, MAXPATHLEN));
if (iElement >= 0)
{ mylog("aszKey='%s', value='%s'\n", aszKey, value);
lpsetupdlg->aAttr[iElement].fSupplied = TRUE;
_fmemcpy(lpsetupdlg->aAttr[iElement].szAttr, // Copy the appropriate value to the conninfo
lpszStart, copyAttributes(&lpsetupdlg->ci, aszKey, value);
MIN(lpsz-lpszStart+1, sizeof(lpsetupdlg->aAttr[0].szAttr)-1));
}
} }
return; return;
} }
...@@ -588,12 +372,12 @@ void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg) ...@@ -588,12 +372,12 @@ void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
BOOL INTFUNC SetDSNAttributes(HWND hwndParent, 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 // Validate arguments
if (lpsetupdlg->fNewDSN && !*lpsetupdlg->aAttr[KEY_DSN].szAttr) if (lpsetupdlg->fNewDSN && !*lpsetupdlg->ci.dsn)
return FALSE; return FALSE;
// Write the data source name // Write the data source name
...@@ -614,64 +398,11 @@ BOOL INTFUNC SetDSNAttributes(HWND hwndParent, LPSETUPDLG lpsetupdlg) ...@@ -614,64 +398,11 @@ BOOL INTFUNC SetDSNAttributes(HWND hwndParent, LPSETUPDLG lpsetupdlg)
// Update ODBC.INI // Update ODBC.INI
// Save the value if the data source is new, if it was edited, or if writeDSNinfo(&lpsetupdlg->ci);
// 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);
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 the data source name has changed, remove the old name
if (lpsetupdlg->aAttr[KEY_DSN].fSupplied && if (lstrcmpi(lpsetupdlg->szDSN, lpsetupdlg->ci.dsn))
lstrcmpi(lpsetupdlg->szDSN, lpsetupdlg->aAttr[KEY_DSN].szAttr))
{ {
SQLRemoveDSNFromIni(lpsetupdlg->szDSN); SQLRemoveDSNFromIni(lpsetupdlg->szDSN);
} }
......
/* Module: socket.c /* Module: socket.c
* *
* Description: This module contains functions for low level socket * Description: This module contains functions for low level socket
* operations (connecting/reading/writing to the backend) * operations (connecting/reading/writing to the backend)
* *
* Classes: SocketClass (Functions prefix: "SOCK_") * Classes: SocketClass (Functions prefix: "SOCK_")
* *
* API functions: none * API functions: none
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "socket.h" #include "socket.h"
...@@ -45,8 +45,8 @@ SocketClass *rv; ...@@ -45,8 +45,8 @@ SocketClass *rv;
return NULL; return NULL;
rv->errormsg = NULL; rv->errormsg = NULL;
rv->errornumber = 0; rv->errornumber = 0;
rv->reverse = FALSE; rv->reverse = FALSE;
} }
return rv; return rv;
...@@ -77,6 +77,7 @@ SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname) ...@@ -77,6 +77,7 @@ SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname)
{ {
struct hostent *host; struct hostent *host;
struct sockaddr_in sadr; struct sockaddr_in sadr;
unsigned long iaddr;
if (self->socket != -1) { if (self->socket != -1) {
self->errornumber = SOCKET_ALREADY_CONNECTED; self->errornumber = SOCKET_ALREADY_CONNECTED;
...@@ -84,15 +85,24 @@ struct sockaddr_in sadr; ...@@ -84,15 +85,24 @@ struct sockaddr_in sadr;
return 0; return 0;
} }
host = gethostbyname(hostname); memset((char *)&sadr, 0, sizeof(sadr));
if (host == NULL) {
self->errornumber = SOCKET_HOST_NOT_FOUND; /* If it is a valid IP address, use it.
self->errormsg = "Could not resolve hostname."; Otherwise use hostname lookup.
return 0; */
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));
memset((char *)&sadr, 0, sizeof(sadr));
memcpy(&(sadr.sin_addr), host->h_addr, host->h_length);
sadr.sin_family = AF_INET; sadr.sin_family = AF_INET;
sadr.sin_port = htons(port); sadr.sin_port = htons(port);
...@@ -185,15 +195,15 @@ char buf[4]; ...@@ -185,15 +195,15 @@ char buf[4];
switch (len) { switch (len) {
case 2: case 2:
SOCK_get_n_char(self, buf, len); SOCK_get_n_char(self, buf, len);
if (self->reverse) if (self->reverse)
return *((unsigned short *) buf); return *((unsigned short *) buf);
else else
return ntohs( *((unsigned short *) buf) ); return ntohs( *((unsigned short *) buf) );
case 4: case 4:
SOCK_get_n_char(self, buf, len); SOCK_get_n_char(self, buf, len);
if (self->reverse) if (self->reverse)
return *((unsigned int *) buf); return *((unsigned int *) buf);
else else
return ntohl( *((unsigned int *) buf) ); return ntohl( *((unsigned int *) buf) );
...@@ -202,7 +212,7 @@ char buf[4]; ...@@ -202,7 +212,7 @@ char buf[4];
self->errormsg = "Cannot read ints of that length"; self->errormsg = "Cannot read ints of that length";
return 0; return 0;
} }
} }
void void
...@@ -211,12 +221,12 @@ SOCK_put_int(SocketClass *self, int value, short len) ...@@ -211,12 +221,12 @@ SOCK_put_int(SocketClass *self, int value, short len)
unsigned int rv; unsigned int rv;
switch (len) { switch (len) {
case 2: case 2:
rv = self->reverse ? value : htons( (unsigned short) value); rv = self->reverse ? value : htons( (unsigned short) value);
SOCK_put_n_char(self, (char *) &rv, 2); SOCK_put_n_char(self, (char *) &rv, 2);
return; return;
case 4: case 4:
rv = self->reverse ? value : htonl( (unsigned int) value); rv = self->reverse ? value : htonl( (unsigned int) value);
SOCK_put_n_char(self, (char *) &rv, 4); SOCK_put_n_char(self, (char *) &rv, 4);
return; return;
...@@ -251,7 +261,7 @@ SOCK_get_next_byte(SocketClass *self) ...@@ -251,7 +261,7 @@ SOCK_get_next_byte(SocketClass *self)
// reload the buffer // reload the buffer
self->buffer_read_in = 0; 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); mylog("read %d, global_socket_buffersize=%d\n", self->buffer_filled_in, globals.socket_buffersize);
......
/* File: socket.h /* File: socket.h
* *
* Description: See "socket.c" * Description: See "socket.c"
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __SOCKET_H__ #ifndef __SOCKET_H__
#define __SOCKET_H__ #define __SOCKET_H__
...@@ -36,8 +36,8 @@ struct SocketClass_ { ...@@ -36,8 +36,8 @@ struct SocketClass_ {
SOCKET socket; SOCKET socket;
char *errormsg; char *errormsg;
int errornumber; int errornumber;
char reverse; /* used to handle Postgres 6.2 protocol (reverse byte order) */ char reverse; /* used to handle Postgres 6.2 protocol (reverse byte order) */
}; };
...@@ -49,9 +49,9 @@ struct SocketClass_ { ...@@ -49,9 +49,9 @@ struct SocketClass_ {
/* error functions */ /* error functions */
#define SOCK_get_errcode(self) (self->errornumber) #define SOCK_get_errcode(self) (self->errornumber)
#define SOCK_get_errmsg(self) (self->errormsg) #define SOCK_get_errmsg(self) (self->errormsg)
/* Socket prototypes */
/* Socket prototypes */
SocketClass *SOCK_Constructor(); SocketClass *SOCK_Constructor();
void SOCK_Destructor(SocketClass *self); void SOCK_Destructor(SocketClass *self);
char SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname); char SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname);
......
/* Module: statement.c /* Module: statement.c
* *
* Description: This module contains functions related to creating * Description: This module contains functions related to creating
* and manipulating a statement. * and manipulating a statement.
* *
* Classes: StatementClass (Functions prefix: "SC_") * Classes: StatementClass (Functions prefix: "SC_")
* *
* API functions: SQLAllocStmt, SQLFreeStmt * API functions: SQLAllocStmt, SQLFreeStmt
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "statement.h" #include "statement.h"
#include "bind.h" #include "bind.h"
...@@ -23,8 +23,25 @@ ...@@ -23,8 +23,25 @@
#include <windows.h> #include <windows.h>
#include <sql.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, RETCODE SQL_API SQLAllocStmt(HDBC hdbc,
HSTMT FAR *phstmt) HSTMT FAR *phstmt)
...@@ -103,7 +120,7 @@ StatementClass *stmt = (StatementClass *) hstmt; ...@@ -103,7 +120,7 @@ StatementClass *stmt = (StatementClass *) hstmt;
// errormsg passed in above // errormsg passed in above
return SQL_ERROR; return SQL_ERROR;
} else if(fOption == SQL_RESET_PARAMS) { } else if(fOption == SQL_RESET_PARAMS) {
SC_free_params(stmt, STMT_FREE_PARAMS_ALL); SC_free_params(stmt, STMT_FREE_PARAMS_ALL);
} else { } else {
...@@ -133,21 +150,28 @@ StatementClass *rv; ...@@ -133,21 +150,28 @@ StatementClass *rv;
rv->prepare = FALSE; rv->prepare = FALSE;
rv->status = STMT_ALLOCATED; rv->status = STMT_ALLOCATED;
rv->maxRows = 0; // driver returns all rows 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->errormsg = NULL;
rv->errornumber = 0; rv->errornumber = 0;
rv->errormsg_created = FALSE; rv->errormsg_created = FALSE;
rv->statement = NULL; rv->statement = NULL;
rv->stmt_with_params[0] = '\0'; rv->stmt_with_params[0] = '\0';
rv->statement_type = STMT_TYPE_UNKNOWN; rv->statement_type = STMT_TYPE_UNKNOWN;
rv->bindings = NULL; rv->bindings = NULL;
rv->bindings_allocated = 0; rv->bindings_allocated = 0;
rv->parameters_allocated = 0; rv->parameters_allocated = 0;
rv->parameters = 0; rv->parameters = 0;
rv->currTuple = -1; rv->currTuple = -1;
rv->current_col = -1;
rv->result = 0; rv->result = 0;
rv->data_at_exec = -1; rv->data_at_exec = -1;
rv->current_exec_param = -1; rv->current_exec_param = -1;
rv->put_data = FALSE; rv->put_data = FALSE;
rv->lobj_fd = -1;
rv->internal = FALSE;
rv->cursor_name[0] = '\0';
} }
return rv; return rv;
} }
...@@ -172,9 +196,9 @@ SC_Destructor(StatementClass *self) ...@@ -172,9 +196,9 @@ SC_Destructor(StatementClass *self)
if (self->statement) if (self->statement)
free(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 */ /* 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 */ /* by by the application that uses that driver, so we don't have to care */
/* about that here. */ /* about that here. */
...@@ -183,62 +207,63 @@ SC_Destructor(StatementClass *self) ...@@ -183,62 +207,63 @@ SC_Destructor(StatementClass *self)
free(self); free(self);
mylog("SC_Destructor: EXIT\n");
return TRUE; 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;
}
}
int /* Free parameters and free the memory from the
statement_type(char *statement) data-at-execution parameters that was allocated in SQLPutData.
*/
void
SC_free_params(StatementClass *self, char option)
{ {
if(strnicmp(statement, "SELECT", 6) == 0) int i;
return STMT_TYPE_SELECT;
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;
}
else if(strnicmp(statement, "INSERT", 6) == 0) mylog("SC_free_params: EXIT\n");
return STMT_TYPE_INSERT; }
else if(strnicmp(statement, "UPDATE", 6) == 0)
return STMT_TYPE_UPDATE;
else if(strnicmp(statement, "DELETE", 6) == 0)
return STMT_TYPE_DELETE;
else int
return STMT_TYPE_OTHER; statement_type(char *statement)
{
int i;
for (i = 0; Statement_Type[i].s; i++)
if ( ! strnicmp(statement, Statement_Type[i].s, strlen(Statement_Type[i].s)))
return Statement_Type[i].type;
return STMT_TYPE_OTHER;
} }
/* Called from SQLPrepare if STMT_PREMATURE, or /* Called from SQLPrepare if STMT_PREMATURE, or
...@@ -298,16 +323,21 @@ ConnectionClass *conn; ...@@ -298,16 +323,21 @@ ConnectionClass *conn;
} }
self->status = STMT_READY; self->status = STMT_READY;
self->manual_result = FALSE; // very important
self->currTuple = -1; self->currTuple = -1;
self->current_col = -1;
self->errormsg = NULL; self->errormsg = NULL;
self->errornumber = 0; self->errornumber = 0;
self->errormsg_created = FALSE; self->errormsg_created = FALSE;
// Free any data at exec params before the statement is executed self->lobj_fd = -1;
// again. If not, then there will be a memory leak when
// the next SQLParamData/SQLPutData is called. // Free any data at exec params before the statement is executed
SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY); // 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; return TRUE;
} }
...@@ -414,132 +444,143 @@ char rv; ...@@ -414,132 +444,143 @@ char rv;
return rv; return rv;
} }
RETCODE SC_execute(StatementClass *self) RETCODE SC_execute(StatementClass *self)
{ {
ConnectionClass *conn; ConnectionClass *conn;
QResultClass *res; QResultClass *res;
char ok, was_ok, was_nonfatal; char ok, was_ok, was_nonfatal;
Int2 oldstatus, numcols; Int2 oldstatus, numcols;
conn = SC_get_conn(self); conn = SC_get_conn(self);
/* Begin a transaction if one is not already in progress */ /* Begin a transaction if one is not already in progress */
/* The reason is because we can't use declare/fetch cursors without /* The reason is because we can't use declare/fetch cursors without
starting a transaction first. starting a transaction first.
*/ */
if ( ! CC_is_in_trans(conn) && (globals.use_declarefetch || STMT_UPDATE(self))) {
if ( ! CC_is_in_trans(conn)) {
mylog(" about to begin a transaction on statement = %u\n", self); mylog(" about to begin a transaction on statement = %u\n", self);
res = CC_send_query(conn, "BEGIN", NULL, NULL); res = CC_send_query(conn, "BEGIN", NULL, NULL);
if ( ! res) { if ( ! res) {
self->errormsg = "Could not begin a transaction"; self->errormsg = "Could not begin a transaction";
self->errornumber = STMT_EXEC_ERROR; self->errornumber = STMT_EXEC_ERROR;
return SQL_ERROR; return SQL_ERROR;
} }
ok = QR_command_successful(res); ok = QR_command_successful(res);
mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res)); mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res));
QR_Destructor(res); QR_Destructor(res);
if (!ok) { if (!ok) {
self->errormsg = "Could not begin a transaction"; self->errormsg = "Could not begin a transaction";
self->errornumber = STMT_EXEC_ERROR; self->errornumber = STMT_EXEC_ERROR;
return SQL_ERROR; return SQL_ERROR;
} }
else else
CC_set_in_trans(conn); CC_set_in_trans(conn);
} }
oldstatus = conn->status; oldstatus = conn->status;
conn->status = CONN_EXECUTING; conn->status = CONN_EXECUTING;
self->status = STMT_EXECUTING; self->status = STMT_EXECUTING;
// If its a SELECT statement, use a cursor. // If its a SELECT statement, use a cursor.
// Note that the declare cursor has already been prepended to the statement // Note that the declare cursor has already been prepended to the statement
// in copy_statement... // in copy_statement...
if (self->statement_type == STMT_TYPE_SELECT) { if (self->statement_type == STMT_TYPE_SELECT) {
char cursor[32]; char fetch[128];
char fetch[64];
mylog(" Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name);
sprintf(cursor, "C%u", self);
/* send the declare/select */
mylog(" Sending SELECT statement on stmt=%u\n", self); self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
/* send the declare/select */ if (globals.use_declarefetch && self->result != NULL) {
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL); /* That worked, so now send the fetch to start getting data back */
if (self->result != NULL) { sprintf(fetch, "fetch %d in %s", globals.fetch_max, self->cursor_name);
/* 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, self->cursor_name);
// Save the cursor in the result for later use }
self->result = CC_send_query( conn, fetch, NULL, cursor);
} mylog(" done sending the query:\n");
mylog(" done sending the query:\n");
} }
else { // not a SELECT statement so don't use a cursor else { // not a SELECT statement so don't use a cursor
mylog(" its NOT a select statement: stmt=%u\n", self); mylog(" its NOT a select statement: stmt=%u\n", self);
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL); self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
// If we are in autocommit, we must send the commit. // If we are in autocommit, we must send the commit.
if (CC_is_in_autocommit(conn)) { if (CC_is_in_autocommit(conn) && STMT_UPDATE(self)) {
CC_send_query(conn, "COMMIT", NULL, NULL); CC_send_query(conn, "COMMIT", NULL, NULL);
CC_set_no_trans(conn); CC_set_no_trans(conn);
} }
} }
conn->status = oldstatus; conn->status = oldstatus;
self->status = STMT_FINISHED; self->status = STMT_FINISHED;
/* Check the status of the result */ /* Check the status of the result */
if (self->result) { if (self->result) {
was_ok = QR_command_successful(self->result); was_ok = QR_command_successful(self->result);
was_nonfatal = QR_command_nonfatal(self->result); was_nonfatal = QR_command_nonfatal(self->result);
if ( was_ok) if ( was_ok)
self->errornumber = STMT_OK; self->errornumber = STMT_OK;
else else
self->errornumber = was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND; 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->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); /* 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) { /* now allocate the array to hold the binding info */
extend_bindings(self, numcols); if (numcols > 0) {
if (self->bindings == NULL) { extend_bindings(self, numcols);
self->errornumber = STMT_NO_MEMORY_ERROR; if (self->bindings == NULL) {
self->errormsg = "Could not get enough free memory to store the binding information"; self->errornumber = STMT_NO_MEMORY_ERROR;
return SQL_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 */
} else { /* Bad Error -- The error message will be in the Connection */
self->errornumber = STMT_EXEC_ERROR;
self->errormsg = "Error while executing the query"; if (self->statement_type == STMT_TYPE_CREATE) {
self->errornumber = STMT_CREATE_TABLE_ERROR;
CC_abort(conn); 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,
if (self->errornumber == STMT_OK) it would fail.
return SQL_SUCCESS; return SQL_SUCCESS_WITH_INFO;
*/
else if (self->errornumber == STMT_INFO_ONLY) }
return SQL_SUCCESS_WITH_INFO; else {
self->errornumber = STMT_EXEC_ERROR;
else self->errormsg = "Error while executing the query";
return SQL_ERROR; }
} 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;
}
/* File: statement.h /* File: statement.h
* *
* Description: See "statement.c" * Description: See "statement.c"
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __STATEMENT_H__ #ifndef __STATEMENT_H__
#define __STATEMENT_H__ #define __STATEMENT_H__
...@@ -45,18 +45,26 @@ typedef enum { ...@@ -45,18 +45,26 @@ typedef enum {
#define STMT_RESTRICTED_DATA_TYPE_ERROR 14 #define STMT_RESTRICTED_DATA_TYPE_ERROR 14
#define STMT_INVALID_CURSOR_STATE_ERROR 15 #define STMT_INVALID_CURSOR_STATE_ERROR 15
#define STMT_OPTION_VALUE_CHANGED 16 #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 */ /* statement types */
#define STMT_TYPE_SELECT 0 enum {
#define STMT_TYPE_INSERT 1 STMT_TYPE_UNKNOWN = -2,
#define STMT_TYPE_UPDATE 2 STMT_TYPE_OTHER = -1,
#define STMT_TYPE_DELETE 3 STMT_TYPE_SELECT = 0,
#define STMT_TYPE_OTHER 4 STMT_TYPE_INSERT,
#define STMT_TYPE_UNKNOWN 666 // 'unknown' means we don't have the statement yet, STMT_TYPE_UPDATE,
// or haven't looked at it to see what type it is. STMT_TYPE_DELETE,
// 'other' means we looked, but couldn't tell. 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 ***********/ /******** Statement Handle ***********/
struct StatementClass_ { struct StatementClass_ {
...@@ -68,6 +76,9 @@ struct StatementClass_ { ...@@ -68,6 +76,9 @@ struct StatementClass_ {
char *errormsg; char *errormsg;
int errornumber; int errornumber;
int maxRows; int maxRows;
int rowset_size;
int cursor_type;
int scroll_concurrency;
/* information on bindings */ /* information on bindings */
BindInfoClass *bindings; /* array to store the binding information */ BindInfoClass *bindings; /* array to store the binding information */
...@@ -78,30 +89,35 @@ struct StatementClass_ { ...@@ -78,30 +89,35 @@ struct StatementClass_ {
ParameterInfoClass *parameters; ParameterInfoClass *parameters;
Int4 currTuple; 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 */ char *statement; /* if non--null pointer to the SQL statement that has been executed */
int statement_type; /* According to the defines above */ int statement_type; /* According to the defines above */
int data_at_exec; /* Number of params needing SQLPutData */ int data_at_exec; /* Number of params needing SQLPutData */
int current_exec_param; /* The current parameter for SQLPutData */ int current_exec_param; /* The current parameter for SQLPutData */
char put_data; /* Has SQLPutData been called yet? */ char put_data; /* Has SQLPutData been called yet? */
char errormsg_created; /* has an informative error msg been created? */ char errormsg_created; /* has an informative error msg been created? */
char manual_result; /* Is the statement result manually built? */ char manual_result; /* Is the statement result manually built? */
char prepare; /* is this statement a prepared statement or direct */ char prepare; /* is this statement a prepared statement or direct */
char stmt_with_params[65536 /* MAX_STATEMENT_LEN */]; /* statement after parameter substitution */ 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_conn(a) (a->hdbc)
#define SC_get_Result(a) (a->result); #define SC_get_Result(a) (a->result);
/* options for SC_free_params() */ /* options for SC_free_params() */
#define STMT_FREE_PARAMS_ALL 0 #define STMT_FREE_PARAMS_ALL 0
#define STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY 1 #define STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY 1
/* Statement prototypes */ /* Statement prototypes */
StatementClass *SC_Constructor(); StatementClass *SC_Constructor();
char SC_Destructor(StatementClass *self); char SC_Destructor(StatementClass *self);
...@@ -113,7 +129,7 @@ char SC_recycle_statement(StatementClass *self); ...@@ -113,7 +129,7 @@ char SC_recycle_statement(StatementClass *self);
void SC_clear_error(StatementClass *self); void SC_clear_error(StatementClass *self);
char SC_get_error(StatementClass *self, int *number, char **message); char SC_get_error(StatementClass *self, int *number, char **message);
char *SC_create_errormsg(StatementClass *self); char *SC_create_errormsg(StatementClass *self);
RETCODE SC_execute(StatementClass *stmt); RETCODE SC_execute(StatementClass *stmt);
void SC_free_params(StatementClass *self, char option); void SC_free_params(StatementClass *self, char option);
#endif #endif
/* Module: tuple.c /* Module: tuple.c
* *
* Description: This module contains functions for setting the data for individual * Description: This module contains functions for setting the data for individual
* fields (TupleField structure) of a manual result set. * fields (TupleField structure) of a manual result set.
* *
* Important Note: These functions are ONLY used in building manual result sets for * Important Note: These functions are ONLY used in building manual result sets for
* info functions (SQLTables, SQLColumns, etc.) * info functions (SQLTables, SQLColumns, etc.)
* *
* Classes: n/a * Classes: n/a
* *
* API functions: none * API functions: none
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include "tuple.h" #include "tuple.h"
#include <string.h> #include <string.h>
......
/* File: tuple.h /* File: tuple.h
* *
* Description: See "tuple.c" * Description: See "tuple.c"
* *
* Important NOTE: The TupleField structure is used both to hold backend data and * Important NOTE: The TupleField structure is used both to hold backend data and
* manual result set data. The "set_" functions and the TupleNode * manual result set data. The "set_" functions and the TupleNode
* structure are only used for manual result sets by info routines. * structure are only used for manual result sets by info routines.
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __TUPLE_H__ #ifndef __TUPLE_H__
#define __TUPLE_H__ #define __TUPLE_H__
#include "psqlodbc.h" #include "psqlodbc.h"
/* Used by backend data AND manual result sets */ /* Used by backend data AND manual result sets */
struct TupleField_ { struct TupleField_ {
Int4 len; /* length of the current Tuple */ Int4 len; /* length of the current Tuple */
void *value; /* an array representing the value */ void *value; /* an array representing the value */
}; };
/* Used ONLY for manual result sets */ /* Used ONLY for manual result sets */
struct TupleNode_ { struct TupleNode_ {
struct TupleNode_ *prev, *next; struct TupleNode_ *prev, *next;
......
/* Module: tuplelist.c /* Module: tuplelist.c
* *
* Description: This module contains functions for creating a manual result set * Description: This module contains functions for creating a manual result set
* (the TupleList) and retrieving data from it for a specific row/column. * (the TupleList) and retrieving data from it for a specific row/column.
* *
* Classes: TupleListClass (Functions prefix: "TL_") * Classes: TupleListClass (Functions prefix: "TL_")
* *
* API functions: none * API functions: none
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <malloc.h> #include <malloc.h>
...@@ -21,8 +21,8 @@ TupleListClass * ...@@ -21,8 +21,8 @@ TupleListClass *
TL_Constructor(UInt4 fieldcnt) TL_Constructor(UInt4 fieldcnt)
{ {
TupleListClass *rv; TupleListClass *rv;
mylog("in TL_Constructor\n"); mylog("in TL_Constructor\n");
rv = (TupleListClass *) malloc(sizeof(TupleListClass)); rv = (TupleListClass *) malloc(sizeof(TupleListClass));
if (rv) { if (rv) {
...@@ -35,8 +35,8 @@ TupleListClass *rv; ...@@ -35,8 +35,8 @@ TupleListClass *rv;
rv->last_indexed = -1; rv->last_indexed = -1;
} }
mylog("exit TL_Constructor\n"); mylog("exit TL_Constructor\n");
return rv; return rv;
} }
...@@ -46,8 +46,8 @@ TL_Destructor(TupleListClass *self) ...@@ -46,8 +46,8 @@ TL_Destructor(TupleListClass *self)
int lf; int lf;
TupleNode *node, *tp; TupleNode *node, *tp;
mylog("TupleList: in DESTRUCTOR\n"); mylog("TupleList: in DESTRUCTOR\n");
node = self->list_start; node = self->list_start;
while(node != NULL) { while(node != NULL) {
for (lf=0; lf < self->num_fields; lf++) for (lf=0; lf < self->num_fields; lf++)
...@@ -57,11 +57,11 @@ TupleNode *node, *tp; ...@@ -57,11 +57,11 @@ TupleNode *node, *tp;
tp = node->next; tp = node->next;
free(node); free(node);
node = tp; node = tp;
} }
free(self); free(self);
mylog("TupleList: exit DESTRUCTOR\n"); mylog("TupleList: exit DESTRUCTOR\n");
} }
......
/* File: tuplelist.h /* File: tuplelist.h
* *
* Description: See "tuplelist.c" * Description: See "tuplelist.c"
* *
* Important Note: This structure and its functions are ONLY used in building manual result * Important Note: This structure and its functions are ONLY used in building manual result
* sets for info functions (SQLTables, SQLColumns, etc.) * sets for info functions (SQLTables, SQLColumns, etc.)
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
* *
*/ */
#ifndef __TUPLELIST_H__ #ifndef __TUPLELIST_H__
#define __TUPLELIST_H__ #define __TUPLELIST_H__
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment