--- /dev/null
+This directory is to save the changes about psqlodbc driver under
+win32 while the main development tree(the parent directory) is
+nearly freezed(i.e. during beta, a while after the release until
+the next branch is made etc). The changes would be reflected
+to the main directory later after all. However note that the
+trial binary version would be sometimes made using the source
+and put on the ftp server for download.
+
+ Hiroshi Inoue inoue@postgresql.org
--- /dev/null
+/*-------
+ * Module: bind.c
+ *
+ * Description: This module contains routines related to binding
+ * columns and parameters.
+ *
+ * Classes: BindInfoClass, ParameterInfoClass
+ *
+ * API functions: SQLBindParameter, SQLBindCol, SQLDescribeParam, SQLNumParams,
+ * SQLParamOptions(NI)
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "bind.h"
+
+#include "environ.h"
+#include "statement.h"
+#include "qresult.h"
+#include "pgtypes.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "pgapifunc.h"
+
+
+/* Bind parameters on a statement handle */
+RETCODE SQL_API
+PGAPI_BindParameter(
+ HSTMT hstmt,
+ UWORD ipar,
+ SWORD fParamType,
+ SWORD fCType,
+ SWORD fSqlType,
+ UDWORD cbColDef,
+ SWORD ibScale,
+ PTR rgbValue,
+ SDWORD cbValueMax,
+ SDWORD FAR * pcbValue)
+{
+ StatementClass *stmt = (StatementClass *) hstmt;
+ static char *func = "PGAPI_BindParameter";
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ SC_clear_error(stmt);
+
+ if (stmt->parameters_allocated < ipar)
+ {
+ ParameterInfoClass *old_parameters;
+ int i,
+ old_parameters_allocated;
+
+ old_parameters = stmt->parameters;
+ old_parameters_allocated = stmt->parameters_allocated;
+
+ stmt->parameters = (ParameterInfoClass *) malloc(sizeof(ParameterInfoClass) * (ipar));
+ if (!stmt->parameters)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Could not allocate memory for statement parameters";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ stmt->parameters_allocated = ipar;
+
+ /* copy the old parameters over */
+ for (i = 0; i < old_parameters_allocated; i++)
+ {
+ /* a structure copy should work */
+ stmt->parameters[i] = old_parameters[i];
+ }
+
+ /* get rid of the old parameters, if there were any */
+ if (old_parameters)
+ free(old_parameters);
+
+ /*
+ * zero out the newly allocated parameters (in case they skipped
+ * some,
+ */
+ /* so we don't accidentally try to use them later) */
+ for (; i < stmt->parameters_allocated; i++)
+ {
+ stmt->parameters[i].buflen = 0;
+ stmt->parameters[i].buffer = 0;
+ stmt->parameters[i].used = 0;
+ stmt->parameters[i].paramType = 0;
+ stmt->parameters[i].CType = 0;
+ stmt->parameters[i].SQLType = 0;
+ stmt->parameters[i].precision = 0;
+ stmt->parameters[i].scale = 0;
+ stmt->parameters[i].data_at_exec = FALSE;
+ stmt->parameters[i].lobj_oid = 0;
+ stmt->parameters[i].EXEC_used = NULL;
+ stmt->parameters[i].EXEC_buffer = NULL;
+ }
+ }
+
+ /* use zero based column numbers for the below part */
+ ipar--;
+
+ /* store the given info */
+ stmt->parameters[ipar].buflen = cbValueMax;
+ stmt->parameters[ipar].buffer = rgbValue;
+ stmt->parameters[ipar].used = pcbValue;
+ stmt->parameters[ipar].paramType = fParamType;
+ stmt->parameters[ipar].CType = fCType;
+ stmt->parameters[ipar].SQLType = fSqlType;
+ stmt->parameters[ipar].precision = cbColDef;
+ stmt->parameters[ipar].scale = ibScale;
+
+ /*
+ * If rebinding a parameter that had data-at-exec stuff in it, then
+ * free that stuff
+ */
+ if (stmt->parameters[ipar].EXEC_used)
+ {
+ free(stmt->parameters[ipar].EXEC_used);
+ stmt->parameters[ipar].EXEC_used = NULL;
+ }
+
+ if (stmt->parameters[ipar].EXEC_buffer)
+ {
+ if (stmt->parameters[ipar].SQLType != SQL_LONGVARBINARY)
+ free(stmt->parameters[ipar].EXEC_buffer);
+ stmt->parameters[ipar].EXEC_buffer = NULL;
+ }
+
+ /* Data at exec macro only valid for C char/binary data */
+ if (pcbValue && (*pcbValue == SQL_DATA_AT_EXEC ||
+ *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET))
+ stmt->parameters[ipar].data_at_exec = TRUE;
+ else
+ stmt->parameters[ipar].data_at_exec = FALSE;
+
+ /* Clear premature result */
+ if (stmt->status == STMT_PREMATURE)
+ SC_recycle_statement(stmt);
+
+ mylog("PGAPI_BindParamater: ipar=%d, paramType=%d, fCType=%d, fSqlType=%d, cbColDef=%d, ibScale=%d, rgbValue=%d, *pcbValue = %d, data_at_exec = %d\n", ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, pcbValue ? *pcbValue : -777, stmt->parameters[ipar].data_at_exec);
+
+ return SQL_SUCCESS;
+}
+
+
+/* Associate a user-supplied buffer with a database column. */
+RETCODE SQL_API
+PGAPI_BindCol(
+ HSTMT hstmt,
+ UWORD icol,
+ SWORD fCType,
+ PTR rgbValue,
+ SDWORD cbValueMax,
+ SDWORD FAR * pcbValue)
+{
+ StatementClass *stmt = (StatementClass *) hstmt;
+ static char *func = "PGAPI_BindCol";
+
+ mylog("%s: entering...\n", func);
+
+ mylog("**** PGAPI_BindCol: stmt = %u, icol = %d\n", stmt, icol);
+ mylog("**** : fCType=%d rgb=%x valusMax=%d pcb=%x\n", fCType, rgbValue, cbValueMax, pcbValue);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+
+ SC_clear_error(stmt);
+
+ if (stmt->status == STMT_EXECUTING)
+ {
+ stmt->errormsg = "Can't bind columns while statement is still executing.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* If the bookmark column is being bound, then just save it */
+ if (icol == 0)
+ {
+ if (rgbValue == NULL)
+ {
+ stmt->bookmark.buffer = NULL;
+ stmt->bookmark.used = NULL;
+ }
+ else
+ {
+ /* Make sure it is the bookmark data type */
+ if (fCType != SQL_C_BOOKMARK)
+ {
+ stmt->errormsg = "Column 0 is not of type SQL_C_BOOKMARK";
+ stmt->errornumber = STMT_PROGRAM_TYPE_OUT_OF_RANGE;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ stmt->bookmark.buffer = rgbValue;
+ stmt->bookmark.used = pcbValue;
+ }
+ return SQL_SUCCESS;
+ }
+
+ /*
+ * Allocate enough bindings if not already done. Most likely,
+ * execution of a statement would have setup the necessary bindings.
+ * But some apps call BindCol before any statement is executed.
+ */
+ if (icol > stmt->bindings_allocated)
+ extend_bindings(stmt, icol);
+
+ /* check to see if the bindings were allocated */
+ if (!stmt->bindings)
+ {
+ stmt->errormsg = "Could not allocate memory for bindings.";
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* use zero based col numbers from here out */
+ icol--;
+
+ /* Reset for SQLGetData */
+ stmt->bindings[icol].data_left = -1;
+
+ if (rgbValue == NULL)
+ {
+ /* we have to unbind the column */
+ stmt->bindings[icol].buflen = 0;
+ stmt->bindings[icol].buffer = NULL;
+ stmt->bindings[icol].used = NULL;
+ stmt->bindings[icol].returntype = SQL_C_CHAR;
+ }
+ else
+ {
+ /* ok, bind that column */
+ stmt->bindings[icol].buflen = cbValueMax;
+ stmt->bindings[icol].buffer = rgbValue;
+ stmt->bindings[icol].used = pcbValue;
+ stmt->bindings[icol].returntype = fCType;
+
+ mylog(" bound buffer[%d] = %u\n", icol, stmt->bindings[icol].buffer);
+ }
+
+ return SQL_SUCCESS;
+}
+
+
+/*
+ * Returns the description of a parameter marker.
+ * This function is listed as not being supported by SQLGetFunctions() because it is
+ * used to describe "parameter markers" (not bound parameters), in which case,
+ * the dbms should return info on the markers. Since Postgres doesn't support that,
+ * it is best to say this function is not supported and let the application assume a
+ * data type (most likely varchar).
+ */
+RETCODE SQL_API
+PGAPI_DescribeParam(
+ HSTMT hstmt,
+ UWORD ipar,
+ SWORD FAR * pfSqlType,
+ UDWORD FAR * pcbColDef,
+ SWORD FAR * pibScale,
+ SWORD FAR * pfNullable)
+{
+ StatementClass *stmt = (StatementClass *) hstmt;
+ static char *func = "PGAPI_DescribeParam";
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ SC_clear_error(stmt);
+
+ if ((ipar < 1) || (ipar > stmt->parameters_allocated))
+ {
+ stmt->errormsg = "Invalid parameter number for PGAPI_DescribeParam.";
+ stmt->errornumber = STMT_BAD_PARAMETER_NUMBER_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ ipar--;
+
+ /*
+ * This implementation is not very good, since it is supposed to
+ * describe
+ */
+ /* parameter markers, not bound parameters. */
+ if (pfSqlType)
+ *pfSqlType = stmt->parameters[ipar].SQLType;
+
+ if (pcbColDef)
+ *pcbColDef = stmt->parameters[ipar].precision;
+
+ if (pibScale)
+ *pibScale = stmt->parameters[ipar].scale;
+
+ if (pfNullable)
+ *pfNullable = pgtype_nullable(stmt, stmt->parameters[ipar].paramType);
+
+ return SQL_SUCCESS;
+}
+
+
+/* Sets multiple values (arrays) for the set of parameter markers. */
+RETCODE SQL_API
+PGAPI_ParamOptions(
+ HSTMT hstmt,
+ UDWORD crow,
+ UDWORD FAR * pirow)
+{
+ static char *func = "PGAPI_ParamOptions";
+ StatementClass *stmt = (StatementClass *) hstmt;
+
+ mylog("%s: entering... %d %x\n", func, crow, pirow);
+
+ if (crow == 1) /* temporary solution and must be
+ * rewritten later */
+ {
+ if (pirow)
+ *pirow = 1;
+ return SQL_SUCCESS;
+ }
+ stmt->errornumber = CONN_UNSUPPORTED_OPTION;
+ stmt->errormsg = "Function not implemented";
+ SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
+ return SQL_ERROR;
+}
+
+
+/*
+ * This function should really talk to the dbms to determine the number of
+ * "parameter markers" (not bound parameters) in the statement. But, since
+ * Postgres doesn't support that, the driver should just count the number of markers
+ * and return that. The reason the driver just can't say this function is unsupported
+ * like it does for SQLDescribeParam is that some applications don't care and try
+ * to call it anyway.
+ * If the statement does not have parameters, it should just return 0.
+ */
+RETCODE SQL_API
+PGAPI_NumParams(
+ HSTMT hstmt,
+ SWORD FAR * pcpar)
+{
+ StatementClass *stmt = (StatementClass *) hstmt;
+ char in_quote = FALSE;
+ unsigned int i;
+ static char *func = "PGAPI_NumParams";
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ SC_clear_error(stmt);
+
+ if (pcpar)
+ *pcpar = 0;
+ else
+ {
+ SC_log_error(func, "pcpar was null", stmt);
+ return SQL_ERROR;
+ }
+
+
+ if (!stmt->statement)
+ {
+ /* no statement has been allocated */
+ stmt->errormsg = "PGAPI_NumParams called with no statement ready.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ else
+ {
+ for (i = 0; i < strlen(stmt->statement); i++)
+ {
+ if (stmt->statement[i] == '?' && !in_quote)
+ (*pcpar)++;
+ else
+ {
+ if (stmt->statement[i] == '\'')
+ in_quote = (in_quote ? FALSE : TRUE);
+ }
+ }
+ return SQL_SUCCESS;
+ }
+}
+
+
+/*
+ * Bindings Implementation
+ */
+BindInfoClass *
+create_empty_bindings(int num_columns)
+{
+ BindInfoClass *new_bindings;
+ int i;
+
+ new_bindings = (BindInfoClass *) malloc(num_columns * sizeof(BindInfoClass));
+ if (!new_bindings)
+ return 0;
+
+ for (i = 0; i < num_columns; i++)
+ {
+ new_bindings[i].buflen = 0;
+ new_bindings[i].buffer = NULL;
+ new_bindings[i].used = NULL;
+ new_bindings[i].data_left = -1;
+ new_bindings[i].ttlbuf = NULL;
+ new_bindings[i].ttlbuflen = 0;
+ }
+
+ return new_bindings;
+}
+
+
+void
+extend_bindings(StatementClass *stmt, int num_columns)
+{
+ static char *func = "extend_bindings";
+ BindInfoClass *new_bindings;
+ int i;
+
+ mylog("%s: entering ... stmt=%u, bindings_allocated=%d, num_columns=%d\n", func, stmt, stmt->bindings_allocated, num_columns);
+
+ /*
+ * if we have too few, allocate room for more, and copy the old
+ * entries into the new structure
+ */
+ if (stmt->bindings_allocated < num_columns)
+ {
+ new_bindings = create_empty_bindings(num_columns);
+ if (!new_bindings)
+ {
+ mylog("%s: unable to create %d new bindings from %d old bindings\n", func, num_columns, stmt->bindings_allocated);
+
+ if (stmt->bindings)
+ {
+ free(stmt->bindings);
+ stmt->bindings = NULL;
+ }
+ stmt->bindings_allocated = 0;
+ return;
+ }
+
+ if (stmt->bindings)
+ {
+ for (i = 0; i < stmt->bindings_allocated; i++)
+ new_bindings[i] = stmt->bindings[i];
+
+ free(stmt->bindings);
+ }
+
+ stmt->bindings = new_bindings;
+ stmt->bindings_allocated = num_columns;
+ }
+
+ /*
+ * There is no reason to zero out extra bindings if there are more
+ * than needed. If an app has allocated extra bindings, let it worry
+ * about it by unbinding those columns.
+ */
+
+ /* SQLBindCol(1..) ... SQLBindCol(10...) # got 10 bindings */
+ /* SQLExecDirect(...) # returns 5 cols */
+ /* SQLExecDirect(...) # returns 10 cols (now OK) */
+
+ mylog("exit extend_bindings\n");
+}
--- /dev/null
+/* File: bind.h
+ *
+ * Description: See "bind.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __BIND_H__
+#define __BIND_H__
+
+#include "psqlodbc.h"
+
+/*
+ * BindInfoClass -- stores information about a bound column
+ */
+struct BindInfoClass_
+{
+ Int4 buflen; /* size of buffer */
+ Int4 data_left; /* amount of data left to read
+ * (SQLGetData) */
+ char *buffer; /* pointer to the buffer */
+ Int4 *used; /* used space in the buffer (for strings
+ * not counting the '\0') */
+ char *ttlbuf; /* to save the large result */
+ Int4 ttlbuflen; /* the buffer length */
+ Int2 returntype; /* kind of conversion to be applied when
+ * returning (SQL_C_DEFAULT,
+ * SQL_C_CHAR...) */
+};
+
+/*
+ * ParameterInfoClass -- stores information about a bound parameter
+ */
+struct ParameterInfoClass_
+{
+ Int4 buflen;
+ char *buffer;
+ Int4 *used;
+ Int2 paramType;
+ Int2 CType;
+ Int2 SQLType;
+ UInt4 precision;
+ Int2 scale;
+ Oid lobj_oid;
+ Int4 *EXEC_used; /* amount of data OR the oid of the large
+ * object */
+ char *EXEC_buffer; /* the data or the FD of the large object */
+ char data_at_exec;
+};
+
+BindInfoClass *create_empty_bindings(int num_columns);
+void extend_bindings(StatementClass *stmt, int num_columns);
+
+#endif
--- /dev/null
+/*-------
+ * Module: columninfo.c
+ *
+ * Description: This module contains routines related to
+ * reading and storing the field information from a query.
+ *
+ * Classes: ColumnInfoClass (Functions prefix: "CI_")
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "pgtypes.h"
+#include "columninfo.h"
+
+#include "connection.h"
+#include "socket.h"
+#include <stdlib.h>
+#include <string.h>
+#include "pgapifunc.h"
+
+ColumnInfoClass *
+CI_Constructor()
+{
+ ColumnInfoClass *rv;
+
+ rv = (ColumnInfoClass *) malloc(sizeof(ColumnInfoClass));
+
+ if (rv)
+ {
+ rv->num_fields = 0;
+ rv->name = NULL;
+ rv->adtid = NULL;
+ rv->adtsize = NULL;
+ rv->display_size = NULL;
+ rv->atttypmod = NULL;
+ }
+
+ return rv;
+}
+
+
+void
+CI_Destructor(ColumnInfoClass *self)
+{
+ CI_free_memory(self);
+
+ free(self);
+}
+
+
+/*
+ * Read in field descriptions.
+ * If self is not null, then also store the information.
+ * If self is null, then just read, don't store.
+ */
+char
+CI_read_fields(ColumnInfoClass *self, ConnectionClass *conn)
+{
+ Int2 lf;
+ int new_num_fields;
+ Oid new_adtid;
+ Int2 new_adtsize;
+ Int4 new_atttypmod = -1;
+
+ /* MAX_COLUMN_LEN may be sufficient but for safety */
+ char new_field_name[2 * MAX_COLUMN_LEN + 1];
+ SocketClass *sock;
+ ConnInfo *ci;
+
+ sock = CC_get_socket(conn);
+ ci = &conn->connInfo;
+
+ /* at first read in the number of fields that are in the query */
+ new_num_fields = (Int2) SOCK_get_int(sock, sizeof(Int2));
+
+ mylog("num_fields = %d\n", new_num_fields);
+
+ if (self)
+ /* according to that allocate memory */
+ CI_set_num_fields(self, new_num_fields);
+
+ /* now read in the descriptions */
+ for (lf = 0; lf < new_num_fields; lf++)
+ {
+ SOCK_get_string(sock, new_field_name, 2 * MAX_COLUMN_LEN);
+ new_adtid = (Oid) SOCK_get_int(sock, 4);
+ new_adtsize = (Int2) SOCK_get_int(sock, 2);
+
+ /* If 6.4 protocol, then read the atttypmod field */
+ if (PG_VERSION_GE(conn, 6.4))
+ {
+ mylog("READING ATTTYPMOD\n");
+ new_atttypmod = (Int4) SOCK_get_int(sock, 4);
+
+ /* Subtract the header length */
+ switch (new_adtid)
+ {
+ case PG_TYPE_DATETIME:
+ case PG_TYPE_TIMESTAMP_NO_TMZONE:
+ case PG_TYPE_TIME:
+ case PG_TYPE_TIME_WITH_TMZONE:
+ break;
+ default:
+ new_atttypmod -= 4;
+ }
+ if (new_atttypmod < 0)
+ new_atttypmod = -1;
+
+ }
+
+ mylog("CI_read_fields: fieldname='%s', adtid=%d, adtsize=%d, atttypmod=%d\n", new_field_name, new_adtid, new_adtsize, new_atttypmod);
+
+ if (self)
+ CI_set_field_info(self, lf, new_field_name, new_adtid, new_adtsize, new_atttypmod);
+ }
+
+ return (SOCK_get_errcode(sock) == 0);
+}
+
+
+void
+CI_free_memory(ColumnInfoClass *self)
+{
+ register Int2 lf;
+ int num_fields = self->num_fields;
+
+ for (lf = 0; lf < num_fields; lf++)
+ {
+ if (self->name[lf])
+ {
+ free(self->name[lf]);
+ self->name[lf] = NULL;
+ }
+ }
+
+ /* Safe to call even if null */
+ self->num_fields = 0;
+ if (self->name)
+ free(self->name);
+ self->name = NULL;
+ if (self->adtid)
+ free(self->adtid);
+ self->adtid = NULL;
+ if (self->adtsize)
+ free(self->adtsize);
+ self->adtsize = NULL;
+ if (self->display_size)
+ free(self->display_size);
+ self->display_size = NULL;
+
+ if (self->atttypmod)
+ free(self->atttypmod);
+ self->atttypmod = NULL;
+}
+
+
+void
+CI_set_num_fields(ColumnInfoClass *self, int new_num_fields)
+{
+ CI_free_memory(self); /* always safe to call */
+
+ self->num_fields = new_num_fields;
+
+ self->name = (char **) malloc(sizeof(char *) * self->num_fields);
+ memset(self->name, 0, sizeof(char *) * self->num_fields);
+ self->adtid = (Oid *) malloc(sizeof(Oid) * self->num_fields);
+ self->adtsize = (Int2 *) malloc(sizeof(Int2) * self->num_fields);
+ self->display_size = (Int2 *) malloc(sizeof(Int2) * self->num_fields);
+ self->atttypmod = (Int4 *) malloc(sizeof(Int4) * self->num_fields);
+}
+
+
+void
+CI_set_field_info(ColumnInfoClass *self, int field_num, char *new_name,
+ Oid new_adtid, Int2 new_adtsize, Int4 new_atttypmod)
+{
+ /* check bounds */
+ if ((field_num < 0) || (field_num >= self->num_fields))
+ return;
+
+ /* store the info */
+ self->name[field_num] = strdup(new_name);
+ self->adtid[field_num] = new_adtid;
+ self->adtsize[field_num] = new_adtsize;
+ self->atttypmod[field_num] = new_atttypmod;
+
+ self->display_size[field_num] = 0;
+}
--- /dev/null
+/* File: columninfo.h
+ *
+ * Description: See "columninfo.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __COLUMNINFO_H__
+#define __COLUMNINFO_H__
+
+#include "psqlodbc.h"
+
+struct ColumnInfoClass_
+{
+ Int2 num_fields;
+ char **name; /* list of type names */
+ Oid *adtid; /* list of type ids */
+ Int2 *adtsize; /* list type sizes */
+ Int2 *display_size; /* the display size (longest row) */
+ Int4 *atttypmod; /* the length of bpchar/varchar */
+};
+
+#define CI_get_num_fields(self) (self->num_fields)
+#define CI_get_oid(self, col) (self->adtid[col])
+#define CI_get_fieldname(self, col) (self->name[col])
+#define CI_get_fieldsize(self, col) (self->adtsize[col])
+#define CI_get_display_size(self, col) (self->display_size[col])
+#define CI_get_atttypmod(self, col) (self->atttypmod[col])
+
+ColumnInfoClass *CI_Constructor(void);
+void CI_Destructor(ColumnInfoClass *self);
+void CI_free_memory(ColumnInfoClass *self);
+char CI_read_fields(ColumnInfoClass *self, ConnectionClass *conn);
+
+/* functions for setting up the fields from within the program, */
+/* without reading from a socket */
+void CI_set_num_fields(ColumnInfoClass *self, int new_num_fields);
+void CI_set_field_info(ColumnInfoClass *self, int field_num, char *new_name,
+ Oid new_adtid, Int2 new_adtsize, Int4 atttypmod);
+
+#endif
--- /dev/null
+/*------
+ * Module: connection.c
+ *
+ * Description: This module contains routines related to
+ * connecting to and disconnecting from the Postgres DBMS.
+ *
+ * Classes: ConnectionClass (Functions prefix: "CC_")
+ *
+ * API functions: SQLAllocConnect, SQLConnect, SQLDisconnect, SQLFreeConnect,
+ * SQLBrowseConnect(NI)
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+/* Multibyte support Eiji Tokuya 2001-03-15 */
+
+#include "connection.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "environ.h"
+#include "socket.h"
+#include "statement.h"
+#include "qresult.h"
+#include "lobj.h"
+#include "dlg_specific.h"
+
+#ifdef MULTIBYTE
+#include "multibyte.h"
+#endif
+
+#include "pgapifunc.h"
+#include "md5.h"
+
+#define STMT_INCREMENT 16 /* how many statement holders to allocate
+ * at a time */
+
+#define PRN_NULLCHECK
+
+extern GLOBAL_VALUES globals;
+
+
+RETCODE SQL_API
+PGAPI_AllocConnect(
+ HENV henv,
+ HDBC FAR * phdbc)
+{
+ EnvironmentClass *env = (EnvironmentClass *) henv;
+ ConnectionClass *conn;
+ static char *func = "PGAPI_AllocConnect";
+
+ mylog("%s: entering...\n", func);
+
+ conn = CC_Constructor();
+ mylog("**** %s: henv = %u, conn = %u\n", func, henv, conn);
+
+ if (!conn)
+ {
+ env->errormsg = "Couldn't allocate memory for Connection object.";
+ env->errornumber = ENV_ALLOC_ERROR;
+ *phdbc = SQL_NULL_HDBC;
+ EN_log_error(func, "", env);
+ return SQL_ERROR;
+ }
+
+ if (!EN_add_connection(env, conn))
+ {
+ env->errormsg = "Maximum number of connections exceeded.";
+ env->errornumber = ENV_ALLOC_ERROR;
+ CC_Destructor(conn);
+ *phdbc = SQL_NULL_HDBC;
+ EN_log_error(func, "", env);
+ return SQL_ERROR;
+ }
+
+ *phdbc = (HDBC) conn;
+
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_Connect(
+ HDBC hdbc,
+ UCHAR FAR * szDSN,
+ SWORD cbDSN,
+ UCHAR FAR * szUID,
+ SWORD cbUID,
+ UCHAR FAR * szAuthStr,
+ SWORD cbAuthStr)
+{
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ ConnInfo *ci;
+ static char *func = "PGAPI_Connect";
+
+ mylog("%s: entering...\n", func);
+
+ if (!conn)
+ {
+ CC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ ci = &conn->connInfo;
+
+ make_string(szDSN, cbDSN, ci->dsn);
+
+ /* get the values for the DSN from the registry */
+ getDSNinfo(ci, CONN_OVERWRITE);
+ logs_on_off(1, ci->drivers.debug, ci->drivers.commlog);
+ /* initialize pg_version from connInfo.protocol */
+ CC_initialize_pg_version(conn);
+
+ /*
+ * override values from DSN info with UID and authStr(pwd) This only
+ * occurs if the values are actually there.
+ */
+ make_string(szUID, cbUID, ci->username);
+ make_string(szAuthStr, cbAuthStr, ci->password);
+
+ /* fill in any defaults */
+ getDSNdefaults(ci);
+
+ qlog("conn = %u, %s(DSN='%s', UID='%s', PWD='%s')\n", conn, func, ci->dsn, ci->username, ci->password);
+
+ if (CC_connect(conn, FALSE) <= 0)
+ {
+ /* Error messages are filled in */
+ CC_log_error(func, "Error on CC_connect", conn);
+ return SQL_ERROR;
+ }
+
+ mylog("%s: returning...\n", func);
+
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_BrowseConnect(
+ HDBC hdbc,
+ UCHAR FAR * szConnStrIn,
+ SWORD cbConnStrIn,
+ UCHAR FAR * szConnStrOut,
+ SWORD cbConnStrOutMax,
+ SWORD FAR * pcbConnStrOut)
+{
+ static char *func = "PGAPI_BrowseConnect";
+
+ mylog("%s: entering...\n", func);
+
+ return SQL_SUCCESS;
+}
+
+
+/* Drop any hstmts open on hdbc and disconnect from database */
+RETCODE SQL_API
+PGAPI_Disconnect(
+ HDBC hdbc)
+{
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ static char *func = "PGAPI_Disconnect";
+
+
+ mylog("%s: entering...\n", func);
+
+ if (!conn)
+ {
+ CC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ qlog("conn=%u, %s\n", conn, func);
+
+ if (conn->status == CONN_EXECUTING)
+ {
+ conn->errornumber = CONN_IN_USE;
+ conn->errormsg = "A transaction is currently being executed";
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+
+ logs_on_off(-1, conn->connInfo.drivers.debug, conn->connInfo.drivers.commlog);
+ mylog("%s: about to CC_cleanup\n", func);
+
+ /* Close the connection and free statements */
+ CC_cleanup(conn);
+
+ mylog("%s: done CC_cleanup\n", func);
+ mylog("%s: returning...\n", func);
+
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_FreeConnect(
+ HDBC hdbc)
+{
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ static char *func = "PGAPI_FreeConnect";
+
+ mylog("%s: entering...\n", func);
+ mylog("**** in %s: hdbc=%u\n", func, hdbc);
+
+ if (!conn)
+ {
+ CC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ /* Remove the connection from the environment */
+ if (!EN_remove_connection(conn->henv, conn))
+ {
+ conn->errornumber = CONN_IN_USE;
+ conn->errormsg = "A transaction is currently being executed";
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+
+ CC_Destructor(conn);
+
+ mylog("%s: returning...\n", func);
+
+ return SQL_SUCCESS;
+}
+
+
+/*
+ * IMPLEMENTATION CONNECTION CLASS
+ */
+ConnectionClass *
+CC_Constructor()
+{
+ ConnectionClass *rv;
+
+ rv = (ConnectionClass *) malloc(sizeof(ConnectionClass));
+
+ if (rv != NULL)
+ {
+ rv->henv = NULL; /* not yet associated with an environment */
+
+ rv->errormsg = NULL;
+ rv->errornumber = 0;
+ rv->errormsg_created = FALSE;
+
+ rv->status = CONN_NOT_CONNECTED;
+ rv->transact_status = CONN_IN_AUTOCOMMIT; /* autocommit by default */
+
+ memset(&rv->connInfo, 0, sizeof(ConnInfo));
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ rv->connInfo.updatable_cursors = 1;
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ memcpy(&(rv->connInfo.drivers), &globals, sizeof(globals));
+ rv->sock = SOCK_Constructor(rv);
+ if (!rv->sock)
+ return NULL;
+
+ rv->stmts = (StatementClass **) malloc(sizeof(StatementClass *) * STMT_INCREMENT);
+ if (!rv->stmts)
+ return NULL;
+ memset(rv->stmts, 0, sizeof(StatementClass *) * STMT_INCREMENT);
+
+ rv->num_stmts = STMT_INCREMENT;
+
+ rv->lobj_type = PG_TYPE_LO;
+
+ rv->ntables = 0;
+ rv->col_info = NULL;
+
+ rv->translation_option = 0;
+ rv->translation_handle = NULL;
+ rv->DataSourceToDriver = NULL;
+ rv->DriverToDataSource = NULL;
+ rv->driver_version = ODBCVER;
+ memset(rv->pg_version, 0, sizeof(rv->pg_version));
+ rv->pg_version_number = .0;
+ rv->pg_version_major = 0;
+ rv->pg_version_minor = 0;
+ rv->ms_jet = 0;
+#ifdef MULTIBYTE
+ rv->client_encoding = NULL;
+ rv->server_encoding = NULL;
+#endif /* MULTIBYTE */
+
+
+ /* Initialize statement options to defaults */
+ /* Statements under this conn will inherit these options */
+
+ InitializeStatementOptions(&rv->stmtOptions);
+
+
+ }
+ return rv;
+}
+
+
+char
+CC_Destructor(ConnectionClass *self)
+{
+ mylog("enter CC_Destructor, self=%u\n", self);
+
+ if (self->status == CONN_EXECUTING)
+ return 0;
+
+ CC_cleanup(self); /* cleanup socket and statements */
+
+ mylog("after CC_Cleanup\n");
+
+#ifdef MULTIBYTE
+ if (self->client_encoding)
+ free(self->client_encoding);
+ if (self->server_encoding)
+ free(self->server_encoding);
+#endif /* MULTIBYTE */
+ /* Free up statement holders */
+ if (self->stmts)
+ {
+ free(self->stmts);
+ self->stmts = NULL;
+ }
+ mylog("after free statement holders\n");
+
+ /* Free cached table info */
+ if (self->col_info)
+ {
+ int i;
+
+ for (i = 0; i < self->ntables; i++)
+ {
+ if (self->col_info[i]->result) /* Free the SQLColumns
+ * result structure */
+ QR_Destructor(self->col_info[i]->result);
+
+ free(self->col_info[i]);
+ }
+ free(self->col_info);
+ }
+
+
+ free(self);
+
+ mylog("exit CC_Destructor\n");
+
+ return 1;
+}
+
+
+/* Return how many cursors are opened on this connection */
+int
+CC_cursor_count(ConnectionClass *self)
+{
+ StatementClass *stmt;
+ int i,
+ count = 0;
+
+ mylog("CC_cursor_count: self=%u, num_stmts=%d\n", self, self->num_stmts);
+
+ for (i = 0; i < self->num_stmts; i++)
+ {
+ stmt = self->stmts[i];
+ if (stmt && stmt->result && stmt->result->cursor)
+ count++;
+ }
+
+ mylog("CC_cursor_count: returning %d\n", count);
+
+ return count;
+}
+
+
+void
+CC_clear_error(ConnectionClass *self)
+{
+ self->errornumber = 0;
+ self->errormsg = NULL;
+ self->errormsg_created = FALSE;
+}
+
+
+/*
+ * Used to cancel a transaction.
+ * We are almost always in the middle of a transaction.
+ */
+char
+CC_abort(ConnectionClass *self)
+{
+ QResultClass *res;
+
+ if (CC_is_in_trans(self))
+ {
+ res = NULL;
+
+ mylog("CC_abort: sending ABORT!\n");
+
+ res = CC_send_query(self, "ABORT", NULL);
+ CC_set_no_trans(self);
+
+ if (res != NULL)
+ QR_Destructor(res);
+ else
+ return FALSE;
+
+ }
+
+ return TRUE;
+}
+
+
+/* This is called by SQLDisconnect also */
+char
+CC_cleanup(ConnectionClass *self)
+{
+ int i;
+ StatementClass *stmt;
+
+ if (self->status == CONN_EXECUTING)
+ return FALSE;
+
+ mylog("in CC_Cleanup, self=%u\n", self);
+
+ /* Cancel an ongoing transaction */
+ /* We are always in the middle of a transaction, */
+ /* even if we are in auto commit. */
+ if (self->sock)
+ CC_abort(self);
+
+ mylog("after CC_abort\n");
+
+ /* This actually closes the connection to the dbase */
+ if (self->sock)
+ {
+ SOCK_Destructor(self->sock);
+ self->sock = NULL;
+ }
+
+ mylog("after SOCK destructor\n");
+
+ /* Free all the stmts on this connection */
+ for (i = 0; i < self->num_stmts; i++)
+ {
+ stmt = self->stmts[i];
+ if (stmt)
+ {
+ stmt->hdbc = NULL; /* prevent any more dbase interactions */
+
+ SC_Destructor(stmt);
+
+ self->stmts[i] = NULL;
+ }
+ }
+
+ /* Check for translation dll */
+#ifdef WIN32
+ if (self->translation_handle)
+ {
+ FreeLibrary(self->translation_handle);
+ self->translation_handle = NULL;
+ }
+#endif
+
+ mylog("exit CC_Cleanup\n");
+ return TRUE;
+}
+
+
+int
+CC_set_translation(ConnectionClass *self)
+{
+
+#ifdef WIN32
+
+ if (self->translation_handle != NULL)
+ {
+ FreeLibrary(self->translation_handle);
+ self->translation_handle = NULL;
+ }
+
+ if (self->connInfo.translation_dll[0] == 0)
+ return TRUE;
+
+ self->translation_option = atoi(self->connInfo.translation_option);
+ self->translation_handle = LoadLibrary(self->connInfo.translation_dll);
+
+ if (self->translation_handle == NULL)
+ {
+ self->errornumber = CONN_UNABLE_TO_LOAD_DLL;
+ self->errormsg = "Could not load the translation DLL.";
+ return FALSE;
+ }
+
+ self->DataSourceToDriver
+ = (DataSourceToDriverProc) GetProcAddress(self->translation_handle,
+ "SQLDataSourceToDriver");
+
+ self->DriverToDataSource
+ = (DriverToDataSourceProc) GetProcAddress(self->translation_handle,
+ "SQLDriverToDataSource");
+
+ if (self->DataSourceToDriver == NULL || self->DriverToDataSource == NULL)
+ {
+ self->errornumber = CONN_UNABLE_TO_LOAD_DLL;
+ self->errormsg = "Could not find translation DLL functions.";
+ return FALSE;
+ }
+#endif
+ return TRUE;
+}
+
+static int
+md5_auth_send(ConnectionClass *self, const char *salt)
+{
+ char *pwd1 = NULL, *pwd2 = NULL;
+ ConnInfo *ci = &(self->connInfo);
+ SocketClass *sock = self->sock;
+
+mylog("MD5 user=%s password=%s\n", ci->username, ci->password);
+ if (!(pwd1 = malloc(MD5_PASSWD_LEN + 1)))
+ return 1;
+ if (!EncryptMD5(ci->password, ci->username, strlen(ci->username), pwd1))
+ {
+ free(pwd1);
+ return 1;
+ }
+ if (!(pwd2 = malloc(MD5_PASSWD_LEN + 1)))
+ {
+ free(pwd1);
+ return 1;
+ }
+ if (!EncryptMD5(pwd1 + strlen("md5"), salt, 4, pwd2))
+ {
+ free(pwd2);
+ free(pwd1);
+ return 1;
+ }
+ free(pwd1);
+ SOCK_put_int(sock, 4 + strlen(pwd2) + 1, 4);
+ SOCK_put_n_char(sock, pwd2, strlen(pwd2) + 1);
+ SOCK_flush_output(sock);
+ free(pwd2);
+ return 0;
+}
+
+char
+CC_connect(ConnectionClass *self, char do_password)
+{
+ StartupPacket sp;
+ StartupPacket6_2 sp62;
+ QResultClass *res;
+ SocketClass *sock;
+ ConnInfo *ci = &(self->connInfo);
+ int areq = -1;
+ int beresp;
+ char msgbuffer[ERROR_MSG_LENGTH];
+ char salt[5];
+ static char *func = "CC_connect";
+
+#ifdef MULTIBYTE
+ char *encoding;
+#endif /* MULTIBYTE */
+
+ mylog("%s: entering...\n", func);
+
+ if (do_password)
+
+ sock = self->sock; /* already connected, just authenticate */
+
+ else
+ {
+ qlog("Global Options: Version='%s', fetch=%d, socket=%d, unknown_sizes=%d, max_varchar_size=%d, max_longvarchar_size=%d\n",
+ POSTGRESDRIVERVERSION,
+ ci->drivers.fetch_max,
+ ci->drivers.socket_buffersize,
+ ci->drivers.unknown_sizes,
+ ci->drivers.max_varchar_size,
+ ci->drivers.max_longvarchar_size);
+ qlog(" disable_optimizer=%d, ksqo=%d, unique_index=%d, use_declarefetch=%d\n",
+ ci->drivers.disable_optimizer,
+ ci->drivers.ksqo,
+ ci->drivers.unique_index,
+ ci->drivers.use_declarefetch);
+ qlog(" text_as_longvarchar=%d, unknowns_as_longvarchar=%d, bools_as_char=%d\n",
+ ci->drivers.text_as_longvarchar,
+ ci->drivers.unknowns_as_longvarchar,
+ ci->drivers.bools_as_char);
+
+#ifdef MULTIBYTE
+ encoding = check_client_encoding(ci->conn_settings);
+ if (encoding && strcmp(encoding, "OTHER"))
+ self->client_encoding = strdup(encoding);
+ else
+ {
+ encoding = check_client_encoding(ci->drivers.conn_settings);
+ if (encoding && strcmp(encoding, "OTHER"))
+ self->client_encoding = strdup(encoding);
+ }
+ qlog(" extra_systable_prefixes='%s', conn_settings='%s' conn_encoding='%s'\n",
+ ci->drivers.extra_systable_prefixes,
+ ci->drivers.conn_settings,
+ encoding ? encoding : "");
+#else
+ qlog(" extra_systable_prefixes='%s', conn_settings='%s'\n",
+ ci->drivers.extra_systable_prefixes,
+ ci->drivers.conn_settings);
+#endif
+
+ if (self->status != CONN_NOT_CONNECTED)
+ {
+ self->errormsg = "Already connected.";
+ self->errornumber = CONN_OPENDB_ERROR;
+ return 0;
+ }
+
+ if (ci->server[0] == '\0' || ci->port[0] == '\0' || ci->database[0] == '\0')
+ {
+ self->errornumber = CONN_INIREAD_ERROR;
+ self->errormsg = "Missing server name, port, or database name in call to CC_connect.";
+ return 0;
+ }
+
+ mylog("CC_connect(): DSN = '%s', server = '%s', port = '%s', database = '%s', username = '%s', password='%s'\n", ci->dsn, ci->server, ci->port, ci->database, ci->username, ci->password);
+
+another_version_retry:
+
+ /*
+ * If the socket was closed for some reason (like a SQLDisconnect,
+ * but no SQLFreeConnect then create a socket now.
+ */
+ if (!self->sock)
+ {
+ self->sock = SOCK_Constructor(self);
+ if (!self->sock)
+ {
+ self->errornumber = CONNECTION_SERVER_NOT_REACHED;
+ self->errormsg = "Could not open a socket to the server";
+ return 0;
+ }
+ }
+
+ sock = self->sock;
+
+ mylog("connecting to the server socket...\n");
+
+ SOCK_connect_to(sock, (short) atoi(ci->port), ci->server);
+ if (SOCK_get_errcode(sock) != 0)
+ {
+ mylog("connection to the server socket failed.\n");
+ self->errornumber = CONNECTION_SERVER_NOT_REACHED;
+ self->errormsg = "Could not connect to the server";
+ return 0;
+ }
+ mylog("connection to the server socket succeeded.\n");
+
+ if (PROTOCOL_62(ci))
+ {
+ sock->reverse = TRUE; /* make put_int and get_int work
+ * for 6.2 */
+
+ memset(&sp62, 0, sizeof(StartupPacket6_2));
+ SOCK_put_int(sock, htonl(4 + sizeof(StartupPacket6_2)), 4);
+ sp62.authtype = htonl(NO_AUTHENTICATION);
+ strncpy(sp62.database, ci->database, PATH_SIZE);
+ strncpy(sp62.user, ci->username, NAMEDATALEN);
+ SOCK_put_n_char(sock, (char *) &sp62, sizeof(StartupPacket6_2));
+ SOCK_flush_output(sock);
+ }
+ else
+ {
+ memset(&sp, 0, sizeof(StartupPacket));
+
+ mylog("sizeof startup packet = %d\n", sizeof(StartupPacket));
+
+ /* Send length of Authentication Block */
+ SOCK_put_int(sock, 4 + sizeof(StartupPacket), 4);
+
+ if (PROTOCOL_63(ci))
+ sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_63);
+ else
+ sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_LATEST);
+
+ strncpy(sp.database, ci->database, SM_DATABASE);
+ strncpy(sp.user, ci->username, SM_USER);
+
+ SOCK_put_n_char(sock, (char *) &sp, sizeof(StartupPacket));
+ SOCK_flush_output(sock);
+ }
+
+ mylog("sent the authentication block.\n");
+
+ if (sock->errornumber != 0)
+ {
+ mylog("couldn't send the authentication block properly.\n");
+ self->errornumber = CONN_INVALID_AUTHENTICATION;
+ self->errormsg = "Sending the authentication packet failed";
+ return 0;
+ }
+ mylog("sent the authentication block successfully.\n");
+ }
+
+
+ mylog("gonna do authentication\n");
+
+
+ /*
+ * Now get the authentication request from backend
+ */
+
+ if (!PROTOCOL_62(ci))
+ {
+ BOOL before_64 = PG_VERSION_LT(self, 6.4),
+ ReadyForQuery = FALSE;
+
+ do
+ {
+ if (do_password)
+ beresp = 'R';
+ else
+ {
+ beresp = SOCK_get_char(sock);
+ mylog("auth got '%c'\n", beresp);
+ }
+
+ switch (beresp)
+ {
+ case 'E':
+
+ SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
+ self->errornumber = CONN_INVALID_AUTHENTICATION;
+ self->errormsg = msgbuffer;
+ qlog("ERROR from backend during authentication: '%s'\n", self->errormsg);
+ if (strncmp(msgbuffer, "Unsupported frontend protocol", 29) == 0)
+ { /* retry older version */
+ if (PROTOCOL_63(ci))
+ strcpy(ci->protocol, PG62);
+ else
+ strcpy(ci->protocol, PG63);
+ SOCK_Destructor(sock);
+ self->sock = (SocketClass *) 0;
+ CC_initialize_pg_version(self);
+ goto another_version_retry;
+ }
+
+ return 0;
+ case 'R':
+
+ if (do_password)
+ {
+ mylog("in 'R' do_password\n");
+ areq = AUTH_REQ_PASSWORD;
+ do_password = FALSE;
+ }
+ else
+ {
+
+ areq = SOCK_get_int(sock, 4);
+ if (areq == AUTH_REQ_MD5)
+ SOCK_get_n_char(sock, salt, 4);
+ if (areq == AUTH_REQ_CRYPT)
+ SOCK_get_n_char(sock, salt, 2);
+
+ mylog("areq = %d\n", areq);
+ }
+ switch (areq)
+ {
+ case AUTH_REQ_OK:
+ break;
+
+ case AUTH_REQ_KRB4:
+ self->errormsg = "Kerberos 4 authentication not supported";
+ self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
+ return 0;
+
+ case AUTH_REQ_KRB5:
+ self->errormsg = "Kerberos 5 authentication not supported";
+ self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
+ return 0;
+
+ case AUTH_REQ_PASSWORD:
+ mylog("in AUTH_REQ_PASSWORD\n");
+
+ if (ci->password[0] == '\0')
+ {
+ self->errornumber = CONNECTION_NEED_PASSWORD;
+ self->errormsg = "A password is required for this connection.";
+ return -1; /* need password */
+ }
+
+ mylog("past need password\n");
+
+ SOCK_put_int(sock, 4 + strlen(ci->password) + 1, 4);
+ SOCK_put_n_char(sock, ci->password, strlen(ci->password) + 1);
+ SOCK_flush_output(sock);
+
+ mylog("past flush\n");
+ break;
+
+ case AUTH_REQ_CRYPT:
+ self->errormsg = "Password crypt authentication not supported";
+ self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
+ return 0;
+ case AUTH_REQ_MD5:
+ mylog("in AUTH_REQ_MD5\n");
+ if (ci->password[0] == '\0')
+ {
+ self->errornumber = CONNECTION_NEED_PASSWORD;
+ self->errormsg = "A password is required for this connection.";
+ return -1; /* need password */
+ }
+ if (md5_auth_send(self, salt))
+ {
+ self->errormsg = "md5 hashing failed";
+ self->errornumber = CONN_INVALID_AUTHENTICATION;
+ return 0;
+ }
+ break;
+
+ case AUTH_REQ_SCM_CREDS:
+ self->errormsg = "Unix socket credential authentication not supported";
+ self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
+ return 0;
+
+ default:
+ self->errormsg = "Unknown authentication type";
+ self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
+ return 0;
+ }
+ break;
+ case 'K': /* Secret key (6.4 protocol) */
+ (void) SOCK_get_int(sock, 4); /* pid */
+ (void) SOCK_get_int(sock, 4); /* key */
+
+ break;
+ case 'Z': /* Backend is ready for new query (6.4) */
+ ReadyForQuery = TRUE;
+ break;
+ default:
+ self->errormsg = "Unexpected protocol character during authentication";
+ self->errornumber = CONN_INVALID_AUTHENTICATION;
+ return 0;
+ }
+
+ /*
+ * There were no ReadyForQuery responce before 6.4.
+ */
+ if (before_64 && areq == AUTH_REQ_OK)
+ ReadyForQuery = TRUE;
+ } while (!ReadyForQuery);
+ }
+
+
+ CC_clear_error(self); /* clear any password error */
+
+ /*
+ * send an empty query in order to find out whether the specified
+ * database really exists on the server machine
+ */
+ mylog("sending an empty query...\n");
+
+ res = CC_send_query(self, " ", NULL);
+ if (res == NULL || QR_get_status(res) != PGRES_EMPTY_QUERY)
+ {
+ mylog("got no result from the empty query. (probably database does not exist)\n");
+ self->errornumber = CONNECTION_NO_SUCH_DATABASE;
+ self->errormsg = "The database does not exist on the server\nor user authentication failed.";
+ if (res != NULL)
+ QR_Destructor(res);
+ return 0;
+ }
+ if (res)
+ QR_Destructor(res);
+
+ mylog("empty query seems to be OK.\n");
+
+ CC_set_translation(self);
+
+ /*
+ * Send any initial settings
+ */
+
+ /*
+ * Since these functions allocate statements, and since the connection
+ * is not established yet, it would violate odbc state transition
+ * rules. Therefore, these functions call the corresponding local
+ * function instead.
+ */
+ CC_send_settings(self);
+ CC_lookup_lo(self); /* a hack to get the oid of our large
+ * object oid type */
+ CC_lookup_pg_version(self); /* Get PostgreSQL version for SQLGetInfo
+ * use */
+
+ CC_clear_error(self); /* clear any initial command errors */
+ self->status = CONN_CONNECTED;
+
+ mylog("%s: returning...\n", func);
+
+ return 1;
+
+}
+
+
+char
+CC_add_statement(ConnectionClass *self, StatementClass *stmt)
+{
+ int i;
+
+ mylog("CC_add_statement: self=%u, stmt=%u\n", self, stmt);
+
+ for (i = 0; i < self->num_stmts; i++)
+ {
+ if (!self->stmts[i])
+ {
+ stmt->hdbc = self;
+ self->stmts[i] = stmt;
+ return TRUE;
+ }
+ }
+
+ /* no more room -- allocate more memory */
+ self->stmts = (StatementClass **) realloc(self->stmts, sizeof(StatementClass *) * (STMT_INCREMENT + self->num_stmts));
+ if (!self->stmts)
+ return FALSE;
+
+ memset(&self->stmts[self->num_stmts], 0, sizeof(StatementClass *) * STMT_INCREMENT);
+
+ stmt->hdbc = self;
+ self->stmts[self->num_stmts] = stmt;
+
+ self->num_stmts += STMT_INCREMENT;
+
+ return TRUE;
+}
+
+
+char
+CC_remove_statement(ConnectionClass *self, StatementClass *stmt)
+{
+ int i;
+
+ for (i = 0; i < self->num_stmts; i++)
+ {
+ if (self->stmts[i] == stmt && stmt->status != STMT_EXECUTING)
+ {
+ self->stmts[i] = NULL;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/*
+ * Create a more informative error message by concatenating the connection
+ * error message with its socket error message.
+ */
+char *
+CC_create_errormsg(ConnectionClass *self)
+{
+ SocketClass *sock = self->sock;
+ int pos;
+ static char msg[4096];
+
+ mylog("enter CC_create_errormsg\n");
+
+ msg[0] = '\0';
+
+ if (self->errormsg)
+ strcpy(msg, self->errormsg);
+
+ mylog("msg = '%s'\n", msg);
+
+ if (sock && sock->errormsg && sock->errormsg[0] != '\0')
+ {
+ pos = strlen(msg);
+ sprintf(&msg[pos], ";\n%s", sock->errormsg);
+ }
+
+ mylog("exit CC_create_errormsg\n");
+ return msg;
+}
+
+
+char
+CC_get_error(ConnectionClass *self, int *number, char **message)
+{
+ int rv;
+
+ mylog("enter CC_get_error\n");
+
+ /* Create a very informative errormsg if it hasn't been done yet. */
+ if (!self->errormsg_created)
+ {
+ self->errormsg = CC_create_errormsg(self);
+ self->errormsg_created = TRUE;
+ }
+
+ if (self->errornumber)
+ {
+ *number = self->errornumber;
+ *message = self->errormsg;
+ }
+ rv = (self->errornumber != 0);
+
+ self->errornumber = 0; /* clear the error */
+
+ mylog("exit CC_get_error\n");
+
+ return rv;
+}
+
+
+/*
+ * The "result_in" is only used by QR_next_tuple() to fetch another group of rows into
+ * the same existing QResultClass (this occurs when the tuple cache is depleted and
+ * needs to be re-filled).
+ *
+ * The "cursor" is used by SQLExecute to associate a statement handle as the cursor name
+ * (i.e., C3326857) for SQL select statements. This cursor is then used in future
+ * 'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements.
+ */
+QResultClass *
+CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi)
+{
+ QResultClass *result_in = NULL,
+ *res = NULL,
+ *retres = NULL;
+ char swallow,
+ *wq;
+ int id;
+ SocketClass *sock = self->sock;
+ int maxlen,
+ empty_reqs;
+ BOOL msg_truncated,
+ ReadyToReturn,
+ tuples_return = FALSE,
+ query_completed = FALSE,
+ before_64 = PG_VERSION_LT(self, 6.4);
+
+ /* ERROR_MSG_LENGTH is suffcient */
+ static char msgbuffer[ERROR_MSG_LENGTH + 1];
+
+ /* QR_set_command() dups this string so doesn't need static */
+ char cmdbuffer[ERROR_MSG_LENGTH + 1];
+
+ mylog("send_query(): conn=%u, query='%s'\n", self, query);
+ qlog("conn=%u, query='%s'\n", self, query);
+
+ /* Indicate that we are sending a query to the backend */
+ maxlen = CC_get_max_query_len(self);
+ if (maxlen > 0 && maxlen < (int) strlen(query) + 1)
+ {
+ self->errornumber = CONNECTION_MSG_TOO_LONG;
+ self->errormsg = "Query string is too long";
+ return NULL;
+ }
+
+ if ((NULL == query) || (query[0] == '\0'))
+ return NULL;
+
+ if (SOCK_get_errcode(sock) != 0)
+ {
+ self->errornumber = CONNECTION_COULD_NOT_SEND;
+ self->errormsg = "Could not send Query to backend";
+ CC_set_no_trans(self);
+ return NULL;
+ }
+
+ SOCK_put_char(sock, 'Q');
+ if (SOCK_get_errcode(sock) != 0)
+ {
+ self->errornumber = CONNECTION_COULD_NOT_SEND;
+ self->errormsg = "Could not send Query to backend";
+ CC_set_no_trans(self);
+ return NULL;
+ }
+
+ SOCK_put_string(sock, query);
+ SOCK_flush_output(sock);
+
+ if (SOCK_get_errcode(sock) != 0)
+ {
+ self->errornumber = CONNECTION_COULD_NOT_SEND;
+ self->errormsg = "Could not send Query to backend";
+ CC_set_no_trans(self);
+ return NULL;
+ }
+
+ mylog("send_query: done sending query\n");
+
+ ReadyToReturn = FALSE;
+ empty_reqs = 0;
+ for (wq = query; isspace((unsigned char) *wq); wq++)
+ ;
+ if (*wq == '\0')
+ empty_reqs = 1;
+ while (!ReadyToReturn)
+ {
+ /* what type of message is coming now ? */
+ id = SOCK_get_char(sock);
+
+ if ((SOCK_get_errcode(sock) != 0) || (id == EOF))
+ {
+ self->errornumber = CONNECTION_NO_RESPONSE;
+ self->errormsg = "No response from the backend";
+
+ mylog("send_query: 'id' - %s\n", self->errormsg);
+ CC_set_no_trans(self);
+ ReadyToReturn = TRUE;
+ retres = NULL;
+ break;
+ }
+
+ mylog("send_query: got id = '%c'\n", id);
+
+ switch (id)
+ {
+ case 'A': /* Asynchronous Messages are ignored */
+ (void) SOCK_get_int(sock, 4); /* id of notification */
+ SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
+ /* name of the relation the message comes from */
+ break;
+ case 'C': /* portal query command, no tuples
+ * returned */
+ /* read in the return message from the backend */
+ SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
+ if (SOCK_get_errcode(sock) != 0)
+ {
+ self->errornumber = CONNECTION_NO_RESPONSE;
+ self->errormsg = "No response from backend while receiving a portal query command";
+ mylog("send_query: 'C' - %s\n", self->errormsg);
+ CC_set_no_trans(self);
+ ReadyToReturn = TRUE;
+ retres = NULL;
+ }
+ else
+ {
+ mylog("send_query: ok - 'C' - %s\n", cmdbuffer);
+
+ if (res == NULL) /* allow for "show" style notices */
+ res = QR_Constructor();
+
+ mylog("send_query: setting cmdbuffer = '%s'\n", cmdbuffer);
+
+ /* Only save the first command */
+ if (QR_command_successful(res))
+ QR_set_status(res, PGRES_COMMAND_OK);
+ QR_set_command(res, cmdbuffer);
+ query_completed = TRUE;
+ mylog("send_query: returning res = %u\n", res);
+ if (!before_64)
+ break;
+
+ /*
+ * (Quotation from the original comments) since
+ * backend may produce more than one result for some
+ * commands we need to poll until clear so we send an
+ * empty query, and keep reading out of the pipe until
+ * an 'I' is received
+ */
+
+ if (empty_reqs == 0)
+ {
+ SOCK_put_string(sock, "Q ");
+ SOCK_flush_output(sock);
+ empty_reqs++;
+ }
+ }
+ break;
+ case 'Z': /* Backend is ready for new query (6.4) */
+ if (empty_reqs == 0)
+ {
+ ReadyToReturn = TRUE;
+ if (res && QR_get_aborted(res))
+ retres = res;
+ else if (tuples_return)
+ retres = result_in;
+ else if (query_completed)
+ retres = res;
+ else
+ ReadyToReturn = FALSE;
+ }
+ break;
+ case 'N': /* NOTICE: */
+ msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
+ if (!res)
+ res = QR_Constructor();
+ if (QR_command_successful(res))
+ QR_set_status(res, PGRES_NONFATAL_ERROR);
+ QR_set_notice(res, cmdbuffer); /* will dup this string */
+
+ mylog("~~~ NOTICE: '%s'\n", cmdbuffer);
+ qlog("NOTICE from backend during send_query: '%s'\n", cmdbuffer);
+ while (msg_truncated)
+ msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
+
+ continue; /* dont return a result -- continue
+ * reading */
+
+ case 'I': /* The server sends an empty query */
+ /* There is a closing '\0' following the 'I', so we eat it */
+ swallow = SOCK_get_char(sock);
+ if (!res)
+ res = QR_Constructor();
+ if ((swallow != '\0') || SOCK_get_errcode(sock) != 0)
+ {
+ self->errornumber = CONNECTION_BACKEND_CRAZY;
+ self->errormsg = "Unexpected protocol character from backend (send_query - I)";
+ QR_set_status(res, PGRES_FATAL_ERROR);
+ ReadyToReturn = TRUE;
+ retres = res;
+ break;
+ }
+ else
+ {
+ /* We return the empty query */
+ QR_set_status(res, PGRES_EMPTY_QUERY);
+ }
+ if (empty_reqs > 0)
+ {
+ if (--empty_reqs == 0)
+ query_completed = TRUE;
+ }
+ break;
+ case 'E':
+ msg_truncated = SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
+
+ /* Remove a newline */
+ if (msgbuffer[0] != '\0' && msgbuffer[strlen(msgbuffer) - 1] == '\n')
+ msgbuffer[strlen(msgbuffer) - 1] = '\0';
+
+ self->errormsg = msgbuffer;
+
+ mylog("send_query: 'E' - %s\n", self->errormsg);
+ qlog("ERROR from backend during send_query: '%s'\n", self->errormsg);
+
+ /* We should report that an error occured. Zoltan */
+ if (!res)
+ res = QR_Constructor();
+
+ if (!strncmp(self->errormsg, "FATAL", 5))
+ {
+ self->errornumber = CONNECTION_SERVER_REPORTED_ERROR;
+ CC_set_no_trans(self);
+ }
+ else
+ self->errornumber = CONNECTION_SERVER_REPORTED_WARNING;
+ QR_set_status(res, PGRES_FATAL_ERROR);
+ QR_set_aborted(res, TRUE);
+ while (msg_truncated)
+ msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
+
+ query_completed = TRUE;
+ break;
+
+ case 'P': /* get the Portal name */
+ SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
+ break;
+ case 'T': /* Tuple results start here */
+ result_in = qi ? qi->result_in : NULL;
+
+ if (result_in == NULL)
+ {
+ result_in = QR_Constructor();
+ mylog("send_query: 'T' no result_in: res = %u\n", result_in);
+ if (!result_in)
+ {
+ self->errornumber = CONNECTION_COULD_NOT_RECEIVE;
+ self->errormsg = "Could not create result info in send_query.";
+ ReadyToReturn = TRUE;
+ retres = NULL;
+ break;
+ }
+
+ if (qi)
+ QR_set_cache_size(result_in, qi->row_size);
+
+ if (!QR_fetch_tuples(result_in, self, qi ? qi->cursor : NULL))
+ {
+ self->errornumber = CONNECTION_COULD_NOT_RECEIVE;
+ self->errormsg = QR_get_message(result_in);
+ ReadyToReturn = TRUE;
+ retres = NULL;
+ break;
+ }
+ }
+ else
+ { /* next fetch, so reuse an existing result */
+
+ /*
+ * called from QR_next_tuple and must return
+ * immediately.
+ */
+ ReadyToReturn = TRUE;
+ if (!QR_fetch_tuples(result_in, NULL, NULL))
+ {
+ self->errornumber = CONNECTION_COULD_NOT_RECEIVE;
+ self->errormsg = QR_get_message(result_in);
+ retres = NULL;
+ break;
+ }
+ retres = result_in;
+ }
+
+ tuples_return = TRUE;
+ break;
+ case 'D': /* Copy in command began successfully */
+ if (!res)
+ res = QR_Constructor();
+ if (QR_command_successful(res))
+ QR_set_status(res, PGRES_COPY_IN);
+ ReadyToReturn = TRUE;
+ retres = res;
+ break;
+ case 'B': /* Copy out command began successfully */
+ if (!res)
+ res = QR_Constructor();
+ if (QR_command_successful(res))
+ QR_set_status(res, PGRES_COPY_OUT);
+ ReadyToReturn = TRUE;
+ retres = res;
+ break;
+ default:
+ self->errornumber = CONNECTION_BACKEND_CRAZY;
+ self->errormsg = "Unexpected protocol character from backend (send_query)";
+ CC_set_no_trans(self);
+
+ mylog("send_query: error - %s\n", self->errormsg);
+ ReadyToReturn = TRUE;
+ retres = NULL;
+ break;
+ }
+
+ /*
+ * There were no ReadyForQuery response before 6.4.
+ */
+ if (before_64)
+ {
+ if (empty_reqs == 0 && (query_completed || tuples_return))
+ break;
+ }
+ }
+
+ /*
+ * Break before being ready to return.
+ */
+ if (!ReadyToReturn)
+ {
+ if (res && QR_get_aborted(res))
+ retres = res;
+ else if (tuples_return)
+ retres = result_in;
+ else
+ retres = res;
+ }
+
+ /*
+ * set notice message to result_in.
+ */
+ if (result_in && res && retres == result_in)
+ {
+ if (QR_command_successful(result_in))
+ QR_set_status(result_in, QR_get_status(res));
+ QR_set_notice(result_in, QR_get_notice(res));
+ }
+
+ /*
+ * Cleanup garbage results before returning.
+ */
+ if (res && retres != res)
+ QR_Destructor(res);
+ if (result_in && retres != result_in)
+ {
+ if (qi && qi->result_in)
+ ;
+ else
+ QR_Destructor(result_in);
+ }
+ return retres;
+}
+
+
+int
+CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *args, int nargs)
+{
+ char id,
+ c,
+ done;
+ SocketClass *sock = self->sock;
+
+ /* ERROR_MSG_LENGTH is sufficient */
+ static char msgbuffer[ERROR_MSG_LENGTH + 1];
+ int i;
+
+ mylog("send_function(): conn=%u, fnid=%d, result_is_int=%d, nargs=%d\n", self, fnid, result_is_int, nargs);
+
+ if (SOCK_get_errcode(sock) != 0)
+ {
+ self->errornumber = CONNECTION_COULD_NOT_SEND;
+ self->errormsg = "Could not send function to backend";
+ CC_set_no_trans(self);
+ return FALSE;
+ }
+
+ SOCK_put_string(sock, "F ");
+ if (SOCK_get_errcode(sock) != 0)
+ {
+ self->errornumber = CONNECTION_COULD_NOT_SEND;
+ self->errormsg = "Could not send function to backend";
+ CC_set_no_trans(self);
+ return FALSE;
+ }
+
+ SOCK_put_int(sock, fnid, 4);
+ SOCK_put_int(sock, nargs, 4);
+
+
+ mylog("send_function: done sending function\n");
+
+ for (i = 0; i < nargs; ++i)
+ {
+ mylog(" arg[%d]: len = %d, isint = %d, integer = %d, ptr = %u\n", i, args[i].len, args[i].isint, args[i].u.integer, args[i].u.ptr);
+
+ SOCK_put_int(sock, args[i].len, 4);
+ if (args[i].isint)
+ SOCK_put_int(sock, args[i].u.integer, 4);
+ else
+ SOCK_put_n_char(sock, (char *) args[i].u.ptr, args[i].len);
+
+
+ }
+
+ mylog(" done sending args\n");
+
+ SOCK_flush_output(sock);
+ mylog(" after flush output\n");
+
+ done = FALSE;
+ while (!done)
+ {
+ id = SOCK_get_char(sock);
+ mylog(" got id = %c\n", id);
+
+ switch (id)
+ {
+ case 'V':
+ done = TRUE;
+ break; /* ok */
+
+ case 'N':
+ SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
+ mylog("send_function(V): 'N' - %s\n", msgbuffer);
+ /* continue reading */
+ break;
+
+ case 'E':
+ SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
+ self->errormsg = msgbuffer;
+
+ mylog("send_function(V): 'E' - %s\n", self->errormsg);
+ qlog("ERROR from backend during send_function: '%s'\n", self->errormsg);
+
+ return FALSE;
+
+ case 'Z':
+ break;
+
+ default:
+ self->errornumber = CONNECTION_BACKEND_CRAZY;
+ self->errormsg = "Unexpected protocol character from backend (send_function, args)";
+ CC_set_no_trans(self);
+
+ mylog("send_function: error - %s\n", self->errormsg);
+ return FALSE;
+ }
+ }
+
+ id = SOCK_get_char(sock);
+ for (;;)
+ {
+ switch (id)
+ {
+ case 'G': /* function returned properly */
+ mylog(" got G!\n");
+
+ *actual_result_len = SOCK_get_int(sock, 4);
+ mylog(" actual_result_len = %d\n", *actual_result_len);
+
+ if (result_is_int)
+ *((int *) result_buf) = SOCK_get_int(sock, 4);
+ else
+ SOCK_get_n_char(sock, (char *) result_buf, *actual_result_len);
+
+ mylog(" after get result\n");
+
+ c = SOCK_get_char(sock); /* get the last '0' */
+
+ mylog(" after get 0\n");
+
+ return TRUE;
+
+ case 'E':
+ SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
+ self->errormsg = msgbuffer;
+
+ mylog("send_function(G): 'E' - %s\n", self->errormsg);
+ qlog("ERROR from backend during send_function: '%s'\n", self->errormsg);
+
+ return FALSE;
+
+ case 'N':
+ SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
+
+ mylog("send_function(G): 'N' - %s\n", msgbuffer);
+ qlog("NOTICE from backend during send_function: '%s'\n", msgbuffer);
+
+ continue; /* dont return a result -- continue
+ * reading */
+
+ case '0': /* empty result */
+ return TRUE;
+
+ default:
+ self->errornumber = CONNECTION_BACKEND_CRAZY;
+ self->errormsg = "Unexpected protocol character from backend (send_function, result)";
+ CC_set_no_trans(self);
+
+ mylog("send_function: error - %s\n", self->errormsg);
+ return FALSE;
+ }
+ }
+}
+
+
+char
+CC_send_settings(ConnectionClass *self)
+{
+ /* char ini_query[MAX_MESSAGE_LEN]; */
+ ConnInfo *ci = &(self->connInfo);
+
+/* QResultClass *res; */
+ HSTMT hstmt;
+ StatementClass *stmt;
+ RETCODE result;
+ char status = TRUE;
+ char *cs,
+ *ptr;
+ static char *func = "CC_send_settings";
+
+
+ mylog("%s: entering...\n", func);
+
+/*
+ * This function must use the local odbc API functions since the odbc state
+ * has not transitioned to "connected" yet.
+ */
+
+ result = PGAPI_AllocStmt(self, &hstmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ return FALSE;
+ stmt = (StatementClass *) hstmt;
+
+ stmt->internal = TRUE; /* ensure no BEGIN/COMMIT/ABORT stuff */
+
+ /* Set the Datestyle to the format the driver expects it to be in */
+ result = PGAPI_ExecDirect(hstmt, "set DateStyle to 'ISO'", SQL_NTS);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ status = FALSE;
+
+ mylog("%s: result %d, status %d from set DateStyle\n", func, result, status);
+
+ /* Disable genetic optimizer based on global flag */
+ if (ci->drivers.disable_optimizer)
+ {
+ result = PGAPI_ExecDirect(hstmt, "set geqo to 'OFF'", SQL_NTS);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ status = FALSE;
+
+ mylog("%s: result %d, status %d from set geqo\n", func, result, status);
+
+ }
+
+ /* KSQO */
+ if (ci->drivers.ksqo)
+ {
+ result = PGAPI_ExecDirect(hstmt, "set ksqo to 'ON'", SQL_NTS);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ status = FALSE;
+
+ mylog("%s: result %d, status %d from set ksqo\n", func, result, status);
+
+ }
+
+ /* Global settings */
+ if (ci->drivers.conn_settings[0] != '\0')
+ {
+ cs = strdup(ci->drivers.conn_settings);
+ ptr = strtok(cs, ";");
+ while (ptr)
+ {
+ result = PGAPI_ExecDirect(hstmt, ptr, SQL_NTS);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ status = FALSE;
+
+ mylog("%s: result %d, status %d from '%s'\n", func, result, status, ptr);
+
+ ptr = strtok(NULL, ";");
+ }
+
+ free(cs);
+ }
+
+ /* Per Datasource settings */
+ if (ci->conn_settings[0] != '\0')
+ {
+ cs = strdup(ci->conn_settings);
+ ptr = strtok(cs, ";");
+ while (ptr)
+ {
+ result = PGAPI_ExecDirect(hstmt, ptr, SQL_NTS);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ status = FALSE;
+
+ mylog("%s: result %d, status %d from '%s'\n", func, result, status, ptr);
+
+ ptr = strtok(NULL, ";");
+ }
+
+ free(cs);
+ }
+
+
+ PGAPI_FreeStmt(hstmt, SQL_DROP);
+
+ return status;
+}
+
+
+/*
+ * This function is just a hack to get the oid of our Large Object oid type.
+ * If a real Large Object oid type is made part of Postgres, this function
+ * will go away and the define 'PG_TYPE_LO' will be updated.
+ */
+void
+CC_lookup_lo(ConnectionClass *self)
+{
+ HSTMT hstmt;
+ StatementClass *stmt;
+ RETCODE result;
+ static char *func = "CC_lookup_lo";
+
+ mylog("%s: entering...\n", func);
+
+/*
+ * This function must use the local odbc API functions since the odbc state
+ * has not transitioned to "connected" yet.
+ */
+ result = PGAPI_AllocStmt(self, &hstmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ return;
+ stmt = (StatementClass *) hstmt;
+
+ result = PGAPI_ExecDirect(hstmt, "select oid from pg_type where typname='" PG_TYPE_LO_NAME "'", SQL_NTS);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ PGAPI_FreeStmt(hstmt, SQL_DROP);
+ return;
+ }
+
+ result = PGAPI_Fetch(hstmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ PGAPI_FreeStmt(hstmt, SQL_DROP);
+ return;
+ }
+
+ result = PGAPI_GetData(hstmt, 1, SQL_C_SLONG, &self->lobj_type, sizeof(self->lobj_type), NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ PGAPI_FreeStmt(hstmt, SQL_DROP);
+ return;
+ }
+
+ mylog("Got the large object oid: %d\n", self->lobj_type);
+ qlog(" [ Large Object oid = %d ]\n", self->lobj_type);
+
+ result = PGAPI_FreeStmt(hstmt, SQL_DROP);
+}
+
+
+/*
+ * This function initializes the version of PostgreSQL from
+ * connInfo.protocol that we're connected to.
+ * h-inoue 01-2-2001
+ */
+void
+CC_initialize_pg_version(ConnectionClass *self)
+{
+ strcpy(self->pg_version, self->connInfo.protocol);
+ if (PROTOCOL_62(&self->connInfo))
+ {
+ self->pg_version_number = (float) 6.2;
+ self->pg_version_major = 6;
+ self->pg_version_minor = 2;
+ }
+ else if (PROTOCOL_63(&self->connInfo))
+ {
+ self->pg_version_number = (float) 6.3;
+ self->pg_version_major = 6;
+ self->pg_version_minor = 3;
+ }
+ else
+ {
+ self->pg_version_number = (float) 6.4;
+ self->pg_version_major = 6;
+ self->pg_version_minor = 4;
+ }
+}
+
+
+/*
+ * This function gets the version of PostgreSQL that we're connected to.
+ * This is used to return the correct info in SQLGetInfo
+ * DJP - 25-1-2001
+ */
+void
+CC_lookup_pg_version(ConnectionClass *self)
+{
+ HSTMT hstmt;
+ StatementClass *stmt;
+ RETCODE result;
+ char szVersion[32];
+ int major,
+ minor;
+ static char *func = "CC_lookup_pg_version";
+
+ mylog("%s: entering...\n", func);
+
+/*
+ * This function must use the local odbc API functions since the odbc state
+ * has not transitioned to "connected" yet.
+ */
+ result = PGAPI_AllocStmt(self, &hstmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ return;
+ stmt = (StatementClass *) hstmt;
+
+ /* get the server's version if possible */
+ result = PGAPI_ExecDirect(hstmt, "select version()", SQL_NTS);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ PGAPI_FreeStmt(hstmt, SQL_DROP);
+ return;
+ }
+
+ result = PGAPI_Fetch(hstmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ PGAPI_FreeStmt(hstmt, SQL_DROP);
+ return;
+ }
+
+ result = PGAPI_GetData(hstmt, 1, SQL_C_CHAR, self->pg_version, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ PGAPI_FreeStmt(hstmt, SQL_DROP);
+ return;
+ }
+
+ /*
+ * Extract the Major and Minor numbers from the string. This assumes
+ * the string starts 'Postgresql X.X'
+ */
+ strcpy(szVersion, "0.0");
+ if (sscanf(self->pg_version, "%*s %d.%d", &major, &minor) >= 2)
+ {
+ sprintf(szVersion, "%d.%d", major, minor);
+ self->pg_version_major = major;
+ self->pg_version_minor = minor;
+ }
+ self->pg_version_number = (float) atof(szVersion);
+
+ mylog("Got the PostgreSQL version string: '%s'\n", self->pg_version);
+ mylog("Extracted PostgreSQL version number: '%1.1f'\n", self->pg_version_number);
+ qlog(" [ PostgreSQL version string = '%s' ]\n", self->pg_version);
+ qlog(" [ PostgreSQL version number = '%1.1f' ]\n", self->pg_version_number);
+
+ result = PGAPI_FreeStmt(hstmt, SQL_DROP);
+}
+
+
+void
+CC_log_error(char *func, char *desc, ConnectionClass *self)
+{
+#ifdef PRN_NULLCHECK
+#define nullcheck(a) (a ? a : "(NULL)")
+#endif
+
+ if (self)
+ {
+ qlog("CONN ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, nullcheck(self->errormsg));
+ mylog("CONN ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, nullcheck(self->errormsg));
+ qlog(" ------------------------------------------------------------\n");
+ qlog(" henv=%u, conn=%u, status=%u, num_stmts=%d\n", self->henv, self, self->status, self->num_stmts);
+ qlog(" sock=%u, stmts=%u, lobj_type=%d\n", self->sock, self->stmts, self->lobj_type);
+
+ qlog(" ---------------- Socket Info -------------------------------\n");
+ if (self->sock)
+ {
+ SocketClass *sock = self->sock;
+
+ qlog(" socket=%d, reverse=%d, errornumber=%d, errormsg='%s'\n", sock->socket, sock->reverse, sock->errornumber, nullcheck(sock->errormsg));
+ qlog(" buffer_in=%u, buffer_out=%u\n", sock->buffer_in, sock->buffer_out);
+ qlog(" buffer_filled_in=%d, buffer_filled_out=%d, buffer_read_in=%d\n", sock->buffer_filled_in, sock->buffer_filled_out, sock->buffer_read_in);
+ }
+ }
+ else
+ qlog("INVALID CONNECTION HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
+#undef PRN_NULLCHECK
+}
+
+int
+CC_get_max_query_len(const ConnectionClass *conn)
+{
+ int value;
+
+ /* Long Queries in 7.0+ */
+ if (PG_VERSION_GE(conn, 7.0))
+ value = 0 /* MAX_STATEMENT_LEN */ ;
+ /* Prior to 7.0 we used 2*BLCKSZ */
+ else if (PG_VERSION_GE(conn, 6.5))
+ value = (2 * BLCKSZ);
+ else
+ /* Prior to 6.5 we used BLCKSZ */
+ value = BLCKSZ;
+ return value;
+}
--- /dev/null
+/* File: connection.h
+ *
+ * Description: See "connection.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __CONNECTION_H__
+#define __CONNECTION_H__
+
+#include "psqlodbc.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+
+typedef enum
+{
+ CONN_NOT_CONNECTED, /* Connection has not been established */
+ CONN_CONNECTED, /* Connection is up and has been
+ * established */
+ CONN_DOWN, /* Connection is broken */
+ CONN_EXECUTING /* the connection is currently executing a
+ * statement */
+} CONN_Status;
+
+/* These errors have general sql error state */
+#define CONNECTION_SERVER_NOT_REACHED 101
+#define CONNECTION_MSG_TOO_LONG 103
+#define CONNECTION_COULD_NOT_SEND 104
+#define CONNECTION_NO_SUCH_DATABASE 105
+#define CONNECTION_BACKEND_CRAZY 106
+#define CONNECTION_NO_RESPONSE 107
+#define CONNECTION_SERVER_REPORTED_ERROR 108
+#define CONNECTION_COULD_NOT_RECEIVE 109
+#define CONNECTION_SERVER_REPORTED_WARNING 110
+#define CONNECTION_NEED_PASSWORD 112
+
+/* These errors correspond to specific SQL states */
+#define CONN_INIREAD_ERROR 201
+#define CONN_OPENDB_ERROR 202
+#define CONN_STMT_ALLOC_ERROR 203
+#define CONN_IN_USE 204
+#define CONN_UNSUPPORTED_OPTION 205
+/* Used by SetConnectoption to indicate unsupported options */
+#define CONN_INVALID_ARGUMENT_NO 206
+/* SetConnectOption: corresponds to ODBC--"S1009" */
+#define CONN_TRANSACT_IN_PROGRES 207
+#define CONN_NO_MEMORY_ERROR 208
+#define CONN_NOT_IMPLEMENTED_ERROR 209
+#define CONN_INVALID_AUTHENTICATION 210
+#define CONN_AUTH_TYPE_UNSUPPORTED 211
+#define CONN_UNABLE_TO_LOAD_DLL 212
+
+#define CONN_OPTION_VALUE_CHANGED 213
+#define CONN_VALUE_OUT_OF_RANGE 214
+
+#define CONN_TRUNCATED 215
+
+/* Conn_status defines */
+#define CONN_IN_AUTOCOMMIT 0x01
+#define CONN_IN_TRANSACTION 0x02
+
+/* AutoCommit functions */
+#define CC_set_autocommit_off(x) (x->transact_status &= ~CONN_IN_AUTOCOMMIT)
+#define CC_set_autocommit_on(x) (x->transact_status |= CONN_IN_AUTOCOMMIT)
+#define CC_is_in_autocommit(x) (x->transact_status & CONN_IN_AUTOCOMMIT)
+
+/* Transaction in/not functions */
+#define CC_set_in_trans(x) (x->transact_status |= CONN_IN_TRANSACTION)
+#define CC_set_no_trans(x) (x->transact_status &= ~CONN_IN_TRANSACTION)
+#define CC_is_in_trans(x) (x->transact_status & CONN_IN_TRANSACTION)
+
+
+/* Authentication types */
+#define AUTH_REQ_OK 0
+#define AUTH_REQ_KRB4 1
+#define AUTH_REQ_KRB5 2
+#define AUTH_REQ_PASSWORD 3
+#define AUTH_REQ_CRYPT 4
+#define AUTH_REQ_MD5 5
+#define AUTH_REQ_SCM_CREDS 6
+
+/* Startup Packet sizes */
+#define SM_DATABASE 64
+#define SM_USER 32
+#define SM_OPTIONS 64
+#define SM_UNUSED 64
+#define SM_TTY 64
+
+/* Old 6.2 protocol defines */
+#define NO_AUTHENTICATION 7
+#define PATH_SIZE 64
+#define ARGV_SIZE 64
+#define NAMEDATALEN 16
+
+typedef unsigned int ProtocolVersion;
+
+#define PG_PROTOCOL(major, minor) (((major) << 16) | (minor))
+#define PG_PROTOCOL_LATEST PG_PROTOCOL(2, 0)
+#define PG_PROTOCOL_63 PG_PROTOCOL(1, 0)
+#define PG_PROTOCOL_62 PG_PROTOCOL(0, 0)
+
+/* This startup packet is to support latest Postgres protocol (6.4, 6.3) */
+typedef struct _StartupPacket
+{
+ ProtocolVersion protoVersion;
+ char database[SM_DATABASE];
+ char user[SM_USER];
+ char options[SM_OPTIONS];
+ char unused[SM_UNUSED];
+ char tty[SM_TTY];
+} StartupPacket;
+
+
+/* This startup packet is to support pre-Postgres 6.3 protocol */
+typedef struct _StartupPacket6_2
+{
+ unsigned int authtype;
+ char database[PATH_SIZE];
+ char user[NAMEDATALEN];
+ char options[ARGV_SIZE];
+ char execfile[ARGV_SIZE];
+ char tty[PATH_SIZE];
+} StartupPacket6_2;
+
+
+/* Structure to hold all the connection attributes for a specific
+ connection (used for both registry and file, DSN and DRIVER)
+*/
+typedef struct
+{
+ char dsn[MEDIUM_REGISTRY_LEN];
+ char desc[MEDIUM_REGISTRY_LEN];
+ char driver[MEDIUM_REGISTRY_LEN];
+ char server[MEDIUM_REGISTRY_LEN];
+ char database[MEDIUM_REGISTRY_LEN];
+ char username[MEDIUM_REGISTRY_LEN];
+ char password[MEDIUM_REGISTRY_LEN];
+ char conn_settings[LARGE_REGISTRY_LEN];
+ char protocol[SMALL_REGISTRY_LEN];
+ char port[SMALL_REGISTRY_LEN];
+ char onlyread[SMALL_REGISTRY_LEN];
+ char fake_oid_index[SMALL_REGISTRY_LEN];
+ char show_oid_column[SMALL_REGISTRY_LEN];
+ char row_versioning[SMALL_REGISTRY_LEN];
+ char show_system_tables[SMALL_REGISTRY_LEN];
+ char translation_dll[MEDIUM_REGISTRY_LEN];
+ char translation_option[SMALL_REGISTRY_LEN];
+ char focus_password;
+ char disallow_premature;
+ char updatable_cursors;
+ GLOBAL_VALUES drivers; /* moved from driver's option */
+} ConnInfo;
+
+/* Macro to determine is the connection using 6.2 protocol? */
+#define PROTOCOL_62(conninfo_) (strncmp((conninfo_)->protocol, PG62, strlen(PG62)) == 0)
+
+/* Macro to determine is the connection using 6.3 protocol? */
+#define PROTOCOL_63(conninfo_) (strncmp((conninfo_)->protocol, PG63, strlen(PG63)) == 0)
+
+/*
+ * Macros to compare the server's version with a specified version
+ * 1st parameter: pointer to a ConnectionClass object
+ * 2nd parameter: major version number
+ * 3rd parameter: minor version number
+ */
+#define SERVER_VERSION_GT(conn, major, minor) \
+ ((conn)->pg_version_major > major || \
+ ((conn)->pg_version_major == major && (conn)->pg_version_minor > minor))
+#define SERVER_VERSION_GE(conn, major, minor) \
+ ((conn)->pg_version_major > major || \
+ ((conn)->pg_version_major == major && (conn)->pg_version_minor >= minor))
+#define SERVER_VERSION_EQ(conn, major, minor) \
+ ((conn)->pg_version_major == major && (conn)->pg_version_minor == minor)
+#define SERVER_VERSION_LE(conn, major, minor) (! SERVER_VERSION_GT(conn, major, minor))
+#define SERVER_VERSION_LT(conn, major, minor) (! SERVER_VERSION_GE(conn, major, minor))
+/*#if ! defined(HAVE_CONFIG_H) || defined(HAVE_STRINGIZE)*/
+#define STRING_AFTER_DOT(string) (strchr(#string, '.') + 1)
+/*#else
+#define STRING_AFTER_DOT(str) (strchr("str", '.') + 1)
+#endif*/
+/*
+ * Simplified macros to compare the server's version with a
+ * specified version
+ * Note: Never pass a variable as the second parameter.
+ * It must be a decimal constant of the form %d.%d .
+ */
+#define PG_VERSION_GT(conn, ver) \
+ (SERVER_VERSION_GT(conn, (int) ver, atoi(STRING_AFTER_DOT(ver))))
+#define PG_VERSION_GE(conn, ver) \
+ (SERVER_VERSION_GE(conn, (int) ver, atoi(STRING_AFTER_DOT(ver))))
+#define PG_VERSION_EQ(conn, ver) \
+ (SERVER_VERSION_EQ(conn, (int) ver, atoi(STRING_AFTER_DOT(ver))))
+#define PG_VERSION_LE(conn, ver) (! PG_VERSION_GT(conn, ver))
+#define PG_VERSION_LT(conn, ver) (! PG_VERSION_GE(conn, ver))
+
+/* This is used to store cached table information in the connection */
+struct col_info
+{
+ QResultClass *result;
+ char name[MAX_TABLE_LEN + 1];
+};
+
+ /* Translation DLL entry points */
+#ifdef WIN32
+#define DLLHANDLE HINSTANCE
+#else
+#define WINAPI CALLBACK
+#define DLLHANDLE void *
+#define HINSTANCE void *
+#endif
+
+typedef BOOL (FAR WINAPI * DataSourceToDriverProc) (UDWORD,
+ SWORD,
+ PTR,
+ SDWORD,
+ PTR,
+ SDWORD,
+ SDWORD FAR *,
+ UCHAR FAR *,
+ SWORD,
+ SWORD FAR *);
+
+typedef BOOL (FAR WINAPI * DriverToDataSourceProc) (UDWORD,
+ SWORD,
+ PTR,
+ SDWORD,
+ PTR,
+ SDWORD,
+ SDWORD FAR *,
+ UCHAR FAR *,
+ SWORD,
+ SWORD FAR *);
+
+/******* The Connection handle ************/
+struct ConnectionClass_
+{
+ HENV henv; /* environment this connection was created
+ * on */
+ StatementOptions stmtOptions;
+ char *errormsg;
+ int errornumber;
+ CONN_Status status;
+ ConnInfo connInfo;
+ StatementClass **stmts;
+ int num_stmts;
+ SocketClass *sock;
+ int lobj_type;
+ int ntables;
+ COL_INFO **col_info;
+ long translation_option;
+ HINSTANCE translation_handle;
+ DataSourceToDriverProc DataSourceToDriver;
+ DriverToDataSourceProc DriverToDataSource;
+ Int2 driver_version; /* prepared for ODBC3.0 */
+ char transact_status;/* Is a transaction is currently in
+ * progress */
+ char errormsg_created; /* has an informative error msg
+ * been created? */
+ char pg_version[MAX_INFO_STRING]; /* Version of PostgreSQL
+ * we're connected to -
+ * DJP 25-1-2001 */
+ float pg_version_number;
+ Int2 pg_version_major;
+ Int2 pg_version_minor;
+ char ms_jet;
+#ifdef MULTIBYTE
+ char *client_encoding;
+ char *server_encoding;
+#endif /* MULTIBYTE */
+};
+
+
+/* Accessor functions */
+#define CC_get_socket(x) (x->sock)
+#define CC_get_database(x) (x->connInfo.database)
+#define CC_get_server(x) (x->connInfo.server)
+#define CC_get_DSN(x) (x->connInfo.dsn)
+#define CC_get_username(x) (x->connInfo.username)
+#define CC_is_onlyread(x) (x->connInfo.onlyread[0] == '1')
+
+
+/* for CC_DSN_info */
+#define CONN_DONT_OVERWRITE 0
+#define CONN_OVERWRITE 1
+
+
+/* prototypes */
+ConnectionClass *CC_Constructor(void);
+char CC_Destructor(ConnectionClass *self);
+int CC_cursor_count(ConnectionClass *self);
+char CC_cleanup(ConnectionClass *self);
+char CC_abort(ConnectionClass *self);
+int CC_set_translation(ConnectionClass *self);
+char CC_connect(ConnectionClass *self, char do_password);
+char CC_add_statement(ConnectionClass *self, StatementClass *stmt);
+char CC_remove_statement(ConnectionClass *self, StatementClass *stmt);
+char CC_get_error(ConnectionClass *self, int *number, char **message);
+QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi);
+void CC_clear_error(ConnectionClass *self);
+char *CC_create_errormsg(ConnectionClass *self);
+int CC_send_function(ConnectionClass *conn, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *argv, int nargs);
+char CC_send_settings(ConnectionClass *self);
+void CC_lookup_lo(ConnectionClass *conn);
+void CC_lookup_pg_version(ConnectionClass *conn);
+void CC_initialize_pg_version(ConnectionClass *conn);
+void CC_log_error(char *func, char *desc, ConnectionClass *self);
+int CC_get_max_query_len(const ConnectionClass *self);
+
+#endif
--- /dev/null
+/*-------
+ * Module: convert.c
+ *
+ * Description: This module contains routines related to
+ * converting parameters and columns into requested data types.
+ * Parameters are converted from their SQL_C data types into
+ * the appropriate postgres type. Columns are converted from
+ * their postgres type (SQL type) into the appropriate SQL_C
+ * data type.
+ *
+ * Classes: n/a
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+/* Multibyte support Eiji Tokuya 2001-03-15 */
+
+#include "convert.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef MULTIBYTE
+#include "multibyte.h"
+#endif
+
+#include <time.h>
+#include <math.h>
+#include <stdlib.h>
+#include "statement.h"
+#include "qresult.h"
+#include "bind.h"
+#include "pgtypes.h"
+#include "lobj.h"
+#include "connection.h"
+#include "pgapifunc.h"
+
+#ifdef __CYGWIN__
+#define TIMEZONE_GLOBAL _timezone
+#elif defined(WIN32) || defined(HAVE_INT_TIMEZONE)
+#define TIMEZONE_GLOBAL timezone
+#endif
+
+/*
+ * How to map ODBC scalar functions {fn func(args)} to Postgres.
+ * This is just a simple substitution. List augmented from:
+ * http://www.merant.com/datadirect/download/docs/odbc16/Odbcref/rappc.htm
+ * - thomas 2000-04-03
+ */
+char *mapFuncs[][2] = {
+/* { "ASCII", "ascii" }, */
+ {"CHAR", "chr"},
+ {"CONCAT", "textcat"},
+/* { "DIFFERENCE", "difference" }, */
+/* { "INSERT", "insert" }, */
+ {"LCASE", "lower"},
+ {"LEFT", "ltrunc"},
+ {"LOCATE", "strpos"},
+ {"LENGTH", "char_length"},
+/* { "LTRIM", "ltrim" }, */
+ {"RIGHT", "rtrunc"},
+/* { "REPEAT", "repeat" }, */
+/* { "REPLACE", "replace" }, */
+/* { "RTRIM", "rtrim" }, */
+/* { "SOUNDEX", "soundex" }, */
+ {"SUBSTRING", "substr"},
+ {"UCASE", "upper"},
+
+/* { "ABS", "abs" }, */
+/* { "ACOS", "acos" }, */
+/* { "ASIN", "asin" }, */
+/* { "ATAN", "atan" }, */
+/* { "ATAN2", "atan2" }, */
+ {"CEILING", "ceil"},
+/* { "COS", "cos" }, */
+/* { "COT", "cot" }, */
+/* { "DEGREES", "degrees" }, */
+/* { "EXP", "exp" }, */
+/* { "FLOOR", "floor" }, */
+ {"LOG", "ln"},
+ {"LOG10", "log"},
+/* { "MOD", "mod" }, */
+/* { "PI", "pi" }, */
+ {"POWER", "pow"},
+/* { "RADIANS", "radians" }, */
+ {"RAND", "random"},
+/* { "ROUND", "round" }, */
+/* { "SIGN", "sign" }, */
+/* { "SIN", "sin" }, */
+/* { "SQRT", "sqrt" }, */
+/* { "TAN", "tan" }, */
+ {"TRUNCATE", "trunc"},
+
+ {"CURRENT_DATE", "curdate"},
+ {"CURRENT_TIME", "curtime"},
+ {"CURRENT_TIMESTAMP", "odbc_timestamp"},
+ {"CURRENT_USER", "odbc_current_user"},
+ {"SESSION_USER", "odbc_session_user"},
+/* { "CURDATE", "curdate" }, */
+/* { "CURTIME", "curtime" }, */
+/* { "DAYNAME", "dayname" }, */
+/* { "DAYOFMONTH", "dayofmonth" }, */
+/* { "DAYOFWEEK", "dayofweek" }, */
+/* { "DAYOFYEAR", "dayofyear" }, */
+/* { "HOUR", "hour" }, */
+/* { "MINUTE", "minute" }, */
+/* { "MONTH", "month" }, */
+/* { "MONTHNAME", "monthname" }, */
+/* { "NOW", "now" }, */
+/* { "QUARTER", "quarter" }, */
+/* { "SECOND", "second" }, */
+/* { "WEEK", "week" }, */
+/* { "YEAR", "year" }, */
+
+/* { "DATABASE", "database" }, */
+ {"IFNULL", "coalesce"},
+ {"USER", "odbc_user"},
+ {0, 0}
+};
+
+static char *mapFunction(const char *func);
+static unsigned int conv_from_octal(const unsigned char *s);
+static unsigned int conv_from_hex(const unsigned char *s);
+static char *conv_to_octal(unsigned char val);
+
+/*---------
+ * A Guide for date/time/timestamp conversions
+ *
+ * field_type fCType Output
+ * ---------- ------ ----------
+ * PG_TYPE_DATE SQL_C_DEFAULT SQL_C_DATE
+ * PG_TYPE_DATE SQL_C_DATE SQL_C_DATE
+ * PG_TYPE_DATE SQL_C_TIMESTAMP SQL_C_TIMESTAMP (time = 0 (midnight))
+ * PG_TYPE_TIME SQL_C_DEFAULT SQL_C_TIME
+ * PG_TYPE_TIME SQL_C_TIME SQL_C_TIME
+ * PG_TYPE_TIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP (date = current date)
+ * PG_TYPE_ABSTIME SQL_C_DEFAULT SQL_C_TIMESTAMP
+ * PG_TYPE_ABSTIME SQL_C_DATE SQL_C_DATE (time is truncated)
+ * PG_TYPE_ABSTIME SQL_C_TIME SQL_C_TIME (date is truncated)
+ * PG_TYPE_ABSTIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP
+ *---------
+ */
+
+
+
+/*
+ * TIMESTAMP <-----> SIMPLE_TIME
+ * precision support since 7.2.
+ * time zone support is unavailable(the stuff is unreliable)
+ */
+static BOOL
+timestamp2stime(const char *str, SIMPLE_TIME *st, BOOL *bZone, int *zone)
+{
+ char rest[64],
+ *ptr;
+ int scnt,
+ i;
+ long timediff;
+ BOOL withZone = *bZone;
+
+ *bZone = FALSE;
+ *zone = 0;
+ st->fr = 0;
+ if ((scnt = sscanf(str, "%4d-%2d-%2d %2d:%2d:%2d%s", &st->y, &st->m, &st->d, &st->hh, &st->mm, &st->ss, rest)) < 6)
+ return FALSE;
+ else if (scnt == 6)
+ return TRUE;
+ switch (rest[0])
+ {
+ case '+':
+ *bZone = TRUE;
+ *zone = atoi(&rest[1]);
+ break;
+ case '-':
+ *bZone = TRUE;
+ *zone = -atoi(&rest[1]);
+ break;
+ case '.':
+ if ((ptr = strchr(rest, '+')) != NULL)
+ {
+ *bZone = TRUE;
+ *zone = atoi(&ptr[1]);
+ *ptr = '\0';
+ }
+ else if ((ptr = strchr(rest, '-')) != NULL)
+ {
+ *bZone = TRUE;
+ *zone = -atoi(&ptr[1]);
+ *ptr = '\0';
+ }
+ for (i = 1; i < 10; i++)
+ {
+ if (!isdigit((unsigned char) rest[i]))
+ break;
+ }
+ for (; i < 10; i++)
+ rest[i] = '0';
+ rest[i] = '\0';
+ st->fr = atoi(&rest[1]);
+ break;
+ default:
+ return TRUE;
+ }
+ if (!withZone || !*bZone || st->y < 1970)
+ return TRUE;
+#if defined(WIN32) || defined(HAVE_INT_TIMEZONE)
+ if (!tzname[0] || !tzname[0][0])
+ {
+ *bZone = FALSE;
+ return TRUE;
+ }
+ timediff = TIMEZONE_GLOBAL + (*zone) * 3600;
+ if (!daylight && timediff == 0) /* the same timezone */
+ return TRUE;
+ else
+ {
+ struct tm tm,
+ *tm2;
+ time_t time0;
+
+ *bZone = FALSE;
+ tm.tm_year = st->y - 1900;
+ tm.tm_mon = st->m - 1;
+ tm.tm_mday = st->d;
+ tm.tm_hour = st->hh;
+ tm.tm_min = st->mm;
+ tm.tm_sec = st->ss;
+ tm.tm_isdst = -1;
+ time0 = mktime(&tm);
+ if (time0 < 0)
+ return TRUE;
+ if (tm.tm_isdst > 0)
+ timediff -= 3600;
+ if (timediff == 0) /* the same time zone */
+ return TRUE;
+ time0 -= timediff;
+ if (time0 >= 0 && (tm2 = localtime(&time0)) != NULL)
+ {
+ st->y = tm2->tm_year + 1900;
+ st->m = tm2->tm_mon + 1;
+ st->d = tm2->tm_mday;
+ st->hh = tm2->tm_hour;
+ st->mm = tm2->tm_min;
+ st->ss = tm2->tm_sec;
+ *bZone = TRUE;
+ }
+ }
+#endif /* WIN32 */
+ return TRUE;
+}
+
+static BOOL
+stime2timestamp(const SIMPLE_TIME *st, char *str, BOOL bZone, BOOL precision)
+{
+ char precstr[16],
+ zonestr[16];
+ int i;
+
+ precstr[0] = '\0';
+ if (precision && st->fr)
+ {
+ sprintf(precstr, ".%09d", st->fr);
+ for (i = 9; i > 0; i--)
+ {
+ if (precstr[i] != '0')
+ break;
+ precstr[i] = '\0';
+ }
+ }
+ zonestr[0] = '\0';
+#if defined(WIN32) || defined(HAVE_INT_TIMEZONE)
+ if (bZone && tzname[0] && tzname[0][0] && st->y >= 1970)
+ {
+ long zoneint;
+ struct tm tm;
+ time_t time0;
+
+ zoneint = TIMEZONE_GLOBAL;
+ if (daylight && st->y >= 1900)
+ {
+ tm.tm_year = st->y - 1900;
+ tm.tm_mon = st->m - 1;
+ tm.tm_mday = st->d;
+ tm.tm_hour = st->hh;
+ tm.tm_min = st->mm;
+ tm.tm_sec = st->ss;
+ tm.tm_isdst = -1;
+ time0 = mktime(&tm);
+ if (time0 >= 0 && tm.tm_isdst > 0)
+ zoneint -= 3600;
+ }
+ if (zoneint > 0)
+ sprintf(zonestr, "-%02d", (int) zoneint / 3600);
+ else
+ sprintf(zonestr, "+%02d", -(int) zoneint / 3600);
+ }
+#endif /* WIN32 */
+ sprintf(str, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d%s%s", st->y, st->m, st->d, st->hh, st->mm, st->ss, precstr, zonestr);
+ return TRUE;
+}
+
+/* This is called by SQLFetch() */
+int
+copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col)
+{
+ BindInfoClass *bic = &(stmt->bindings[col]);
+
+ return copy_and_convert_field(stmt, field_type, value, (Int2) bic->returntype, (PTR) bic->buffer,
+ (SDWORD) bic->buflen, (SDWORD *) bic->used);
+}
+
+
+/* This is called by SQLGetData() */
+int
+copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType,
+ PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue)
+{
+ Int4 len = 0,
+ copy_len = 0;
+ SIMPLE_TIME st;
+ time_t t = time(NULL);
+ struct tm *tim;
+ int pcbValueOffset,
+ rgbValueOffset;
+ char *rgbValueBindRow;
+ const char *ptr;
+ int bind_row = stmt->bind_row;
+ int bind_size = stmt->options.bind_size;
+ int result = COPY_OK;
+ BOOL changed;
+ const char *neut_str = value;
+ char midtemp[2][32];
+ int mtemp_cnt = 0;
+ static BindInfoClass sbic;
+ BindInfoClass *pbic;
+
+ if (stmt->current_col >= 0)
+ {
+ pbic = &stmt->bindings[stmt->current_col];
+ if (pbic->data_left == -2)
+ pbic->data_left = (cbValueMax > 0) ? 0 : -1; /* This seems to be *
+ * needed for ADO ? */
+ if (pbic->data_left == 0)
+ {
+ if (pbic->ttlbuf != NULL)
+ {
+ free(pbic->ttlbuf);
+ pbic->ttlbuf = NULL;
+ pbic->ttlbuflen = 0;
+ }
+ pbic->data_left = -2; /* needed by ADO ? */
+ return COPY_NO_DATA_FOUND;
+ }
+ }
+ /*---------
+ * rgbValueOffset is *ONLY* for character and binary data.
+ * pcbValueOffset is for computing any pcbValue location
+ *---------
+ */
+
+ if (bind_size > 0)
+ pcbValueOffset = rgbValueOffset = (bind_size * bind_row);
+ else
+ {
+ pcbValueOffset = bind_row * sizeof(SDWORD);
+ rgbValueOffset = bind_row * cbValueMax;
+
+ }
+
+ memset(&st, 0, sizeof(SIMPLE_TIME));
+
+ /* Initialize current date */
+ tim = localtime(&t);
+ st.m = tim->tm_mon + 1;
+ st.d = tim->tm_mday;
+ st.y = tim->tm_year + 1900;
+
+ mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, (value == NULL) ? "<NULL>" : value, cbValueMax);
+
+ if (!value)
+ {
+ /*
+ * handle a null just by returning SQL_NULL_DATA in pcbValue, and
+ * doing nothing to the buffer.
+ */
+ if (pcbValue)
+ *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = SQL_NULL_DATA;
+ return COPY_OK;
+ }
+
+ if (stmt->hdbc->DataSourceToDriver != NULL)
+ {
+ int length = strlen(value);
+
+ stmt->hdbc->DataSourceToDriver(stmt->hdbc->translation_option,
+ SQL_CHAR,
+ value, length,
+ value, length, NULL,
+ NULL, 0, NULL);
+ }
+
+ /*
+ * First convert any specific postgres types into more useable data.
+ *
+ * NOTE: Conversions from PG char/varchar of a date/time/timestamp value
+ * to SQL_C_DATE,SQL_C_TIME, SQL_C_TIMESTAMP not supported
+ */
+ switch (field_type)
+ {
+ /*
+ * $$$ need to add parsing for date/time/timestamp strings in
+ * PG_TYPE_CHAR,VARCHAR $$$
+ */
+ case PG_TYPE_DATE:
+ sscanf(value, "%4d-%2d-%2d", &st.y, &st.m, &st.d);
+ break;
+
+ case PG_TYPE_TIME:
+ sscanf(value, "%2d:%2d:%2d", &st.hh, &st.mm, &st.ss);
+ break;
+
+ case PG_TYPE_ABSTIME:
+ case PG_TYPE_DATETIME:
+ case PG_TYPE_TIMESTAMP:
+ st.fr = 0;
+ if (strnicmp(value, "invalid", 7) != 0)
+ {
+ BOOL bZone = (field_type != PG_TYPE_TIMESTAMP_NO_TMZONE && PG_VERSION_GE(SC_get_conn(stmt), 7.2));
+ int zone;
+
+ /*
+ * sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m,
+ * &st.d, &st.hh, &st.mm, &st.ss);
+ */
+ bZone = FALSE; /* time zone stuff is unreliable */
+ timestamp2stime(value, &st, &bZone, &zone);
+ }
+ else
+ {
+ /*
+ * The timestamp is invalid so set something conspicuous,
+ * like the epoch
+ */
+ t = 0;
+ tim = localtime(&t);
+ st.m = tim->tm_mon + 1;
+ st.d = tim->tm_mday;
+ st.y = tim->tm_year + 1900;
+ st.hh = tim->tm_hour;
+ st.mm = tim->tm_min;
+ st.ss = tim->tm_sec;
+ }
+ break;
+
+ case PG_TYPE_BOOL:
+ { /* change T/F to 1/0 */
+ char *s;
+
+ s = midtemp[mtemp_cnt];
+ strcpy(s, (char *) value);
+ if (s[0] == 'f' || s[0] == 'F' || s[0] == 'n' || s[0] == 'N' || s[0] == '0')
+ s[0] = '0';
+ else
+ s[0] = '1';
+ s[1] = '\0';
+ neut_str = midtemp[mtemp_cnt];
+ mtemp_cnt++;
+
+ }
+ break;
+
+ /* This is for internal use by SQLStatistics() */
+ case PG_TYPE_INT2VECTOR:
+ {
+ int nval,
+ i;
+ const char *vp;
+
+ /* this is an array of eight integers */
+ short *short_array = (short *) ((char *) rgbValue + rgbValueOffset);
+
+ len = 32;
+ vp = value;
+ nval = 0;
+ mylog("index=(");
+ for (i = 0; i < 16; i++)
+ {
+ if (sscanf(vp, "%hd", &short_array[i]) != 1)
+ break;
+
+ mylog(" %d", short_array[i]);
+ nval++;
+
+ /* skip the current token */
+ while ((*vp != '\0') && (!isspace((unsigned char) *vp)))
+ vp++;
+ /* and skip the space to the next token */
+ while ((*vp != '\0') && (isspace((unsigned char) *vp)))
+ vp++;
+ if (*vp == '\0')
+ break;
+ }
+ mylog(") nval = %d\n", nval);
+
+ for (i = nval; i < 16; i++)
+ short_array[i] = 0;
+
+#if 0
+ sscanf(value, "%hd %hd %hd %hd %hd %hd %hd %hd",
+ &short_array[0],
+ &short_array[1],
+ &short_array[2],
+ &short_array[3],
+ &short_array[4],
+ &short_array[5],
+ &short_array[6],
+ &short_array[7]);
+#endif
+
+ /* There is no corresponding fCType for this. */
+ if (pcbValue)
+ *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = len;
+
+ return COPY_OK; /* dont go any further or the data will be
+ * trashed */
+ }
+
+ /*
+ * This is a large object OID, which is used to store
+ * LONGVARBINARY objects.
+ */
+ case PG_TYPE_LO:
+
+ return convert_lo(stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset));
+
+ default:
+
+ if (field_type == stmt->hdbc->lobj_type) /* hack until permanent
+ * type available */
+ return convert_lo(stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset));
+ }
+
+ /* Change default into something useable */
+ if (fCType == SQL_C_DEFAULT)
+ {
+ fCType = pgtype_to_ctype(stmt, field_type);
+
+ mylog("copy_and_convert, SQL_C_DEFAULT: fCType = %d\n", fCType);
+ }
+
+ rgbValueBindRow = (char *) rgbValue + rgbValueOffset;
+
+ if (fCType == SQL_C_CHAR)
+ {
+ /* Special character formatting as required */
+
+ /*
+ * These really should return error if cbValueMax is not big
+ * enough.
+ */
+ switch (field_type)
+ {
+ case PG_TYPE_DATE:
+ len = 10;
+ if (cbValueMax > len)
+ sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d", st.y, st.m, st.d);
+ break;
+
+ case PG_TYPE_TIME:
+ len = 8;
+ if (cbValueMax > len)
+ sprintf(rgbValueBindRow, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss);
+ break;
+
+ case PG_TYPE_ABSTIME:
+ case PG_TYPE_DATETIME:
+ case PG_TYPE_TIMESTAMP:
+ len = 19;
+ if (cbValueMax > len)
+ sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
+ st.y, st.m, st.d, st.hh, st.mm, st.ss);
+ break;
+
+ case PG_TYPE_BOOL:
+ len = 1;
+ if (cbValueMax > len)
+ {
+ strcpy(rgbValueBindRow, neut_str);
+ mylog("PG_TYPE_BOOL: rgbValueBindRow = '%s'\n", rgbValueBindRow);
+ }
+ break;
+
+ /*
+ * Currently, data is SILENTLY TRUNCATED for BYTEA and
+ * character data types if there is not enough room in
+ * cbValueMax because the driver can't handle multiple
+ * calls to SQLGetData for these, yet. Most likely, the
+ * buffer passed in will be big enough to handle the
+ * maximum limit of postgres, anyway.
+ *
+ * LongVarBinary types are handled correctly above, observing
+ * truncation and all that stuff since there is
+ * essentially no limit on the large object used to store
+ * those.
+ */
+ case PG_TYPE_BYTEA:/* convert binary data to hex strings
+ * (i.e, 255 = "FF") */
+ len = convert_pgbinary_to_char(neut_str, rgbValueBindRow, cbValueMax);
+
+ /***** THIS IS NOT PROPERLY IMPLEMENTED *****/
+ break;
+
+ default:
+ if (stmt->current_col < 0)
+ {
+ pbic = &sbic;
+ pbic->data_left = -1;
+ }
+ else
+ pbic = &stmt->bindings[stmt->current_col];
+ if (pbic->data_left < 0)
+ {
+ /* convert linefeeds to carriage-return/linefeed */
+ len = convert_linefeeds(neut_str, NULL, 0, &changed);
+ if (cbValueMax == 0) /* just returns length
+ * info */
+ {
+ result = COPY_RESULT_TRUNCATED;
+ break;
+ }
+ if (!pbic->ttlbuf)
+ pbic->ttlbuflen = 0;
+ if (changed || len >= cbValueMax)
+ {
+ if (len >= (int) pbic->ttlbuflen)
+ {
+ pbic->ttlbuf = realloc(pbic->ttlbuf, len + 1);
+ pbic->ttlbuflen = len + 1;
+ }
+ convert_linefeeds(neut_str, pbic->ttlbuf, pbic->ttlbuflen, &changed);
+ ptr = pbic->ttlbuf;
+ }
+ else
+ {
+ if (pbic->ttlbuf)
+ {
+ free(pbic->ttlbuf);
+ pbic->ttlbuf = NULL;
+ }
+ ptr = neut_str;
+ }
+ }
+ else
+ ptr = pbic->ttlbuf;
+
+ mylog("DEFAULT: len = %d, ptr = '%s'\n", len, ptr);
+
+ if (stmt->current_col >= 0)
+ {
+ if (pbic->data_left > 0)
+ {
+ ptr += strlen(ptr) - pbic->data_left;
+ len = pbic->data_left;
+ }
+ else
+ pbic->data_left = len;
+ }
+
+ if (cbValueMax > 0)
+ {
+ copy_len = (len >= cbValueMax) ? cbValueMax - 1 : len;
+
+ /* Copy the data */
+ memcpy(rgbValueBindRow, ptr, copy_len);
+ rgbValueBindRow[copy_len] = '\0';
+
+ /* Adjust data_left for next time */
+ if (stmt->current_col >= 0)
+ pbic->data_left -= copy_len;
+ }
+
+ /*
+ * Finally, check for truncation so that proper status can
+ * be returned
+ */
+ if (cbValueMax > 0 && len >= cbValueMax)
+ result = COPY_RESULT_TRUNCATED;
+ else
+ {
+ if (pbic->ttlbuf != NULL)
+ {
+ free(pbic->ttlbuf);
+ pbic->ttlbuf = NULL;
+ }
+ }
+
+
+ mylog(" SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValueBindRow = '%s'\n", len, cbValueMax, rgbValueBindRow);
+ break;
+ }
+
+
+ }
+ else
+ {
+ /*
+ * for SQL_C_CHAR, it's probably ok to leave currency symbols in.
+ * But to convert to numeric types, it is necessary to get rid of
+ * those.
+ */
+ if (field_type == PG_TYPE_MONEY)
+ {
+ if (convert_money(neut_str, midtemp[mtemp_cnt], sizeof(midtemp[0])))
+ {
+ neut_str = midtemp[mtemp_cnt];
+ mtemp_cnt++;
+ }
+ else
+ return COPY_UNSUPPORTED_TYPE;
+ }
+
+ switch (fCType)
+ {
+ case SQL_C_DATE:
+#if (ODBCVER >= 0x0300)
+ case SQL_C_TYPE_DATE: /* 91 */
+#endif
+ len = 6;
+ {
+ DATE_STRUCT *ds;
+
+ if (bind_size > 0)
+ ds = (DATE_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
+ else
+ ds = (DATE_STRUCT *) rgbValue + bind_row;
+ ds->year = st.y;
+ ds->month = st.m;
+ ds->day = st.d;
+ }
+ break;
+
+ case SQL_C_TIME:
+#if (ODBCVER >= 0x0300)
+ case SQL_C_TYPE_TIME: /* 92 */
+#endif
+ len = 6;
+ {
+ TIME_STRUCT *ts;
+
+ if (bind_size > 0)
+ ts = (TIME_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
+ else
+ ts = (TIME_STRUCT *) rgbValue + bind_row;
+ ts->hour = st.hh;
+ ts->minute = st.mm;
+ ts->second = st.ss;
+ }
+ break;
+
+ case SQL_C_TIMESTAMP:
+#if (ODBCVER >= 0x0300)
+ case SQL_C_TYPE_TIMESTAMP: /* 93 */
+#endif
+ len = 16;
+ {
+ TIMESTAMP_STRUCT *ts;
+
+ if (bind_size > 0)
+ ts = (TIMESTAMP_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
+ else
+ ts = (TIMESTAMP_STRUCT *) rgbValue + bind_row;
+ ts->year = st.y;
+ ts->month = st.m;
+ ts->day = st.d;
+ ts->hour = st.hh;
+ ts->minute = st.mm;
+ ts->second = st.ss;
+ ts->fraction = st.fr;
+ }
+ break;
+
+ case SQL_C_BIT:
+ len = 1;
+ if (bind_size > 0)
+ *(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str);
+ else
+ *((UCHAR *) rgbValue + bind_row) = atoi(neut_str);
+
+ /*
+ * mylog("SQL_C_BIT: bind_row = %d val = %d, cb = %d,
+ * rgb=%d\n", bind_row, atoi(neut_str), cbValueMax,
+ * *((UCHAR *)rgbValue));
+ */
+ break;
+
+ case SQL_C_STINYINT:
+ case SQL_C_TINYINT:
+ len = 1;
+ if (bind_size > 0)
+ *(SCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str);
+ else
+ *((SCHAR *) rgbValue + bind_row) = atoi(neut_str);
+ break;
+
+ case SQL_C_UTINYINT:
+ len = 1;
+ if (bind_size > 0)
+ *(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str);
+ else
+ *((UCHAR *) rgbValue + bind_row) = atoi(neut_str);
+ break;
+
+ case SQL_C_FLOAT:
+ len = 4;
+ if (bind_size > 0)
+ *(SFLOAT *) ((char *) rgbValue + (bind_row * bind_size)) = (float) atof(neut_str);
+ else
+ *((SFLOAT *) rgbValue + bind_row) = (float) atof(neut_str);
+ break;
+
+ case SQL_C_DOUBLE:
+ len = 8;
+ if (bind_size > 0)
+ *(SDOUBLE *) ((char *) rgbValue + (bind_row * bind_size)) = atof(neut_str);
+ else
+ *((SDOUBLE *) rgbValue + bind_row) = atof(neut_str);
+ break;
+
+ case SQL_C_SSHORT:
+ case SQL_C_SHORT:
+ len = 2;
+ if (bind_size > 0)
+ *(SWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str);
+ else
+ *((SWORD *) rgbValue + bind_row) = atoi(neut_str);
+ break;
+
+ case SQL_C_USHORT:
+ len = 2;
+ if (bind_size > 0)
+ *(UWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str);
+ else
+ *((UWORD *) rgbValue + bind_row) = atoi(neut_str);
+ break;
+
+ case SQL_C_SLONG:
+ case SQL_C_LONG:
+ len = 4;
+ if (bind_size > 0)
+ *(SDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(neut_str);
+ else
+ *((SDWORD *) rgbValue + bind_row) = atol(neut_str);
+ break;
+
+ case SQL_C_ULONG:
+ len = 4;
+ if (bind_size > 0)
+ *(UDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(neut_str);
+ else
+ *((UDWORD *) rgbValue + bind_row) = atol(neut_str);
+ break;
+
+ case SQL_C_BINARY:
+
+ /* truncate if necessary */
+ /* convert octal escapes to bytes */
+
+ if (stmt->current_col < 0)
+ {
+ pbic = &sbic;
+ pbic->data_left = -1;
+ }
+ else
+ pbic = &stmt->bindings[stmt->current_col];
+ if (!pbic->ttlbuf)
+ pbic->ttlbuflen = 0;
+ if (len = strlen(neut_str), len >= (int) pbic->ttlbuflen)
+ {
+ pbic->ttlbuf = realloc(pbic->ttlbuf, len + 1);
+ pbic->ttlbuflen = len + 1;
+ }
+ len = convert_from_pgbinary(neut_str, pbic->ttlbuf, pbic->ttlbuflen);
+ ptr = pbic->ttlbuf;
+
+ if (stmt->current_col >= 0)
+ {
+ /*
+ * Second (or more) call to SQLGetData so move the
+ * pointer
+ */
+ if (pbic->data_left > 0)
+ {
+ ptr += len - pbic->data_left;
+ len = pbic->data_left;
+ }
+
+ /* First call to SQLGetData so initialize data_left */
+ else
+ pbic->data_left = len;
+
+ }
+
+ if (cbValueMax > 0)
+ {
+ copy_len = (len > cbValueMax) ? cbValueMax : len;
+
+ /* Copy the data */
+ memcpy(rgbValueBindRow, ptr, copy_len);
+
+ /* Adjust data_left for next time */
+ if (stmt->current_col >= 0)
+ pbic->data_left -= copy_len;
+ }
+
+ /*
+ * Finally, check for truncation so that proper status can
+ * be returned
+ */
+ if (len > cbValueMax)
+ result = COPY_RESULT_TRUNCATED;
+
+ if (pbic->ttlbuf)
+ {
+ free(pbic->ttlbuf);
+ pbic->ttlbuf = NULL;
+ }
+ mylog("SQL_C_BINARY: len = %d, copy_len = %d\n", len, copy_len);
+ break;
+
+ default:
+ return COPY_UNSUPPORTED_TYPE;
+ }
+ }
+
+ /* store the length of what was copied, if there's a place for it */
+ if (pcbValue)
+ *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = len;
+
+ if (result == COPY_OK && stmt->current_col >= 0)
+ stmt->bindings[stmt->current_col].data_left = 0;
+ return result;
+
+}
+
+
+/*--------------------------------------------------------------------
+ * Functions/Macros to get rid of query size limit.
+ *
+ * I always used the follwoing macros to convert from
+ * old_statement to new_statement. Please improve it
+ * if you have a better way. Hiroshi 2001/05/22
+ *--------------------------------------------------------------------
+ */
+#define INIT_MIN_ALLOC 4096
+static int
+enlarge_statement(StatementClass *stmt, unsigned int newsize)
+{
+ unsigned int newalsize = INIT_MIN_ALLOC;
+ static char *func = "enlarge_statement";
+
+ if (stmt->stmt_size_limit > 0 && stmt->stmt_size_limit < (int) newsize)
+ {
+ stmt->errormsg = "Query buffer overflow in copy_statement_with_parameters";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return -1;
+ }
+ while (newalsize <= newsize)
+ newalsize *= 2;
+ if (!(stmt->stmt_with_params = realloc(stmt->stmt_with_params, newalsize)))
+ {
+ stmt->errormsg = "Query buffer allocate error in copy_statement_with_parameters";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return 0;
+ }
+ return newalsize;
+}
+
+/*----------
+ * Enlarge stmt_with_params if necessary.
+ *----------
+ */
+#define ENLARGE_NEWSTATEMENT(newpos) \
+ if (newpos >= new_stsize) \
+ { \
+ if ((new_stsize = enlarge_statement(stmt, newpos)) <= 0) \
+ return SQL_ERROR; \
+ new_statement = stmt->stmt_with_params; \
+ }
+/*----------
+ * Initialize stmt_with_params, new_statement etc.
+ *----------
+ */
+#define CVT_INIT(size) \
+do { \
+ if (stmt->stmt_with_params) \
+ free(stmt->stmt_with_params); \
+ if (stmt->stmt_size_limit > 0) \
+ new_stsize = stmt->stmt_size_limit; \
+ else \
+ { \
+ new_stsize = INIT_MIN_ALLOC; \
+ while (new_stsize <= size) \
+ new_stsize *= 2; \
+ } \
+ new_statement = malloc(new_stsize); \
+ stmt->stmt_with_params = new_statement; \
+ npos = 0; \
+ new_statement[0] = '\0'; \
+} while (0)
+
+/*----------
+ * Terminate the stmt_with_params string with NULL.
+ *----------
+ */
+#define CVT_TERMINATE \
+do { \
+ new_statement[npos] = '\0'; \
+} while (0)
+
+/*----------
+ * Append a data.
+ *----------
+ */
+#define CVT_APPEND_DATA(s, len) \
+do { \
+ unsigned int newpos = npos + len; \
+ ENLARGE_NEWSTATEMENT(newpos) \
+ memcpy(&new_statement[npos], s, len); \
+ npos = newpos; \
+ new_statement[npos] = '\0'; \
+} while (0)
+
+/*----------
+ * Append a string.
+ *----------
+ */
+#define CVT_APPEND_STR(s) \
+do { \
+ unsigned int len = strlen(s); \
+ CVT_APPEND_DATA(s, len); \
+} while (0)
+
+/*----------
+ * Append a char.
+ *----------
+ */
+#define CVT_APPEND_CHAR(c) \
+do { \
+ ENLARGE_NEWSTATEMENT(npos + 1); \
+ new_statement[npos++] = c; \
+} while (0)
+
+/*----------
+ * Append a binary data.
+ * Newly reqeuired size may be overestimated currently.
+ *----------
+ */
+#define CVT_APPEND_BINARY(buf, used) \
+do { \
+ unsigned int newlimit = npos + 5 * used; \
+ ENLARGE_NEWSTATEMENT(newlimit); \
+ npos += convert_to_pgbinary(buf, &new_statement[npos], used); \
+} while (0)
+
+/*----------
+ *
+ *----------
+ */
+#define CVT_SPECIAL_CHARS(buf, used) \
+do { \
+ int cnvlen = convert_special_chars(buf, NULL, used); \
+ unsigned int newlimit = npos + cnvlen; \
+\
+ ENLARGE_NEWSTATEMENT(newlimit); \
+ convert_special_chars(buf, &new_statement[npos], used); \
+ npos += cnvlen; \
+} while (0)
+
+/*----------
+ * Check if the statement is
+ * SELECT ... INTO table FROM .....
+ * This isn't really a strict check but ...
+ *----------
+ */
+static BOOL
+into_table_from(const char *stmt)
+{
+ if (strnicmp(stmt, "into", 4))
+ return FALSE;
+ stmt += 4;
+ if (!isspace((unsigned char) *stmt))
+ return FALSE;
+ while (isspace((unsigned char) *(++stmt)));
+ switch (*stmt)
+ {
+ case '\0':
+ case ',':
+ case '\'':
+ return FALSE;
+ case '\"': /* double quoted table name ? */
+ do
+ {
+ do
+ while (*(++stmt) != '\"' && *stmt);
+ while (*stmt && *(++stmt) == '\"');
+ while (*stmt && !isspace((unsigned char) *stmt) && *stmt != '\"')
+ stmt++;
+ }
+ while (*stmt == '\"');
+ break;
+ default:
+ while (!isspace((unsigned char) *(++stmt)));
+ break;
+ }
+ if (!*stmt)
+ return FALSE;
+ while (isspace((unsigned char) *(++stmt)));
+ if (strnicmp(stmt, "from", 4))
+ return FALSE;
+ return isspace((unsigned char) stmt[4]);
+}
+
+/*----------
+ * Check if the statement is
+ * SELECT ... FOR UPDATE .....
+ * This isn't really a strict check but ...
+ *----------
+ */
+static BOOL
+table_for_update(const char *stmt, int *endpos)
+{
+ const char *wstmt = stmt;
+
+ while (isspace((unsigned char) *(++wstmt)));
+ if (!*wstmt)
+ return FALSE;
+ if (strnicmp(wstmt, "update", 6))
+ return FALSE;
+ wstmt += 6;
+ *endpos = wstmt - stmt;
+ return !wstmt[0] || isspace((unsigned char) wstmt[0]);
+}
+
+/*
+ * This function inserts parameters into an SQL statements.
+ * It will also modify a SELECT statement for use with declare/fetch cursors.
+ * This function does a dynamic memory allocation to get rid of query size limit!
+ */
+int
+copy_statement_with_parameters(StatementClass *stmt)
+{
+ static char *func = "copy_statement_with_parameters";
+ unsigned int opos,
+ npos,
+ oldstmtlen;
+ char param_string[128],
+ tmp[256],
+ cbuf[PG_NUMERIC_MAX_PRECISION * 2]; /* seems big enough to
+ * handle the data in
+ * this function */
+ int param_number;
+ Int2 param_ctype,
+ param_sqltype;
+ char *old_statement = stmt->statement,
+ oldchar;
+ char *new_statement = stmt->stmt_with_params;
+ unsigned int new_stsize = 0;
+ SIMPLE_TIME st;
+ time_t t = time(NULL);
+ struct tm *tim;
+ SDWORD used;
+ char *buffer,
+ *buf;
+ BOOL in_quote = FALSE,
+ in_dquote = FALSE,
+ in_escape = FALSE;
+ Oid lobj_oid;
+ int lobj_fd,
+ retval;
+ BOOL check_cursor_ok = FALSE; /* check cursor
+ * restriction */
+ BOOL proc_no_param = TRUE;
+ unsigned int declare_pos = 0;
+ ConnectionClass *conn = SC_get_conn(stmt);
+ ConnInfo *ci = &(conn->connInfo);
+ BOOL prepare_dummy_cursor = FALSE,
+ begin_first = FALSE;
+ char token_save[64];
+ int token_len;
+ BOOL prev_token_end;
+
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ BOOL search_from_pos = FALSE;
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ if (ci->disallow_premature)
+ prepare_dummy_cursor = stmt->pre_executing;
+
+ if (!old_statement)
+ {
+ SC_log_error(func, "No statement string", stmt);
+ return SQL_ERROR;
+ }
+
+ memset(&st, 0, sizeof(SIMPLE_TIME));
+
+ /* Initialize current date */
+ tim = localtime(&t);
+ st.m = tim->tm_mon + 1;
+ st.d = tim->tm_mday;
+ st.y = tim->tm_year + 1900;
+
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ if (stmt->statement_type != STMT_TYPE_SELECT)
+ {
+ stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ }
+ else if (stmt->options.cursor_type == SQL_CURSOR_FORWARD_ONLY)
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ else if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
+ {
+ if (stmt->parse_status == STMT_PARSE_NONE)
+ parse_statement(stmt);
+ if (stmt->parse_status != STMT_PARSE_COMPLETE)
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ else if (!stmt->ti || stmt->ntab != 1)
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ else
+ search_from_pos = TRUE;
+ }
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+
+ /* If the application hasn't set a cursor name, then generate one */
+ if (stmt->cursor_name[0] == '\0')
+ sprintf(stmt->cursor_name, "SQL_CUR%p", stmt);
+ oldstmtlen = strlen(old_statement);
+ CVT_INIT(oldstmtlen);
+
+ stmt->miscinfo = 0;
+ token_len = 0;
+ prev_token_end = TRUE;
+ /* For selects, prepend a declare cursor to the statement */
+ if (stmt->statement_type == STMT_TYPE_SELECT)
+ {
+ SC_set_pre_executable(stmt);
+ if (prepare_dummy_cursor || ci->drivers.use_declarefetch)
+ {
+ if (prepare_dummy_cursor)
+ {
+ if (!CC_is_in_trans(conn) && PG_VERSION_GE(conn, 7.1))
+ {
+ strcpy(new_statement, "BEGIN;");
+ begin_first = TRUE;
+ }
+ }
+ else if (ci->drivers.use_declarefetch)
+ SC_set_fetchcursor(stmt);
+ sprintf(new_statement, "%sdeclare %s cursor for ",
+ new_statement, stmt->cursor_name);
+ npos = strlen(new_statement);
+ check_cursor_ok = TRUE;
+ declare_pos = npos;
+ }
+ }
+ param_number = -1;
+#ifdef MULTIBYTE
+ multibyte_init();
+#endif
+
+ for (opos = 0; opos < oldstmtlen; opos++)
+ {
+ oldchar = old_statement[opos];
+#ifdef MULTIBYTE
+ if (multibyte_char_check(oldchar) != 0)
+ {
+ CVT_APPEND_CHAR(oldchar);
+ continue;
+ }
+
+ /*
+ * From here we are guaranteed to handle a 1-byte character.
+ */
+#endif
+
+ if (in_escape) /* escape check */
+ {
+ in_escape = FALSE;
+ CVT_APPEND_CHAR(oldchar);
+ continue;
+ }
+ else if (in_quote || in_dquote) /* quote/double quote check */
+ {
+ if (oldchar == '\\')
+ in_escape = TRUE;
+ else if (oldchar == '\'' && in_quote)
+ in_quote = FALSE;
+ else if (oldchar == '\"' && in_dquote)
+ in_dquote = FALSE;
+ CVT_APPEND_CHAR(oldchar);
+ continue;
+ }
+
+ /*
+ * From here we are guranteed to be in neither an escape, a quote
+ * nor a double quote.
+ */
+ /* Squeeze carriage-return/linefeed pairs to linefeed only */
+ else if (oldchar == '\r' && opos + 1 < oldstmtlen &&
+ old_statement[opos + 1] == '\n')
+ continue;
+
+ /*
+ * Handle literals (date, time, timestamp) and ODBC scalar
+ * functions
+ */
+ else if (oldchar == '{')
+ {
+ char *esc;
+ char *begin = &old_statement[opos + 1];
+
+#ifdef MULTIBYTE
+ char *end = multibyte_strchr(begin, '}');
+
+#else
+ char *end = strchr(begin, '}');
+#endif
+
+ if (!end)
+ continue;
+ /* procedure calls */
+ if (stmt->statement_type == STMT_TYPE_PROCCALL)
+ {
+ int lit_call_len = 4;
+
+ while (isspace((unsigned char) old_statement[++opos]));
+ /* '=?' to accept return values exists ? */
+ if (old_statement[opos] == '?')
+ {
+ param_number++;
+ while (isspace((unsigned char) old_statement[++opos]));
+ if (old_statement[opos] != '=')
+ {
+ opos--;
+ continue;
+ }
+ while (isspace((unsigned char) old_statement[++opos]));
+ }
+ if (strnicmp(&old_statement[opos], "call", lit_call_len) ||
+ !isspace((unsigned char) old_statement[opos + lit_call_len]))
+ {
+ opos--;
+ continue;
+ }
+ opos += lit_call_len;
+ CVT_APPEND_STR("SELECT ");
+#ifdef MULTIBYTE
+ if (multibyte_strchr(&old_statement[opos], '('))
+#else
+ if (strchr(&old_statement[opos], '('))
+#endif /* MULTIBYTE */
+ proc_no_param = FALSE;
+ continue;
+ }
+ *end = '\0';
+
+ esc = convert_escape(begin);
+ if (esc)
+ CVT_APPEND_STR(esc);
+ else
+ { /* it's not a valid literal so just copy */
+ *end = '}';
+ CVT_APPEND_CHAR(oldchar);
+ continue;
+ }
+
+ opos += end - begin + 1;
+ *end = '}';
+ continue;
+ }
+ /* End of a procedure call */
+ else if (oldchar == '}' && stmt->statement_type == STMT_TYPE_PROCCALL)
+ {
+ if (proc_no_param)
+ CVT_APPEND_STR("()");
+ continue;
+ }
+
+ /*
+ * Can you have parameter markers inside of quotes? I dont think
+ * so. All the queries I've seen expect the driver to put quotes
+ * if needed.
+ */
+ else if (oldchar == '?')
+ ; /* ok */
+ else
+ {
+ if (oldchar == '\'')
+ in_quote = TRUE;
+ else if (oldchar == '\\')
+ in_escape = TRUE;
+ else if (oldchar == '\"')
+ in_dquote = TRUE;
+ else
+ {
+ if (isspace((unsigned char) oldchar))
+ {
+ if (!prev_token_end)
+ {
+ prev_token_end = TRUE;
+ token_save[token_len] = '\0';
+ if (token_len == 4)
+ {
+ if (check_cursor_ok &&
+ into_table_from(&old_statement[opos - token_len]))
+ {
+ stmt->statement_type = STMT_TYPE_CREATE;
+ SC_no_pre_executable(stmt);
+ SC_no_fetchcursor(stmt);
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ memmove(new_statement, new_statement + declare_pos, npos - declare_pos);
+ npos -= declare_pos;
+ }
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ else if (search_from_pos && /* where's from clause */
+ strnicmp(token_save, "from", 4) == 0)
+ {
+ search_from_pos = FALSE;
+ npos -= 5;
+ CVT_APPEND_STR(", CTID, OID from");
+ }
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ }
+ if (token_len == 3)
+ {
+ int endpos;
+
+ if (check_cursor_ok &&
+ strnicmp(token_save, "for", 3) == 0 &&
+ table_for_update(&old_statement[opos], &endpos))
+ {
+ SC_no_fetchcursor(stmt);
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ if (prepare_dummy_cursor)
+ {
+ npos -= 4;
+ opos += endpos;
+ }
+ else
+ {
+ memmove(new_statement, new_statement + declare_pos, npos - declare_pos);
+ npos -= declare_pos;
+ }
+ }
+ }
+ }
+ }
+ else if (prev_token_end)
+ {
+ prev_token_end = FALSE;
+ token_save[0] = oldchar;
+ token_len = 1;
+ }
+ else if (token_len + 1 < sizeof(token_save))
+ token_save[token_len++] = oldchar;
+ }
+ CVT_APPEND_CHAR(oldchar);
+ continue;
+ }
+
+ /*
+ * Its a '?' parameter alright
+ */
+ param_number++;
+
+ if (param_number >= stmt->parameters_allocated)
+ {
+ if (stmt->pre_executing)
+ {
+ CVT_APPEND_STR("NULL");
+ stmt->inaccurate_result = TRUE;
+ continue;
+ }
+ else
+ {
+ CVT_APPEND_CHAR('?');
+ continue;
+ }
+ }
+
+ /* Assign correct buffers based on data at exec param or not */
+ if (stmt->parameters[param_number].data_at_exec)
+ {
+ used = stmt->parameters[param_number].EXEC_used ? *stmt->parameters[param_number].EXEC_used : SQL_NTS;
+ buffer = stmt->parameters[param_number].EXEC_buffer;
+ }
+ else
+ {
+
+
+ used = stmt->parameters[param_number].used ? *stmt->parameters[param_number].used : SQL_NTS;
+
+ buffer = stmt->parameters[param_number].buffer;
+ }
+
+ /* Handle NULL parameter data */
+ if (used == SQL_NULL_DATA)
+ {
+ CVT_APPEND_STR("NULL");
+ continue;
+ }
+
+ /*
+ * If no buffer, and it's not null, then what the hell is it? Just
+ * leave it alone then.
+ */
+ if (!buffer)
+ {
+ if (stmt->pre_executing)
+ {
+ CVT_APPEND_STR("NULL");
+ stmt->inaccurate_result = TRUE;
+ continue;
+ }
+ else
+ {
+ CVT_APPEND_CHAR('?');
+ continue;
+ }
+ }
+
+ param_ctype = stmt->parameters[param_number].CType;
+ param_sqltype = stmt->parameters[param_number].SQLType;
+
+ mylog("copy_statement_with_params: from(fcType)=%d, to(fSqlType)=%d\n", param_ctype, param_sqltype);
+
+ /* replace DEFAULT with something we can use */
+ if (param_ctype == SQL_C_DEFAULT)
+ param_ctype = sqltype_to_default_ctype(param_sqltype);
+
+ buf = NULL;
+ param_string[0] = '\0';
+ cbuf[0] = '\0';
+
+ /* Convert input C type to a neutral format */
+ switch (param_ctype)
+ {
+ case SQL_C_BINARY:
+ case SQL_C_CHAR:
+ buf = buffer;
+ break;
+
+ case SQL_C_DOUBLE:
+ sprintf(param_string, "%.15g",
+ *((SDOUBLE *) buffer));
+ break;
+
+ case SQL_C_FLOAT:
+ sprintf(param_string, "%.6g",
+ *((SFLOAT *) buffer));
+ break;
+
+ case SQL_C_SLONG:
+ case SQL_C_LONG:
+ sprintf(param_string, "%ld",
+ *((SDWORD *) buffer));
+ break;
+
+ case SQL_C_SSHORT:
+ case SQL_C_SHORT:
+ sprintf(param_string, "%d",
+ *((SWORD *) buffer));
+ break;
+
+ case SQL_C_STINYINT:
+ case SQL_C_TINYINT:
+ sprintf(param_string, "%d",
+ *((SCHAR *) buffer));
+ break;
+
+ case SQL_C_ULONG:
+ sprintf(param_string, "%lu",
+ *((UDWORD *) buffer));
+ break;
+
+ case SQL_C_USHORT:
+ sprintf(param_string, "%u",
+ *((UWORD *) buffer));
+ break;
+
+ case SQL_C_UTINYINT:
+ sprintf(param_string, "%u",
+ *((UCHAR *) buffer));
+ break;
+
+ case SQL_C_BIT:
+ {
+ int i = *((UCHAR *) buffer);
+
+ sprintf(param_string, "%d", i ? 1 : 0);
+ break;
+ }
+
+ case SQL_C_DATE:
+#if (ODBCVER >= 0x0300)
+ case SQL_C_TYPE_DATE: /* 91 */
+#endif
+ {
+ DATE_STRUCT *ds = (DATE_STRUCT *) buffer;
+
+ st.m = ds->month;
+ st.d = ds->day;
+ st.y = ds->year;
+
+ break;
+ }
+
+ case SQL_C_TIME:
+#if (ODBCVER >= 0x0300)
+ case SQL_C_TYPE_TIME: /* 92 */
+#endif
+ {
+ TIME_STRUCT *ts = (TIME_STRUCT *) buffer;
+
+ st.hh = ts->hour;
+ st.mm = ts->minute;
+ st.ss = ts->second;
+
+ break;
+ }
+
+ case SQL_C_TIMESTAMP:
+#if (ODBCVER >= 0x0300)
+ case SQL_C_TYPE_TIMESTAMP: /* 93 */
+#endif
+ {
+ TIMESTAMP_STRUCT *tss = (TIMESTAMP_STRUCT *) buffer;
+
+ st.m = tss->month;
+ st.d = tss->day;
+ st.y = tss->year;
+ st.hh = tss->hour;
+ st.mm = tss->minute;
+ st.ss = tss->second;
+ st.fr = tss->fraction;
+
+ mylog("m=%d,d=%d,y=%d,hh=%d,mm=%d,ss=%d\n", st.m, st.d, st.y, st.hh, st.mm, st.ss);
+
+ break;
+
+ }
+ default:
+ /* error */
+ stmt->errormsg = "Unrecognized C_parameter type in copy_statement_with_parameters";
+ stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+ CVT_TERMINATE; /* just in case */
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /*
+ * Now that the input data is in a neutral format, convert it to
+ * the desired output format (sqltype)
+ */
+
+ switch (param_sqltype)
+ {
+ case SQL_CHAR:
+ case SQL_VARCHAR:
+ case SQL_LONGVARCHAR:
+
+ CVT_APPEND_CHAR('\''); /* Open Quote */
+
+ /* it was a SQL_C_CHAR */
+ if (buf)
+ CVT_SPECIAL_CHARS(buf, used);
+
+ /* it was a numeric type */
+ else if (param_string[0] != '\0')
+ CVT_APPEND_STR(param_string);
+
+ /* it was date,time,timestamp -- use m,d,y,hh,mm,ss */
+ else
+ {
+ sprintf(tmp, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
+ st.y, st.m, st.d, st.hh, st.mm, st.ss);
+
+ CVT_APPEND_STR(tmp);
+ }
+
+ CVT_APPEND_CHAR('\''); /* Close Quote */
+
+ break;
+
+ case SQL_DATE:
+#if (ODBCVER >= 0x0300)
+ case SQL_TYPE_DATE: /* 91 */
+#endif
+ if (buf)
+ { /* copy char data to time */
+ my_strcpy(cbuf, sizeof(cbuf), buf, used);
+ parse_datetime(cbuf, &st);
+ }
+
+ sprintf(tmp, "'%.4d-%.2d-%.2d'", st.y, st.m, st.d);
+
+ CVT_APPEND_STR(tmp);
+ break;
+
+ case SQL_TIME:
+#if (ODBCVER >= 0x0300)
+ case SQL_TYPE_TIME: /* 92 */
+#endif
+ if (buf)
+ { /* copy char data to time */
+ my_strcpy(cbuf, sizeof(cbuf), buf, used);
+ parse_datetime(cbuf, &st);
+ }
+
+ sprintf(tmp, "'%.2d:%.2d:%.2d'", st.hh, st.mm, st.ss);
+
+ CVT_APPEND_STR(tmp);
+ break;
+
+ case SQL_TIMESTAMP:
+#if (ODBCVER >= 0x0300)
+ case SQL_TYPE_TIMESTAMP: /* 93 */
+#endif
+
+ if (buf)
+ {
+ my_strcpy(cbuf, sizeof(cbuf), buf, used);
+ parse_datetime(cbuf, &st);
+ }
+
+ /*
+ * sprintf(tmp, "'%.4d-%.2d-%.2d %.2d:%.2d:%.2d'", st.y,
+ * st.m, st.d, st.hh, st.mm, st.ss);
+ */
+ tmp[0] = '\'';
+ /* Time zone stuff is unreliable */
+ stime2timestamp(&st, tmp + 1, FALSE, PG_VERSION_GE(conn, 7.2));
+ strcat(tmp, "'");
+
+ CVT_APPEND_STR(tmp);
+
+ break;
+
+ case SQL_BINARY:
+ case SQL_VARBINARY:/* non-ascii characters should be
+ * converted to octal */
+ CVT_APPEND_CHAR('\''); /* Open Quote */
+
+ mylog("SQL_VARBINARY: about to call convert_to_pgbinary, used = %d\n", used);
+
+ CVT_APPEND_BINARY(buf, used);
+
+ CVT_APPEND_CHAR('\''); /* Close Quote */
+
+ break;
+
+ case SQL_LONGVARBINARY:
+
+ if (stmt->parameters[param_number].data_at_exec)
+ lobj_oid = stmt->parameters[param_number].lobj_oid;
+ else
+ {
+ /* begin transaction if needed */
+ if (!CC_is_in_trans(conn))
+ {
+ QResultClass *res;
+ char ok;
+
+ res = CC_send_query(conn, "BEGIN", NULL);
+ if (!res)
+ {
+ stmt->errormsg = "Could not begin (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ ok = QR_command_successful(res);
+ QR_Destructor(res);
+ if (!ok)
+ {
+ stmt->errormsg = "Could not begin (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ CC_set_in_trans(conn);
+ }
+
+ /* store the oid */
+ lobj_oid = lo_creat(conn, INV_READ | INV_WRITE);
+ if (lobj_oid == 0)
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Couldnt create (in-line) large object.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* store the fd */
+ lobj_fd = lo_open(conn, lobj_oid, INV_WRITE);
+ if (lobj_fd < 0)
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Couldnt open (in-line) large object for writing.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ retval = lo_write(conn, lobj_fd, buffer, used);
+
+ lo_close(conn, lobj_fd);
+
+ /* commit transaction if needed */
+ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn))
+ {
+ QResultClass *res;
+ char ok;
+
+ res = CC_send_query(conn, "COMMIT", NULL);
+ if (!res)
+ {
+ stmt->errormsg = "Could not commit (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ ok = QR_command_successful(res);
+ QR_Destructor(res);
+ if (!ok)
+ {
+ stmt->errormsg = "Could not commit (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ CC_set_no_trans(conn);
+ }
+ }
+
+ /*
+ * the oid of the large object -- just put that in for the
+ * parameter marker -- the data has already been sent to
+ * the large object
+ */
+ sprintf(param_string, "'%d'", lobj_oid);
+ CVT_APPEND_STR(param_string);
+
+ break;
+
+ /*
+ * because of no conversion operator for bool and int4,
+ * SQL_BIT
+ */
+ /* must be quoted (0 or 1 is ok to use inside the quotes) */
+
+ case SQL_REAL:
+ if (buf)
+ my_strcpy(param_string, sizeof(param_string), buf, used);
+ sprintf(tmp, "'%s'::float4", param_string);
+ CVT_APPEND_STR(tmp);
+ break;
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ if (buf)
+ my_strcpy(param_string, sizeof(param_string), buf, used);
+ sprintf(tmp, "'%s'::float8", param_string);
+ CVT_APPEND_STR(tmp);
+ break;
+ case SQL_NUMERIC:
+ if (buf)
+ {
+ cbuf[0] = '\'';
+ my_strcpy(cbuf + 1, sizeof(cbuf) - 12, buf, used); /* 12 = 1('\'') +
+ * strlen("'::numeric")
+ * + 1('\0') */
+ strcat(cbuf, "'::numeric");
+ }
+ else
+ sprintf(cbuf, "'%s'::numeric", param_string);
+ CVT_APPEND_STR(cbuf);
+ break;
+ default: /* a numeric type or SQL_BIT */
+ if (param_sqltype == SQL_BIT)
+ CVT_APPEND_CHAR('\''); /* Open Quote */
+
+ if (buf)
+ {
+ switch (used)
+ {
+ case SQL_NULL_DATA:
+ break;
+ case SQL_NTS:
+ CVT_APPEND_STR(buf);
+ break;
+ default:
+ CVT_APPEND_DATA(buf, used);
+ }
+ }
+ else
+ CVT_APPEND_STR(param_string);
+
+ if (param_sqltype == SQL_BIT)
+ CVT_APPEND_CHAR('\''); /* Close Quote */
+
+ break;
+ }
+ } /* end, for */
+
+ /* make sure new_statement is always null-terminated */
+ CVT_TERMINATE;
+
+ if (conn->DriverToDataSource != NULL)
+ {
+ int length = strlen(new_statement);
+
+ conn->DriverToDataSource(conn->translation_option,
+ SQL_CHAR,
+ new_statement, length,
+ new_statement, length, NULL,
+ NULL, 0, NULL);
+ }
+
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ if (search_from_pos)
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ if (prepare_dummy_cursor && SC_is_pre_executable(stmt))
+ {
+ char fetchstr[128];
+
+ sprintf(fetchstr, ";fetch backward in %s;close %s;",
+ stmt->cursor_name, stmt->cursor_name);
+ if (begin_first && CC_is_in_autocommit(conn))
+ strcat(fetchstr, "COMMIT;");
+ CVT_APPEND_STR(fetchstr);
+ stmt->inaccurate_result = TRUE;
+ }
+
+ return SQL_SUCCESS;
+}
+
+
+static char *
+mapFunction(const char *func)
+{
+ int i;
+
+ for (i = 0; mapFuncs[i][0]; i++)
+ if (!stricmp(mapFuncs[i][0], func))
+ return mapFuncs[i][1];
+
+ return NULL;
+}
+
+
+/*
+ * convert_escape()
+ *
+ * This function returns a pointer to static memory!
+ */
+char *
+convert_escape(char *value)
+{
+ static char escape[1024];
+ char key[33];
+
+ /* Separate off the key, skipping leading and trailing whitespace */
+ while ((*value != '\0') && isspace((unsigned char) *value))
+ value++;
+ sscanf(value, "%32s", key);
+ while ((*value != '\0') && (!isspace((unsigned char) *value)))
+ value++;
+ while ((*value != '\0') && isspace((unsigned char) *value))
+ value++;
+
+ mylog("convert_escape: key='%s', val='%s'\n", key, value);
+
+ if ((strcmp(key, "d") == 0) ||
+ (strcmp(key, "t") == 0) ||
+ (strcmp(key, "oj") == 0) || /* {oj syntax support for 7.1
+ * servers */
+ (strcmp(key, "ts") == 0))
+ {
+ /* Literal; return the escape part as-is */
+ strncpy(escape, value, sizeof(escape) - 1);
+ }
+ else if (strcmp(key, "fn") == 0)
+ {
+ /*
+ * Function invocation Separate off the func name, skipping
+ * trailing whitespace.
+ */
+ char *funcEnd = value;
+ char svchar;
+ char *mapFunc;
+
+ while ((*funcEnd != '\0') && (*funcEnd != '(') &&
+ (!isspace((unsigned char) *funcEnd)))
+ funcEnd++;
+ svchar = *funcEnd;
+ *funcEnd = '\0';
+ sscanf(value, "%32s", key);
+ *funcEnd = svchar;
+ while ((*funcEnd != '\0') && isspace((unsigned char) *funcEnd))
+ funcEnd++;
+
+ /*
+ * We expect left parenthesis here, else return fn body as-is
+ * since it is one of those "function constants".
+ */
+ if (*funcEnd != '(')
+ {
+ strncpy(escape, value, sizeof(escape) - 1);
+ return escape;
+ }
+ mapFunc = mapFunction(key);
+
+ /*
+ * We could have mapFunction() return key if not in table... -
+ * thomas 2000-04-03
+ */
+ if (mapFunc == NULL)
+ {
+ /* If unrecognized function name, return fn body as-is */
+ strncpy(escape, value, sizeof(escape) - 1);
+ return escape;
+ }
+ /* copy mapped name and remaining input string */
+ strcpy(escape, mapFunc);
+ strncat(escape, funcEnd, sizeof(escape) - 1 - strlen(mapFunc));
+ }
+ else
+ {
+ /* Bogus key, leave untranslated */
+ return NULL;
+ }
+
+ return escape;
+}
+
+
+BOOL
+convert_money(const char *s, char *sout, size_t soutmax)
+{
+ size_t i = 0,
+ out = 0;
+
+ for (i = 0; s[i]; i++)
+ {
+ if (s[i] == '$' || s[i] == ',' || s[i] == ')')
+ ; /* skip these characters */
+ else
+ {
+ if (out + 1 >= soutmax)
+ return FALSE; /* sout is too short */
+ if (s[i] == '(')
+ sout[out++] = '-';
+ else
+ sout[out++] = s[i];
+ }
+ }
+ sout[out] = '\0';
+ return TRUE;
+}
+
+
+/*
+ * This function parses a character string for date/time info and fills in SIMPLE_TIME
+ * It does not zero out SIMPLE_TIME in case it is desired to initialize it with a value
+ */
+char
+parse_datetime(char *buf, SIMPLE_TIME *st)
+{
+ int y,
+ m,
+ d,
+ hh,
+ mm,
+ ss;
+ int nf;
+
+ y = m = d = hh = mm = ss = 0;
+
+ /* escape sequence ? */
+ if (buf[0] == '{')
+ {
+ while (*(++buf) && *buf != '\'');
+ if (!(*buf))
+ return FALSE;
+ buf++;
+ }
+ if (buf[4] == '-') /* year first */
+ nf = sscanf(buf, "%4d-%2d-%2d %2d:%2d:%2d", &y, &m, &d, &hh, &mm, &ss);
+ else
+ nf = sscanf(buf, "%2d-%2d-%4d %2d:%2d:%2d", &m, &d, &y, &hh, &mm, &ss);
+
+ if (nf == 5 || nf == 6)
+ {
+ st->y = y;
+ st->m = m;
+ st->d = d;
+ st->hh = hh;
+ st->mm = mm;
+ st->ss = ss;
+
+ return TRUE;
+ }
+
+ if (buf[4] == '-') /* year first */
+ nf = sscanf(buf, "%4d-%2d-%2d", &y, &m, &d);
+ else
+ nf = sscanf(buf, "%2d-%2d-%4d", &m, &d, &y);
+
+ if (nf == 3)
+ {
+ st->y = y;
+ st->m = m;
+ st->d = d;
+
+ return TRUE;
+ }
+
+ nf = sscanf(buf, "%2d:%2d:%2d", &hh, &mm, &ss);
+ if (nf == 2 || nf == 3)
+ {
+ st->hh = hh;
+ st->mm = mm;
+ st->ss = ss;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* Change linefeed to carriage-return/linefeed */
+int
+convert_linefeeds(const char *si, char *dst, size_t max, BOOL *changed)
+{
+ size_t i = 0,
+ out = 0;
+
+ if (max == 0)
+ max = 0xffffffff;
+ *changed = FALSE;
+ for (i = 0; si[i] && out < max - 1; i++)
+ {
+ if (si[i] == '\n')
+ {
+ /* Only add the carriage-return if needed */
+ if (i > 0 && si[i - 1] == '\r')
+ {
+ if (dst)
+ dst[out++] = si[i];
+ else
+ out++;
+ continue;
+ }
+ *changed = TRUE;
+
+ if (dst)
+ {
+ dst[out++] = '\r';
+ dst[out++] = '\n';
+ }
+ else
+ out += 2;
+ }
+ else
+ {
+ if (dst)
+ dst[out++] = si[i];
+ else
+ out++;
+ }
+ }
+ if (dst)
+ dst[out] = '\0';
+ return out;
+}
+
+
+/*
+ * Change carriage-return/linefeed to just linefeed
+ * Plus, escape any special characters.
+ */
+int
+convert_special_chars(const char *si, char *dst, int used)
+{
+ size_t i = 0,
+ out = 0,
+ max;
+ char *p = NULL;
+
+
+ if (used == SQL_NTS)
+ max = strlen(si);
+ else
+ max = used;
+ if (dst)
+ {
+ p = dst;
+ p[0] = '\0';
+ }
+#ifdef MULTIBYTE
+ multibyte_init();
+#endif
+
+ for (i = 0; i < max; i++)
+ {
+#ifdef MULTIBYTE
+ if (multibyte_char_check(si[i]) != 0)
+ {
+ if (p)
+ p[out] = si[i];
+ out++;
+ continue;
+ }
+#endif
+ if (si[i] == '\r' && si[i + 1] == '\n')
+ continue;
+ else if (si[i] == '\'' || si[i] == '\\')
+ {
+ if (p)
+ p[out++] = '\\';
+ else
+ out++;
+ }
+ if (p)
+ p[out++] = si[i];
+ else
+ out++;
+ }
+ if (p)
+ p[out] = '\0';
+ return out;
+}
+
+
+/* !!! Need to implement this function !!! */
+int
+convert_pgbinary_to_char(const char *value, char *rgbValue, int cbValueMax)
+{
+ mylog("convert_pgbinary_to_char: value = '%s'\n", value);
+
+ strncpy_null(rgbValue, value, cbValueMax);
+ return 0;
+}
+
+
+static unsigned int
+conv_from_octal(const unsigned char *s)
+{
+ int i,
+ y = 0;
+
+ for (i = 1; i <= 3; i++)
+ y += (s[i] - 48) * (int) pow(8, 3 - i);
+
+ return y;
+
+}
+
+
+static unsigned int
+conv_from_hex(const unsigned char *s)
+{
+ int i,
+ y = 0,
+ val;
+
+ for (i = 1; i <= 2; i++)
+ {
+ if (s[i] >= 'a' && s[i] <= 'f')
+ val = s[i] - 'a' + 10;
+ else if (s[i] >= 'A' && s[i] <= 'F')
+ val = s[i] - 'A' + 10;
+ else
+ val = s[i] - '0';
+
+ y += val * (int) pow(16, 2 - i);
+ }
+
+ return y;
+}
+
+
+/* convert octal escapes to bytes */
+int
+convert_from_pgbinary(const unsigned char *value, unsigned char *rgbValue, int cbValueMax)
+{
+ size_t i,
+ ilen = strlen(value);
+ int o = 0;
+
+
+ for (i = 0; i < ilen;)
+ {
+ if (value[i] == '\\')
+ {
+ if (value[i + 1] == '\\')
+ {
+ rgbValue[o] = value[i];
+ i += 2;
+ }
+ else
+ {
+ rgbValue[o] = conv_from_octal(&value[i]);
+ i += 4;
+ }
+ }
+ else
+ rgbValue[o] = value[i++];
+ mylog("convert_from_pgbinary: i=%d, rgbValue[%d] = %d, %c\n", i, o, rgbValue[o], rgbValue[o]);
+ o++;
+ }
+
+ rgbValue[o] = '\0'; /* extra protection */
+
+ return o;
+}
+
+
+static char *
+conv_to_octal(unsigned char val)
+{
+ int i;
+ static char x[6];
+
+ x[0] = '\\';
+ x[1] = '\\';
+ x[5] = '\0';
+
+ for (i = 4; i > 1; i--)
+ {
+ x[i] = (val & 7) + 48;
+ val >>= 3;
+ }
+
+ return x;
+}
+
+
+/* convert non-ascii bytes to octal escape sequences */
+int
+convert_to_pgbinary(const unsigned char *in, char *out, int len)
+{
+ int i,
+ o = 0;
+
+ for (i = 0; i < len; i++)
+ {
+ mylog("convert_to_pgbinary: in[%d] = %d, %c\n", i, in[i], in[i]);
+ if (isalnum(in[i]) || in[i] == ' ')
+ out[o++] = in[i];
+ else
+ {
+ strcpy(&out[o], conv_to_octal(in[i]));
+ o += 5;
+ }
+ }
+
+ mylog("convert_to_pgbinary: returning %d, out='%.*s'\n", o, o, out);
+
+ return o;
+}
+
+
+void
+encode(const char *in, char *out)
+{
+ unsigned int i,
+ ilen = strlen(in),
+ o = 0;
+
+ for (i = 0; i < ilen; i++)
+ {
+ if (in[i] == '+')
+ {
+ sprintf(&out[o], "%%2B");
+ o += 3;
+ }
+ else if (isspace((unsigned char) in[i]))
+ out[o++] = '+';
+ else if (!isalnum((unsigned char) in[i]))
+ {
+ sprintf(&out[o], "%%%02x", (unsigned char) in[i]);
+ o += 3;
+ }
+ else
+ out[o++] = in[i];
+ }
+ out[o++] = '\0';
+}
+
+
+void
+decode(const char *in, char *out)
+{
+ unsigned int i,
+ ilen = strlen(in),
+ o = 0;
+
+ for (i = 0; i < ilen; i++)
+ {
+ if (in[i] == '+')
+ out[o++] = ' ';
+ else if (in[i] == '%')
+ {
+ sprintf(&out[o++], "%c", conv_from_hex(&in[i]));
+ i += 2;
+ }
+ else
+ out[o++] = in[i];
+ }
+ out[o++] = '\0';
+}
+
+static const char *hextbl = "0123456789ABCDEF";
+static int
+pg_bin2hex(UCHAR *src, UCHAR *dst, int length)
+{
+ UCHAR chr,
+ *src_wk,
+ *dst_wk;
+ BOOL backwards;
+ int i;
+
+ backwards = FALSE;
+ if (dst < src)
+ {
+ if (dst + length > src + 1)
+ return -1;
+ }
+ else if (dst < src + length)
+ backwards = TRUE;
+ if (backwards)
+ {
+ for (i = 0, src_wk = src + length - 1, dst_wk = dst + 2 * length - 1; i < length; i++, src_wk--)
+ {
+ chr = *src_wk;
+ *dst_wk-- = hextbl[chr % 16];
+ *dst_wk-- = hextbl[chr >> 4];
+ }
+ }
+ else
+ {
+ for (i = 0, src_wk = src, dst_wk = dst; i < length; i++, src_wk++)
+ {
+ chr = *src_wk;
+ *dst_wk++ = hextbl[chr >> 4];
+ *dst_wk++ = hextbl[chr % 16];
+ }
+ }
+ dst[2 * length] = '\0';
+ return length;
+}
+
+/*-------
+ * 1. get oid (from 'value')
+ * 2. open the large object
+ * 3. read from the large object (handle multiple GetData)
+ * 4. close when read less than requested? -OR-
+ * lseek/read each time
+ * handle case where application receives truncated and
+ * decides not to continue reading.
+ *
+ * CURRENTLY, ONLY LONGVARBINARY is handled, since that is the only
+ * data type currently mapped to a PG_TYPE_LO. But, if any other types
+ * are desired to map to a large object (PG_TYPE_LO), then that would
+ * need to be handled here. For example, LONGVARCHAR could possibly be
+ * mapped to PG_TYPE_LO someday, instead of PG_TYPE_TEXT as it is now.
+ *-------
+ */
+int
+convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue,
+ SDWORD cbValueMax, SDWORD *pcbValue)
+{
+ Oid oid;
+ int retval,
+ result,
+ left = -1;
+ BindInfoClass *bindInfo = NULL;
+ ConnectionClass *conn = SC_get_conn(stmt);
+ ConnInfo *ci = &(conn->connInfo);
+ int factor = (fCType == SQL_C_CHAR ? 2 : 1);
+
+ /* If using SQLGetData, then current_col will be set */
+ if (stmt->current_col >= 0)
+ {
+ bindInfo = &stmt->bindings[stmt->current_col];
+ left = bindInfo->data_left;
+ }
+
+ /*
+ * if this is the first call for this column, open the large object
+ * for reading
+ */
+
+ if (!bindInfo || bindInfo->data_left == -1)
+ {
+ /* begin transaction if needed */
+ if (!CC_is_in_trans(conn))
+ {
+ QResultClass *res;
+ char ok;
+
+ res = CC_send_query(conn, "BEGIN", NULL);
+ if (!res)
+ {
+ stmt->errormsg = "Could not begin (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ return COPY_GENERAL_ERROR;
+ }
+ ok = QR_command_successful(res);
+ QR_Destructor(res);
+ if (!ok)
+ {
+ stmt->errormsg = "Could not begin (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ return COPY_GENERAL_ERROR;
+ }
+
+ CC_set_in_trans(conn);
+ }
+
+ oid = atoi(value);
+ stmt->lobj_fd = lo_open(conn, oid, INV_READ);
+ if (stmt->lobj_fd < 0)
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Couldnt open large object for reading.";
+ return COPY_GENERAL_ERROR;
+ }
+
+ /* Get the size */
+ retval = lo_lseek(conn, stmt->lobj_fd, 0L, SEEK_END);
+ if (retval >= 0)
+ {
+ left = lo_tell(conn, stmt->lobj_fd);
+ if (bindInfo)
+ bindInfo->data_left = left;
+
+ /* return to beginning */
+ lo_lseek(conn, stmt->lobj_fd, 0L, SEEK_SET);
+ }
+ }
+ mylog("lo data left = %d\n", left);
+
+ if (left == 0)
+ return COPY_NO_DATA_FOUND;
+
+ if (stmt->lobj_fd < 0)
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Large object FD undefined for multiple read.";
+ return COPY_GENERAL_ERROR;
+ }
+
+ retval = lo_read(conn, stmt->lobj_fd, (char *) rgbValue, factor > 1 ? (cbValueMax - 1) / factor : cbValueMax);
+ if (retval < 0)
+ {
+ lo_close(conn, stmt->lobj_fd);
+
+ /* commit transaction if needed */
+ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn))
+ {
+ QResultClass *res;
+ char ok;
+
+ res = CC_send_query(conn, "COMMIT", NULL);
+ if (!res)
+ {
+ stmt->errormsg = "Could not commit (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ return COPY_GENERAL_ERROR;
+ }
+ ok = QR_command_successful(res);
+ QR_Destructor(res);
+ if (!ok)
+ {
+ stmt->errormsg = "Could not commit (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ return COPY_GENERAL_ERROR;
+ }
+
+ CC_set_no_trans(conn);
+ }
+
+ stmt->lobj_fd = -1;
+
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Error reading from large object.";
+ return COPY_GENERAL_ERROR;
+ }
+
+ if (factor > 1)
+ pg_bin2hex((char *) rgbValue, (char *) rgbValue, retval);
+ if (retval < left)
+ result = COPY_RESULT_TRUNCATED;
+ else
+ result = COPY_OK;
+
+ if (pcbValue)
+ *pcbValue = left < 0 ? SQL_NO_TOTAL : left * factor;
+
+ if (bindInfo && bindInfo->data_left > 0)
+ bindInfo->data_left -= retval;
+
+ if (!bindInfo || bindInfo->data_left == 0)
+ {
+ lo_close(conn, stmt->lobj_fd);
+
+ /* commit transaction if needed */
+ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn))
+ {
+ QResultClass *res;
+ char ok;
+
+ res = CC_send_query(conn, "COMMIT", NULL);
+ if (!res)
+ {
+ stmt->errormsg = "Could not commit (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ return COPY_GENERAL_ERROR;
+ }
+ ok = QR_command_successful(res);
+ QR_Destructor(res);
+ if (!ok)
+ {
+ stmt->errormsg = "Could not commit (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ return COPY_GENERAL_ERROR;
+ }
+
+ CC_set_no_trans(conn);
+ }
+
+ stmt->lobj_fd = -1; /* prevent further reading */
+ }
+
+ return result;
+}
--- /dev/null
+/* File: convert.h
+ *
+ * Description: See "convert.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __CONVERT_H__
+#define __CONVERT_H__
+
+#include "psqlodbc.h"
+
+/* copy_and_convert results */
+#define COPY_OK 0
+#define COPY_UNSUPPORTED_TYPE 1
+#define COPY_UNSUPPORTED_CONVERSION 2
+#define COPY_RESULT_TRUNCATED 3
+#define COPY_GENERAL_ERROR 4
+#define COPY_NO_DATA_FOUND 5
+
+typedef struct
+{
+ int m;
+ int d;
+ int y;
+ int hh;
+ int mm;
+ int ss;
+ int fr;
+} SIMPLE_TIME;
+
+int copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col);
+int copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType,
+ PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue);
+
+int copy_statement_with_parameters(StatementClass *stmt);
+char *convert_escape(char *value);
+BOOL convert_money(const char *s, char *sout, size_t soutmax);
+char parse_datetime(char *buf, SIMPLE_TIME *st);
+int convert_linefeeds(const char *s, char *dst, size_t max, BOOL *changed);
+int convert_special_chars(const char *si, char *dst, int used);
+
+int convert_pgbinary_to_char(const char *value, char *rgbValue, int cbValueMax);
+int convert_from_pgbinary(const unsigned char *value, unsigned char *rgbValue, int cbValueMax);
+int convert_to_pgbinary(const unsigned char *in, char *out, int len);
+void encode(const char *in, char *out);
+void decode(const char *in, char *out);
+int convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue,
+ SDWORD cbValueMax, SDWORD *pcbValue);
+
+#endif
--- /dev/null
+/*-------
+ * Module: dlg_specific.c
+ *
+ * Description: This module contains any specific code for handling
+ * dialog boxes such as driver/datasource options. Both the
+ * ConfigDSN() and the SQLDriverConnect() functions use
+ * functions in this module. If you were to add a new option
+ * to any dialog box, you would most likely only have to change
+ * things in here rather than in 2 separate places as before.
+ *
+ * Classes: none
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+/* Multibyte support Eiji Tokuya 2001-03-15 */
+
+#include "dlg_specific.h"
+
+#include "convert.h"
+
+#ifdef MULTIBYTE
+#include "multibyte.h"
+#endif
+#include "pgapifunc.h"
+
+#ifndef BOOL
+#define BOOL int
+#endif
+#ifndef FALSE
+#define FALSE (BOOL)0
+#endif
+#ifndef TRUE
+#define TRUE (BOOL)1
+#endif
+
+extern GLOBAL_VALUES globals;
+
+#ifdef WIN32
+static int driver_optionsDraw(HWND, const ConnInfo *, int src, BOOL enable);
+static int driver_options_update(HWND hdlg, ConnInfo *ci, BOOL);
+static void updateCommons(const ConnInfo *ci);
+#endif
+
+#ifdef WIN32
+void
+SetDlgStuff(HWND hdlg, const ConnInfo *ci)
+{
+ /*
+ * If driver attribute NOT present, then set the datasource name and
+ * description
+ */
+ if (ci->driver[0] == '\0')
+ {
+ SetDlgItemText(hdlg, IDC_DSNAME, ci->dsn);
+ SetDlgItemText(hdlg, IDC_DESC, ci->desc);
+ }
+
+ SetDlgItemText(hdlg, IDC_DATABASE, ci->database);
+ SetDlgItemText(hdlg, IDC_SERVER, ci->server);
+ SetDlgItemText(hdlg, IDC_USER, ci->username);
+ SetDlgItemText(hdlg, IDC_PASSWORD, ci->password);
+ SetDlgItemText(hdlg, IDC_PORT, ci->port);
+}
+
+
+void
+GetDlgStuff(HWND hdlg, ConnInfo *ci)
+{
+ GetDlgItemText(hdlg, IDC_DESC, ci->desc, sizeof(ci->desc));
+
+ GetDlgItemText(hdlg, IDC_DATABASE, ci->database, sizeof(ci->database));
+ GetDlgItemText(hdlg, IDC_SERVER, ci->server, sizeof(ci->server));
+ GetDlgItemText(hdlg, IDC_USER, ci->username, sizeof(ci->username));
+ GetDlgItemText(hdlg, IDC_PASSWORD, ci->password, sizeof(ci->password));
+ GetDlgItemText(hdlg, IDC_PORT, ci->port, sizeof(ci->port));
+}
+
+
+static int
+driver_optionsDraw(HWND hdlg, const ConnInfo *ci, int src, BOOL enable)
+{
+ const GLOBAL_VALUES *comval;
+ static BOOL defset = FALSE;
+ static GLOBAL_VALUES defval;
+
+ switch (src)
+ {
+ case 0: /* driver common */
+ comval = &globals;
+ break;
+ case 1: /* dsn specific */
+ comval = &(ci->drivers);
+ break;
+ case 2: /* default */
+ if (!defset)
+ {
+ defval.commlog = DEFAULT_COMMLOG;
+ defval.disable_optimizer = DEFAULT_OPTIMIZER;
+ defval.ksqo = DEFAULT_KSQO;
+ defval.unique_index = DEFAULT_UNIQUEINDEX;
+ defval.onlyread = DEFAULT_READONLY;
+ defval.use_declarefetch = DEFAULT_USEDECLAREFETCH;
+
+ defval.parse = DEFAULT_PARSE;
+ defval.cancel_as_freestmt = DEFAULT_CANCELASFREESTMT;
+ defval.debug = DEFAULT_DEBUG;
+
+ /* Unknown Sizes */
+ defval.unknown_sizes = DEFAULT_UNKNOWNSIZES;
+ defval.text_as_longvarchar = DEFAULT_TEXTASLONGVARCHAR;
+ defval.unknowns_as_longvarchar = DEFAULT_UNKNOWNSASLONGVARCHAR;
+ defval.bools_as_char = DEFAULT_BOOLSASCHAR;
+ }
+ defset = TRUE;
+ comval = &defval;
+ break;
+ }
+
+ CheckDlgButton(hdlg, DRV_COMMLOG, comval->commlog);
+ CheckDlgButton(hdlg, DRV_OPTIMIZER, comval->disable_optimizer);
+ CheckDlgButton(hdlg, DRV_KSQO, comval->ksqo);
+ CheckDlgButton(hdlg, DRV_UNIQUEINDEX, comval->unique_index);
+ EnableWindow(GetDlgItem(hdlg, DRV_UNIQUEINDEX), enable);
+ CheckDlgButton(hdlg, DRV_READONLY, comval->onlyread);
+ EnableWindow(GetDlgItem(hdlg, DRV_READONLY), enable);
+ CheckDlgButton(hdlg, DRV_USEDECLAREFETCH, comval->use_declarefetch);
+
+ /* Unknown Sizes clear */
+ CheckDlgButton(hdlg, DRV_UNKNOWN_DONTKNOW, 0);
+ CheckDlgButton(hdlg, DRV_UNKNOWN_LONGEST, 0);
+ CheckDlgButton(hdlg, DRV_UNKNOWN_MAX, 0);
+ /* Unknown (Default) Data Type sizes */
+ switch (comval->unknown_sizes)
+ {
+ case UNKNOWNS_AS_DONTKNOW:
+ CheckDlgButton(hdlg, DRV_UNKNOWN_DONTKNOW, 1);
+ break;
+ case UNKNOWNS_AS_LONGEST:
+ CheckDlgButton(hdlg, DRV_UNKNOWN_LONGEST, 1);
+ break;
+ case UNKNOWNS_AS_MAX:
+ default:
+ CheckDlgButton(hdlg, DRV_UNKNOWN_MAX, 1);
+ break;
+ }
+
+ CheckDlgButton(hdlg, DRV_TEXT_LONGVARCHAR, comval->text_as_longvarchar);
+ CheckDlgButton(hdlg, DRV_UNKNOWNS_LONGVARCHAR, comval->unknowns_as_longvarchar);
+ CheckDlgButton(hdlg, DRV_BOOLS_CHAR, comval->bools_as_char);
+ CheckDlgButton(hdlg, DRV_PARSE, comval->parse);
+ CheckDlgButton(hdlg, DRV_CANCELASFREESTMT, comval->cancel_as_freestmt);
+ CheckDlgButton(hdlg, DRV_DEBUG, comval->debug);
+ SetDlgItemInt(hdlg, DRV_CACHE_SIZE, comval->fetch_max, FALSE);
+ SetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, comval->max_varchar_size, FALSE);
+ SetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, comval->max_longvarchar_size, TRUE);
+ SetDlgItemText(hdlg, DRV_EXTRASYSTABLEPREFIXES, comval->extra_systable_prefixes);
+
+ /* Driver Connection Settings */
+ SetDlgItemText(hdlg, DRV_CONNSETTINGS, comval->conn_settings);
+ EnableWindow(GetDlgItem(hdlg, DRV_CONNSETTINGS), enable);
+ return 0;
+}
+static int
+driver_options_update(HWND hdlg, ConnInfo *ci, BOOL updateProfile)
+{
+ GLOBAL_VALUES *comval;
+
+ if (ci)
+ comval = &(ci->drivers);
+ else
+ comval = &globals;
+ comval->commlog = IsDlgButtonChecked(hdlg, DRV_COMMLOG);
+ comval->disable_optimizer = IsDlgButtonChecked(hdlg, DRV_OPTIMIZER);
+ comval->ksqo = IsDlgButtonChecked(hdlg, DRV_KSQO);
+ if (!ci)
+ {
+ comval->unique_index = IsDlgButtonChecked(hdlg, DRV_UNIQUEINDEX);
+ comval->onlyread = IsDlgButtonChecked(hdlg, DRV_READONLY);
+ }
+ comval->use_declarefetch = IsDlgButtonChecked(hdlg, DRV_USEDECLAREFETCH);
+
+ /* Unknown (Default) Data Type sizes */
+ if (IsDlgButtonChecked(hdlg, DRV_UNKNOWN_MAX))
+ comval->unknown_sizes = UNKNOWNS_AS_MAX;
+ else if (IsDlgButtonChecked(hdlg, DRV_UNKNOWN_DONTKNOW))
+ comval->unknown_sizes = UNKNOWNS_AS_DONTKNOW;
+ else if (IsDlgButtonChecked(hdlg, DRV_UNKNOWN_LONGEST))
+ comval->unknown_sizes = UNKNOWNS_AS_LONGEST;
+ else
+ comval->unknown_sizes = UNKNOWNS_AS_MAX;
+
+ comval->text_as_longvarchar = IsDlgButtonChecked(hdlg, DRV_TEXT_LONGVARCHAR);
+ comval->unknowns_as_longvarchar = IsDlgButtonChecked(hdlg, DRV_UNKNOWNS_LONGVARCHAR);
+ comval->bools_as_char = IsDlgButtonChecked(hdlg, DRV_BOOLS_CHAR);
+
+ comval->parse = IsDlgButtonChecked(hdlg, DRV_PARSE);
+
+ comval->cancel_as_freestmt = IsDlgButtonChecked(hdlg, DRV_CANCELASFREESTMT);
+ comval->debug = IsDlgButtonChecked(hdlg, DRV_DEBUG);
+
+ comval->fetch_max = GetDlgItemInt(hdlg, DRV_CACHE_SIZE, NULL, FALSE);
+ comval->max_varchar_size = GetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, NULL, FALSE);
+ comval->max_longvarchar_size = GetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, NULL, TRUE); /* allows for
+ * SQL_NO_TOTAL */
+
+ GetDlgItemText(hdlg, DRV_EXTRASYSTABLEPREFIXES, comval->extra_systable_prefixes, sizeof(comval->extra_systable_prefixes));
+
+ /* Driver Connection Settings */
+ if (!ci)
+ GetDlgItemText(hdlg, DRV_CONNSETTINGS, comval->conn_settings, sizeof(comval->conn_settings));
+
+ if (updateProfile)
+ updateCommons(ci);
+
+ /* fall through */
+ return 0;
+}
+
+int CALLBACK
+driver_optionsProc(HWND hdlg,
+ WORD wMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ ConnInfo *ci;
+
+ switch (wMsg)
+ {
+ case WM_INITDIALOG:
+ SetWindowLong(hdlg, DWL_USER, lParam); /* save for OK etc */
+ ci = (ConnInfo *) lParam;
+ CheckDlgButton(hdlg, DRV_OR_DSN, 0);
+ if (ci && ci->dsn && ci->dsn[0])
+ SetWindowText(hdlg, "Advanced Options (per DSN)");
+ else
+ {
+ SetWindowText(hdlg, "Advanced Options (Connection)");
+ ShowWindow(GetDlgItem(hdlg, DRV_OR_DSN), SW_HIDE);
+ }
+ driver_optionsDraw(hdlg, ci, 1, FALSE);
+ break;
+
+ case WM_COMMAND:
+ switch (GET_WM_COMMAND_ID(wParam, lParam))
+ {
+ case IDOK:
+ ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
+ driver_options_update(hdlg, IsDlgButtonChecked(hdlg, DRV_OR_DSN) ? NULL : ci,
+ ci && ci->dsn && ci->dsn[0]);
+
+ case IDCANCEL:
+ EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
+ return TRUE;
+
+ case IDDEFAULTS:
+ if (IsDlgButtonChecked(hdlg, DRV_OR_DSN))
+ driver_optionsDraw(hdlg, NULL, 2, TRUE);
+ else
+ {
+ ConnInfo *ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
+
+ driver_optionsDraw(hdlg, ci, 0, FALSE);
+ }
+ break;
+
+ case DRV_OR_DSN:
+ if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
+ {
+ mylog("DRV_OR_DSN clicked\n");
+ if (IsDlgButtonChecked(hdlg, DRV_OR_DSN))
+ {
+ SetWindowText(hdlg, "Advanced Options (Common)");
+ driver_optionsDraw(hdlg, NULL, 0, TRUE);
+ }
+ else
+ {
+ ConnInfo *ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
+
+ SetWindowText(hdlg, "Advanced Options (per DSN)");
+ driver_optionsDraw(hdlg, ci, ci ? 1 : 0, ci == NULL);
+ }
+ }
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int CALLBACK
+ds_optionsProc(HWND hdlg,
+ WORD wMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ ConnInfo *ci;
+ char buf[128];
+
+ switch (wMsg)
+ {
+ case WM_INITDIALOG:
+ ci = (ConnInfo *) lParam;
+ SetWindowLong(hdlg, DWL_USER, lParam); /* save for OK */
+
+ /* Change window caption */
+ if (ci->driver[0])
+ SetWindowText(hdlg, "Advanced Options (Connection)");
+ else
+ {
+ sprintf(buf, "Advanced Options (%s)", ci->dsn);
+ SetWindowText(hdlg, buf);
+ }
+
+ /* Readonly */
+ CheckDlgButton(hdlg, DS_READONLY, atoi(ci->onlyread));
+
+ /* Protocol */
+ if (strncmp(ci->protocol, PG62, strlen(PG62)) == 0)
+ CheckDlgButton(hdlg, DS_PG62, 1);
+ else if (strncmp(ci->protocol, PG63, strlen(PG63)) == 0)
+ CheckDlgButton(hdlg, DS_PG63, 1);
+ else
+ /* latest */
+ CheckDlgButton(hdlg, DS_PG64, 1);
+
+ CheckDlgButton(hdlg, DS_SHOWOIDCOLUMN, atoi(ci->show_oid_column));
+ CheckDlgButton(hdlg, DS_FAKEOIDINDEX, atoi(ci->fake_oid_index));
+ CheckDlgButton(hdlg, DS_ROWVERSIONING, atoi(ci->row_versioning));
+ CheckDlgButton(hdlg, DS_SHOWSYSTEMTABLES, atoi(ci->show_system_tables));
+ CheckDlgButton(hdlg, DS_DISALLOWPREMATURE, ci->disallow_premature);
+
+ EnableWindow(GetDlgItem(hdlg, DS_FAKEOIDINDEX), atoi(ci->show_oid_column));
+
+ /* Datasource Connection Settings */
+ SetDlgItemText(hdlg, DS_CONNSETTINGS, ci->conn_settings);
+ break;
+
+ case WM_COMMAND:
+ switch (GET_WM_COMMAND_ID(wParam, lParam))
+ {
+ case DS_SHOWOIDCOLUMN:
+ mylog("WM_COMMAND: DS_SHOWOIDCOLUMN\n");
+ EnableWindow(GetDlgItem(hdlg, DS_FAKEOIDINDEX), IsDlgButtonChecked(hdlg, DS_SHOWOIDCOLUMN));
+ return TRUE;
+
+ case IDOK:
+ ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
+ mylog("IDOK: got ci = %u\n", ci);
+
+ /* Readonly */
+ sprintf(ci->onlyread, "%d", IsDlgButtonChecked(hdlg, DS_READONLY));
+
+ /* Protocol */
+ if (IsDlgButtonChecked(hdlg, DS_PG62))
+ strcpy(ci->protocol, PG62);
+ else if (IsDlgButtonChecked(hdlg, DS_PG63))
+ strcpy(ci->protocol, PG63);
+ else
+ /* latest */
+ strcpy(ci->protocol, PG64);
+
+ sprintf(ci->show_system_tables, "%d", IsDlgButtonChecked(hdlg, DS_SHOWSYSTEMTABLES));
+
+ sprintf(ci->row_versioning, "%d", IsDlgButtonChecked(hdlg, DS_ROWVERSIONING));
+ ci->disallow_premature = IsDlgButtonChecked(hdlg, DS_DISALLOWPREMATURE);
+
+ /* OID Options */
+ sprintf(ci->fake_oid_index, "%d", IsDlgButtonChecked(hdlg, DS_FAKEOIDINDEX));
+ sprintf(ci->show_oid_column, "%d", IsDlgButtonChecked(hdlg, DS_SHOWOIDCOLUMN));
+
+ /* Datasource Connection Settings */
+ GetDlgItemText(hdlg, DS_CONNSETTINGS, ci->conn_settings, sizeof(ci->conn_settings));
+
+ /* fall through */
+
+ case IDCANCEL:
+ EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * This function writes any global parameters (that can be manipulated)
+ * to the ODBCINST.INI portion of the registry
+ */
+static void
+updateCommons(const ConnInfo *ci)
+{
+ const char *sectionName;
+ const char *fileName;
+ const GLOBAL_VALUES *comval;
+ char tmp[128];
+
+ if (ci)
+ if (ci->dsn && ci->dsn[0])
+ {
+ mylog("DSN=%s updating\n", ci->dsn);
+ comval = &(ci->drivers);
+ sectionName = ci->dsn;
+ fileName = ODBC_INI;
+ }
+ else
+ {
+ mylog("ci but dsn==NULL\n");
+ return;
+ }
+ else
+ {
+ mylog("drivers updating\n");
+ comval = &globals;
+ sectionName = DBMS_NAME;
+ fileName = ODBCINST_INI;
+ }
+ sprintf(tmp, "%d", comval->fetch_max);
+ SQLWritePrivateProfileString(sectionName,
+ INI_FETCH, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->commlog);
+ SQLWritePrivateProfileString(sectionName,
+ INI_COMMLOG, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->debug);
+ SQLWritePrivateProfileString(sectionName,
+ INI_DEBUG, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->disable_optimizer);
+ SQLWritePrivateProfileString(sectionName,
+ INI_OPTIMIZER, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->ksqo);
+ SQLWritePrivateProfileString(sectionName,
+ INI_KSQO, tmp, fileName);
+
+ /*
+ * Never update the onlyread, unique_index from this module.
+ */
+ if (!ci)
+ {
+ sprintf(tmp, "%d", comval->unique_index);
+ SQLWritePrivateProfileString(sectionName, INI_UNIQUEINDEX, tmp,
+ fileName);
+
+ sprintf(tmp, "%d", comval->onlyread);
+ SQLWritePrivateProfileString(sectionName, INI_READONLY, tmp,
+ fileName);
+ }
+
+ sprintf(tmp, "%d", comval->use_declarefetch);
+ SQLWritePrivateProfileString(sectionName,
+ INI_USEDECLAREFETCH, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->unknown_sizes);
+ SQLWritePrivateProfileString(sectionName,
+ INI_UNKNOWNSIZES, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->text_as_longvarchar);
+ SQLWritePrivateProfileString(sectionName,
+ INI_TEXTASLONGVARCHAR, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->unknowns_as_longvarchar);
+ SQLWritePrivateProfileString(sectionName,
+ INI_UNKNOWNSASLONGVARCHAR, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->bools_as_char);
+ SQLWritePrivateProfileString(sectionName,
+ INI_BOOLSASCHAR, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->parse);
+ SQLWritePrivateProfileString(sectionName,
+ INI_PARSE, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->cancel_as_freestmt);
+ SQLWritePrivateProfileString(sectionName,
+ INI_CANCELASFREESTMT, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->max_varchar_size);
+ SQLWritePrivateProfileString(sectionName,
+ INI_MAXVARCHARSIZE, tmp, fileName);
+
+ sprintf(tmp, "%d", comval->max_longvarchar_size);
+ SQLWritePrivateProfileString(sectionName,
+ INI_MAXLONGVARCHARSIZE, tmp, fileName);
+
+ SQLWritePrivateProfileString(sectionName,
+ INI_EXTRASYSTABLEPREFIXES, comval->extra_systable_prefixes, fileName);
+
+ /*
+ * Never update the conn_setting from this module
+ * SQLWritePrivateProfileString(sectionName, INI_CONNSETTINGS,
+ * comval->conn_settings, fileName);
+ */
+}
+#endif /* WIN32 */
+
+
+void
+makeConnectString(char *connect_string, const ConnInfo *ci, UWORD len)
+{
+ char got_dsn = (ci->dsn[0] != '\0');
+ char encoded_conn_settings[LARGE_REGISTRY_LEN];
+ UWORD hlen;
+ /*BOOL abbrev = (len <= 400);*/
+ BOOL abbrev = (len < 1024);
+
+ /* fundamental info */
+ sprintf(connect_string, "%s=%s;DATABASE=%s;SERVER=%s;PORT=%s;UID=%s;PWD=%s",
+ got_dsn ? "DSN" : "DRIVER",
+ got_dsn ? ci->dsn : ci->driver,
+ ci->database,
+ ci->server,
+ ci->port,
+ ci->username,
+ ci->password);
+
+ encode(ci->conn_settings, encoded_conn_settings);
+
+ /* extra info */
+ hlen = strlen(connect_string);
+ if (!abbrev)
+ sprintf(&connect_string[hlen],
+ ";READONLY=%s;PROTOCOL=%s;FAKEOIDINDEX=%s;SHOWOIDCOLUMN=%s;ROWVERSIONING=%s;SHOWSYSTEMTABLES=%s;CONNSETTINGS=%s;FETCH=%d;SOCKET=%d;UNKNOWNSIZES=%d;MAXVARCHARSIZE=%d;MAXLONGVARCHARSIZE=%d;DEBUG=%d;COMMLOG=%d;OPTIMIZER=%d;KSQO=%d;USEDECLAREFETCH=%d;TEXTASLONGVARCHAR=%d;UNKNOWNSASLONGVARCHAR=%d;BOOLSASCHAR=%d;PARSE=%d;CANCELASFREESTMT=%d;EXTRASYSTABLEPREFIXES=%s",
+ ci->onlyread,
+ ci->protocol,
+ ci->fake_oid_index,
+ ci->show_oid_column,
+ ci->row_versioning,
+ ci->show_system_tables,
+ encoded_conn_settings,
+ ci->drivers.fetch_max,
+ ci->drivers.socket_buffersize,
+ ci->drivers.unknown_sizes,
+ ci->drivers.max_varchar_size,
+ ci->drivers.max_longvarchar_size,
+ ci->drivers.debug,
+ ci->drivers.commlog,
+ ci->drivers.disable_optimizer,
+ ci->drivers.ksqo,
+ ci->drivers.use_declarefetch,
+ ci->drivers.text_as_longvarchar,
+ ci->drivers.unknowns_as_longvarchar,
+ ci->drivers.bools_as_char,
+ ci->drivers.parse,
+ ci->drivers.cancel_as_freestmt,
+ ci->drivers.extra_systable_prefixes);
+ /* Abbrebiation is needed ? */
+ if (abbrev || strlen(connect_string) >= len)
+ sprintf(&connect_string[hlen],
+ ";A0=%s;A1=%s;A2=%s;A3=%s;A4=%s;A5=%s;A6=%s;A7=%d;A8=%d;A9=%d;B0=%d;B1=%d;B2=%d;B3=%d;B4=%d;B5=%d;B6=%d;B7=%d;B8=%d;B9=%d;C0=%d;C1=%d;C2=%s",
+ ci->onlyread,
+ ci->protocol,
+ ci->fake_oid_index,
+ ci->show_oid_column,
+ ci->row_versioning,
+ ci->show_system_tables,
+ encoded_conn_settings,
+ ci->drivers.fetch_max,
+ ci->drivers.socket_buffersize,
+ ci->drivers.unknown_sizes,
+ ci->drivers.max_varchar_size,
+ ci->drivers.max_longvarchar_size,
+ ci->drivers.debug,
+ ci->drivers.commlog,
+ ci->drivers.disable_optimizer,
+ ci->drivers.ksqo,
+ ci->drivers.use_declarefetch,
+ ci->drivers.text_as_longvarchar,
+ ci->drivers.unknowns_as_longvarchar,
+ ci->drivers.bools_as_char,
+ ci->drivers.parse,
+ ci->drivers.cancel_as_freestmt,
+ ci->drivers.extra_systable_prefixes);
+}
+
+
+void
+copyAttributes(ConnInfo *ci, const char *attribute, const char *value)
+{
+ if (stricmp(attribute, "DSN") == 0)
+ strcpy(ci->dsn, value);
+
+ else if (stricmp(attribute, "driver") == 0)
+ strcpy(ci->driver, value);
+
+ else if (stricmp(attribute, INI_DATABASE) == 0)
+ strcpy(ci->database, value);
+
+ else if (stricmp(attribute, INI_SERVER) == 0 || stricmp(attribute, "server") == 0)
+ strcpy(ci->server, value);
+
+ else if (stricmp(attribute, INI_USER) == 0 || stricmp(attribute, "uid") == 0)
+ strcpy(ci->username, value);
+
+ else if (stricmp(attribute, INI_PASSWORD) == 0 || stricmp(attribute, "pwd") == 0)
+ strcpy(ci->password, value);
+
+ else if (stricmp(attribute, INI_PORT) == 0)
+ strcpy(ci->port, value);
+
+ else if (stricmp(attribute, INI_READONLY) == 0 || stricmp(attribute, "A0") == 0)
+ strcpy(ci->onlyread, value);
+
+ else if (stricmp(attribute, INI_PROTOCOL) == 0 || stricmp(attribute, "A1") == 0)
+ strcpy(ci->protocol, value);
+
+ else if (stricmp(attribute, INI_SHOWOIDCOLUMN) == 0 || stricmp(attribute, "A3") == 0)
+ strcpy(ci->show_oid_column, value);
+
+ else if (stricmp(attribute, INI_FAKEOIDINDEX) == 0 || stricmp(attribute, "A2") == 0)
+ strcpy(ci->fake_oid_index, value);
+
+ else if (stricmp(attribute, INI_ROWVERSIONING) == 0 || stricmp(attribute, "A4") == 0)
+ strcpy(ci->row_versioning, value);
+
+ else if (stricmp(attribute, INI_SHOWSYSTEMTABLES) == 0 || stricmp(attribute, "A5") == 0)
+ strcpy(ci->show_system_tables, value);
+
+ else if (stricmp(attribute, INI_CONNSETTINGS) == 0 || stricmp(attribute, "A6") == 0)
+ {
+ decode(value, ci->conn_settings);
+ /* strcpy(ci->conn_settings, value); */
+ }
+ else if (stricmp(attribute, INI_DISALLOWPREMATURE) == 0 || stricmp(attribute, "C3") == 0)
+ ci->disallow_premature = atoi(value);
+ else if (stricmp(attribute, INI_UPDATABLECURSORS) == 0 || stricmp(attribute, "C4") == 0)
+ ci->updatable_cursors = atoi(value);
+
+ mylog("copyAttributes: DSN='%s',server='%s',dbase='%s',user='%s',passwd='%s',port='%s',onlyread='%s',protocol='%s',conn_settings='%s',disallow_premature=%d)\n", ci->dsn, ci->server, ci->database, ci->username, ci->password, ci->port, ci->onlyread, ci->protocol, ci->conn_settings, ci->disallow_premature);
+}
+
+void
+copyCommonAttributes(ConnInfo *ci, const char *attribute, const char *value)
+{
+ if (stricmp(attribute, INI_FETCH) == 0 || stricmp(attribute, "A7") == 0)
+ ci->drivers.fetch_max = atoi(value);
+ else if (stricmp(attribute, INI_SOCKET) == 0 || stricmp(attribute, "A8") == 0)
+ ci->drivers.socket_buffersize = atoi(value);
+ else if (stricmp(attribute, INI_DEBUG) == 0 || stricmp(attribute, "B2") == 0)
+ ci->drivers.debug = atoi(value);
+ else if (stricmp(attribute, INI_COMMLOG) == 0 || stricmp(attribute, "B3") == 0)
+ ci->drivers.commlog = atoi(value);
+ else if (stricmp(attribute, INI_OPTIMIZER) == 0 || stricmp(attribute, "B4") == 0)
+ ci->drivers.disable_optimizer = atoi(value);
+ else if (stricmp(attribute, INI_KSQO) == 0 || stricmp(attribute, "B5") == 0)
+ ci->drivers.ksqo = atoi(value);
+
+ /*
+ * else if (stricmp(attribute, INI_UNIQUEINDEX) == 0 ||
+ * stricmp(attribute, "UIX") == 0) ci->drivers.unique_index =
+ * atoi(value);
+ */
+ else if (stricmp(attribute, INI_UNKNOWNSIZES) == 0 || stricmp(attribute, "A9") == 0)
+ ci->drivers.unknown_sizes = atoi(value);
+ else if (stricmp(attribute, INI_LIE) == 0)
+ ci->drivers.lie = atoi(value);
+ else if (stricmp(attribute, INI_PARSE) == 0 || stricmp(attribute, "C0") == 0)
+ ci->drivers.parse = atoi(value);
+ else if (stricmp(attribute, INI_CANCELASFREESTMT) == 0 || stricmp(attribute, "C1") == 0)
+ ci->drivers.cancel_as_freestmt = atoi(value);
+ else if (stricmp(attribute, INI_USEDECLAREFETCH) == 0 || stricmp(attribute, "B6") == 0)
+ ci->drivers.use_declarefetch = atoi(value);
+ else if (stricmp(attribute, INI_MAXVARCHARSIZE) == 0 || stricmp(attribute, "B0") == 0)
+ ci->drivers.max_varchar_size = atoi(value);
+ else if (stricmp(attribute, INI_MAXLONGVARCHARSIZE) == 0 || stricmp(attribute, "B1") == 0)
+ ci->drivers.max_longvarchar_size = atoi(value);
+ else if (stricmp(attribute, INI_TEXTASLONGVARCHAR) == 0 || stricmp(attribute, "B7") == 0)
+ ci->drivers.text_as_longvarchar = atoi(value);
+ else if (stricmp(attribute, INI_UNKNOWNSASLONGVARCHAR) == 0 || stricmp(attribute, "B8") == 0)
+ ci->drivers.unknowns_as_longvarchar = atoi(value);
+ else if (stricmp(attribute, INI_BOOLSASCHAR) == 0 || stricmp(attribute, "B9") == 0)
+ ci->drivers.bools_as_char = atoi(value);
+ else if (stricmp(attribute, INI_EXTRASYSTABLEPREFIXES) == 0 || stricmp(attribute, "C2") == 0)
+ strcpy(ci->drivers.extra_systable_prefixes, value);
+ mylog("CopyCommonAttributes: A7=%d;A8=%d;A9=%d;B0=%d;B1=%d;B2=%d;B3=%d;B4=%d;B5=%d;B6=%d;B7=%d;B8=%d;B9=%d;C0=%d;C1=%d;C2=%s",
+ ci->drivers.fetch_max,
+ ci->drivers.socket_buffersize,
+ ci->drivers.unknown_sizes,
+ ci->drivers.max_varchar_size,
+ ci->drivers.max_longvarchar_size,
+ ci->drivers.debug,
+ ci->drivers.commlog,
+ ci->drivers.disable_optimizer,
+ ci->drivers.ksqo,
+ ci->drivers.use_declarefetch,
+ ci->drivers.text_as_longvarchar,
+ ci->drivers.unknowns_as_longvarchar,
+ ci->drivers.bools_as_char,
+ ci->drivers.parse,
+ ci->drivers.cancel_as_freestmt,
+ ci->drivers.extra_systable_prefixes);
+}
+
+
+void
+getDSNdefaults(ConnInfo *ci)
+{
+ if (ci->port[0] == '\0')
+ strcpy(ci->port, DEFAULT_PORT);
+
+ if (ci->onlyread[0] == '\0')
+ sprintf(ci->onlyread, "%d", globals.onlyread);
+
+ if (ci->protocol[0] == '\0')
+ strcpy(ci->protocol, globals.protocol);
+
+ if (ci->fake_oid_index[0] == '\0')
+ sprintf(ci->fake_oid_index, "%d", DEFAULT_FAKEOIDINDEX);
+
+ if (ci->show_oid_column[0] == '\0')
+ sprintf(ci->show_oid_column, "%d", DEFAULT_SHOWOIDCOLUMN);
+
+ if (ci->show_system_tables[0] == '\0')
+ sprintf(ci->show_system_tables, "%d", DEFAULT_SHOWSYSTEMTABLES);
+
+ if (ci->row_versioning[0] == '\0')
+ sprintf(ci->row_versioning, "%d", DEFAULT_ROWVERSIONING);
+}
+
+
+void
+getDSNinfo(ConnInfo *ci, char overwrite)
+{
+ char *DSN = ci->dsn;
+ char encoded_conn_settings[LARGE_REGISTRY_LEN],
+ temp[SMALL_REGISTRY_LEN];
+
+/*
+ * If a driver keyword was present, then dont use a DSN and return.
+ * If DSN is null and no driver, then use the default datasource.
+ */
+ memcpy(&ci->drivers, &globals, sizeof(globals));
+ if (DSN[0] == '\0')
+ {
+ if (ci->driver[0] != '\0')
+ return;
+ else
+ strcpy(DSN, INI_DSN);
+ }
+
+ /* brute-force chop off trailing blanks... */
+ while (*(DSN + strlen(DSN) - 1) == ' ')
+ *(DSN + strlen(DSN) - 1) = '\0';
+
+ /* Proceed with getting info for the given DSN. */
+
+ if (ci->desc[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_KDESC, "", ci->desc, sizeof(ci->desc), ODBC_INI);
+
+ if (ci->server[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_SERVER, "", ci->server, sizeof(ci->server), ODBC_INI);
+
+ if (ci->database[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_DATABASE, "", ci->database, sizeof(ci->database), ODBC_INI);
+
+ if (ci->username[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_USER, "", ci->username, sizeof(ci->username), ODBC_INI);
+
+ if (ci->password[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_PASSWORD, "", ci->password, sizeof(ci->password), ODBC_INI);
+
+ if (ci->port[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_PORT, "", ci->port, sizeof(ci->port), ODBC_INI);
+
+ if (ci->onlyread[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_READONLY, "", ci->onlyread, sizeof(ci->onlyread), ODBC_INI);
+
+ if (ci->show_oid_column[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_SHOWOIDCOLUMN, "", ci->show_oid_column, sizeof(ci->show_oid_column), ODBC_INI);
+
+ if (ci->fake_oid_index[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_FAKEOIDINDEX, "", ci->fake_oid_index, sizeof(ci->fake_oid_index), ODBC_INI);
+
+ if (ci->row_versioning[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_ROWVERSIONING, "", ci->row_versioning, sizeof(ci->row_versioning), ODBC_INI);
+
+ if (ci->show_system_tables[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_SHOWSYSTEMTABLES, "", ci->show_system_tables, sizeof(ci->show_system_tables), ODBC_INI);
+
+ if (ci->protocol[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_PROTOCOL, "", ci->protocol, sizeof(ci->protocol), ODBC_INI);
+
+ if (ci->conn_settings[0] == '\0' || overwrite)
+ {
+ SQLGetPrivateProfileString(DSN, INI_CONNSETTINGS, "", encoded_conn_settings, sizeof(encoded_conn_settings), ODBC_INI);
+ decode(encoded_conn_settings, ci->conn_settings);
+ }
+
+ if (ci->translation_dll[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_TRANSLATIONDLL, "", ci->translation_dll, sizeof(ci->translation_dll), ODBC_INI);
+
+ if (ci->translation_option[0] == '\0' || overwrite)
+ SQLGetPrivateProfileString(DSN, INI_TRANSLATIONOPTION, "", ci->translation_option, sizeof(ci->translation_option), ODBC_INI);
+
+ if (ci->disallow_premature == 0 || overwrite)
+ {
+ SQLGetPrivateProfileString(DSN, INI_DISALLOWPREMATURE, "", temp, sizeof(temp), ODBC_INI);
+ ci->disallow_premature = atoi(temp);
+ }
+
+ if (ci->updatable_cursors == 0 || overwrite)
+ {
+ SQLGetPrivateProfileString(DSN, INI_UPDATABLECURSORS, "", temp, sizeof(temp), ODBC_INI);
+ ci->updatable_cursors = atoi(temp);
+ }
+
+ /* Allow override of odbcinst.ini parameters here */
+ getCommonDefaults(DSN, ODBC_INI, ci);
+
+ qlog("DSN info: DSN='%s',server='%s',port='%s',dbase='%s',user='%s',passwd='%s'\n",
+ DSN,
+ ci->server,
+ ci->port,
+ ci->database,
+ ci->username,
+ ci->password);
+ qlog(" onlyread='%s',protocol='%s',showoid='%s',fakeoidindex='%s',showsystable='%s'\n",
+ ci->onlyread,
+ ci->protocol,
+ ci->show_oid_column,
+ ci->fake_oid_index,
+ ci->show_system_tables);
+
+#ifdef MULTIBYTE
+ check_client_encoding(ci->conn_settings);
+ qlog(" conn_settings='%s',conn_encoding='%s'\n",
+ ci->conn_settings,
+ check_client_encoding(ci->conn_settings));
+#else
+ qlog(" conn_settings='%s'\n",
+ ci->conn_settings);
+#endif
+
+ qlog(" translation_dll='%s',translation_option='%s'\n",
+ ci->translation_dll,
+ ci->translation_option);
+}
+
+
+/* This is for datasource based options only */
+void
+writeDSNinfo(const ConnInfo *ci)
+{
+ const char *DSN = ci->dsn;
+ char encoded_conn_settings[LARGE_REGISTRY_LEN],
+ temp[SMALL_REGISTRY_LEN];
+
+ encode(ci->conn_settings, encoded_conn_settings);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_KDESC,
+ ci->desc,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_DATABASE,
+ ci->database,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_SERVER,
+ ci->server,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_PORT,
+ ci->port,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_USER,
+ ci->username,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_PASSWORD,
+ ci->password,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_READONLY,
+ ci->onlyread,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_SHOWOIDCOLUMN,
+ ci->show_oid_column,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_FAKEOIDINDEX,
+ ci->fake_oid_index,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_ROWVERSIONING,
+ ci->row_versioning,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_SHOWSYSTEMTABLES,
+ ci->show_system_tables,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_PROTOCOL,
+ ci->protocol,
+ ODBC_INI);
+
+ SQLWritePrivateProfileString(DSN,
+ INI_CONNSETTINGS,
+ encoded_conn_settings,
+ ODBC_INI);
+
+ sprintf(temp, "%d", ci->disallow_premature);
+ SQLWritePrivateProfileString(DSN,
+ INI_DISALLOWPREMATURE,
+ temp,
+ ODBC_INI);
+ sprintf(temp, "%d", ci->updatable_cursors);
+ SQLWritePrivateProfileString(DSN,
+ INI_UPDATABLECURSORS,
+ temp,
+ ODBC_INI);
+}
+
+
+/*
+ * This function reads the ODBCINST.INI portion of
+ * the registry and gets any driver defaults.
+ */
+void
+getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
+{
+ char temp[256];
+ GLOBAL_VALUES *comval;
+
+ if (ci)
+ comval = &(ci->drivers);
+ else
+ comval = &globals;
+ /* Fetch Count is stored in driver section */
+ SQLGetPrivateProfileString(section, INI_FETCH, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ {
+ comval->fetch_max = atoi(temp);
+ /* sanity check if using cursors */
+ if (comval->fetch_max <= 0)
+ comval->fetch_max = FETCH_MAX;
+ }
+ else if (!ci)
+ comval->fetch_max = FETCH_MAX;
+
+ /* Socket Buffersize is stored in driver section */
+ SQLGetPrivateProfileString(section, INI_SOCKET, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->socket_buffersize = atoi(temp);
+ else if (!ci)
+ comval->socket_buffersize = SOCK_BUFFER_SIZE;
+
+ /* Debug is stored in the driver section */
+ SQLGetPrivateProfileString(section, INI_DEBUG, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->debug = atoi(temp);
+ else if (!ci)
+ comval->debug = DEFAULT_DEBUG;
+
+ /* CommLog is stored in the driver section */
+ SQLGetPrivateProfileString(section, INI_COMMLOG, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->commlog = atoi(temp);
+ else if (!ci)
+ comval->commlog = DEFAULT_COMMLOG;
+
+ if (!ci)
+ logs_on_off(0, 0, 0);
+ /* Optimizer is stored in the driver section only */
+ SQLGetPrivateProfileString(section, INI_OPTIMIZER, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->disable_optimizer = atoi(temp);
+ else if (!ci)
+ comval->disable_optimizer = DEFAULT_OPTIMIZER;
+
+ /* KSQO is stored in the driver section only */
+ SQLGetPrivateProfileString(section, INI_KSQO, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->ksqo = atoi(temp);
+ else if (!ci)
+ comval->ksqo = DEFAULT_KSQO;
+
+ /* Recognize Unique Index is stored in the driver section only */
+ SQLGetPrivateProfileString(section, INI_UNIQUEINDEX, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->unique_index = atoi(temp);
+ else if (!ci)
+ comval->unique_index = DEFAULT_UNIQUEINDEX;
+
+
+ /* Unknown Sizes is stored in the driver section only */
+ SQLGetPrivateProfileString(section, INI_UNKNOWNSIZES, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->unknown_sizes = atoi(temp);
+ else if (!ci)
+ comval->unknown_sizes = DEFAULT_UNKNOWNSIZES;
+
+
+ /* Lie about supported functions? */
+ SQLGetPrivateProfileString(section, INI_LIE, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->lie = atoi(temp);
+ else if (!ci)
+ comval->lie = DEFAULT_LIE;
+
+ /* Parse statements */
+ SQLGetPrivateProfileString(section, INI_PARSE, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->parse = atoi(temp);
+ else if (!ci)
+ comval->parse = DEFAULT_PARSE;
+
+ /* SQLCancel calls SQLFreeStmt in Driver Manager */
+ SQLGetPrivateProfileString(section, INI_CANCELASFREESTMT, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->cancel_as_freestmt = atoi(temp);
+ else if (!ci)
+ comval->cancel_as_freestmt = DEFAULT_CANCELASFREESTMT;
+
+ /* UseDeclareFetch is stored in the driver section only */
+ SQLGetPrivateProfileString(section, INI_USEDECLAREFETCH, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->use_declarefetch = atoi(temp);
+ else if (!ci)
+ comval->use_declarefetch = DEFAULT_USEDECLAREFETCH;
+
+ /* Max Varchar Size */
+ SQLGetPrivateProfileString(section, INI_MAXVARCHARSIZE, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->max_varchar_size = atoi(temp);
+ else if (!ci)
+ comval->max_varchar_size = MAX_VARCHAR_SIZE;
+
+ /* Max TextField Size */
+ SQLGetPrivateProfileString(section, INI_MAXLONGVARCHARSIZE, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->max_longvarchar_size = atoi(temp);
+ else if (!ci)
+ comval->max_longvarchar_size = TEXT_FIELD_SIZE;
+
+ /* Text As LongVarchar */
+ SQLGetPrivateProfileString(section, INI_TEXTASLONGVARCHAR, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->text_as_longvarchar = atoi(temp);
+ else if (!ci)
+ comval->text_as_longvarchar = DEFAULT_TEXTASLONGVARCHAR;
+
+ /* Unknowns As LongVarchar */
+ SQLGetPrivateProfileString(section, INI_UNKNOWNSASLONGVARCHAR, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->unknowns_as_longvarchar = atoi(temp);
+ else if (!ci)
+ comval->unknowns_as_longvarchar = DEFAULT_UNKNOWNSASLONGVARCHAR;
+
+ /* Bools As Char */
+ SQLGetPrivateProfileString(section, INI_BOOLSASCHAR, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->bools_as_char = atoi(temp);
+ else if (!ci)
+ comval->bools_as_char = DEFAULT_BOOLSASCHAR;
+
+ /* Extra Systable prefixes */
+
+ /*
+ * Use @@@ to distinguish between blank extra prefixes and no key
+ * entry
+ */
+ SQLGetPrivateProfileString(section, INI_EXTRASYSTABLEPREFIXES, "@@@",
+ temp, sizeof(temp), filename);
+ if (strcmp(temp, "@@@"))
+ strcpy(comval->extra_systable_prefixes, temp);
+ else if (!ci)
+ strcpy(comval->extra_systable_prefixes, DEFAULT_EXTRASYSTABLEPREFIXES);
+
+ mylog("globals.extra_systable_prefixes = '%s'\n", comval->extra_systable_prefixes);
+
+
+ /* Dont allow override of an override! */
+ if (!ci)
+ {
+ /*
+ * ConnSettings is stored in the driver section and per datasource
+ * for override
+ */
+ SQLGetPrivateProfileString(section, INI_CONNSETTINGS, "",
+ comval->conn_settings, sizeof(comval->conn_settings), filename);
+
+ /* Default state for future DSN's Readonly attribute */
+ SQLGetPrivateProfileString(section, INI_READONLY, "",
+ temp, sizeof(temp), filename);
+ if (temp[0])
+ comval->onlyread = atoi(temp);
+ else
+ comval->onlyread = DEFAULT_READONLY;
+
+ /*
+ * Default state for future DSN's protocol attribute This isn't a
+ * real driver option YET. This is more intended for
+ * customization from the install.
+ */
+ SQLGetPrivateProfileString(section, INI_PROTOCOL, "@@@",
+ temp, sizeof(temp), filename);
+ if (strcmp(temp, "@@@"))
+ strcpy(comval->protocol, temp);
+ else
+ strcpy(comval->protocol, DEFAULT_PROTOCOL);
+ }
+}
--- /dev/null
+/* File: dlg_specific.h
+ *
+ * Description: See "dlg_specific.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __DLG_SPECIFIC_H__
+#define __DLG_SPECIFIC_H__
+
+#include "psqlodbc.h"
+#include "connection.h"
+
+#ifdef WIN32
+#include <windowsx.h>
+#include "resource.h"
+#endif
+
+/* Unknown data type sizes */
+#define UNKNOWNS_AS_MAX 0
+#define UNKNOWNS_AS_DONTKNOW 1
+#define UNKNOWNS_AS_LONGEST 2
+
+/* ODBC initialization files */
+#ifndef WIN32
+#define ODBC_INI ".odbc.ini"
+#define ODBCINST_INI "odbcinst.ini"
+#else
+#define ODBC_INI "ODBC.INI"
+#define ODBCINST_INI "ODBCINST.INI"
+#endif
+
+
+#define INI_DSN DBMS_NAME /* Name of default
+ * Datasource in ini
+ * file (not used?) */
+#define INI_KDESC "Description" /* Data source
+ * description */
+#define INI_SERVER "Servername" /* Name of Server
+ * running the Postgres
+ * service */
+#define INI_PORT "Port" /* Port on which the
+ * Postmaster is listening */
+#define INI_DATABASE "Database" /* Database Name */
+#define INI_USER "Username" /* Default User Name */
+#define INI_PASSWORD "Password" /* Default Password */
+#define INI_DEBUG "Debug" /* Debug flag */
+#define INI_FETCH "Fetch" /* Fetch Max Count */
+#define INI_SOCKET "Socket" /* Socket buffer size */
+#define INI_READONLY "ReadOnly" /* Database is read only */
+#define INI_COMMLOG "CommLog" /* Communication to
+ * backend logging */
+#define INI_PROTOCOL "Protocol" /* What protocol (6.2) */
+#define INI_OPTIMIZER "Optimizer" /* Use backend genetic
+ * optimizer */
+#define INI_KSQO "Ksqo" /* Keyset query
+ * optimization */
+#define INI_CONNSETTINGS "ConnSettings" /* Anything to send to
+ * backend on successful
+ * connection */
+#define INI_UNIQUEINDEX "UniqueIndex" /* Recognize unique
+ * indexes */
+#define INI_UNKNOWNSIZES "UnknownSizes" /* How to handle unknown
+ * result set sizes */
+
+#define INI_CANCELASFREESTMT "CancelAsFreeStmt"
+
+#define INI_USEDECLAREFETCH "UseDeclareFetch" /* Use Declare/Fetch
+ * cursors */
+
+/* More ini stuff */
+#define INI_TEXTASLONGVARCHAR "TextAsLongVarchar"
+#define INI_UNKNOWNSASLONGVARCHAR "UnknownsAsLongVarchar"
+#define INI_BOOLSASCHAR "BoolsAsChar"
+#define INI_MAXVARCHARSIZE "MaxVarcharSize"
+#define INI_MAXLONGVARCHARSIZE "MaxLongVarcharSize"
+
+#define INI_FAKEOIDINDEX "FakeOidIndex"
+#define INI_SHOWOIDCOLUMN "ShowOidColumn"
+#define INI_ROWVERSIONING "RowVersioning"
+#define INI_SHOWSYSTEMTABLES "ShowSystemTables"
+#define INI_LIE "Lie"
+#define INI_PARSE "Parse"
+#define INI_EXTRASYSTABLEPREFIXES "ExtraSysTablePrefixes"
+
+#define INI_TRANSLATIONNAME "TranslationName"
+#define INI_TRANSLATIONDLL "TranslationDLL"
+#define INI_TRANSLATIONOPTION "TranslationOption"
+#define INI_DISALLOWPREMATURE "DisallowPremature"
+#define INI_UPDATABLECURSORS "UpdatableCursors"
+
+
+/* Connection Defaults */
+#define DEFAULT_PORT "5432"
+#define DEFAULT_READONLY 0
+#define DEFAULT_PROTOCOL "6.4" /* the latest protocol is
+ * the default */
+#define DEFAULT_USEDECLAREFETCH 0
+#define DEFAULT_TEXTASLONGVARCHAR 1
+#define DEFAULT_UNKNOWNSASLONGVARCHAR 0
+#define DEFAULT_BOOLSASCHAR 1
+#define DEFAULT_OPTIMIZER 1 /* disable */
+#define DEFAULT_KSQO 1 /* on */
+#define DEFAULT_UNIQUEINDEX 1 /* dont recognize */
+#define DEFAULT_COMMLOG 0 /* dont log */
+#define DEFAULT_DEBUG 0
+#define DEFAULT_UNKNOWNSIZES UNKNOWNS_AS_MAX
+
+
+#define DEFAULT_FAKEOIDINDEX 0
+#define DEFAULT_SHOWOIDCOLUMN 0
+#define DEFAULT_ROWVERSIONING 0
+#define DEFAULT_SHOWSYSTEMTABLES 0 /* dont show system tables */
+#define DEFAULT_LIE 0
+#define DEFAULT_PARSE 0
+
+#define DEFAULT_CANCELASFREESTMT 0
+
+#define DEFAULT_EXTRASYSTABLEPREFIXES "dd_;"
+
+/* prototypes */
+void getCommonDefaults(const char *section, const char *filename, ConnInfo *ci);
+
+#ifdef WIN32
+void SetDlgStuff(HWND hdlg, const ConnInfo *ci);
+void GetDlgStuff(HWND hdlg, ConnInfo *ci);
+
+int CALLBACK driver_optionsProc(HWND hdlg,
+ WORD wMsg,
+ WPARAM wParam,
+ LPARAM lParam);
+int CALLBACK ds_optionsProc(HWND hdlg,
+ WORD wMsg,
+ WPARAM wParam,
+ LPARAM lParam);
+#endif /* WIN32 */
+
+void updateGlobals(void);
+void writeDSNinfo(const ConnInfo *ci);
+void getDSNdefaults(ConnInfo *ci);
+void getDSNinfo(ConnInfo *ci, char overwrite);
+void makeConnectString(char *connect_string, const ConnInfo *ci, UWORD);
+void copyAttributes(ConnInfo *ci, const char *attribute, const char *value);
+void copyCommonAttributes(ConnInfo *ci, const char *attribute, const char *value);
+
+#endif
--- /dev/null
+/*-------
+ Module: drvconn.c
+ *
+ * Description: This module contains only routines related to
+ * implementing SQLDriverConnect.
+ *
+ * Classes: n/a
+ *
+ * API functions: SQLDriverConnect
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "psqlodbc.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "connection.h"
+
+#ifndef WIN32
+#include <sys/types.h>
+#include <sys/socket.h>
+#define NEAR
+#else
+#include <winsock.h>
+#endif
+
+#include <string.h>
+
+#ifdef WIN32
+#include <windowsx.h>
+#include "resource.h"
+#endif
+#include "pgapifunc.h"
+
+#ifndef TRUE
+#define TRUE (BOOL)1
+#endif
+#ifndef FALSE
+#define FALSE (BOOL)0
+#endif
+
+#include "dlg_specific.h"
+
+/* prototypes */
+void dconn_get_connect_attributes(const UCHAR FAR * connect_string, ConnInfo *ci);
+static void dconn_get_common_attributes(const UCHAR FAR * connect_string, ConnInfo *ci);
+
+#ifdef WIN32
+BOOL FAR PASCAL dconn_FDriverConnectProc(HWND hdlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
+RETCODE dconn_DoDialog(HWND hwnd, ConnInfo *ci);
+
+extern HINSTANCE NEAR s_hModule; /* Saved module handle. */
+#endif
+
+
+RETCODE SQL_API
+PGAPI_DriverConnect(
+ HDBC hdbc,
+ HWND hwnd,
+ UCHAR FAR * szConnStrIn,
+ SWORD cbConnStrIn,
+ UCHAR FAR * szConnStrOut,
+ SWORD cbConnStrOutMax,
+ SWORD FAR * pcbConnStrOut,
+ UWORD fDriverCompletion)
+{
+ static char *func = "PGAPI_DriverConnect";
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ ConnInfo *ci;
+
+#ifdef WIN32
+ RETCODE dialog_result;
+#endif
+ RETCODE result;
+ char connStrIn[MAX_CONNECT_STRING];
+ char connStrOut[MAX_CONNECT_STRING];
+ int retval;
+ char password_required = FALSE;
+ int len = 0;
+ SWORD lenStrout;
+
+
+ mylog("%s: entering...\n", func);
+
+ if (!conn)
+ {
+ CC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ make_string(szConnStrIn, cbConnStrIn, connStrIn);
+
+ mylog("**** PGAPI_DriverConnect: fDriverCompletion=%d, connStrIn='%s'\n", fDriverCompletion, connStrIn);
+ qlog("conn=%u, PGAPI_DriverConnect( in)='%s', fDriverCompletion=%d\n", conn, connStrIn, fDriverCompletion);
+
+ ci = &(conn->connInfo);
+
+ /* Parse the connect string and fill in conninfo for this hdbc. */
+ dconn_get_connect_attributes(connStrIn, ci);
+
+ /*
+ * If the ConnInfo in the hdbc is missing anything, this function will
+ * fill them in from the registry (assuming of course there is a DSN
+ * given -- if not, it does nothing!)
+ */
+ getDSNinfo(ci, CONN_DONT_OVERWRITE);
+ dconn_get_common_attributes(connStrIn, ci);
+ logs_on_off(1, ci->drivers.debug, ci->drivers.commlog);
+
+ /* Fill in any default parameters if they are not there. */
+ getDSNdefaults(ci);
+ /* initialize pg_version */
+ CC_initialize_pg_version(conn);
+
+#ifdef WIN32
+dialog:
+#endif
+ ci->focus_password = password_required;
+
+ switch (fDriverCompletion)
+ {
+#ifdef WIN32
+ case SQL_DRIVER_PROMPT:
+ dialog_result = dconn_DoDialog(hwnd, ci);
+ if (dialog_result != SQL_SUCCESS)
+ return dialog_result;
+ break;
+
+ case SQL_DRIVER_COMPLETE_REQUIRED:
+
+ /* Fall through */
+
+ case SQL_DRIVER_COMPLETE:
+
+ /* Password is not a required parameter. */
+ if (ci->username[0] == '\0' ||
+ ci->server[0] == '\0' ||
+ ci->database[0] == '\0' ||
+ ci->port[0] == '\0' ||
+ password_required)
+ {
+ dialog_result = dconn_DoDialog(hwnd, ci);
+ if (dialog_result != SQL_SUCCESS)
+ return dialog_result;
+ }
+ break;
+#else
+ case SQL_DRIVER_PROMPT:
+ case SQL_DRIVER_COMPLETE:
+ case SQL_DRIVER_COMPLETE_REQUIRED:
+#endif
+ case SQL_DRIVER_NOPROMPT:
+ break;
+ }
+
+ /*
+ * Password is not a required parameter unless authentication asks for
+ * it. For now, I think it's better to just let the application ask
+ * over and over until a password is entered (the user can always hit
+ * Cancel to get out)
+ */
+ if (ci->username[0] == '\0' ||
+ ci->server[0] == '\0' ||
+ ci->database[0] == '\0' ||
+ ci->port[0] == '\0')
+ {
+ /* (password_required && ci->password[0] == '\0')) */
+
+ return SQL_NO_DATA_FOUND;
+ }
+
+ /* do the actual connect */
+ retval = CC_connect(conn, password_required);
+ if (retval < 0)
+ { /* need a password */
+ if (fDriverCompletion == SQL_DRIVER_NOPROMPT)
+ {
+ CC_log_error(func, "Need password but Driver_NoPrompt", conn);
+ return SQL_ERROR; /* need a password but not allowed to
+ * prompt so error */
+ }
+ else
+ {
+#ifdef WIN32
+ password_required = TRUE;
+ goto dialog;
+#else
+ return SQL_ERROR; /* until a better solution is found. */
+#endif
+ }
+ }
+ else if (retval == 0)
+ {
+ /* error msg filled in above */
+ CC_log_error(func, "Error from CC_Connect", conn);
+ return SQL_ERROR;
+ }
+
+ /*
+ * Create the Output Connection String
+ */
+ result = SQL_SUCCESS;
+
+ lenStrout = cbConnStrOutMax;
+ if (conn->ms_jet && lenStrout > 255)
+ lenStrout = 255;
+ makeConnectString(connStrOut, ci, lenStrout);
+ len = strlen(connStrOut);
+
+ if (szConnStrOut)
+ {
+ /*
+ * Return the completed string to the caller. The correct method
+ * is to only construct the connect string if a dialog was put up,
+ * otherwise, it should just copy the connection input string to
+ * the output. However, it seems ok to just always construct an
+ * output string. There are possible bad side effects on working
+ * applications (Access) by implementing the correct behavior,
+ * anyway.
+ */
+ strncpy_null(szConnStrOut, connStrOut, cbConnStrOutMax);
+
+ if (len >= cbConnStrOutMax)
+ {
+ int clen;
+
+ for (clen = strlen(szConnStrOut) - 1; clen >= 0 && szConnStrOut[clen] != ';'; clen--)
+ szConnStrOut[clen] = '\0';
+ result = SQL_SUCCESS_WITH_INFO;
+ conn->errornumber = CONN_TRUNCATED;
+ conn->errormsg = "The buffer was too small for the ConnStrOut.";
+ }
+ }
+
+ if (pcbConnStrOut)
+ *pcbConnStrOut = len;
+
+ mylog("szConnStrOut = '%s' len=%d,%d\n", szConnStrOut, len, cbConnStrOutMax);
+ qlog("conn=%u, PGAPI_DriverConnect(out)='%s'\n", conn, szConnStrOut);
+
+
+ mylog("PGAPI_DRiverConnect: returning %d\n", result);
+ return result;
+}
+
+
+#ifdef WIN32
+RETCODE
+dconn_DoDialog(HWND hwnd, ConnInfo *ci)
+{
+ int dialog_result;
+
+ mylog("dconn_DoDialog: ci = %u\n", ci);
+
+ if (hwnd)
+ {
+ dialog_result = DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_CONFIG),
+ hwnd, dconn_FDriverConnectProc, (LPARAM) ci);
+ if (!dialog_result || (dialog_result == -1))
+ return SQL_NO_DATA_FOUND;
+ else
+ return SQL_SUCCESS;
+ }
+
+ return SQL_ERROR;
+}
+
+
+BOOL FAR PASCAL
+dconn_FDriverConnectProc(
+ HWND hdlg,
+ UINT wMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ ConnInfo *ci;
+
+ switch (wMsg)
+ {
+ case WM_INITDIALOG:
+ ci = (ConnInfo *) lParam;
+
+ /* Change the caption for the setup dialog */
+ SetWindowText(hdlg, "PostgreSQL Connection");
+
+ SetWindowText(GetDlgItem(hdlg, IDC_DATASOURCE), "Connection");
+
+ /* Hide the DSN and description fields */
+ ShowWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), SW_HIDE);
+ ShowWindow(GetDlgItem(hdlg, IDC_DSNAME), SW_HIDE);
+ ShowWindow(GetDlgItem(hdlg, IDC_DESCTEXT), SW_HIDE);
+ ShowWindow(GetDlgItem(hdlg, IDC_DESC), SW_HIDE);
+
+ SetWindowLong(hdlg, DWL_USER, lParam); /* Save the ConnInfo for
+ * the "OK" */
+ SetDlgStuff(hdlg, ci);
+
+ if (ci->database[0] == '\0')
+ ; /* default focus */
+ else if (ci->server[0] == '\0')
+ SetFocus(GetDlgItem(hdlg, IDC_SERVER));
+ else if (ci->port[0] == '\0')
+ SetFocus(GetDlgItem(hdlg, IDC_PORT));
+ else if (ci->username[0] == '\0')
+ SetFocus(GetDlgItem(hdlg, IDC_USER));
+ else if (ci->focus_password)
+ SetFocus(GetDlgItem(hdlg, IDC_PASSWORD));
+ break;
+
+ case WM_COMMAND:
+ switch (GET_WM_COMMAND_ID(wParam, lParam))
+ {
+ case IDOK:
+ ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
+
+ GetDlgStuff(hdlg, ci);
+
+ case IDCANCEL:
+ EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
+ return TRUE;
+
+ case IDC_DRIVER:
+ ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
+ DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DRV),
+ hdlg, driver_optionsProc, (LPARAM) ci);
+ break;
+
+ case IDC_DATASOURCE:
+ ci = (ConnInfo *) GetWindowLong(hdlg, DWL_USER);
+ DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DS),
+ hdlg, ds_optionsProc, (LPARAM) ci);
+ break;
+ }
+ }
+
+ return FALSE;
+}
+#endif /* WIN32 */
+
+
+void
+dconn_get_connect_attributes(const UCHAR FAR * connect_string, ConnInfo *ci)
+{
+ char *our_connect_string;
+ char *pair,
+ *attribute,
+ *value,
+ *equals;
+ char *strtok_arg;
+
+ memset(ci, 0, sizeof(ConnInfo));
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ ci->updatable_cursors = 1;
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+
+ our_connect_string = strdup(connect_string);
+ strtok_arg = our_connect_string;
+
+ mylog("our_connect_string = '%s'\n", our_connect_string);
+
+ while (1)
+ {
+ pair = strtok(strtok_arg, ";");
+ if (strtok_arg)
+ strtok_arg = 0;
+ if (!pair)
+ break;
+
+ equals = strchr(pair, '=');
+ if (!equals)
+ continue;
+
+ *equals = '\0';
+ attribute = pair; /* ex. DSN */
+ value = equals + 1; /* ex. 'CEO co1' */
+
+ mylog("attribute = '%s', value = '%s'\n", attribute, value);
+
+ if (!attribute || !value)
+ continue;
+
+ /* Copy the appropriate value to the conninfo */
+ copyAttributes(ci, attribute, value);
+
+ }
+
+ free(our_connect_string);
+}
+
+static void
+dconn_get_common_attributes(const UCHAR FAR * connect_string, ConnInfo *ci)
+{
+ char *our_connect_string;
+ char *pair,
+ *attribute,
+ *value,
+ *equals;
+ char *strtok_arg;
+
+ our_connect_string = strdup(connect_string);
+ strtok_arg = our_connect_string;
+
+ mylog("our_connect_string = '%s'\n", our_connect_string);
+
+ while (1)
+ {
+ pair = strtok(strtok_arg, ";");
+ if (strtok_arg)
+ strtok_arg = 0;
+ if (!pair)
+ break;
+
+ equals = strchr(pair, '=');
+ if (!equals)
+ continue;
+
+ *equals = '\0';
+ attribute = pair; /* ex. DSN */
+ value = equals + 1; /* ex. 'CEO co1' */
+
+ mylog("attribute = '%s', value = '%s'\n", attribute, value);
+
+ if (!attribute || !value)
+ continue;
+
+ /* Copy the appropriate value to the conninfo */
+ copyCommonAttributes(ci, attribute, value);
+
+ }
+
+ free(our_connect_string);
+}
--- /dev/null
+/*-------
+ * Module: environ.c
+ *
+ * Description: This module contains routines related to
+ * the environment, such as storing connection handles,
+ * and returning errors.
+ *
+ * Classes: EnvironmentClass (Functions prefix: "EN_")
+ *
+ * API functions: SQLAllocEnv, SQLFreeEnv, SQLError
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "environ.h"
+
+#include "connection.h"
+#include "dlg_specific.h"
+#include "statement.h"
+#include <stdlib.h>
+#include <string.h>
+#include "pgapifunc.h"
+
+extern GLOBAL_VALUES globals;
+
+/* The one instance of the handles */
+ConnectionClass *conns[MAX_CONNECTIONS];
+
+
+RETCODE SQL_API
+PGAPI_AllocEnv(HENV FAR * phenv)
+{
+ static char *func = "PGAPI_AllocEnv";
+
+ mylog("**** in PGAPI_AllocEnv ** \n");
+
+ /*
+ * Hack for systems on which none of the constructor-making techniques
+ * in psqlodbc.c work: if globals appears not to have been
+ * initialized, then cause it to be initialized. Since this should be
+ * the first function called in this shared library, doing it here
+ * should work.
+ */
+ if (globals.socket_buffersize <= 0)
+ getCommonDefaults(DBMS_NAME, ODBCINST_INI, NULL);
+
+ *phenv = (HENV) EN_Constructor();
+ if (!*phenv)
+ {
+ *phenv = SQL_NULL_HENV;
+ EN_log_error(func, "Error allocating environment", NULL);
+ return SQL_ERROR;
+ }
+
+ mylog("** exit PGAPI_AllocEnv: phenv = %u **\n", *phenv);
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_FreeEnv(HENV henv)
+{
+ static char *func = "PGAPI_FreeEnv";
+ EnvironmentClass *env = (EnvironmentClass *) henv;
+
+ mylog("**** in PGAPI_FreeEnv: env = %u ** \n", env);
+
+ if (env && EN_Destructor(env))
+ {
+ mylog(" ok\n");
+ return SQL_SUCCESS;
+ }
+
+ mylog(" error\n");
+ EN_log_error(func, "Error freeing environment", env);
+ return SQL_ERROR;
+}
+
+
+/* Returns the next SQL error information. */
+RETCODE SQL_API
+PGAPI_Error(
+ HENV henv,
+ HDBC hdbc,
+ HSTMT hstmt,
+ UCHAR FAR * szSqlState,
+ SDWORD FAR * pfNativeError,
+ UCHAR FAR * szErrorMsg,
+ SWORD cbErrorMsgMax,
+ SWORD FAR * pcbErrorMsg)
+{
+ char *msg;
+ int status;
+ BOOL once_again = FALSE;
+ SWORD msglen;
+
+ mylog("**** PGAPI_Error: henv=%u, hdbc=%u, hstmt=%u <%d>\n", henv, hdbc, hstmt, cbErrorMsgMax);
+
+ if (cbErrorMsgMax < 0)
+ return SQL_ERROR;
+ if (SQL_NULL_HSTMT != hstmt)
+ {
+ /* CC: return an error of a hstmt */
+ StatementClass *stmt = (StatementClass *) hstmt;
+
+ if (SC_get_error(stmt, &status, &msg))
+ {
+ mylog("SC_get_error: status = %d, msg = #%s#\n", status, msg);
+ if (NULL == msg)
+ {
+ if (NULL != szSqlState)
+ strcpy(szSqlState, "00000");
+ if (NULL != pcbErrorMsg)
+ *pcbErrorMsg = 0;
+ if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
+ szErrorMsg[0] = '\0';
+
+ return SQL_NO_DATA_FOUND;
+ }
+ msglen = (SWORD) strlen(msg);
+ if (NULL != pcbErrorMsg)
+ {
+ *pcbErrorMsg = msglen;
+ if (cbErrorMsgMax == 0)
+ once_again = TRUE;
+ else if (msglen >= cbErrorMsgMax)
+ {
+ once_again = TRUE;
+ *pcbErrorMsg = cbErrorMsgMax - 1;
+ }
+ }
+
+ if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
+ strncpy_null(szErrorMsg, msg, cbErrorMsgMax);
+
+ if (NULL != pfNativeError)
+ *pfNativeError = status;
+
+ if (NULL != szSqlState)
+
+ switch (status)
+ {
+ /* now determine the SQLSTATE to be returned */
+ case STMT_ROW_VERSION_CHANGED:
+ strcpy(szSqlState, "01001");
+ /* data truncated */
+ break;
+ case STMT_TRUNCATED:
+ strcpy(szSqlState, "01004");
+ /* data truncated */
+ break;
+ case STMT_INFO_ONLY:
+ strcpy(szSqlState, "00000");
+ /* just information that is returned, no error */
+ break;
+ case STMT_BAD_ERROR:
+ strcpy(szSqlState, "08S01");
+ /* communication link failure */
+ break;
+ case STMT_CREATE_TABLE_ERROR:
+ strcpy(szSqlState, "S0001");
+ /* table already exists */
+ break;
+ case STMT_STATUS_ERROR:
+ case STMT_SEQUENCE_ERROR:
+ strcpy(szSqlState, "S1010");
+ /* Function sequence error */
+ break;
+ case STMT_NO_MEMORY_ERROR:
+ strcpy(szSqlState, "S1001");
+ /* memory allocation failure */
+ break;
+ case STMT_COLNUM_ERROR:
+ strcpy(szSqlState, "S1002");
+ /* invalid column number */
+ break;
+ case STMT_NO_STMTSTRING:
+ strcpy(szSqlState, "S1001");
+ /* having no stmtstring is also a malloc problem */
+ break;
+ case STMT_ERROR_TAKEN_FROM_BACKEND:
+ strcpy(szSqlState, "S1000");
+ /* general error */
+ break;
+ case STMT_INTERNAL_ERROR:
+ strcpy(szSqlState, "S1000");
+ /* general error */
+ break;
+ case STMT_ROW_OUT_OF_RANGE:
+ strcpy(szSqlState, "S1107");
+ break;
+
+ case STMT_OPERATION_CANCELLED:
+ strcpy(szSqlState, "S1008");
+ break;
+
+ case STMT_NOT_IMPLEMENTED_ERROR:
+ strcpy(szSqlState, "S1C00"); /* == 'driver not
+ * capable' */
+ break;
+ case STMT_OPTION_OUT_OF_RANGE_ERROR:
+ strcpy(szSqlState, "S1092");
+ break;
+ case STMT_BAD_PARAMETER_NUMBER_ERROR:
+ strcpy(szSqlState, "S1093");
+ break;
+ case STMT_INVALID_COLUMN_NUMBER_ERROR:
+ strcpy(szSqlState, "S1002");
+ break;
+ case STMT_RESTRICTED_DATA_TYPE_ERROR:
+ strcpy(szSqlState, "07006");
+ break;
+ case STMT_INVALID_CURSOR_STATE_ERROR:
+ strcpy(szSqlState, "24000");
+ break;
+ case STMT_OPTION_VALUE_CHANGED:
+ strcpy(szSqlState, "01S02");
+ break;
+ case STMT_POS_BEFORE_RECORDSET:
+ strcpy(szSqlState, "01S06");
+ break;
+ case STMT_INVALID_CURSOR_NAME:
+ strcpy(szSqlState, "34000");
+ break;
+ case STMT_NO_CURSOR_NAME:
+ strcpy(szSqlState, "S1015");
+ break;
+ case STMT_INVALID_ARGUMENT_NO:
+ strcpy(szSqlState, "S1009");
+ /* invalid argument value */
+ break;
+ case STMT_INVALID_CURSOR_POSITION:
+ strcpy(szSqlState, "S1109");
+ break;
+ case STMT_VALUE_OUT_OF_RANGE:
+ strcpy(szSqlState, "22003");
+ break;
+ case STMT_OPERATION_INVALID:
+ strcpy(szSqlState, "S1011");
+ break;
+ case STMT_INVALID_OPTION_IDENTIFIER:
+ strcpy(szSqlState, "HY092");
+ break;
+ case STMT_EXEC_ERROR:
+ default:
+ strcpy(szSqlState, "S1000");
+ /* also a general error */
+ break;
+ }
+ mylog(" szSqlState = '%s', szError='%s'\n", szSqlState, szErrorMsg);
+ }
+ else
+ {
+ if (NULL != szSqlState)
+ strcpy(szSqlState, "00000");
+ if (NULL != pcbErrorMsg)
+ *pcbErrorMsg = 0;
+ if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
+ szErrorMsg[0] = '\0';
+
+ mylog(" returning NO_DATA_FOUND\n");
+
+ return SQL_NO_DATA_FOUND;
+ }
+
+ if (once_again)
+ {
+ int outlen;
+
+ stmt->errornumber = status;
+ if (cbErrorMsgMax > 0)
+ outlen = *pcbErrorMsg;
+ else
+ outlen = 0;
+ if (!stmt->errormsg_malloced || !stmt->errormsg)
+ {
+ stmt->errormsg = malloc(msglen - outlen + 1);
+ stmt->errormsg_malloced = TRUE;
+ }
+ memmove(stmt->errormsg, msg + outlen, msglen - outlen + 1);
+ }
+ else if (stmt->errormsg_malloced)
+ SC_clear_error(stmt);
+ if (cbErrorMsgMax == 0)
+ return SQL_SUCCESS_WITH_INFO;
+ else
+ return SQL_SUCCESS;
+ }
+ else if (SQL_NULL_HDBC != hdbc)
+ {
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+
+ mylog("calling CC_get_error\n");
+ if (CC_get_error(conn, &status, &msg))
+ {
+ mylog("CC_get_error: status = %d, msg = #%s#\n", status, msg);
+ if (NULL == msg)
+ {
+ if (NULL != szSqlState)
+ strcpy(szSqlState, "00000");
+ if (NULL != pcbErrorMsg)
+ *pcbErrorMsg = 0;
+ if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
+ szErrorMsg[0] = '\0';
+
+ return SQL_NO_DATA_FOUND;
+ }
+
+ msglen = strlen(msg);
+ if (NULL != pcbErrorMsg)
+ {
+ *pcbErrorMsg = msglen;
+ if (cbErrorMsgMax == 0)
+ once_again = TRUE;
+ else if (msglen >= cbErrorMsgMax)
+ *pcbErrorMsg = cbErrorMsgMax - 1;
+ }
+ if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
+ strncpy_null(szErrorMsg, msg, cbErrorMsgMax);
+ if (NULL != pfNativeError)
+ *pfNativeError = status;
+
+ if (NULL != szSqlState)
+ switch (status)
+ {
+ case STMT_OPTION_VALUE_CHANGED:
+ case CONN_OPTION_VALUE_CHANGED:
+ strcpy(szSqlState, "01S02");
+ break;
+ case STMT_TRUNCATED:
+ case CONN_TRUNCATED:
+ strcpy(szSqlState, "01004");
+ /* data truncated */
+ break;
+ case CONN_INIREAD_ERROR:
+ strcpy(szSqlState, "IM002");
+ /* data source not found */
+ break;
+ case CONN_OPENDB_ERROR:
+ strcpy(szSqlState, "08001");
+ /* unable to connect to data source */
+ break;
+ case CONN_INVALID_AUTHENTICATION:
+ case CONN_AUTH_TYPE_UNSUPPORTED:
+ strcpy(szSqlState, "28000");
+ break;
+ case CONN_STMT_ALLOC_ERROR:
+ strcpy(szSqlState, "S1001");
+ /* memory allocation failure */
+ break;
+ case CONN_IN_USE:
+ strcpy(szSqlState, "S1000");
+ /* general error */
+ break;
+ case CONN_UNSUPPORTED_OPTION:
+ strcpy(szSqlState, "IM001");
+ /* driver does not support this function */
+ case CONN_INVALID_ARGUMENT_NO:
+ strcpy(szSqlState, "S1009");
+ /* invalid argument value */
+ break;
+ case CONN_TRANSACT_IN_PROGRES:
+ strcpy(szSqlState, "S1010");
+
+ /*
+ * when the user tries to switch commit mode in a
+ * transaction
+ */
+ /* -> function sequence error */
+ break;
+ case CONN_NO_MEMORY_ERROR:
+ strcpy(szSqlState, "S1001");
+ break;
+ case CONN_NOT_IMPLEMENTED_ERROR:
+ case STMT_NOT_IMPLEMENTED_ERROR:
+ strcpy(szSqlState, "S1C00");
+ break;
+ case CONN_VALUE_OUT_OF_RANGE:
+ case STMT_VALUE_OUT_OF_RANGE:
+ strcpy(szSqlState, "22003");
+ break;
+ default:
+ strcpy(szSqlState, "S1000");
+ /* general error */
+ break;
+ }
+ }
+ else
+ {
+ mylog("CC_Get_error returned nothing.\n");
+ if (NULL != szSqlState)
+ strcpy(szSqlState, "00000");
+ if (NULL != pcbErrorMsg)
+ *pcbErrorMsg = 0;
+ if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
+ szErrorMsg[0] = '\0';
+
+ return SQL_NO_DATA_FOUND;
+ }
+
+ if (once_again)
+ {
+ conn->errornumber = status;
+ return SQL_SUCCESS_WITH_INFO;
+ }
+ else
+ return SQL_SUCCESS;
+ }
+ else if (SQL_NULL_HENV != henv)
+ {
+ EnvironmentClass *env = (EnvironmentClass *) henv;
+
+ if (EN_get_error(env, &status, &msg))
+ {
+ mylog("EN_get_error: status = %d, msg = #%s#\n", status, msg);
+ if (NULL == msg)
+ {
+ if (NULL != szSqlState)
+ strcpy(szSqlState, "00000");
+ if (NULL != pcbErrorMsg)
+ *pcbErrorMsg = 0;
+ if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
+ szErrorMsg[0] = '\0';
+
+ return SQL_NO_DATA_FOUND;
+ }
+
+ if (NULL != pcbErrorMsg)
+ *pcbErrorMsg = (SWORD) strlen(msg);
+ if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
+ strncpy_null(szErrorMsg, msg, cbErrorMsgMax);
+ if (NULL != pfNativeError)
+ *pfNativeError = status;
+
+ if (szSqlState)
+ {
+ switch (status)
+ {
+ case ENV_ALLOC_ERROR:
+ /* memory allocation failure */
+ strcpy(szSqlState, "S1001");
+ break;
+ default:
+ strcpy(szSqlState, "S1000");
+ /* general error */
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (NULL != szSqlState)
+ strcpy(szSqlState, "00000");
+ if (NULL != pcbErrorMsg)
+ *pcbErrorMsg = 0;
+ if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
+ szErrorMsg[0] = '\0';
+
+ return SQL_NO_DATA_FOUND;
+ }
+
+ return SQL_SUCCESS;
+ }
+
+ if (NULL != szSqlState)
+ strcpy(szSqlState, "00000");
+ if (NULL != pcbErrorMsg)
+ *pcbErrorMsg = 0;
+ if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
+ szErrorMsg[0] = '\0';
+
+ return SQL_NO_DATA_FOUND;
+}
+
+
+/*
+ * EnvironmentClass implementation
+ */
+EnvironmentClass *
+EN_Constructor(void)
+{
+ EnvironmentClass *rv;
+
+ rv = (EnvironmentClass *) malloc(sizeof(EnvironmentClass));
+ if (rv)
+ {
+ rv->errormsg = 0;
+ rv->errornumber = 0;
+ }
+
+ return rv;
+}
+
+
+char
+EN_Destructor(EnvironmentClass *self)
+{
+ int lf;
+ char rv = 1;
+
+ mylog("in EN_Destructor, self=%u\n", self);
+
+ /*
+ * the error messages are static strings distributed throughout the
+ * source--they should not be freed
+ */
+
+ /* Free any connections belonging to this environment */
+ for (lf = 0; lf < MAX_CONNECTIONS; lf++)
+ {
+ if (conns[lf] && conns[lf]->henv == self)
+ rv = rv && CC_Destructor(conns[lf]);
+ }
+ free(self);
+
+ mylog("exit EN_Destructor: rv = %d\n", rv);
+#ifdef _MEMORY_DEBUG_
+ debug_memory_inouecheck();
+#endif /* _MEMORY_DEBUG_ */
+ return rv;
+}
+
+
+char
+EN_get_error(EnvironmentClass *self, int *number, char **message)
+{
+ if (self && self->errormsg && self->errornumber)
+ {
+ *message = self->errormsg;
+ *number = self->errornumber;
+ self->errormsg = 0;
+ self->errornumber = 0;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+char
+EN_add_connection(EnvironmentClass *self, ConnectionClass *conn)
+{
+ int i;
+
+ mylog("EN_add_connection: self = %u, conn = %u\n", self, conn);
+
+ for (i = 0; i < MAX_CONNECTIONS; i++)
+ {
+ if (!conns[i])
+ {
+ conn->henv = self;
+ conns[i] = conn;
+
+ mylog(" added at i =%d, conn->henv = %u, conns[i]->henv = %u\n", i, conn->henv, conns[i]->henv);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+char
+EN_remove_connection(EnvironmentClass *self, ConnectionClass *conn)
+{
+ int i;
+
+ for (i = 0; i < MAX_CONNECTIONS; i++)
+ if (conns[i] == conn && conns[i]->status != CONN_EXECUTING)
+ {
+ conns[i] = NULL;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+void
+EN_log_error(char *func, char *desc, EnvironmentClass *self)
+{
+ if (self)
+ qlog("ENVIRON ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, self->errormsg);
+ else
+ qlog("INVALID ENVIRON HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
+}
--- /dev/null
+/* File: environ.h
+ *
+ * Description: See "environ.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __ENVIRON_H__
+#define __ENVIRON_H__
+
+#include "psqlodbc.h"
+
+#define ENV_ALLOC_ERROR 1
+
+/********** Environment Handle *************/
+struct EnvironmentClass_
+{
+ char *errormsg;
+ int errornumber;
+};
+
+/* Environment prototypes */
+EnvironmentClass *EN_Constructor(void);
+char EN_Destructor(EnvironmentClass *self);
+char EN_get_error(EnvironmentClass *self, int *number, char **message);
+char EN_add_connection(EnvironmentClass *self, ConnectionClass *conn);
+char EN_remove_connection(EnvironmentClass *self, ConnectionClass *conn);
+void EN_log_error(char *func, char *desc, EnvironmentClass *self);
+
+#endif
--- /dev/null
+/*-------
+ * Module: execute.c
+ *
+ * Description: This module contains routines related to
+ * preparing and executing an SQL statement.
+ *
+ * Classes: n/a
+ *
+ * API functions: SQLPrepare, SQLExecute, SQLExecDirect, SQLTransact,
+ * SQLCancel, SQLNativeSql, SQLParamData, SQLPutData
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "psqlodbc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "connection.h"
+#include "statement.h"
+#include "qresult.h"
+#include "convert.h"
+#include "bind.h"
+#include "pgtypes.h"
+#include "lobj.h"
+#include "pgapifunc.h"
+
+/*extern GLOBAL_VALUES globals;*/
+
+
+/* Perform a Prepare on the SQL statement */
+RETCODE SQL_API
+PGAPI_Prepare(HSTMT hstmt,
+ UCHAR FAR * szSqlStr,
+ SDWORD cbSqlStr)
+{
+ static char *func = "PGAPI_Prepare";
+ StatementClass *self = (StatementClass *) hstmt;
+
+ mylog("%s: entering...\n", func);
+
+ if (!self)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ /*
+ * According to the ODBC specs it is valid to call SQLPrepare mulitple
+ * times. In that case, the bound SQL statement is replaced by the new
+ * one
+ */
+
+ switch (self->status)
+ {
+ case STMT_PREMATURE:
+ mylog("**** PGAPI_Prepare: STMT_PREMATURE, recycle\n");
+ SC_recycle_statement(self); /* recycle the statement, but do
+ * not remove parameter bindings */
+ break;
+
+ case STMT_FINISHED:
+ mylog("**** PGAPI_Prepare: STMT_FINISHED, recycle\n");
+ SC_recycle_statement(self); /* recycle the statement, but do
+ * not remove parameter bindings */
+ break;
+
+ case STMT_ALLOCATED:
+ mylog("**** PGAPI_Prepare: STMT_ALLOCATED, copy\n");
+ self->status = STMT_READY;
+ break;
+
+ case STMT_READY:
+ mylog("**** PGAPI_Prepare: STMT_READY, change SQL\n");
+ break;
+
+ case STMT_EXECUTING:
+ mylog("**** PGAPI_Prepare: STMT_EXECUTING, error!\n");
+
+ self->errornumber = STMT_SEQUENCE_ERROR;
+ self->errormsg = "PGAPI_Prepare(): The handle does not point to a statement that is ready to be executed";
+ SC_log_error(func, "", self);
+
+ return SQL_ERROR;
+
+ default:
+ self->errornumber = STMT_INTERNAL_ERROR;
+ self->errormsg = "An Internal Error has occured -- Unknown statement status.";
+ SC_log_error(func, "", self);
+ return SQL_ERROR;
+ }
+
+ if (self->statement)
+ free(self->statement);
+
+ self->statement = make_string(szSqlStr, cbSqlStr, NULL);
+ if (!self->statement)
+ {
+ self->errornumber = STMT_NO_MEMORY_ERROR;
+ self->errormsg = "No memory available to store statement";
+ SC_log_error(func, "", self);
+ return SQL_ERROR;
+ }
+
+ self->prepare = TRUE;
+ self->statement_type = statement_type(self->statement);
+
+ /* Check if connection is onlyread (only selects are allowed) */
+ if (CC_is_onlyread(self->hdbc) && STMT_UPDATE(self))
+ {
+ self->errornumber = STMT_EXEC_ERROR;
+ self->errormsg = "Connection is readonly, only select statements are allowed.";
+ SC_log_error(func, "", self);
+ return SQL_ERROR;
+ }
+
+ return SQL_SUCCESS;
+}
+
+
+/* Performs the equivalent of SQLPrepare, followed by SQLExecute. */
+RETCODE SQL_API
+PGAPI_ExecDirect(
+ HSTMT hstmt,
+ UCHAR FAR * szSqlStr,
+ SDWORD cbSqlStr)
+{
+ StatementClass *stmt = (StatementClass *) hstmt;
+ RETCODE result;
+ static char *func = "PGAPI_ExecDirect";
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ if (stmt->statement)
+ free(stmt->statement);
+
+ /*
+ * keep a copy of the un-parametrized statement, in case they try to
+ * execute this statement again
+ */
+ stmt->statement = make_string(szSqlStr, cbSqlStr, NULL);
+ if (!stmt->statement)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "No memory available to store statement";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ mylog("**** %s: hstmt=%u, statement='%s'\n", func, hstmt, stmt->statement);
+
+ stmt->prepare = FALSE;
+
+ /*
+ * If an SQLPrepare was performed prior to this, but was left in the
+ * premature state because an error occurred prior to SQLExecute then
+ * set the statement to finished so it can be recycled.
+ */
+ if (stmt->status == STMT_PREMATURE)
+ stmt->status = STMT_FINISHED;
+
+ stmt->statement_type = statement_type(stmt->statement);
+
+ /* Check if connection is onlyread (only selects are allowed) */
+ if (CC_is_onlyread(stmt->hdbc) && STMT_UPDATE(stmt))
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Connection is readonly, only select statements are allowed.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ mylog("%s: calling PGAPI_Execute...\n", func);
+
+ result = PGAPI_Execute(hstmt);
+
+ mylog("%s: returned %hd from PGAPI_Execute\n", func, result);
+ return result;
+}
+
+
+/* Execute a prepared SQL statement */
+RETCODE SQL_API
+PGAPI_Execute(
+ HSTMT hstmt)
+{
+ static char *func = "PGAPI_Execute";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ ConnectionClass *conn;
+ int i,
+ retval;
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ mylog("%s: NULL statement so return SQL_INVALID_HANDLE\n", func);
+ return SQL_INVALID_HANDLE;
+ }
+
+ /*
+ * If the statement is premature, it means we already executed it from
+ * an SQLPrepare/SQLDescribeCol type of scenario. So just return
+ * success.
+ */
+ if (stmt->prepare && stmt->status == STMT_PREMATURE)
+ {
+ if (stmt->inaccurate_result)
+ SC_recycle_statement(stmt);
+ else
+ {
+ stmt->status = STMT_FINISHED;
+ if (stmt->errormsg == NULL)
+ {
+ mylog("%s: premature statement but return SQL_SUCCESS\n", func);
+ return SQL_SUCCESS;
+ }
+ else
+ {
+ SC_log_error(func, "", stmt);
+ mylog("%s: premature statement so return SQL_ERROR\n", func);
+ return SQL_ERROR;
+ }
+ }
+ }
+
+ mylog("%s: clear errors...\n", func);
+
+ SC_clear_error(stmt);
+
+ conn = SC_get_conn(stmt);
+ if (conn->status == CONN_EXECUTING)
+ {
+ stmt->errormsg = "Connection is already in use.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ mylog("%s: problem with connection\n", func);
+ return SQL_ERROR;
+ }
+
+ if (!stmt->statement)
+ {
+ stmt->errornumber = STMT_NO_STMTSTRING;
+ stmt->errormsg = "This handle does not have a SQL statement stored in it";
+ SC_log_error(func, "", stmt);
+ mylog("%s: problem with handle\n", func);
+ return SQL_ERROR;
+ }
+
+ /*
+ * If SQLExecute is being called again, recycle the statement. Note
+ * this should have been done by the application in a call to
+ * SQLFreeStmt(SQL_CLOSE) or SQLCancel.
+ */
+ if (stmt->status == STMT_FINISHED)
+ {
+ mylog("%s: recycling statement (should have been done by app)...\n", func);
+ SC_recycle_statement(stmt);
+ }
+
+ /* Check if the statement is in the correct state */
+ if ((stmt->prepare && stmt->status != STMT_READY) ||
+ (stmt->status != STMT_ALLOCATED && stmt->status != STMT_READY))
+ {
+ stmt->errornumber = STMT_STATUS_ERROR;
+ stmt->errormsg = "The handle does not point to a statement that is ready to be executed";
+ SC_log_error(func, "", stmt);
+ mylog("%s: problem with statement\n", func);
+ return SQL_ERROR;
+ }
+
+ /*
+ * Check if statement has any data-at-execute parameters when it is
+ * not in SC_pre_execute.
+ */
+ if (!stmt->pre_executing)
+ {
+ /*
+ * The bound parameters could have possibly changed since the last
+ * execute of this statement? Therefore check for params and
+ * re-copy.
+ */
+ stmt->data_at_exec = -1;
+ for (i = 0; i < stmt->parameters_allocated; i++)
+ {
+ Int4 *pcVal = stmt->parameters[i].used;
+
+ if (pcVal && (*pcVal == SQL_DATA_AT_EXEC || *pcVal <= SQL_LEN_DATA_AT_EXEC_OFFSET))
+ stmt->parameters[i].data_at_exec = TRUE;
+ else
+ stmt->parameters[i].data_at_exec = FALSE;
+ /* Check for data at execution parameters */
+ if (stmt->parameters[i].data_at_exec == TRUE)
+ {
+ if (stmt->data_at_exec < 0)
+ stmt->data_at_exec = 1;
+ else
+ stmt->data_at_exec++;
+ }
+ }
+
+ /*
+ * If there are some data at execution parameters, return need
+ * data
+ */
+
+ /*
+ * SQLParamData and SQLPutData will be used to send params and
+ * execute the statement.
+ */
+ if (stmt->data_at_exec > 0)
+ return SQL_NEED_DATA;
+
+ }
+
+
+ mylog("%s: copying statement params: trans_status=%d, len=%d, stmt='%s'\n", func, conn->transact_status, strlen(stmt->statement), stmt->statement);
+
+ /* Create the statement with parameters substituted. */
+ retval = copy_statement_with_parameters(stmt);
+ if (retval != SQL_SUCCESS)
+ /* error msg passed from above */
+ return retval;
+
+ mylog(" stmt_with_params = '%s'\n", stmt->stmt_with_params);
+
+ /*
+ * Get the field info for the prepared query using dummy backward
+ * fetch.
+ */
+ if (stmt->inaccurate_result && conn->connInfo.disallow_premature)
+ {
+ if (SC_is_pre_executable(stmt))
+ {
+ BOOL in_trans = CC_is_in_trans(conn);
+ BOOL issued_begin = FALSE,
+ begin_included = FALSE;
+ QResultClass *res;
+
+ if (strnicmp(stmt->stmt_with_params, "BEGIN;", 6) == 0)
+ begin_included = TRUE;
+ else if (!in_trans)
+ {
+ res = CC_send_query(conn, "BEGIN", NULL);
+ if (res && !QR_aborted(res))
+ issued_begin = TRUE;
+ if (res)
+ QR_Destructor(res);
+ if (!issued_begin)
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Handle prepare error";
+ return SQL_ERROR;
+ }
+ }
+ /* we are now in a transaction */
+ CC_set_in_trans(conn);
+ stmt->result = res = CC_send_query(conn, stmt->stmt_with_params, NULL);
+ if (!res || QR_aborted(res))
+ {
+ CC_abort(conn);
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Handle prepare error";
+ return SQL_ERROR;
+ }
+ else
+ {
+ if (CC_is_in_autocommit(conn))
+ {
+ if (issued_begin)
+ {
+ res = CC_send_query(conn, "COMMIT", NULL);
+ CC_set_no_trans(conn);
+ if (res)
+ QR_Destructor(res);
+ }
+ else if (!in_trans && begin_included)
+ CC_set_no_trans(conn);
+ }
+ stmt->status = STMT_FINISHED;
+ return SQL_SUCCESS;
+ }
+ }
+ else
+ return SQL_SUCCESS;
+ }
+
+ return SC_execute(stmt);
+}
+
+
+RETCODE SQL_API
+PGAPI_Transact(
+ HENV henv,
+ HDBC hdbc,
+ UWORD fType)
+{
+ static char *func = "PGAPI_Transact";
+ extern ConnectionClass *conns[];
+ ConnectionClass *conn;
+ QResultClass *res;
+ char ok,
+ *stmt_string;
+ int lf;
+
+ mylog("entering %s: hdbc=%u, henv=%u\n", func, hdbc, henv);
+
+ if (hdbc == SQL_NULL_HDBC && henv == SQL_NULL_HENV)
+ {
+ CC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ /*
+ * If hdbc is null and henv is valid, it means transact all
+ * connections on that henv.
+ */
+ if (hdbc == SQL_NULL_HDBC && henv != SQL_NULL_HENV)
+ {
+ for (lf = 0; lf < MAX_CONNECTIONS; lf++)
+ {
+ conn = conns[lf];
+
+ if (conn && conn->henv == henv)
+ if (PGAPI_Transact(henv, (HDBC) conn, fType) != SQL_SUCCESS)
+ return SQL_ERROR;
+ }
+ return SQL_SUCCESS;
+ }
+
+ conn = (ConnectionClass *) hdbc;
+
+ if (fType == SQL_COMMIT)
+ stmt_string = "COMMIT";
+ else if (fType == SQL_ROLLBACK)
+ stmt_string = "ROLLBACK";
+ else
+ {
+ conn->errornumber = CONN_INVALID_ARGUMENT_NO;
+ conn->errormsg = "PGAPI_Transact can only be called with SQL_COMMIT or SQL_ROLLBACK as parameter";
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+
+ /* If manual commit and in transaction, then proceed. */
+ if (!CC_is_in_autocommit(conn) && CC_is_in_trans(conn))
+ {
+ mylog("PGAPI_Transact: sending on conn %d '%s'\n", conn, stmt_string);
+
+ res = CC_send_query(conn, stmt_string, NULL);
+ CC_set_no_trans(conn);
+
+ if (!res)
+ {
+ /* error msg will be in the connection */
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+
+ ok = QR_command_successful(res);
+ QR_Destructor(res);
+
+ if (!ok)
+ {
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+ }
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_Cancel(
+ HSTMT hstmt) /* Statement to cancel. */
+{
+ static char *func = "PGAPI_Cancel";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ RETCODE result;
+ ConnInfo *ci;
+
+#ifdef WIN32
+ HMODULE hmodule;
+ FARPROC addr;
+#endif
+
+ mylog("%s: entering...\n", func);
+
+ /* Check if this can handle canceling in the middle of a SQLPutData? */
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ ci = &(SC_get_conn(stmt)->connInfo);
+
+ /*
+ * Not in the middle of SQLParamData/SQLPutData so cancel like a
+ * close.
+ */
+ if (stmt->data_at_exec < 0)
+ {
+ /*
+ * MAJOR HACK for Windows to reset the driver manager's cursor
+ * state: Because of what seems like a bug in the Odbc driver
+ * manager, SQLCancel does not act like a SQLFreeStmt(CLOSE), as
+ * many applications depend on this behavior. So, this brute
+ * force method calls the driver manager's function on behalf of
+ * the application.
+ */
+
+#ifdef WIN32
+ if (ci->drivers.cancel_as_freestmt)
+ {
+ hmodule = GetModuleHandle("ODBC32");
+ addr = GetProcAddress(hmodule, "SQLFreeStmt");
+ result = addr((char *) (stmt->phstmt) - 96, SQL_CLOSE);
+ }
+ else
+ result = PGAPI_FreeStmt(hstmt, SQL_CLOSE);
+#else
+ result = PGAPI_FreeStmt(hstmt, SQL_CLOSE);
+#endif
+
+ mylog("PGAPI_Cancel: PGAPI_FreeStmt returned %d\n", result);
+
+ SC_clear_error(hstmt);
+ return SQL_SUCCESS;
+ }
+
+ /* In the middle of SQLParamData/SQLPutData, so cancel that. */
+
+ /*
+ * Note, any previous data-at-exec buffers will be freed in the
+ * recycle
+ */
+ /* if they call SQLExecDirect or SQLExecute again. */
+
+ stmt->data_at_exec = -1;
+ stmt->current_exec_param = -1;
+ stmt->put_data = FALSE;
+
+ return SQL_SUCCESS;
+}
+
+
+/*
+ * Returns the SQL string as modified by the driver.
+ * Currently, just copy the input string without modification
+ * observing buffer limits and truncation.
+ */
+RETCODE SQL_API
+PGAPI_NativeSql(
+ HDBC hdbc,
+ UCHAR FAR * szSqlStrIn,
+ SDWORD cbSqlStrIn,
+ UCHAR FAR * szSqlStr,
+ SDWORD cbSqlStrMax,
+ SDWORD FAR * pcbSqlStr)
+{
+ static char *func = "PGAPI_NativeSql";
+ int len = 0;
+ char *ptr;
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ RETCODE result;
+
+ mylog("%s: entering...cbSqlStrIn=%d\n", func, cbSqlStrIn);
+
+ ptr = (cbSqlStrIn == 0) ? "" : make_string(szSqlStrIn, cbSqlStrIn, NULL);
+ if (!ptr)
+ {
+ conn->errornumber = CONN_NO_MEMORY_ERROR;
+ conn->errormsg = "No memory available to store native sql string";
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+
+ result = SQL_SUCCESS;
+ len = strlen(ptr);
+
+ if (szSqlStr)
+ {
+ strncpy_null(szSqlStr, ptr, cbSqlStrMax);
+
+ if (len >= cbSqlStrMax)
+ {
+ result = SQL_SUCCESS_WITH_INFO;
+ conn->errornumber = STMT_TRUNCATED;
+ conn->errormsg = "The buffer was too small for the NativeSQL.";
+ }
+ }
+
+ if (pcbSqlStr)
+ *pcbSqlStr = len;
+
+ if (cbSqlStrIn)
+ free(ptr);
+
+ return result;
+}
+
+
+/*
+ * Supplies parameter data at execution time.
+ * Used in conjuction with SQLPutData.
+ */
+RETCODE SQL_API
+PGAPI_ParamData(
+ HSTMT hstmt,
+ PTR FAR * prgbValue)
+{
+ static char *func = "PGAPI_ParamData";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ int i,
+ retval;
+ ConnInfo *ci;
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ ci = &(SC_get_conn(stmt)->connInfo);
+
+ mylog("%s: data_at_exec=%d, params_alloc=%d\n", func, stmt->data_at_exec, stmt->parameters_allocated);
+
+ if (stmt->data_at_exec < 0)
+ {
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ stmt->errormsg = "No execution-time parameters for this statement";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (stmt->data_at_exec > stmt->parameters_allocated)
+ {
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ stmt->errormsg = "Too many execution-time parameters were present";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* close the large object */
+ if (stmt->lobj_fd >= 0)
+ {
+ lo_close(stmt->hdbc, stmt->lobj_fd);
+
+ /* commit transaction if needed */
+ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(stmt->hdbc))
+ {
+ QResultClass *res;
+ char ok;
+
+ res = CC_send_query(stmt->hdbc, "COMMIT", NULL);
+ if (!res)
+ {
+ stmt->errormsg = "Could not commit (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ ok = QR_command_successful(res);
+ CC_set_no_trans(stmt->hdbc);
+ QR_Destructor(res);
+ if (!ok)
+ {
+ stmt->errormsg = "Could not commit (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+ stmt->lobj_fd = -1;
+ }
+
+ /* Done, now copy the params and then execute the statement */
+ if (stmt->data_at_exec == 0)
+ {
+ retval = copy_statement_with_parameters(stmt);
+ if (retval != SQL_SUCCESS)
+ return retval;
+
+ stmt->current_exec_param = -1;
+
+ return SC_execute(stmt);
+ }
+
+ /*
+ * Set beginning param; if first time SQLParamData is called , start
+ * at 0. Otherwise, start at the last parameter + 1.
+ */
+ i = stmt->current_exec_param >= 0 ? stmt->current_exec_param + 1 : 0;
+
+ /* At least 1 data at execution parameter, so Fill in the token value */
+ for (; i < stmt->parameters_allocated; i++)
+ {
+ if (stmt->parameters[i].data_at_exec == TRUE)
+ {
+ stmt->data_at_exec--;
+ stmt->current_exec_param = i;
+ stmt->put_data = FALSE;
+ *prgbValue = stmt->parameters[i].buffer; /* token */
+ break;
+ }
+ }
+
+ return SQL_NEED_DATA;
+}
+
+
+/*
+ * Supplies parameter data at execution time.
+ * Used in conjunction with SQLParamData.
+ */
+RETCODE SQL_API
+PGAPI_PutData(
+ HSTMT hstmt,
+ PTR rgbValue,
+ SDWORD cbValue)
+{
+ static char *func = "PGAPI_PutData";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ int old_pos,
+ retval;
+ ParameterInfoClass *current_param;
+ char *buffer;
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ if (stmt->current_exec_param < 0)
+ {
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ stmt->errormsg = "Previous call was not SQLPutData or SQLParamData";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ current_param = &(stmt->parameters[stmt->current_exec_param]);
+
+ if (!stmt->put_data)
+ { /* first call */
+ mylog("PGAPI_PutData: (1) cbValue = %d\n", cbValue);
+
+ stmt->put_data = TRUE;
+
+ current_param->EXEC_used = (SDWORD *) malloc(sizeof(SDWORD));
+ if (!current_param->EXEC_used)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (1)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ *current_param->EXEC_used = cbValue;
+
+ if (cbValue == SQL_NULL_DATA)
+ return SQL_SUCCESS;
+
+ /* Handle Long Var Binary with Large Objects */
+ if (current_param->SQLType == SQL_LONGVARBINARY)
+ {
+ /* begin transaction if needed */
+ if (!CC_is_in_trans(stmt->hdbc))
+ {
+ QResultClass *res;
+ char ok;
+
+ res = CC_send_query(stmt->hdbc, "BEGIN", NULL);
+ if (!res)
+ {
+ stmt->errormsg = "Could not begin (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ ok = QR_command_successful(res);
+ QR_Destructor(res);
+ if (!ok)
+ {
+ stmt->errormsg = "Could not begin (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ CC_set_in_trans(stmt->hdbc);
+ }
+
+ /* store the oid */
+ current_param->lobj_oid = lo_creat(stmt->hdbc, INV_READ | INV_WRITE);
+ if (current_param->lobj_oid == 0)
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Couldnt create large object.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /*
+ * major hack -- to allow convert to see somethings there have
+ * to modify convert to handle this better
+ */
+ current_param->EXEC_buffer = (char *) ¤t_param->lobj_oid;
+
+ /* store the fd */
+ stmt->lobj_fd = lo_open(stmt->hdbc, current_param->lobj_oid, INV_WRITE);
+ if (stmt->lobj_fd < 0)
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Couldnt open large object for writing.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ retval = lo_write(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValue);
+ mylog("lo_write: cbValue=%d, wrote %d bytes\n", cbValue, retval);
+ }
+ else
+ {
+ /* for handling fields */
+ if (cbValue == SQL_NTS)
+ {
+ current_param->EXEC_buffer = strdup(rgbValue);
+ if (!current_param->EXEC_buffer)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (2)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+ else
+ {
+ Int2 ctype = current_param->CType;
+
+ if (ctype == SQL_C_DEFAULT)
+ ctype = sqltype_to_default_ctype(current_param->SQLType);
+ if (ctype == SQL_C_CHAR || ctype == SQL_C_BINARY)
+ {
+ current_param->EXEC_buffer = malloc(cbValue + 1);
+ if (!current_param->EXEC_buffer)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (2)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ memcpy(current_param->EXEC_buffer, rgbValue, cbValue);
+ current_param->EXEC_buffer[cbValue] = '\0';
+ }
+ else
+ {
+ Int4 used = ctype_length(ctype);
+
+ current_param->EXEC_buffer = malloc(used);
+ if (!current_param->EXEC_buffer)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (2)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ memcpy(current_param->EXEC_buffer, rgbValue, used);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* calling SQLPutData more than once */
+ mylog("PGAPI_PutData: (>1) cbValue = %d\n", cbValue);
+
+ if (current_param->SQLType == SQL_LONGVARBINARY)
+ {
+ /* the large object fd is in EXEC_buffer */
+ retval = lo_write(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValue);
+ mylog("lo_write(2): cbValue = %d, wrote %d bytes\n", cbValue, retval);
+
+ *current_param->EXEC_used += cbValue;
+ }
+ else
+ {
+ buffer = current_param->EXEC_buffer;
+
+ if (cbValue == SQL_NTS)
+ {
+ buffer = realloc(buffer, strlen(buffer) + strlen(rgbValue) + 1);
+ if (!buffer)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (3)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ strcat(buffer, rgbValue);
+
+ mylog(" cbValue = SQL_NTS: strlen(buffer) = %d\n", strlen(buffer));
+
+ *current_param->EXEC_used = cbValue;
+
+ /* reassign buffer incase realloc moved it */
+ current_param->EXEC_buffer = buffer;
+ }
+ else if (cbValue > 0)
+ {
+ old_pos = *current_param->EXEC_used;
+
+ *current_param->EXEC_used += cbValue;
+
+ mylog(" cbValue = %d, old_pos = %d, *used = %d\n", cbValue, old_pos, *current_param->EXEC_used);
+
+ /* dont lose the old pointer in case out of memory */
+ buffer = realloc(current_param->EXEC_buffer, *current_param->EXEC_used + 1);
+ if (!buffer)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (3)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ memcpy(&buffer[old_pos], rgbValue, cbValue);
+ buffer[*current_param->EXEC_used] = '\0';
+
+ /* reassign buffer incase realloc moved it */
+ current_param->EXEC_buffer = buffer;
+ }
+ else
+ {
+ SC_log_error(func, "bad cbValue", stmt);
+ return SQL_ERROR;
+ }
+ }
+ }
+
+ return SQL_SUCCESS;
+}
--- /dev/null
+/*--------
+ * Module: info.c
+ *
+ * Description: This module contains routines related to
+ * ODBC informational functions.
+ *
+ * Classes: n/a
+ *
+ * API functions: SQLGetInfo, SQLGetTypeInfo, SQLGetFunctions,
+ * SQLTables, SQLColumns, SQLStatistics, SQLSpecialColumns,
+ * SQLPrimaryKeys, SQLForeignKeys,
+ * SQLProcedureColumns(NI), SQLProcedures(NI),
+ * SQLTablePrivileges(NI), SQLColumnPrivileges(NI)
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *--------
+ */
+
+#include "psqlodbc.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#ifndef WIN32
+#include <ctype.h>
+#endif
+
+#include "tuple.h"
+#include "pgtypes.h"
+
+#include "environ.h"
+#include "connection.h"
+#include "statement.h"
+#include "qresult.h"
+#include "bind.h"
+#include "misc.h"
+#include "pgtypes.h"
+#include "pgapifunc.h"
+
+
+/* Trigger related stuff for SQLForeign Keys */
+#define TRIGGER_SHIFT 3
+#define TRIGGER_MASK 0x03
+#define TRIGGER_DELETE 0x01
+#define TRIGGER_UPDATE 0x02
+
+
+/* extern GLOBAL_VALUES globals; */
+
+
+
+RETCODE SQL_API
+PGAPI_GetInfo(
+ HDBC hdbc,
+ UWORD fInfoType,
+ PTR rgbInfoValue,
+ SWORD cbInfoValueMax,
+ SWORD FAR * pcbInfoValue)
+{
+ static char *func = "PGAPI_GetInfo";
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ ConnInfo *ci;
+ char *p = NULL,
+ tmp[MAX_INFO_STRING];
+ int len = 0,
+ value = 0;
+ RETCODE result;
+
+ mylog("%s: entering...fInfoType=%d\n", func, fInfoType);
+
+ if (!conn)
+ {
+ CC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ ci = &(conn->connInfo);
+
+ switch (fInfoType)
+ {
+ case SQL_ACCESSIBLE_PROCEDURES: /* ODBC 1.0 */
+ p = "N";
+ break;
+
+ case SQL_ACCESSIBLE_TABLES: /* ODBC 1.0 */
+ p = "N";
+ break;
+
+ case SQL_ACTIVE_CONNECTIONS: /* ODBC 1.0 */
+ len = 2;
+ value = MAX_CONNECTIONS;
+ break;
+
+ case SQL_ACTIVE_STATEMENTS: /* ODBC 1.0 */
+ len = 2;
+ value = 0;
+ break;
+
+ case SQL_ALTER_TABLE: /* ODBC 2.0 */
+ len = 4;
+ value = SQL_AT_ADD_COLUMN;
+ break;
+
+ case SQL_BOOKMARK_PERSISTENCE: /* ODBC 2.0 */
+ /* very simple bookmark support */
+ len = 4;
+ value = ci->drivers.use_declarefetch ? 0 : (SQL_BP_SCROLL);
+ break;
+
+ case SQL_COLUMN_ALIAS: /* ODBC 2.0 */
+ p = "N";
+ break;
+
+ case SQL_CONCAT_NULL_BEHAVIOR: /* ODBC 1.0 */
+ len = 2;
+ value = SQL_CB_NON_NULL;
+ break;
+
+ case SQL_CONVERT_BIGINT:
+ case SQL_CONVERT_BINARY:
+ case SQL_CONVERT_BIT:
+ case SQL_CONVERT_CHAR:
+ case SQL_CONVERT_DATE:
+ case SQL_CONVERT_DECIMAL:
+ case SQL_CONVERT_DOUBLE:
+ case SQL_CONVERT_FLOAT:
+ case SQL_CONVERT_INTEGER:
+ case SQL_CONVERT_LONGVARBINARY:
+ case SQL_CONVERT_LONGVARCHAR:
+ case SQL_CONVERT_NUMERIC:
+ case SQL_CONVERT_REAL:
+ case SQL_CONVERT_SMALLINT:
+ case SQL_CONVERT_TIME:
+ case SQL_CONVERT_TIMESTAMP:
+ case SQL_CONVERT_TINYINT:
+ case SQL_CONVERT_VARBINARY:
+ case SQL_CONVERT_VARCHAR: /* ODBC 1.0 */
+ len = 4;
+ value = fInfoType;
+ break;
+
+ case SQL_CONVERT_FUNCTIONS: /* ODBC 1.0 */
+ len = 4;
+ value = 0;
+ break;
+
+ case SQL_CORRELATION_NAME: /* ODBC 1.0 */
+
+ /*
+ * Saying no correlation name makes Query not work right.
+ * value = SQL_CN_NONE;
+ */
+ len = 2;
+ value = SQL_CN_ANY;
+ break;
+
+ case SQL_CURSOR_COMMIT_BEHAVIOR: /* ODBC 1.0 */
+ len = 2;
+ value = SQL_CB_CLOSE;
+ if (ci->updatable_cursors)
+ if (!ci->drivers.use_declarefetch)
+ value = SQL_CB_PRESERVE;
+ break;
+
+ case SQL_CURSOR_ROLLBACK_BEHAVIOR: /* ODBC 1.0 */
+ len = 2;
+ value = SQL_CB_CLOSE;
+ if (ci->updatable_cursors)
+ if (!ci->drivers.use_declarefetch)
+ value = SQL_CB_PRESERVE;
+ break;
+
+ case SQL_DATA_SOURCE_NAME: /* ODBC 1.0 */
+ p = CC_get_DSN(conn);
+ break;
+
+ case SQL_DATA_SOURCE_READ_ONLY: /* ODBC 1.0 */
+ p = CC_is_onlyread(conn) ? "Y" : "N";
+ break;
+
+ case SQL_DATABASE_NAME: /* Support for old ODBC 1.0 Apps */
+
+ /*
+ * Returning the database name causes problems in MS Query. It
+ * generates query like: "SELECT DISTINCT a FROM byronnbad3
+ * bad3"
+ *
+ * p = CC_get_database(conn);
+ */
+ p = "";
+ break;
+
+ case SQL_DBMS_NAME: /* ODBC 1.0 */
+ p = DBMS_NAME;
+ break;
+
+ case SQL_DBMS_VER: /* ODBC 1.0 */
+
+ /*
+ * The ODBC spec wants ##.##.#### ...whatever... so prepend
+ * the driver
+ */
+ /* version number to the dbms version string */
+ sprintf(tmp, "%s %s", POSTGRESDRIVERVERSION, conn->pg_version);
+ p = tmp;
+ break;
+
+ case SQL_DEFAULT_TXN_ISOLATION: /* ODBC 1.0 */
+ len = 4;
+ value = SQL_TXN_READ_COMMITTED; /* SQL_TXN_SERIALIZABLE; */
+ break;
+
+ case SQL_DRIVER_NAME: /* ODBC 1.0 */
+ p = DRIVER_FILE_NAME;
+ break;
+
+ case SQL_DRIVER_ODBC_VER:
+ p = DRIVER_ODBC_VER;
+ break;
+
+ case SQL_DRIVER_VER: /* ODBC 1.0 */
+ p = POSTGRESDRIVERVERSION;
+ break;
+
+ case SQL_EXPRESSIONS_IN_ORDERBY: /* ODBC 1.0 */
+ p = "N";
+ break;
+
+ case SQL_FETCH_DIRECTION: /* ODBC 1.0 */
+ len = 4;
+ value = ci->drivers.use_declarefetch ? (SQL_FD_FETCH_NEXT) : (SQL_FD_FETCH_NEXT |
+ SQL_FD_FETCH_FIRST |
+ SQL_FD_FETCH_LAST |
+ SQL_FD_FETCH_PRIOR |
+ SQL_FD_FETCH_ABSOLUTE |
+ SQL_FD_FETCH_RELATIVE |
+ SQL_FD_FETCH_BOOKMARK);
+ break;
+
+ case SQL_FILE_USAGE: /* ODBC 2.0 */
+ len = 2;
+ value = SQL_FILE_NOT_SUPPORTED;
+ break;
+
+ case SQL_GETDATA_EXTENSIONS: /* ODBC 2.0 */
+ len = 4;
+ value = (SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND | SQL_GD_BLOCK);
+ break;
+
+ case SQL_GROUP_BY: /* ODBC 2.0 */
+ len = 2;
+ value = SQL_GB_GROUP_BY_EQUALS_SELECT;
+ break;
+
+ case SQL_IDENTIFIER_CASE: /* ODBC 1.0 */
+
+ /*
+ * are identifiers case-sensitive (yes, but only when quoted.
+ * If not quoted, they default to lowercase)
+ */
+ len = 2;
+ value = SQL_IC_LOWER;
+ break;
+
+ case SQL_IDENTIFIER_QUOTE_CHAR: /* ODBC 1.0 */
+ /* the character used to quote "identifiers" */
+ p = PG_VERSION_LE(conn, 6.2) ? " " : "\"";
+ break;
+
+ case SQL_KEYWORDS: /* ODBC 2.0 */
+ p = "";
+ break;
+
+ case SQL_LIKE_ESCAPE_CLAUSE: /* ODBC 2.0 */
+
+ /*
+ * is there a character that escapes '%' and '_' in a LIKE
+ * clause? not as far as I can tell
+ */
+ p = "N";
+ break;
+
+ case SQL_LOCK_TYPES: /* ODBC 2.0 */
+ len = 4;
+ value = ci->drivers.lie ? (SQL_LCK_NO_CHANGE | SQL_LCK_EXCLUSIVE | SQL_LCK_UNLOCK) : SQL_LCK_NO_CHANGE;
+ break;
+
+ case SQL_MAX_BINARY_LITERAL_LEN: /* ODBC 2.0 */
+ len = 4;
+ value = 0;
+ break;
+
+ case SQL_MAX_CHAR_LITERAL_LEN: /* ODBC 2.0 */
+ len = 4;
+ value = 0;
+ break;
+
+ case SQL_MAX_COLUMN_NAME_LEN: /* ODBC 1.0 */
+ len = 2;
+ value = MAX_COLUMN_LEN;
+ break;
+
+ case SQL_MAX_COLUMNS_IN_GROUP_BY: /* ODBC 2.0 */
+ len = 2;
+ value = 0;
+ break;
+
+ case SQL_MAX_COLUMNS_IN_INDEX: /* ODBC 2.0 */
+ len = 2;
+ value = 0;
+ break;
+
+ case SQL_MAX_COLUMNS_IN_ORDER_BY: /* ODBC 2.0 */
+ len = 2;
+ value = 0;
+ break;
+
+ case SQL_MAX_COLUMNS_IN_SELECT: /* ODBC 2.0 */
+ len = 2;
+ value = 0;
+ break;
+
+ case SQL_MAX_COLUMNS_IN_TABLE: /* ODBC 2.0 */
+ len = 2;
+ value = 0;
+ break;
+
+ case SQL_MAX_CURSOR_NAME_LEN: /* ODBC 1.0 */
+ len = 2;
+ value = MAX_CURSOR_LEN;
+ break;
+
+ case SQL_MAX_INDEX_SIZE: /* ODBC 2.0 */
+ len = 4;
+ value = 0;
+ break;
+
+ case SQL_MAX_OWNER_NAME_LEN: /* ODBC 1.0 */
+ len = 2;
+ value = 0;
+ break;
+
+ case SQL_MAX_PROCEDURE_NAME_LEN: /* ODBC 1.0 */
+ len = 2;
+ value = 0;
+ break;
+
+ case SQL_MAX_QUALIFIER_NAME_LEN: /* ODBC 1.0 */
+ len = 2;
+ value = 0;
+ break;
+
+ case SQL_MAX_ROW_SIZE: /* ODBC 2.0 */
+ len = 4;
+ if (PG_VERSION_GE(conn, 7.1))
+ {
+ /* Large Rowa in 7.1+ */
+ value = MAX_ROW_SIZE;
+ }
+ else
+ {
+ /* Without the Toaster we're limited to the blocksize */
+ value = BLCKSZ;
+ }
+ break;
+
+ case SQL_MAX_ROW_SIZE_INCLUDES_LONG: /* ODBC 2.0 */
+
+ /*
+ * does the preceding value include LONGVARCHAR and
+ * LONGVARBINARY fields? Well, it does include longvarchar,
+ * but not longvarbinary.
+ */
+ p = "Y";
+ break;
+
+ case SQL_MAX_STATEMENT_LEN: /* ODBC 2.0 */
+ /* maybe this should be 0? */
+ len = 4;
+ value = CC_get_max_query_len(conn);
+ break;
+
+ case SQL_MAX_TABLE_NAME_LEN: /* ODBC 1.0 */
+ len = 2;
+ value = MAX_TABLE_LEN;
+ break;
+
+ case SQL_MAX_TABLES_IN_SELECT: /* ODBC 2.0 */
+ len = 2;
+ value = 0;
+ break;
+
+ case SQL_MAX_USER_NAME_LEN:
+ len = 2;
+ value = 0;
+ break;
+
+ case SQL_MULT_RESULT_SETS: /* ODBC 1.0 */
+ /* Don't support multiple result sets but say yes anyway? */
+ p = "Y";
+ break;
+
+ case SQL_MULTIPLE_ACTIVE_TXN: /* ODBC 1.0 */
+ p = "Y";
+ break;
+
+ case SQL_NEED_LONG_DATA_LEN: /* ODBC 2.0 */
+
+ /*
+ * Don't need the length, SQLPutData can handle any size and
+ * multiple calls
+ */
+ p = "N";
+ break;
+
+ case SQL_NON_NULLABLE_COLUMNS: /* ODBC 1.0 */
+ len = 2;
+ value = SQL_NNC_NON_NULL;
+ break;
+
+ case SQL_NULL_COLLATION: /* ODBC 2.0 */
+ /* where are nulls sorted? */
+ len = 2;
+ value = SQL_NC_END;
+ break;
+
+ case SQL_NUMERIC_FUNCTIONS: /* ODBC 1.0 */
+ len = 4;
+ value = 0;
+ break;
+
+ case SQL_ODBC_API_CONFORMANCE: /* ODBC 1.0 */
+ len = 2;
+ value = SQL_OAC_LEVEL1;
+ break;
+
+ case SQL_ODBC_SAG_CLI_CONFORMANCE: /* ODBC 1.0 */
+ len = 2;
+ value = SQL_OSCC_NOT_COMPLIANT;
+ break;
+
+ case SQL_ODBC_SQL_CONFORMANCE: /* ODBC 1.0 */
+ len = 2;
+ value = SQL_OSC_CORE;
+ break;
+
+ case SQL_ODBC_SQL_OPT_IEF: /* ODBC 1.0 */
+ p = "N";
+ break;
+
+ case SQL_OJ_CAPABILITIES: /* ODBC 2.01 */
+ len = 4;
+ if (PG_VERSION_GE(conn, 7.1))
+ {
+ /* OJs in 7.1+ */
+ value = (SQL_OJ_LEFT |
+ SQL_OJ_RIGHT |
+ SQL_OJ_FULL |
+ SQL_OJ_NESTED |
+ SQL_OJ_NOT_ORDERED |
+ SQL_OJ_INNER |
+ SQL_OJ_ALL_COMPARISON_OPS);
+ }
+ else
+ /* OJs not in <7.1 */
+ value = 0;
+ break;
+
+ case SQL_ORDER_BY_COLUMNS_IN_SELECT: /* ODBC 2.0 */
+ p = (PG_VERSION_LE(conn, 6.3)) ? "Y" : "N";
+ break;
+
+ case SQL_OUTER_JOINS: /* ODBC 1.0 */
+ if (PG_VERSION_GE(conn, 7.1))
+ /* OJs in 7.1+ */
+ p = "Y";
+ else
+ /* OJs not in <7.1 */
+ p = "N";
+ break;
+
+ case SQL_OWNER_TERM: /* ODBC 1.0 */
+ p = "owner";
+ break;
+
+ case SQL_OWNER_USAGE: /* ODBC 2.0 */
+ len = 4;
+ value = 0;
+ break;
+
+ case SQL_POS_OPERATIONS: /* ODBC 2.0 */
+ len = 4;
+ value = ci->drivers.lie ? (SQL_POS_POSITION | SQL_POS_REFRESH | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD) : (SQL_POS_POSITION | SQL_POS_REFRESH);
+ if (ci->updatable_cursors)
+ value |= (SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD);
+ break;
+
+ case SQL_POSITIONED_STATEMENTS: /* ODBC 2.0 */
+ len = 4;
+ value = ci->drivers.lie ? (SQL_PS_POSITIONED_DELETE |
+ SQL_PS_POSITIONED_UPDATE |
+ SQL_PS_SELECT_FOR_UPDATE) : 0;
+ break;
+
+ case SQL_PROCEDURE_TERM: /* ODBC 1.0 */
+ p = "procedure";
+ break;
+
+ case SQL_PROCEDURES: /* ODBC 1.0 */
+ p = "Y";
+ break;
+
+ case SQL_QUALIFIER_LOCATION: /* ODBC 2.0 */
+ len = 2;
+ value = SQL_QL_START;
+ break;
+
+ case SQL_QUALIFIER_NAME_SEPARATOR: /* ODBC 1.0 */
+ p = "";
+ break;
+
+ case SQL_QUALIFIER_TERM: /* ODBC 1.0 */
+ p = "";
+ break;
+
+ case SQL_QUALIFIER_USAGE: /* ODBC 2.0 */
+ len = 4;
+ value = 0;
+ break;
+
+ case SQL_QUOTED_IDENTIFIER_CASE: /* ODBC 2.0 */
+ /* are "quoted" identifiers case-sensitive? YES! */
+ len = 2;
+ value = SQL_IC_SENSITIVE;
+ break;
+
+ case SQL_ROW_UPDATES: /* ODBC 1.0 */
+
+ /*
+ * Driver doesn't support keyset-driven or mixed cursors, so
+ * not much point in saying row updates are supported
+ */
+ p = (ci->drivers.lie || ci->updatable_cursors) ? "Y" : "N";
+ break;
+
+ case SQL_SCROLL_CONCURRENCY: /* ODBC 1.0 */
+ len = 4;
+ value = ci->drivers.lie ? (SQL_SCCO_READ_ONLY |
+ SQL_SCCO_LOCK |
+ SQL_SCCO_OPT_ROWVER |
+ SQL_SCCO_OPT_VALUES) :
+ (SQL_SCCO_READ_ONLY);
+ if (ci->updatable_cursors)
+ value |= SQL_SCCO_OPT_ROWVER;
+ break;
+
+ case SQL_SCROLL_OPTIONS: /* ODBC 1.0 */
+ len = 4;
+ value = ci->drivers.lie ? (SQL_SO_FORWARD_ONLY |
+ SQL_SO_STATIC |
+ SQL_SO_KEYSET_DRIVEN |
+ SQL_SO_DYNAMIC |
+ SQL_SO_MIXED)
+ : (ci->drivers.use_declarefetch ? SQL_SO_FORWARD_ONLY : (SQL_SO_FORWARD_ONLY | SQL_SO_STATIC));
+ if (ci->updatable_cursors)
+ value |= 0; /* SQL_SO_KEYSET_DRIVEN in the furure */
+ break;
+
+ case SQL_SEARCH_PATTERN_ESCAPE: /* ODBC 1.0 */
+ p = "";
+ break;
+
+ case SQL_SERVER_NAME: /* ODBC 1.0 */
+ p = CC_get_server(conn);
+ break;
+
+ case SQL_SPECIAL_CHARACTERS: /* ODBC 2.0 */
+ p = "_";
+ break;
+
+ case SQL_STATIC_SENSITIVITY: /* ODBC 2.0 */
+ len = 4;
+ value = ci->drivers.lie ? (SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES) : 0;
+ if (ci->updatable_cursors)
+ value |= (SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES);
+ break;
+
+ case SQL_STRING_FUNCTIONS: /* ODBC 1.0 */
+ len = 4;
+ value = (SQL_FN_STR_CONCAT |
+ 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);
+ break;
+
+ case SQL_SUBQUERIES: /* ODBC 2.0 */
+ /* postgres 6.3 supports subqueries */
+ len = 4;
+ value = (SQL_SQ_QUANTIFIED |
+ SQL_SQ_IN |
+ SQL_SQ_EXISTS |
+ SQL_SQ_COMPARISON);
+ break;
+
+ case SQL_SYSTEM_FUNCTIONS: /* ODBC 1.0 */
+ len = 4;
+ value = 0;
+ break;
+
+ case SQL_TABLE_TERM: /* ODBC 1.0 */
+ p = "table";
+ break;
+
+ case SQL_TIMEDATE_ADD_INTERVALS: /* ODBC 2.0 */
+ len = 4;
+ value = 0;
+ break;
+
+ case SQL_TIMEDATE_DIFF_INTERVALS: /* ODBC 2.0 */
+ len = 4;
+ value = 0;
+ break;
+
+ case SQL_TIMEDATE_FUNCTIONS: /* ODBC 1.0 */
+ len = 4;
+ value = (SQL_FN_TD_NOW);
+ break;
+
+ case SQL_TXN_CAPABLE: /* ODBC 1.0 */
+
+ /*
+ * Postgres can deal with create or drop table statements in a
+ * transaction
+ */
+ len = 2;
+ value = SQL_TC_ALL;
+ break;
+
+ case SQL_TXN_ISOLATION_OPTION: /* ODBC 1.0 */
+ len = 4;
+ value = SQL_TXN_READ_COMMITTED; /* SQL_TXN_SERIALIZABLE; */
+ break;
+
+ case SQL_UNION: /* ODBC 2.0 */
+ /* unions with all supported in postgres 6.3 */
+ len = 4;
+ value = (SQL_U_UNION | SQL_U_UNION_ALL);
+ break;
+
+ case SQL_USER_NAME: /* ODBC 1.0 */
+ p = CC_get_username(conn);
+ break;
+
+ default:
+ /* unrecognized key */
+ conn->errormsg = "Unrecognized key passed to PGAPI_GetInfo.";
+ conn->errornumber = CONN_NOT_IMPLEMENTED_ERROR;
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+
+ result = SQL_SUCCESS;
+
+ mylog("%s: p='%s', len=%d, value=%d, cbMax=%d\n", func, p ? p : "<NULL>", len, value, cbInfoValueMax);
+
+ /*
+ * NOTE, that if rgbInfoValue is NULL, then no warnings or errors
+ * should result and just pcbInfoValue is returned, which indicates
+ * what length would be required if a real buffer had been passed in.
+ */
+ if (p)
+ {
+ /* char/binary data */
+ len = strlen(p);
+
+ if (rgbInfoValue)
+ {
+ strncpy_null((char *) rgbInfoValue, p, (size_t) cbInfoValueMax);
+
+ if (len >= cbInfoValueMax)
+ {
+ result = SQL_SUCCESS_WITH_INFO;
+ conn->errornumber = STMT_TRUNCATED;
+ conn->errormsg = "The buffer was too small for tthe InfoValue.";
+ }
+ }
+ }
+ else
+ {
+ /* numeric data */
+ if (rgbInfoValue)
+ {
+ if (len == 2)
+ *((WORD *) rgbInfoValue) = (WORD) value;
+ else if (len == 4)
+ *((DWORD *) rgbInfoValue) = (DWORD) value;
+ }
+ }
+
+ if (pcbInfoValue)
+ *pcbInfoValue = len;
+
+ return result;
+}
+
+
+RETCODE SQL_API
+PGAPI_GetTypeInfo(
+ HSTMT hstmt,
+ SWORD fSqlType)
+{
+ static char *func = "PGAPI_GetTypeInfo";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ TupleNode *row;
+ int i;
+
+ /* Int4 type; */
+ Int4 pgType;
+ Int2 sqlType;
+
+ mylog("%s: entering...fSqlType = %d\n", func, fSqlType);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ stmt->manual_result = TRUE;
+ stmt->result = QR_Constructor();
+ if (!stmt->result)
+ {
+ SC_log_error(func, "Error creating result.", stmt);
+ return SQL_ERROR;
+ }
+
+ extend_bindings(stmt, 15);
+
+ QR_set_num_fields(stmt->result, 15);
+ QR_set_field_info(stmt->result, 0, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 1, "DATA_TYPE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 2, "PRECISION", PG_TYPE_INT4, 4);
+ QR_set_field_info(stmt->result, 3, "LITERAL_PREFIX", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 4, "LITERAL_SUFFIX", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 5, "CREATE_PARAMS", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 6, "NULLABLE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 7, "CASE_SENSITIVE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 8, "SEARCHABLE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 9, "UNSIGNED_ATTRIBUTE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 10, "MONEY", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 11, "AUTO_INCREMENT", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 12, "LOCAL_TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 13, "MINIMUM_SCALE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 14, "MAXIMUM_SCALE", PG_TYPE_INT2, 2);
+
+ for (i = 0, sqlType = sqlTypes[0]; sqlType; sqlType = sqlTypes[++i])
+ {
+ pgType = sqltype_to_pgtype(stmt, sqlType);
+
+ if (fSqlType == SQL_ALL_TYPES || fSqlType == sqlType)
+ {
+ row = (TupleNode *) malloc(sizeof(TupleNode) + (15 - 1) *sizeof(TupleField));
+
+ /* These values can't be NULL */
+ set_tuplefield_string(&row->tuple[0], pgtype_to_name(stmt, pgType));
+ set_tuplefield_int2(&row->tuple[1], (Int2) sqlType);
+ set_tuplefield_int2(&row->tuple[6], pgtype_nullable(stmt, pgType));
+ set_tuplefield_int2(&row->tuple[7], pgtype_case_sensitive(stmt, pgType));
+ set_tuplefield_int2(&row->tuple[8], pgtype_searchable(stmt, pgType));
+ set_tuplefield_int2(&row->tuple[10], pgtype_money(stmt, pgType));
+
+ /*
+ * Localized data-source dependent data type name (always
+ * NULL)
+ */
+ set_tuplefield_null(&row->tuple[12]);
+
+ /* These values can be NULL */
+ set_nullfield_int4(&row->tuple[2], pgtype_precision(stmt, pgType, PG_STATIC, PG_STATIC));
+ set_nullfield_string(&row->tuple[3], pgtype_literal_prefix(stmt, pgType));
+ set_nullfield_string(&row->tuple[4], pgtype_literal_suffix(stmt, pgType));
+ set_nullfield_string(&row->tuple[5], pgtype_create_params(stmt, pgType));
+ set_nullfield_int2(&row->tuple[9], pgtype_unsigned(stmt, pgType));
+ set_nullfield_int2(&row->tuple[11], pgtype_auto_increment(stmt, pgType));
+ set_nullfield_int2(&row->tuple[13], pgtype_scale(stmt, pgType, PG_STATIC));
+ set_nullfield_int2(&row->tuple[14], pgtype_scale(stmt, pgType, PG_STATIC));
+
+ QR_add_tuple(stmt->result, row);
+ }
+ }
+
+ stmt->status = STMT_FINISHED;
+ stmt->currTuple = -1;
+ stmt->rowset_start = -1;
+ stmt->current_col = -1;
+
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_GetFunctions(
+ HDBC hdbc,
+ UWORD fFunction,
+ UWORD FAR * pfExists)
+{
+ static char *func = "PGAPI_GetFunctions";
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ ConnInfo *ci = &(conn->connInfo);
+
+ mylog("%s: entering...%u\n", func, fFunction);
+
+ if (fFunction == SQL_API_ALL_FUNCTIONS)
+ {
+#if (ODBCVER < 0x0300)
+ if (ci->drivers.lie)
+ {
+ int i;
+
+ memset(pfExists, 0, sizeof(UWORD) * 100);
+
+ pfExists[SQL_API_SQLALLOCENV] = TRUE;
+ pfExists[SQL_API_SQLFREEENV] = TRUE;
+ for (i = SQL_API_SQLALLOCCONNECT; i <= SQL_NUM_FUNCTIONS; i++)
+ pfExists[i] = TRUE;
+ for (i = SQL_EXT_API_START; i <= SQL_EXT_API_LAST; i++)
+ pfExists[i] = TRUE;
+ }
+ else
+#endif
+ {
+ memset(pfExists, 0, sizeof(UWORD) * 100);
+
+ /* ODBC core functions */
+ pfExists[SQL_API_SQLALLOCCONNECT] = TRUE;
+ pfExists[SQL_API_SQLALLOCENV] = TRUE;
+ pfExists[SQL_API_SQLALLOCSTMT] = TRUE;
+ pfExists[SQL_API_SQLBINDCOL] = TRUE;
+ pfExists[SQL_API_SQLCANCEL] = TRUE;
+ pfExists[SQL_API_SQLCOLATTRIBUTES] = TRUE;
+ pfExists[SQL_API_SQLCONNECT] = TRUE;
+ pfExists[SQL_API_SQLDESCRIBECOL] = TRUE; /* partial */
+ pfExists[SQL_API_SQLDISCONNECT] = TRUE;
+ pfExists[SQL_API_SQLERROR] = TRUE;
+ pfExists[SQL_API_SQLEXECDIRECT] = TRUE;
+ pfExists[SQL_API_SQLEXECUTE] = TRUE;
+ pfExists[SQL_API_SQLFETCH] = TRUE;
+ pfExists[SQL_API_SQLFREECONNECT] = TRUE;
+ pfExists[SQL_API_SQLFREEENV] = TRUE;
+ pfExists[SQL_API_SQLFREESTMT] = TRUE;
+ pfExists[SQL_API_SQLGETCURSORNAME] = TRUE;
+ pfExists[SQL_API_SQLNUMRESULTCOLS] = TRUE;
+ pfExists[SQL_API_SQLPREPARE] = TRUE; /* complete? */
+ pfExists[SQL_API_SQLROWCOUNT] = TRUE;
+ pfExists[SQL_API_SQLSETCURSORNAME] = TRUE;
+ pfExists[SQL_API_SQLSETPARAM] = FALSE; /* odbc 1.0 */
+ pfExists[SQL_API_SQLTRANSACT] = TRUE;
+
+ /* ODBC level 1 functions */
+ pfExists[SQL_API_SQLBINDPARAMETER] = TRUE;
+ pfExists[SQL_API_SQLCOLUMNS] = TRUE;
+ pfExists[SQL_API_SQLDRIVERCONNECT] = TRUE;
+ pfExists[SQL_API_SQLGETCONNECTOPTION] = TRUE; /* partial */
+ pfExists[SQL_API_SQLGETDATA] = TRUE;
+ pfExists[SQL_API_SQLGETFUNCTIONS] = TRUE;
+ pfExists[SQL_API_SQLGETINFO] = TRUE;
+ pfExists[SQL_API_SQLGETSTMTOPTION] = TRUE; /* partial */
+ pfExists[SQL_API_SQLGETTYPEINFO] = TRUE;
+ pfExists[SQL_API_SQLPARAMDATA] = TRUE;
+ pfExists[SQL_API_SQLPUTDATA] = TRUE;
+ pfExists[SQL_API_SQLSETCONNECTOPTION] = TRUE; /* partial */
+ pfExists[SQL_API_SQLSETSTMTOPTION] = TRUE;
+ pfExists[SQL_API_SQLSPECIALCOLUMNS] = TRUE;
+ pfExists[SQL_API_SQLSTATISTICS] = TRUE;
+ pfExists[SQL_API_SQLTABLES] = TRUE;
+
+ /* ODBC level 2 functions */
+ pfExists[SQL_API_SQLBROWSECONNECT] = FALSE;
+ pfExists[SQL_API_SQLCOLUMNPRIVILEGES] = FALSE;
+ pfExists[SQL_API_SQLDATASOURCES] = FALSE; /* only implemented by
+ * DM */
+ pfExists[SQL_API_SQLDESCRIBEPARAM] = FALSE; /* not properly
+ * implemented */
+ pfExists[SQL_API_SQLDRIVERS] = FALSE; /* only implemented by
+ * DM */
+ pfExists[SQL_API_SQLEXTENDEDFETCH] = TRUE;
+ pfExists[SQL_API_SQLFOREIGNKEYS] = TRUE;
+ pfExists[SQL_API_SQLMORERESULTS] = TRUE;
+ pfExists[SQL_API_SQLNATIVESQL] = TRUE;
+ pfExists[SQL_API_SQLNUMPARAMS] = TRUE;
+ pfExists[SQL_API_SQLPARAMOPTIONS] = FALSE;
+ pfExists[SQL_API_SQLPRIMARYKEYS] = TRUE;
+ pfExists[SQL_API_SQLPROCEDURECOLUMNS] = FALSE;
+ if (PG_VERSION_LT(conn, 6.5))
+ pfExists[SQL_API_SQLPROCEDURES] = FALSE;
+ else
+ pfExists[SQL_API_SQLPROCEDURES] = TRUE;
+ pfExists[SQL_API_SQLSETPOS] = TRUE;
+ pfExists[SQL_API_SQLSETSCROLLOPTIONS] = TRUE; /* odbc 1.0 */
+ pfExists[SQL_API_SQLTABLEPRIVILEGES] = FALSE;
+ }
+ }
+ else
+ {
+ if (ci->drivers.lie)
+ *pfExists = TRUE;
+ else
+ {
+ switch (fFunction)
+ {
+ case SQL_API_SQLALLOCCONNECT:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLALLOCENV:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLALLOCSTMT:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLBINDCOL:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLCANCEL:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLCOLATTRIBUTES:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLCONNECT:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLDESCRIBECOL:
+ *pfExists = TRUE;
+ break; /* partial */
+ case SQL_API_SQLDISCONNECT:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLERROR:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLEXECDIRECT:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLEXECUTE:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLFETCH:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLFREECONNECT:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLFREEENV:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLFREESTMT:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLGETCURSORNAME:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLNUMRESULTCOLS:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLPREPARE:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLROWCOUNT:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLSETCURSORNAME:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLSETPARAM:
+ *pfExists = FALSE;
+ break; /* odbc 1.0 */
+ case SQL_API_SQLTRANSACT:
+ *pfExists = TRUE;
+ break;
+
+ /* ODBC level 1 functions */
+ case SQL_API_SQLBINDPARAMETER:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLCOLUMNS:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLDRIVERCONNECT:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLGETCONNECTOPTION:
+ *pfExists = TRUE;
+ break; /* partial */
+ case SQL_API_SQLGETDATA:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLGETFUNCTIONS:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLGETINFO:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLGETSTMTOPTION:
+ *pfExists = TRUE;
+ break; /* partial */
+ case SQL_API_SQLGETTYPEINFO:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLPARAMDATA:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLPUTDATA:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLSETCONNECTOPTION:
+ *pfExists = TRUE;
+ break; /* partial */
+ case SQL_API_SQLSETSTMTOPTION:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLSPECIALCOLUMNS:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLSTATISTICS:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLTABLES:
+ *pfExists = TRUE;
+ break;
+
+ /* ODBC level 2 functions */
+ case SQL_API_SQLBROWSECONNECT:
+ *pfExists = FALSE;
+ break;
+ case SQL_API_SQLCOLUMNPRIVILEGES:
+ *pfExists = FALSE;
+ break;
+ case SQL_API_SQLDATASOURCES:
+ *pfExists = FALSE;
+ break; /* only implemented by DM */
+ case SQL_API_SQLDESCRIBEPARAM:
+ *pfExists = FALSE;
+ break; /* not properly implemented */
+ case SQL_API_SQLDRIVERS:
+ *pfExists = FALSE;
+ break; /* only implemented by DM */
+ case SQL_API_SQLEXTENDEDFETCH:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLFOREIGNKEYS:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLMORERESULTS:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLNATIVESQL:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLNUMPARAMS:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLPARAMOPTIONS:
+ *pfExists = FALSE;
+ break;
+ case SQL_API_SQLPRIMARYKEYS:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLPROCEDURECOLUMNS:
+ *pfExists = FALSE;
+ break;
+ case SQL_API_SQLPROCEDURES:
+ if (PG_VERSION_LT(conn, 6.5))
+ *pfExists = FALSE;
+ else
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLSETPOS:
+ *pfExists = TRUE;
+ break;
+ case SQL_API_SQLSETSCROLLOPTIONS:
+ *pfExists = TRUE;
+ break; /* odbc 1.0 */
+ case SQL_API_SQLTABLEPRIVILEGES:
+ *pfExists = FALSE;
+ break;
+ }
+ }
+ }
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_Tables(
+ HSTMT hstmt,
+ UCHAR FAR * szTableQualifier,
+ SWORD cbTableQualifier,
+ UCHAR FAR * szTableOwner,
+ SWORD cbTableOwner,
+ UCHAR FAR * szTableName,
+ SWORD cbTableName,
+ UCHAR FAR * szTableType,
+ SWORD cbTableType)
+{
+ static char *func = "PGAPI_Tables";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ StatementClass *tbl_stmt;
+ TupleNode *row;
+ HSTMT htbl_stmt;
+ RETCODE result;
+ char *tableType;
+ char tables_query[INFO_INQUIRY_LEN];
+ char table_name[MAX_INFO_STRING],
+ table_owner[MAX_INFO_STRING],
+ relkind_or_hasrules[MAX_INFO_STRING];
+ ConnectionClass *conn;
+ 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("%s: entering...stmt=%u\n", func, stmt);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ stmt->manual_result = TRUE;
+ stmt->errormsg_created = TRUE;
+
+ conn = SC_get_conn(stmt);
+ ci = &(conn->connInfo);
+
+ result = PGAPI_AllocStmt(stmt->hdbc, &htbl_stmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Couldn't allocate statement for PGAPI_Tables result.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ tbl_stmt = (StatementClass *) htbl_stmt;
+
+ /*
+ * Create the query to find out the tables
+ */
+ if (PG_VERSION_GE(conn, 7.1))
+ {
+ /* view is represented by its relkind since 7.1 */
+ strcpy(tables_query, "select relname, usename, relkind from pg_class, pg_user");
+ strcat(tables_query, " where relkind in ('r', 'v')");
+ }
+ else
+ {
+ strcpy(tables_query, "select relname, usename, relhasrules from pg_class, pg_user");
+ strcat(tables_query, " where relkind = 'r'");
+ }
+
+ my_strcat(tables_query, " and usename like '%.*s'", szTableOwner, cbTableOwner);
+ my_strcat(tables_query, " and relname like '%.*s'", szTableName, cbTableName);
+
+ /* Parse the extra systable prefix */
+ strcpy(prefixes, ci->drivers.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 */
+ tableType = make_string(szTableType, cbTableType, NULL);
+ if (tableType)
+ {
+ strcpy(table_types, tableType);
+ free(tableType);
+ i = 0;
+ table_type[i] = strtok(table_types, ",");
+ while (table_type[i] && i < 32)
+ table_type[++i] = strtok(NULL, ",");
+
+ /* 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++;
+ }
+ }
+ else
+ {
+ show_regular_tables = TRUE;
+ show_views = TRUE;
+ }
+
+ /*
+ * 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, "'");
+ }
+
+ /* match users */
+ if (PG_VERSION_LT(conn, 7.1))
+ /* filter out large objects in older versions */
+ strcat(tables_query, " and relname !~ '^xinv[0-9]+'");
+
+ strcat(tables_query, " and usesysid = relowner");
+ strcat(tables_query, " order by relname");
+
+ result = PGAPI_ExecDirect(htbl_stmt, tables_query, strlen(tables_query));
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = SC_create_errormsg(htbl_stmt);
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 1, SQL_C_CHAR,
+ table_name, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 2, SQL_C_CHAR,
+ table_owner, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+ result = PGAPI_BindCol(htbl_stmt, 3, SQL_C_CHAR,
+ relkind_or_hasrules, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ stmt->result = QR_Constructor();
+ if (!stmt->result)
+ {
+ stmt->errormsg = "Couldn't allocate memory for PGAPI_Tables result.";
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ /* the binding structure for a statement is not set up until */
+
+ /*
+ * a statement is actually executed, so we'll have to do this
+ * ourselves.
+ */
+ extend_bindings(stmt, 5);
+
+ /* set the field names */
+ QR_set_num_fields(stmt->result, 5);
+ QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 3, "TABLE_TYPE", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 4, "REMARKS", PG_TYPE_TEXT, 254);
+
+ /* add the tuples */
+ result = PGAPI_Fetch(htbl_stmt);
+ while ((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO))
+ {
+ /*
+ * 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++;
+ }
+ }
+ }
+
+ /* Determine if the table name is a view */
+ if (PG_VERSION_GE(conn, 7.1))
+ /* view is represented by its relkind since 7.1 */
+ view = (relkind_or_hasrules[0] == 'v');
+ else
+ view = (relkind_or_hasrules[0] == '1');
+
+ /* It must be a regular table */
+ regular_table = (!systable && !view);
+
+
+ /* Include the row in the result set if meets all criteria */
+
+ /*
+ * NOTE: Unsupported table types (i.e., LOCAL TEMPORARY, ALIAS,
+ * etc) will return nothing
+ */
+ if ((systable && show_system_tables) ||
+ (view && show_views) ||
+ (regular_table && show_regular_tables))
+ {
+ row = (TupleNode *) malloc(sizeof(TupleNode) + (5 - 1) *sizeof(TupleField));
+
+ 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("%s: table_name = '%s'\n", func, 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 = PGAPI_Fetch(htbl_stmt);
+ }
+ if (result != SQL_NO_DATA_FOUND)
+ {
+ stmt->errormsg = SC_create_errormsg(htbl_stmt);
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ /*
+ * also, things need to think that this statement is finished so the
+ * results can be retrieved.
+ */
+ stmt->status = STMT_FINISHED;
+
+ /* set up the current tuple pointer for SQLFetch */
+ stmt->currTuple = -1;
+ stmt->rowset_start = -1;
+ stmt->current_col = -1;
+
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ mylog("%s: EXIT, stmt=%u\n", func, stmt);
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_Columns(
+ HSTMT hstmt,
+ UCHAR FAR * szTableQualifier,
+ SWORD cbTableQualifier,
+ UCHAR FAR * szTableOwner,
+ SWORD cbTableOwner,
+ UCHAR FAR * szTableName,
+ SWORD cbTableName,
+ UCHAR FAR * szColumnName,
+ SWORD cbColumnName)
+{
+ static char *func = "PGAPI_Columns";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ TupleNode *row;
+ HSTMT hcol_stmt;
+ StatementClass *col_stmt;
+ char columns_query[INFO_INQUIRY_LEN];
+ RETCODE result;
+ char table_owner[MAX_INFO_STRING],
+ table_name[MAX_INFO_STRING],
+ field_name[MAX_INFO_STRING],
+ field_type_name[MAX_INFO_STRING];
+ Int2 field_number,
+ result_cols,
+ scale;
+ Int4 field_type,
+ the_type,
+ field_length,
+ mod_length,
+ precision;
+ char useStaticPrecision;
+ char not_null[MAX_INFO_STRING],
+ relhasrules[MAX_INFO_STRING];
+ ConnInfo *ci;
+ ConnectionClass *conn;
+
+
+ mylog("%s: entering...stmt=%u\n", func, stmt);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ stmt->manual_result = TRUE;
+ stmt->errormsg_created = TRUE;
+
+ conn = SC_get_conn(stmt);
+ ci = &(conn->connInfo);
+
+ /*
+ * 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, a.attnotnull, c.relhasrules"
+ " from pg_user u, pg_class c, pg_attribute a, pg_type t"
+ " where u.usesysid = c.relowner"
+ " and c.oid= a.attrelid and a.atttypid = t.oid and (a.attnum > 0)",
+ PG_VERSION_LE(conn, 6.2) ? "a.attlen" : "a.atttypmod");
+
+ 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 a.attname like '%.*s'", szColumnName, cbColumnName);
+
+ /*
+ * give the output in the order the columns were defined when the
+ * table was created
+ */
+ strcat(columns_query, " order by attnum");
+
+ result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Couldn't allocate statement for PGAPI_Columns result.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ col_stmt = (StatementClass *) hcol_stmt;
+
+ mylog("%s: hcol_stmt = %u, col_stmt = %u\n", func, hcol_stmt, col_stmt);
+
+ result = PGAPI_ExecDirect(hcol_stmt, columns_query,
+ strlen(columns_query));
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = SC_create_errormsg(hcol_stmt);
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(hcol_stmt, 1, SQL_C_CHAR,
+ table_owner, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg;
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(hcol_stmt, 2, SQL_C_CHAR,
+ table_name, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg;
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(hcol_stmt, 3, SQL_C_CHAR,
+ field_name, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg;
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(hcol_stmt, 4, SQL_C_LONG,
+ &field_type, 4, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg;
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(hcol_stmt, 5, SQL_C_CHAR,
+ field_type_name, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg;
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(hcol_stmt, 6, SQL_C_SHORT,
+ &field_number, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg;
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(hcol_stmt, 7, SQL_C_LONG,
+ &field_length, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg;
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(hcol_stmt, 8, SQL_C_LONG,
+ &mod_length, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg;
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(hcol_stmt, 9, SQL_C_CHAR,
+ not_null, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg;
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(hcol_stmt, 10, SQL_C_CHAR,
+ relhasrules, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg;
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ stmt->result = QR_Constructor();
+ if (!stmt->result)
+ {
+ stmt->errormsg = "Couldn't allocate memory for PGAPI_Columns result.";
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ /* the binding structure for a statement is not set up until */
+
+ /*
+ * a statement is actually executed, so we'll have to do this
+ * ourselves.
+ */
+ result_cols = 14;
+ extend_bindings(stmt, result_cols);
+
+ /* set the field names */
+ QR_set_num_fields(stmt->result, result_cols);
+ QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 3, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 4, "DATA_TYPE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 5, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 6, "PRECISION", PG_TYPE_INT4, 4);
+ QR_set_field_info(stmt->result, 7, "LENGTH", PG_TYPE_INT4, 4);
+ QR_set_field_info(stmt->result, 8, "SCALE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 9, "RADIX", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 10, "NULLABLE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 11, "REMARKS", PG_TYPE_TEXT, 254);
+
+ /* User defined fields */
+ QR_set_field_info(stmt->result, 12, "DISPLAY_SIZE", PG_TYPE_INT4, 4);
+ QR_set_field_info(stmt->result, 13, "FIELD_TYPE", PG_TYPE_INT4, 4);
+
+ result = PGAPI_Fetch(hcol_stmt);
+
+ /*
+ * Only show oid if option AND there are other columns AND it's not
+ * being called by SQLStatistics . Always show OID if it's a system
+ * table
+ */
+
+ if (result != SQL_ERROR && !stmt->internal)
+ {
+ if (relhasrules[0] != '1' &&
+ (atoi(ci->show_oid_column) ||
+ strncmp(table_name, POSTGRES_SYS_PREFIX, strlen(POSTGRES_SYS_PREFIX)) == 0))
+ {
+ /* For OID fields */
+ the_type = PG_TYPE_OID;
+ row = (TupleNode *) malloc(sizeof(TupleNode) +
+ (result_cols - 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, the_type));
+ set_tuplefield_string(&row->tuple[5], "OID");
+
+ set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC));
+ set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, the_type, PG_STATIC, PG_STATIC));
+
+ set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, the_type, PG_STATIC));
+ set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, the_type));
+ set_tuplefield_int2(&row->tuple[10], SQL_NO_NULLS);
+ set_tuplefield_string(&row->tuple[11], "");
+
+ set_tuplefield_int4(&row->tuple[12], pgtype_display_size(stmt, the_type, PG_STATIC, PG_STATIC));
+ set_tuplefield_int4(&row->tuple[13], the_type);
+
+ QR_add_tuple(stmt->result, row);
+ }
+ }
+
+ while ((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO))
+ {
+ row = (TupleNode *) malloc(sizeof(TupleNode) +
+ (result_cols - 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], field_name);
+ set_tuplefield_int2(&row->tuple[4], pgtype_to_sqltype(stmt, 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
+ * BPCHAR - the length is also stored as varchar is
+ *
+ * NUMERIC - the scale is stored in atttypmod as follows:
+ *
+ * precision =((atttypmod - VARHDRSZ) >> 16) & 0xffff
+ * scale = (atttypmod - VARHDRSZ) & 0xffff
+ *
+ *----------
+ */
+ qlog("PGAPI_Columns: table='%s',field_name='%s',type=%d,sqltype=%d,name='%s'\n",
+ table_name, field_name, field_type, pgtype_to_sqltype, field_type_name);
+
+ useStaticPrecision = TRUE;
+
+ if (field_type == PG_TYPE_NUMERIC)
+ {
+ if (mod_length >= 4)
+ mod_length -= 4; /* the length is in atttypmod - 4 */
+
+ if (mod_length >= 0)
+ {
+ useStaticPrecision = FALSE;
+
+ precision = (mod_length >> 16) & 0xffff;
+ scale = mod_length & 0xffff;
+
+ mylog("%s: field type is NUMERIC: field_type = %d, mod_length=%d, precision=%d, scale=%d\n", func, field_type, mod_length, precision, scale);
+
+ set_tuplefield_int4(&row->tuple[7], precision + 2); /* sign+dec.point */
+ set_tuplefield_int4(&row->tuple[6], precision);
+ set_tuplefield_int4(&row->tuple[12], precision + 2); /* sign+dec.point */
+ set_nullfield_int2(&row->tuple[8], scale);
+ }
+ }
+
+ if ((field_type == PG_TYPE_VARCHAR) ||
+ (field_type == PG_TYPE_BPCHAR))
+ {
+ useStaticPrecision = FALSE;
+
+ if (mod_length >= 4)
+ mod_length -= 4; /* the length is in atttypmod - 4 */
+
+ if (mod_length > ci->drivers.max_varchar_size || mod_length <= 0)
+ mod_length = ci->drivers.max_varchar_size;
+
+ mylog("%s: field type is VARCHAR,BPCHAR: field_type = %d, mod_length = %d\n", func, field_type, mod_length);
+
+ set_tuplefield_int4(&row->tuple[7], mod_length);
+ set_tuplefield_int4(&row->tuple[6], mod_length);
+ set_tuplefield_int4(&row->tuple[12], mod_length);
+ set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, field_type, PG_STATIC));
+ }
+
+ if (useStaticPrecision)
+ {
+ mylog("%s: field type is OTHER: field_type = %d, pgtype_length = %d\n", func, field_type, pgtype_length(stmt, field_type, PG_STATIC, PG_STATIC));
+
+ set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, field_type, PG_STATIC, PG_STATIC));
+ set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, field_type, PG_STATIC, PG_STATIC));
+ set_tuplefield_int4(&row->tuple[12], pgtype_display_size(stmt, field_type, PG_STATIC, PG_STATIC));
+ set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, field_type, PG_STATIC));
+ }
+
+ set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, field_type));
+ set_tuplefield_int2(&row->tuple[10], (Int2) (not_null[0] == '1' ? SQL_NO_NULLS : pgtype_nullable(stmt, field_type)));
+ set_tuplefield_string(&row->tuple[11], "");
+ set_tuplefield_int4(&row->tuple[13], field_type);
+
+ QR_add_tuple(stmt->result, row);
+
+
+ result = PGAPI_Fetch(hcol_stmt);
+
+ }
+ if (result != SQL_NO_DATA_FOUND)
+ {
+ stmt->errormsg = SC_create_errormsg(hcol_stmt);
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ /*
+ * Put the row version column at the end so it might not be mistaken
+ * for a key field.
+ */
+ if (relhasrules[0] != '1' && !stmt->internal && atoi(ci->row_versioning))
+ {
+ /* For Row Versioning fields */
+ the_type = PG_TYPE_INT4;
+
+ row = (TupleNode *) malloc(sizeof(TupleNode) +
+ (result_cols - 1) *sizeof(TupleField));
+
+ set_tuplefield_string(&row->tuple[0], "");
+ set_tuplefield_string(&row->tuple[1], "");
+ set_tuplefield_string(&row->tuple[2], table_name);
+ set_tuplefield_string(&row->tuple[3], "xmin");
+ set_tuplefield_int2(&row->tuple[4], pgtype_to_sqltype(stmt, the_type));
+ set_tuplefield_string(&row->tuple[5], pgtype_to_name(stmt, the_type));
+ set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, the_type, PG_STATIC, PG_STATIC));
+ set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC));
+ set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, the_type, PG_STATIC));
+ set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, the_type));
+ set_tuplefield_int2(&row->tuple[10], SQL_NO_NULLS);
+ set_tuplefield_string(&row->tuple[11], "");
+ set_tuplefield_int4(&row->tuple[12], pgtype_display_size(stmt, the_type, PG_STATIC, PG_STATIC));
+ set_tuplefield_int4(&row->tuple[13], the_type);
+
+ QR_add_tuple(stmt->result, row);
+ }
+
+ /*
+ * also, things need to think that this statement is finished so the
+ * results can be retrieved.
+ */
+ stmt->status = STMT_FINISHED;
+
+ /* set up the current tuple pointer for SQLFetch */
+ stmt->currTuple = -1;
+ stmt->rowset_start = -1;
+ stmt->current_col = -1;
+
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ mylog("%s: EXIT, stmt=%u\n", func, stmt);
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_SpecialColumns(
+ HSTMT hstmt,
+ UWORD fColType,
+ UCHAR FAR * szTableQualifier,
+ SWORD cbTableQualifier,
+ UCHAR FAR * szTableOwner,
+ SWORD cbTableOwner,
+ UCHAR FAR * szTableName,
+ SWORD cbTableName,
+ UWORD fScope,
+ UWORD fNullable)
+{
+ static char *func = "PGAPI_SpecialColumns";
+ TupleNode *row;
+ StatementClass *stmt = (StatementClass *) hstmt;
+ ConnInfo *ci;
+ HSTMT hcol_stmt;
+ StatementClass *col_stmt;
+ char columns_query[INFO_INQUIRY_LEN];
+ RETCODE result;
+ char relhasrules[MAX_INFO_STRING];
+
+ mylog("%s: entering...stmt=%u\n", func, stmt);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ ci = &(SC_get_conn(stmt)->connInfo);
+
+ stmt->manual_result = TRUE;
+
+ /*
+ * Create the query to find out if this is a view or not...
+ */
+ sprintf(columns_query, "select c.relhasrules "
+ "from pg_user u, pg_class c where "
+ "u.usesysid = c.relowner");
+
+ my_strcat(columns_query, " and c.relname like '%.*s'", szTableName, cbTableName);
+ my_strcat(columns_query, " and u.usename like '%.*s'", szTableOwner, cbTableOwner);
+
+
+ result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Couldn't allocate statement for SQLSpecialColumns result.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ col_stmt = (StatementClass *) hcol_stmt;
+
+ mylog("%s: hcol_stmt = %u, col_stmt = %u\n", func, hcol_stmt, col_stmt);
+
+ result = PGAPI_ExecDirect(hcol_stmt, columns_query,
+ strlen(columns_query));
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = SC_create_errormsg(hcol_stmt);
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(hcol_stmt, 1, SQL_C_CHAR,
+ relhasrules, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg;
+ stmt->errornumber = col_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_Fetch(hcol_stmt);
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+
+ stmt->result = QR_Constructor();
+ extend_bindings(stmt, 8);
+
+ QR_set_num_fields(stmt->result, 8);
+ QR_set_field_info(stmt->result, 0, "SCOPE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 1, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 2, "DATA_TYPE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 3, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 4, "PRECISION", PG_TYPE_INT4, 4);
+ QR_set_field_info(stmt->result, 5, "LENGTH", PG_TYPE_INT4, 4);
+ QR_set_field_info(stmt->result, 6, "SCALE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 7, "PSEUDO_COLUMN", PG_TYPE_INT2, 2);
+
+ if (relhasrules[0] != '1')
+ {
+ /* use the oid value for the rowid */
+ if (fColType == SQL_BEST_ROWID)
+ {
+ row = (TupleNode *) malloc(sizeof(TupleNode) + (8 - 1) *sizeof(TupleField));
+
+ set_tuplefield_int2(&row->tuple[0], SQL_SCOPE_SESSION);
+ set_tuplefield_string(&row->tuple[1], "oid");
+ set_tuplefield_int2(&row->tuple[2], pgtype_to_sqltype(stmt, PG_TYPE_OID));
+ set_tuplefield_string(&row->tuple[3], "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(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC));
+ set_tuplefield_int2(&row->tuple[6], pgtype_scale(stmt, PG_TYPE_OID, PG_STATIC));
+ set_tuplefield_int2(&row->tuple[7], SQL_PC_PSEUDO);
+
+ QR_add_tuple(stmt->result, row);
+
+ }
+ else if (fColType == SQL_ROWVER)
+ {
+ Int2 the_type = PG_TYPE_INT4;
+
+ if (atoi(ci->row_versioning))
+ {
+ row = (TupleNode *) malloc(sizeof(TupleNode) + (8 - 1) *sizeof(TupleField));
+
+ set_tuplefield_null(&row->tuple[0]);
+ set_tuplefield_string(&row->tuple[1], "xmin");
+ set_tuplefield_int2(&row->tuple[2], pgtype_to_sqltype(stmt, the_type));
+ set_tuplefield_string(&row->tuple[3], pgtype_to_name(stmt, the_type));
+ set_tuplefield_int4(&row->tuple[4], pgtype_precision(stmt, the_type, PG_STATIC, PG_STATIC));
+ set_tuplefield_int4(&row->tuple[5], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC));
+ set_tuplefield_int2(&row->tuple[6], pgtype_scale(stmt, the_type, PG_STATIC));
+ set_tuplefield_int2(&row->tuple[7], SQL_PC_PSEUDO);
+
+ QR_add_tuple(stmt->result, row);
+ }
+ }
+ }
+
+ stmt->status = STMT_FINISHED;
+ stmt->currTuple = -1;
+ stmt->rowset_start = -1;
+ stmt->current_col = -1;
+
+ mylog("%s: EXIT, stmt=%u\n", func, stmt);
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_Statistics(
+ HSTMT hstmt,
+ UCHAR FAR * szTableQualifier,
+ SWORD cbTableQualifier,
+ UCHAR FAR * szTableOwner,
+ SWORD cbTableOwner,
+ UCHAR FAR * szTableName,
+ SWORD cbTableName,
+ UWORD fUnique,
+ UWORD fAccuracy)
+{
+ static char *func = "PGAPI_Statistics";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ char index_query[INFO_INQUIRY_LEN];
+ HSTMT hindx_stmt;
+ RETCODE result;
+ char *table_name;
+ char index_name[MAX_INFO_STRING];
+ short fields_vector[16];
+ char isunique[10],
+ isclustered[10],
+ ishash[MAX_INFO_STRING];
+ SDWORD index_name_len,
+ fields_vector_len;
+ TupleNode *row;
+ int i;
+ HSTMT hcol_stmt;
+ StatementClass *col_stmt,
+ *indx_stmt;
+ char column_name[MAX_INFO_STRING],
+ relhasrules[MAX_INFO_STRING];
+ char **column_names = 0;
+ SQLINTEGER column_name_len;
+ int total_columns = 0;
+ char error = TRUE;
+ ConnInfo *ci;
+ char buf[256];
+
+ mylog("%s: entering...stmt=%u\n", func, stmt);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ stmt->manual_result = TRUE;
+ stmt->errormsg_created = TRUE;
+
+ ci = &(SC_get_conn(stmt)->connInfo);
+
+ stmt->result = QR_Constructor();
+ if (!stmt->result)
+ {
+ stmt->errormsg = "Couldn't allocate memory for PGAPI_Statistics result.";
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* the binding structure for a statement is not set up until */
+
+ /*
+ * a statement is actually executed, so we'll have to do this
+ * ourselves.
+ */
+ extend_bindings(stmt, 13);
+
+ /* set the field names */
+ QR_set_num_fields(stmt->result, 13);
+ QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 3, "NON_UNIQUE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 4, "INDEX_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 5, "INDEX_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 6, "TYPE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 7, "SEQ_IN_INDEX", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 8, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 9, "COLLATION", PG_TYPE_CHAR, 1);
+ QR_set_field_info(stmt->result, 10, "CARDINALITY", 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);
+
+ /*
+ * 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 PGAPI_Statistics.";
+ stmt->errornumber = STMT_INTERNAL_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /*
+ * we need to get a list of the field names first, so we can return
+ * them later.
+ */
+ result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = "PGAPI_AllocStmt failed in PGAPI_Statistics for columns.";
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ goto SEEYA;
+ }
+
+ col_stmt = (StatementClass *) hcol_stmt;
+
+ /*
+ * "internal" prevents SQLColumns from returning the oid if it is
+ * being shown. This would throw everything off.
+ */
+ col_stmt->internal = TRUE;
+ result = PGAPI_Columns(hcol_stmt, "", 0, "", 0,
+ table_name, (SWORD) strlen(table_name), "", 0);
+ col_stmt->internal = FALSE;
+
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = col_stmt->errormsg; /* "SQLColumns failed in
+ * SQLStatistics."; */
+ stmt->errornumber = col_stmt->errornumber; /* STMT_EXEC_ERROR; */
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ goto SEEYA;
+ }
+ result = PGAPI_BindCol(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;
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ goto SEEYA;
+
+ }
+
+ result = PGAPI_Fetch(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("%s: column_name = '%s'\n", func, column_name);
+
+ result = PGAPI_Fetch(hcol_stmt);
+ }
+
+ 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;
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ goto SEEYA;
+
+ }
+
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+
+ /* get a list of indexes on this table */
+ result = PGAPI_AllocStmt(stmt->hdbc, &hindx_stmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = "PGAPI_AllocStmt failed in SQLStatistics for indices.";
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ goto SEEYA;
+
+ }
+ indx_stmt = (StatementClass *) hindx_stmt;
+
+ sprintf(index_query, "select c.relname, i.indkey, i.indisunique"
+ ", i.indisclustered, a.amname, c.relhasrules"
+ " from pg_index i, pg_class c, pg_class d, pg_am a"
+ " where d.relname = '%s'"
+ " and d.oid = i.indrelid"
+ " and i.indexrelid = c.oid"
+ " and c.relam = a.oid"
+ ,table_name);
+ if (PG_VERSION_GT(SC_get_conn(stmt), 6.4))
+ strcat(index_query, " order by i.indisprimary desc");
+
+ result = PGAPI_ExecDirect(hindx_stmt, index_query, strlen(index_query));
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ /*
+ * "Couldn't execute index query (w/SQLExecDirect) in
+ * SQLStatistics.";
+ */
+ stmt->errormsg = SC_create_errormsg(hindx_stmt);
+
+ stmt->errornumber = indx_stmt->errornumber;
+ PGAPI_FreeStmt(hindx_stmt, SQL_DROP);
+ goto SEEYA;
+ }
+
+ /* bind the index name column */
+ result = PGAPI_BindCol(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;
+ PGAPI_FreeStmt(hindx_stmt, SQL_DROP);
+ goto SEEYA;
+
+ }
+ /* bind the vector column */
+ result = PGAPI_BindCol(hindx_stmt, 2, SQL_C_DEFAULT,
+ fields_vector, 32, &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;
+ PGAPI_FreeStmt(hindx_stmt, SQL_DROP);
+ goto SEEYA;
+
+ }
+ /* bind the "is unique" column */
+ result = PGAPI_BindCol(hindx_stmt, 3, SQL_C_CHAR,
+ isunique, sizeof(isunique), 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;
+ PGAPI_FreeStmt(hindx_stmt, SQL_DROP);
+ goto SEEYA;
+ }
+
+ /* bind the "is clustered" column */
+ result = PGAPI_BindCol(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;
+ PGAPI_FreeStmt(hindx_stmt, SQL_DROP);
+ goto SEEYA;
+
+ }
+
+ /* bind the "is hash" column */
+ result = PGAPI_BindCol(hindx_stmt, 5, SQL_C_CHAR,
+ ishash, sizeof(ishash), 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;
+ PGAPI_FreeStmt(hindx_stmt, SQL_DROP);
+ goto SEEYA;
+
+ }
+
+ result = PGAPI_BindCol(hindx_stmt, 6, SQL_C_CHAR,
+ relhasrules, MAX_INFO_STRING, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = indx_stmt->errormsg;
+ stmt->errornumber = indx_stmt->errornumber;
+ PGAPI_FreeStmt(hindx_stmt, SQL_DROP);
+ goto SEEYA;
+ }
+
+ /* fake index of OID */
+ if (relhasrules[0] != '1' && atoi(ci->show_oid_column) && atoi(ci->fake_oid_index))
+ {
+ row = (TupleNode *) malloc(sizeof(TupleNode) +
+ (13 - 1) *sizeof(TupleField));
+
+ /* 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);
+
+ /* non-unique index? */
+ set_tuplefield_int2(&row->tuple[3], (Int2) (ci->drivers.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/HASH index?
+ */
+ 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 = PGAPI_Fetch(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;
+ /* add a row in this table for each field in the index */
+ while (i < 16 && fields_vector[i] != 0)
+ {
+ row = (TupleNode *) malloc(sizeof(TupleNode) +
+ (13 - 1) *sizeof(TupleField));
+
+ /* 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);
+
+ /* non-unique index? */
+ if (ci->drivers.unique_index)
+ set_tuplefield_int2(&row->tuple[3], (Int2) (atoi(isunique) ? FALSE : TRUE));
+ else
+ set_tuplefield_int2(&row->tuple[3], TRUE);
+
+ /* no index qualifier */
+ set_tuplefield_string(&row->tuple[4], "");
+ set_tuplefield_string(&row->tuple[5], index_name);
+
+ /*
+ * Clustered/HASH index?
+ */
+ set_tuplefield_int2(&row->tuple[6], (Int2)
+ (atoi(isclustered) ? SQL_INDEX_CLUSTERED :
+ (!strncmp(ishash, "hash", 4)) ? SQL_INDEX_HASHED : SQL_INDEX_OTHER));
+ set_tuplefield_int2(&row->tuple[7], (Int2) (i + 1));
+
+ if (fields_vector[i] == OID_ATTNUM)
+ {
+ set_tuplefield_string(&row->tuple[8], "oid");
+ mylog("%s: column name = oid\n", func);
+ }
+ else if (fields_vector[i] < 0 || fields_vector[i] > total_columns)
+ {
+ set_tuplefield_string(&row->tuple[8], "UNKNOWN");
+ mylog("%s: column name = UNKNOWN\n", func);
+ }
+ else
+ {
+ set_tuplefield_string(&row->tuple[8], column_names[fields_vector[i] - 1]);
+ mylog("%s: column name = '%s'\n", func, column_names[fields_vector[i] - 1]);
+ }
+
+ 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);
+ i++;
+ }
+ }
+
+ result = PGAPI_Fetch(hindx_stmt);
+ }
+ if (result != SQL_NO_DATA_FOUND)
+ {
+ /* "SQLFetch failed in SQLStatistics."; */
+ stmt->errormsg = SC_create_errormsg(hindx_stmt);
+ stmt->errornumber = indx_stmt->errornumber;
+ PGAPI_FreeStmt(hindx_stmt, SQL_DROP);
+ goto SEEYA;
+ }
+
+ PGAPI_FreeStmt(hindx_stmt, SQL_DROP);
+
+ /*
+ * also, things need to think that this statement is finished so the
+ * results can be retrieved.
+ */
+ stmt->status = STMT_FINISHED;
+
+ /* set up the current tuple pointer for SQLFetch */
+ stmt->currTuple = -1;
+ stmt->rowset_start = -1;
+ stmt->current_col = -1;
+
+ error = FALSE;
+
+SEEYA:
+ /* These things should be freed on any error ALSO! */
+ free(table_name);
+ for (i = 0; i < total_columns; i++)
+ free(column_names[i]);
+ free(column_names);
+
+ mylog("%s: EXIT, %s, stmt=%u\n", func, error ? "error" : "success", stmt);
+
+ if (error)
+ {
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ else
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_ColumnPrivileges(
+ HSTMT hstmt,
+ UCHAR FAR * szTableQualifier,
+ SWORD cbTableQualifier,
+ UCHAR FAR * szTableOwner,
+ SWORD cbTableOwner,
+ UCHAR FAR * szTableName,
+ SWORD cbTableName,
+ UCHAR FAR * szColumnName,
+ SWORD cbColumnName)
+{
+ static char *func = "PGAPI_ColumnPrivileges";
+
+ mylog("%s: entering...\n", func);
+
+ /* Neither Access or Borland care about this. */
+
+ SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
+ return SQL_ERROR;
+}
+
+
+/*
+ * SQLPrimaryKeys()
+ *
+ * Retrieve the primary key columns for the specified table.
+ */
+RETCODE SQL_API
+PGAPI_PrimaryKeys(
+ HSTMT hstmt,
+ UCHAR FAR * szTableQualifier,
+ SWORD cbTableQualifier,
+ UCHAR FAR * szTableOwner,
+ SWORD cbTableOwner,
+ UCHAR FAR * szTableName,
+ SWORD cbTableName)
+{
+ static char *func = "PGAPI_PrimaryKeys";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ ConnectionClass *conn;
+ TupleNode *row;
+ RETCODE result;
+ int seq = 0;
+ HSTMT htbl_stmt;
+ StatementClass *tbl_stmt;
+ char tables_query[INFO_INQUIRY_LEN];
+ char attname[MAX_INFO_STRING];
+ SDWORD attname_len;
+ char pktab[MAX_TABLE_LEN + 1];
+ Int2 result_cols;
+ int qno,
+ qstart,
+ qend;
+
+ mylog("%s: entering...stmt=%u\n", func, stmt);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ stmt->manual_result = TRUE;
+ stmt->errormsg_created = TRUE;
+
+ stmt->result = QR_Constructor();
+ if (!stmt->result)
+ {
+ stmt->errormsg = "Couldn't allocate memory for PGAPI_PrimaryKeys result.";
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* the binding structure for a statement is not set up until */
+
+ /*
+ * a statement is actually executed, so we'll have to do this
+ * ourselves.
+ */
+ result_cols = 6;
+ extend_bindings(stmt, result_cols);
+
+ /* set the field names */
+ QR_set_num_fields(stmt->result, result_cols);
+ QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 3, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 4, "KEY_SEQ", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 5, "PK_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+
+
+ result = PGAPI_AllocStmt(stmt->hdbc, &htbl_stmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Couldn't allocate statement for Primary Key result.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ tbl_stmt = (StatementClass *) htbl_stmt;
+
+ pktab[0] = '\0';
+ make_string(szTableName, cbTableName, pktab);
+ if (pktab[0] == '\0')
+ {
+ stmt->errormsg = "No Table specified to PGAPI_PrimaryKeys.";
+ stmt->errornumber = STMT_INTERNAL_ERROR;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 1, SQL_C_CHAR,
+ attname, MAX_INFO_STRING, &attname_len);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ conn = SC_get_conn(stmt);
+ if (PG_VERSION_LE(conn, 6.4))
+ qstart = 2;
+ else
+ qstart = 1;
+ qend = 2;
+ for (qno = qstart; qno <= qend; qno++)
+ {
+ switch (qno)
+ {
+ case 1:
+
+ /*
+ * Simplified query to remove assumptions about number of
+ * possible index columns. Courtesy of Tom Lane - thomas
+ * 2000-03-21
+ */
+ sprintf(tables_query, "select ta.attname, ia.attnum"
+ " from pg_attribute ta, pg_attribute ia, pg_class c, pg_index i"
+ " where c.relname = '%s'"
+ " AND c.oid = i.indrelid"
+ " AND i.indisprimary = 't'"
+ " AND ia.attrelid = i.indexrelid"
+ " AND ta.attrelid = i.indrelid"
+ " AND ta.attnum = i.indkey[ia.attnum-1]"
+ " order by ia.attnum", pktab);
+ break;
+ case 2:
+
+ /*
+ * Simplified query to search old fashoned primary key
+ */
+ sprintf(tables_query, "select ta.attname, ia.attnum"
+ " from pg_attribute ta, pg_attribute ia, pg_class c, pg_index i"
+ " where c.relname = '%s_pkey'"
+ " AND c.oid = i.indexrelid"
+ " AND ia.attrelid = i.indexrelid"
+ " AND ta.attrelid = i.indrelid"
+ " AND ta.attnum = i.indkey[ia.attnum-1]"
+ " order by ia.attnum", pktab);
+ break;
+ }
+ mylog("%s: tables_query='%s'\n", func, tables_query);
+
+ result = PGAPI_ExecDirect(htbl_stmt, tables_query, strlen(tables_query));
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = SC_create_errormsg(htbl_stmt);
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_Fetch(htbl_stmt);
+ if (result != SQL_NO_DATA_FOUND)
+ break;
+ }
+
+ while ((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO))
+ {
+ row = (TupleNode *) malloc(sizeof(TupleNode) + (result_cols - 1) *sizeof(TupleField));
+
+ set_tuplefield_null(&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], "");
+ set_tuplefield_string(&row->tuple[2], pktab);
+ set_tuplefield_string(&row->tuple[3], attname);
+ set_tuplefield_int2(&row->tuple[4], (Int2) (++seq));
+ set_tuplefield_null(&row->tuple[5]);
+
+ QR_add_tuple(stmt->result, row);
+
+ mylog(">> primaryKeys: pktab = '%s', attname = '%s', seq = %d\n", pktab, attname, seq);
+
+ result = PGAPI_Fetch(htbl_stmt);
+ }
+
+ if (result != SQL_NO_DATA_FOUND)
+ {
+ stmt->errormsg = SC_create_errormsg(htbl_stmt);
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+
+
+ /*
+ * also, things need to think that this statement is finished so the
+ * results can be retrieved.
+ */
+ stmt->status = STMT_FINISHED;
+
+ /* set up the current tuple pointer for SQLFetch */
+ stmt->currTuple = -1;
+ stmt->rowset_start = -1;
+ stmt->current_col = -1;
+
+ mylog("%s: EXIT, stmt=%u\n", func, stmt);
+ return SQL_SUCCESS;
+}
+
+
+#ifdef MULTIBYTE
+/*
+ * Multibyte support stuff for SQLForeignKeys().
+ * There may be much more effective way in the
+ * future version. The way is very forcible currently.
+ */
+static BOOL
+isMultibyte(const unsigned char *str)
+{
+ for (; *str; str++)
+ {
+ if (*str >= 0x80)
+ return TRUE;
+ }
+ return FALSE;
+}
+static char *
+getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloced)
+{
+ char query[1024],
+ saveoid[24],
+ *ret = serverTableName;
+ BOOL continueExec = TRUE,
+ bError = FALSE;
+ QResultClass *res;
+
+ *nameAlloced = FALSE;
+ if (!conn->client_encoding || !isMultibyte(serverTableName))
+ return ret;
+ if (!conn->server_encoding)
+ {
+ if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL), res)
+ {
+ if (QR_get_num_tuples(res) > 0)
+ conn->server_encoding = strdup(QR_get_value_backend_row(res, 0, 0));
+ QR_Destructor(res);
+ }
+ }
+ if (!conn->server_encoding)
+ return ret;
+ sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->server_encoding);
+ if (res = CC_send_query(conn, query, NULL), res)
+ {
+ bError = QR_get_aborted(res);
+ QR_Destructor(res);
+ }
+ else
+ bError = TRUE;
+ if (!bError && continueExec)
+ {
+ sprintf(query, "select OID from pg_class where relname = '%s'", serverTableName);
+ if (res = CC_send_query(conn, query, NULL), res)
+ {
+ if (QR_get_num_tuples(res) > 0)
+ strcpy(saveoid, QR_get_value_backend_row(res, 0, 0));
+ else
+ {
+ continueExec = FALSE;
+ bError = QR_get_aborted(res);
+ }
+ QR_Destructor(res);
+ }
+ else
+ bError = TRUE;
+ }
+ continueExec = (continueExec && !bError);
+ if (bError && CC_is_in_trans(conn))
+ {
+ if (res = CC_send_query(conn, "abort", NULL), res)
+ QR_Destructor(res);
+ CC_set_no_trans(conn);
+ bError = FALSE;
+ }
+ /* restore the client encoding */
+ sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->client_encoding);
+ if (res = CC_send_query(conn, query, NULL), res)
+ {
+ bError = QR_get_aborted(res);
+ QR_Destructor(res);
+ }
+ else
+ bError = TRUE;
+ if (bError || !continueExec)
+ return ret;
+ sprintf(query, "select relname from pg_class where OID = %s", saveoid);
+ if (res = CC_send_query(conn, query, NULL), res)
+ {
+ if (QR_get_num_tuples(res) > 0)
+ {
+ ret = strdup(QR_get_value_backend_row(res, 0, 0));
+ *nameAlloced = TRUE;
+ }
+ QR_Destructor(res);
+ }
+ return ret;
+}
+static char *
+getClientColumnName(ConnectionClass *conn, const char *serverTableName, char *serverColumnName, BOOL *nameAlloced)
+{
+ char query[1024],
+ saveattrelid[24],
+ saveattnum[16],
+ *ret = serverColumnName;
+ BOOL continueExec = TRUE,
+ bError = FALSE;
+ QResultClass *res;
+
+ *nameAlloced = FALSE;
+ if (!conn->client_encoding || !isMultibyte(serverColumnName))
+ return ret;
+ if (!conn->server_encoding)
+ {
+ if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL), res)
+ {
+ if (QR_get_num_tuples(res) > 0)
+ conn->server_encoding = strdup(QR_get_value_backend_row(res, 0, 0));
+ QR_Destructor(res);
+ }
+ }
+ if (!conn->server_encoding)
+ return ret;
+ sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->server_encoding);
+ if (res = CC_send_query(conn, query, NULL), res)
+ {
+ bError = QR_get_aborted(res);
+ QR_Destructor(res);
+ }
+ else
+ bError = TRUE;
+ if (!bError && continueExec)
+ {
+ sprintf(query, "select attrelid, attnum from pg_class, pg_attribute "
+ "where relname = '%s' and attrelid = pg_class.oid "
+ "and attname = '%s'", serverTableName, serverColumnName);
+ if (res = CC_send_query(conn, query, NULL), res)
+ {
+ if (QR_get_num_tuples(res) > 0)
+ {
+ strcpy(saveattrelid, QR_get_value_backend_row(res, 0, 0));
+ strcpy(saveattnum, QR_get_value_backend_row(res, 0, 1));
+ }
+ else
+ {
+ continueExec = FALSE;
+ bError = QR_get_aborted(res);
+ }
+ QR_Destructor(res);
+ }
+ else
+ bError = TRUE;
+ }
+ continueExec = (continueExec && !bError);
+ if (bError && CC_is_in_trans(conn))
+ {
+ if (res = CC_send_query(conn, "abort", NULL), res)
+ QR_Destructor(res);
+ CC_set_no_trans(conn);
+ bError = FALSE;
+ }
+ /* restore the cleint encoding */
+ sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->client_encoding);
+ if (res = CC_send_query(conn, query, NULL), res)
+ {
+ bError = QR_get_aborted(res);
+ QR_Destructor(res);
+ }
+ else
+ bError = TRUE;
+ if (bError || !continueExec)
+ return ret;
+ sprintf(query, "select attname from pg_attribute where attrelid = %s and attnum = %s", saveattrelid, saveattnum);
+ if (res = CC_send_query(conn, query, NULL), res)
+ {
+ if (QR_get_num_tuples(res) > 0)
+ {
+ ret = strdup(QR_get_value_backend_row(res, 0, 0));
+ *nameAlloced = TRUE;
+ }
+ QR_Destructor(res);
+ }
+ return ret;
+}
+#endif /* MULTIBYTE */
+
+RETCODE SQL_API
+PGAPI_ForeignKeys(
+ HSTMT hstmt,
+ UCHAR FAR * szPkTableQualifier,
+ SWORD cbPkTableQualifier,
+ UCHAR FAR * szPkTableOwner,
+ SWORD cbPkTableOwner,
+ UCHAR FAR * szPkTableName,
+ SWORD cbPkTableName,
+ UCHAR FAR * szFkTableQualifier,
+ SWORD cbFkTableQualifier,
+ UCHAR FAR * szFkTableOwner,
+ SWORD cbFkTableOwner,
+ UCHAR FAR * szFkTableName,
+ SWORD cbFkTableName)
+{
+ static char *func = "PGAPI_ForeignKeys";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ TupleNode *row;
+ HSTMT htbl_stmt,
+ hpkey_stmt;
+ StatementClass *tbl_stmt;
+ RETCODE result,
+ keyresult;
+ char tables_query[INFO_INQUIRY_LEN];
+ char trig_deferrable[2];
+ char trig_initdeferred[2];
+ char trig_args[1024];
+ char upd_rule[MAX_TABLE_LEN],
+ del_rule[MAX_TABLE_LEN];
+ char pk_table_needed[MAX_TABLE_LEN + 1];
+ char fk_table_needed[MAX_TABLE_LEN + 1];
+ char *pkey_ptr,
+ *pkey_text,
+ *fkey_ptr,
+ *fkey_text,
+ *pk_table,
+ *pkt_text,
+ *fk_table,
+ *fkt_text;
+
+#ifdef MULTIBYTE
+ BOOL pkey_alloced,
+ fkey_alloced,
+ pkt_alloced,
+ fkt_alloced;
+ ConnectionClass *conn;
+#endif /* MULTIBYTE */
+ int i,
+ j,
+ k,
+ num_keys;
+ SWORD trig_nargs,
+ upd_rule_type = 0,
+ del_rule_type = 0;
+
+#if (ODBCVER >= 0x0300)
+ SWORD defer_type;
+#endif
+ char pkey[MAX_INFO_STRING];
+ Int2 result_cols;
+
+ mylog("%s: entering...stmt=%u\n", func, stmt);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ stmt->manual_result = TRUE;
+ stmt->errormsg_created = TRUE;
+
+ stmt->result = QR_Constructor();
+ if (!stmt->result)
+ {
+ stmt->errormsg = "Couldn't allocate memory for PGAPI_ForeignKeys result.";
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* the binding structure for a statement is not set up until */
+
+ /*
+ * a statement is actually executed, so we'll have to do this
+ * ourselves.
+ */
+ result_cols = 14;
+ extend_bindings(stmt, result_cols);
+
+ /* set the field names */
+ QR_set_num_fields(stmt->result, result_cols);
+ QR_set_field_info(stmt->result, 0, "PKTABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 1, "PKTABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 2, "PKTABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 3, "PKCOLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 4, "FKTABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 5, "FKTABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 6, "FKTABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 7, "FKCOLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 8, "KEY_SEQ", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 9, "UPDATE_RULE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 10, "DELETE_RULE", PG_TYPE_INT2, 2);
+ QR_set_field_info(stmt->result, 11, "FK_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 12, "PK_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 13, "TRIGGER_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+#if (ODBCVER >= 0x0300)
+ QR_set_field_info(stmt->result, 14, "DEFERRABILITY", PG_TYPE_INT2, 2);
+#endif /* ODBCVER >= 0x0300 */
+
+ /*
+ * also, things need to think that this statement is finished so the
+ * results can be retrieved.
+ */
+ stmt->status = STMT_FINISHED;
+
+ /* set up the current tuple pointer for SQLFetch */
+ stmt->currTuple = -1;
+ stmt->rowset_start = -1;
+ stmt->current_col = -1;
+
+
+ result = PGAPI_AllocStmt(stmt->hdbc, &htbl_stmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Couldn't allocate statement for PGAPI_ForeignKeys result.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ tbl_stmt = (StatementClass *) htbl_stmt;
+
+ pk_table_needed[0] = '\0';
+ fk_table_needed[0] = '\0';
+
+ make_string(szPkTableName, cbPkTableName, pk_table_needed);
+ make_string(szFkTableName, cbFkTableName, fk_table_needed);
+
+#ifdef MULTIBYTE
+ pkey_text = fkey_text = pkt_text = fkt_text = NULL;
+ pkey_alloced = fkey_alloced = pkt_alloced = fkt_alloced = FALSE;
+ conn = SC_get_conn(stmt);
+#endif /* MULTIBYTE */
+
+ /*
+ * Case #2 -- Get the foreign keys in the specified table (fktab) that
+ * refer to the primary keys of other table(s).
+ */
+ if (fk_table_needed[0] != '\0')
+ {
+ mylog("%s: entering Foreign Key Case #2", func);
+ sprintf(tables_query, "SELECT pt.tgargs, "
+ " pt.tgnargs, "
+ " pt.tgdeferrable, "
+ " pt.tginitdeferred, "
+ " pg_proc.proname, "
+ " pg_proc_1.proname "
+ "FROM pg_class pc, "
+ " pg_proc pg_proc, "
+ " pg_proc pg_proc_1, "
+ " pg_trigger pg_trigger, "
+ " pg_trigger pg_trigger_1, "
+ " pg_proc pp, "
+ " pg_trigger pt "
+ "WHERE pt.tgrelid = pc.oid "
+ "AND pp.oid = pt.tgfoid "
+ "AND pg_trigger.tgconstrrelid = pc.oid "
+ "AND pg_proc.oid = pg_trigger.tgfoid "
+ "AND pg_trigger_1.tgfoid = pg_proc_1.oid "
+ "AND pg_trigger_1.tgconstrrelid = pc.oid "
+ "AND ((pc.relname='%s') "
+ "AND (pp.proname LIKE '%%ins') "
+ "AND (pg_proc.proname LIKE '%%upd') "
+ "AND (pg_proc_1.proname LIKE '%%del') "
+ "AND (pg_trigger.tgrelid=pt.tgconstrrelid) "
+ "AND (pg_trigger.tgconstrname=pt.tgconstrname) "
+ "AND (pg_trigger_1.tgrelid=pt.tgconstrrelid) "
+ "AND (pg_trigger_1.tgconstrname=pt.tgconstrname))",
+ fk_table_needed);
+
+ result = PGAPI_ExecDirect(htbl_stmt, tables_query, strlen(tables_query));
+
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = SC_create_errormsg(htbl_stmt);
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 1, SQL_C_BINARY,
+ trig_args, sizeof(trig_args), NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 2, SQL_C_SHORT,
+ &trig_nargs, 0, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 3, SQL_C_CHAR,
+ trig_deferrable, sizeof(trig_deferrable), NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 4, SQL_C_CHAR,
+ trig_initdeferred, sizeof(trig_initdeferred), NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 5, SQL_C_CHAR,
+ upd_rule, sizeof(upd_rule), NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 6, SQL_C_CHAR,
+ del_rule, sizeof(del_rule), NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_Fetch(htbl_stmt);
+ if (result == SQL_NO_DATA_FOUND)
+ return SQL_SUCCESS;
+
+ if (result != SQL_SUCCESS)
+ {
+ stmt->errormsg = SC_create_errormsg(htbl_stmt);
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ keyresult = PGAPI_AllocStmt(stmt->hdbc, &hpkey_stmt);
+ if ((keyresult != SQL_SUCCESS) && (keyresult != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Couldn't allocate statement for PGAPI_ForeignKeys (pkeys) result.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ keyresult = PGAPI_BindCol(hpkey_stmt, 4, SQL_C_CHAR,
+ pkey, sizeof(pkey), NULL);
+ if (keyresult != SQL_SUCCESS)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Couldn't bindcol for primary keys for PGAPI_ForeignKeys result.";
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hpkey_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ while (result == SQL_SUCCESS)
+ {
+ /* Compute the number of keyparts. */
+ num_keys = (trig_nargs - 4) / 2;
+
+ mylog("Foreign Key Case#2: trig_nargs = %d, num_keys = %d\n", trig_nargs, num_keys);
+
+ pk_table = trig_args;
+
+ /* Get to the PK Table Name */
+ for (k = 0; k < 2; k++)
+ pk_table += strlen(pk_table) + 1;
+
+#ifdef MULTIBYTE
+ fk_table = trig_args + strlen(trig_args) + 1;
+ pkt_text = getClientTableName(conn, pk_table, &pkt_alloced);
+#else
+ pkt_text = pk_table;
+#endif /* MULTIBYTE */
+ /* If there is a pk table specified, then check it. */
+ if (pk_table_needed[0] != '\0')
+ {
+ /* If it doesn't match, then continue */
+ if (strcmp(pkt_text, pk_table_needed))
+ {
+ result = PGAPI_Fetch(htbl_stmt);
+ continue;
+ }
+ }
+
+ keyresult = PGAPI_PrimaryKeys(hpkey_stmt, NULL, 0, NULL, 0, pkt_text, SQL_NTS);
+ if (keyresult != SQL_SUCCESS)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Couldn't get primary keys for PGAPI_ForeignKeys result.";
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(hpkey_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+
+ /* Get to first primary key */
+ pkey_ptr = trig_args;
+ for (i = 0; i < 5; i++)
+ pkey_ptr += strlen(pkey_ptr) + 1;
+
+ for (k = 0; k < num_keys; k++)
+ {
+ /* Check that the key listed is the primary key */
+ keyresult = PGAPI_Fetch(hpkey_stmt);
+ if (keyresult != SQL_SUCCESS)
+ {
+ num_keys = 0;
+ break;
+ }
+#ifdef MULTIBYTE
+ pkey_text = getClientColumnName(conn, pk_table, pkey_ptr, &pkey_alloced);
+#else
+ pkey_text = pkey_ptr;
+#endif /* MULTIBYTE */
+ mylog("%s: pkey_ptr='%s', pkey='%s'\n", func, pkey_text, pkey);
+ if (strcmp(pkey_text, pkey))
+ {
+ num_keys = 0;
+ break;
+ }
+#ifdef MULTIBYTE
+ if (pkey_alloced)
+ free(pkey_text);
+#endif /* MULTIBYTE */
+ /* Get to next primary key */
+ for (k = 0; k < 2; k++)
+ pkey_ptr += strlen(pkey_ptr) + 1;
+
+ }
+
+ /* Set to first fk column */
+ fkey_ptr = trig_args;
+ for (k = 0; k < 4; k++)
+ fkey_ptr += strlen(fkey_ptr) + 1;
+
+ /* Set update and delete actions for foreign keys */
+ if (!strcmp(upd_rule, "RI_FKey_cascade_upd"))
+ upd_rule_type = SQL_CASCADE;
+ else if (!strcmp(upd_rule, "RI_FKey_noaction_upd"))
+ upd_rule_type = SQL_NO_ACTION;
+ else if (!strcmp(upd_rule, "RI_FKey_restrict_upd"))
+ upd_rule_type = SQL_NO_ACTION;
+ else if (!strcmp(upd_rule, "RI_FKey_setdefault_upd"))
+ upd_rule_type = SQL_SET_DEFAULT;
+ else if (!strcmp(upd_rule, "RI_FKey_setnull_upd"))
+ upd_rule_type = SQL_SET_NULL;
+
+ if (!strcmp(upd_rule, "RI_FKey_cascade_del"))
+ del_rule_type = SQL_CASCADE;
+ else if (!strcmp(upd_rule, "RI_FKey_noaction_del"))
+ del_rule_type = SQL_NO_ACTION;
+ else if (!strcmp(upd_rule, "RI_FKey_restrict_del"))
+ del_rule_type = SQL_NO_ACTION;
+ else if (!strcmp(upd_rule, "RI_FKey_setdefault_del"))
+ del_rule_type = SQL_SET_DEFAULT;
+ else if (!strcmp(upd_rule, "RI_FKey_setnull_del"))
+ del_rule_type = SQL_SET_NULL;
+
+#if (ODBCVER >= 0x0300)
+ /* Set deferrability type */
+ if (!strcmp(trig_initdeferred, "y"))
+ defer_type = SQL_INITIALLY_DEFERRED;
+ else if (!strcmp(trig_deferrable, "y"))
+ defer_type = SQL_INITIALLY_IMMEDIATE;
+ else
+ defer_type = SQL_NOT_DEFERRABLE;
+#endif /* ODBCVER >= 0x0300 */
+
+ /* Get to first primary key */
+ pkey_ptr = trig_args;
+ for (i = 0; i < 5; i++)
+ pkey_ptr += strlen(pkey_ptr) + 1;
+
+ for (k = 0; k < num_keys; k++)
+ {
+ row = (TupleNode *) malloc(sizeof(TupleNode) + (result_cols - 1) *sizeof(TupleField));
+
+#ifdef MULTIBYTE
+ pkey_text = getClientColumnName(conn, pk_table, pkey_ptr, &pkey_alloced);
+ fkey_text = getClientColumnName(conn, fk_table, fkey_ptr, &fkey_alloced);
+#else
+ pkey_text = pkey_ptr;
+ fkey_text = fkey_ptr;
+#endif /* MULTIBYTE */
+ mylog("%s: pk_table = '%s', pkey_ptr = '%s'\n", func, pkt_text, pkey_text);
+ set_tuplefield_null(&row->tuple[0]);
+ set_tuplefield_string(&row->tuple[1], "");
+ set_tuplefield_string(&row->tuple[2], pkt_text);
+ set_tuplefield_string(&row->tuple[3], pkey_text);
+
+ mylog("%s: fk_table_needed = '%s', fkey_ptr = '%s'\n", func, fk_table_needed, fkey_text);
+ set_tuplefield_null(&row->tuple[4]);
+ set_tuplefield_string(&row->tuple[5], "");
+ set_tuplefield_string(&row->tuple[6], fk_table_needed);
+ set_tuplefield_string(&row->tuple[7], fkey_text);
+
+ mylog("%s: upd_rule_type = '%i', del_rule_type = '%i'\n, trig_name = '%s'", func, upd_rule_type, del_rule_type, trig_args);
+ set_tuplefield_int2(&row->tuple[8], (Int2) (k + 1));
+ set_tuplefield_int2(&row->tuple[9], (Int2) upd_rule_type);
+ set_tuplefield_int2(&row->tuple[10], (Int2) del_rule_type);
+ set_tuplefield_null(&row->tuple[11]);
+ set_tuplefield_null(&row->tuple[12]);
+ set_tuplefield_string(&row->tuple[13], trig_args);
+#if (ODBCVER >= 0x0300)
+ set_tuplefield_int2(&row->tuple[14], defer_type);
+#endif /* ODBCVER >= 0x0300 */
+
+ QR_add_tuple(stmt->result, row);
+#ifdef MULTIBYTE
+ if (fkey_alloced)
+ free(fkey_text);
+ fkey_alloced = FALSE;
+ if (pkey_alloced)
+ free(pkey_text);
+ pkey_alloced = FALSE;
+#endif /* MULTIBYTE */
+ /* next primary/foreign key */
+ for (i = 0; i < 2; i++)
+ {
+ fkey_ptr += strlen(fkey_ptr) + 1;
+ pkey_ptr += strlen(pkey_ptr) + 1;
+ }
+ }
+#ifdef MULTIBYTE
+ if (pkt_alloced)
+ free(pkt_text);
+ pkt_alloced = FALSE;
+#endif /* MULTIBYTE */
+
+ result = PGAPI_Fetch(htbl_stmt);
+ }
+ PGAPI_FreeStmt(hpkey_stmt, SQL_DROP);
+ }
+
+ /*
+ * Case #1 -- Get the foreign keys in other tables that refer to the
+ * primary key in the specified table (pktab). i.e., Who points to
+ * me?
+ */
+ else if (pk_table_needed[0] != '\0')
+ {
+ sprintf(tables_query, "SELECT pg_trigger.tgargs, "
+ " pg_trigger.tgnargs, "
+ " pg_trigger.tgdeferrable, "
+ " pg_trigger.tginitdeferred, "
+ " pg_proc.proname, "
+ " pg_proc_1.proname "
+ "FROM pg_class pg_class, "
+ " pg_class pg_class_1, "
+ " pg_class pg_class_2, "
+ " pg_proc pg_proc, "
+ " pg_proc pg_proc_1, "
+ " pg_trigger pg_trigger, "
+ " pg_trigger pg_trigger_1, "
+ " pg_trigger pg_trigger_2 "
+ "WHERE pg_trigger.tgconstrrelid = pg_class.oid "
+ " AND pg_trigger.tgrelid = pg_class_1.oid "
+ " AND pg_trigger_1.tgfoid = pg_proc_1.oid "
+ " AND pg_trigger_1.tgconstrrelid = pg_class_1.oid "
+ " AND pg_trigger_2.tgconstrrelid = pg_class_2.oid "
+ " AND pg_trigger_2.tgfoid = pg_proc.oid "
+ " AND pg_class_2.oid = pg_trigger.tgrelid "
+ " AND ("
+ " (pg_class.relname='%s') "
+ " AND (pg_proc.proname Like '%%upd') "
+ " AND (pg_proc_1.proname Like '%%del')"
+ " AND (pg_trigger_1.tgrelid = pg_trigger.tgconstrrelid) "
+ " AND (pg_trigger_2.tgrelid = pg_trigger.tgconstrrelid) "
+ " )",
+ pk_table_needed);
+
+ result = PGAPI_ExecDirect(htbl_stmt, tables_query, strlen(tables_query));
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = SC_create_errormsg(htbl_stmt);
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 1, SQL_C_BINARY,
+ trig_args, sizeof(trig_args), NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 2, SQL_C_SHORT,
+ &trig_nargs, 0, NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 3, SQL_C_CHAR,
+ trig_deferrable, sizeof(trig_deferrable), NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 4, SQL_C_CHAR,
+ trig_initdeferred, sizeof(trig_initdeferred), NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 5, SQL_C_CHAR,
+ upd_rule, sizeof(upd_rule), NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_BindCol(htbl_stmt, 6, SQL_C_CHAR,
+ del_rule, sizeof(del_rule), NULL);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = tbl_stmt->errormsg;
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ result = PGAPI_Fetch(htbl_stmt);
+ if (result == SQL_NO_DATA_FOUND)
+ return SQL_SUCCESS;
+
+ if (result != SQL_SUCCESS)
+ {
+ stmt->errormsg = SC_create_errormsg(htbl_stmt);
+ stmt->errornumber = tbl_stmt->errornumber;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+
+ while (result == SQL_SUCCESS)
+ {
+ /* Calculate the number of key parts */
+ num_keys = (trig_nargs - 4) / 2;;
+
+ /* Handle action (i.e., 'cascade', 'restrict', 'setnull') */
+ if (!strcmp(upd_rule, "RI_FKey_cascade_upd"))
+ upd_rule_type = SQL_CASCADE;
+ else if (!strcmp(upd_rule, "RI_FKey_noaction_upd"))
+ upd_rule_type = SQL_NO_ACTION;
+ else if (!strcmp(upd_rule, "RI_FKey_restrict_upd"))
+ upd_rule_type = SQL_NO_ACTION;
+ else if (!strcmp(upd_rule, "RI_FKey_setdefault_upd"))
+ upd_rule_type = SQL_SET_DEFAULT;
+ else if (!strcmp(upd_rule, "RI_FKey_setnull_upd"))
+ upd_rule_type = SQL_SET_NULL;
+
+ if (!strcmp(upd_rule, "RI_FKey_cascade_del"))
+ del_rule_type = SQL_CASCADE;
+ else if (!strcmp(upd_rule, "RI_FKey_noaction_del"))
+ del_rule_type = SQL_NO_ACTION;
+ else if (!strcmp(upd_rule, "RI_FKey_restrict_del"))
+ del_rule_type = SQL_NO_ACTION;
+ else if (!strcmp(upd_rule, "RI_FKey_setdefault_del"))
+ del_rule_type = SQL_SET_DEFAULT;
+ else if (!strcmp(upd_rule, "RI_FKey_setnull_del"))
+ del_rule_type = SQL_SET_NULL;
+
+#if (ODBCVER >= 0x0300)
+ /* Set deferrability type */
+ if (!strcmp(trig_initdeferred, "y"))
+ defer_type = SQL_INITIALLY_DEFERRED;
+ else if (!strcmp(trig_deferrable, "y"))
+ defer_type = SQL_INITIALLY_IMMEDIATE;
+ else
+ defer_type = SQL_NOT_DEFERRABLE;
+#endif /* ODBCVER >= 0x0300 */
+
+ mylog("Foreign Key Case#1: trig_nargs = %d, num_keys = %d\n", trig_nargs, num_keys);
+
+ /* Get to first primary key */
+ pkey_ptr = trig_args;
+ for (i = 0; i < 5; i++)
+ pkey_ptr += strlen(pkey_ptr) + 1;
+
+ /* Get to first foreign table */
+ fk_table = trig_args;
+ fk_table += strlen(fk_table) + 1;
+#ifdef MULTIBYTE
+ pk_table = fk_table + strlen(fk_table) + 1;
+ fkt_text = getClientTableName(conn, fk_table, &fkt_alloced);
+#else
+ fkt_text = fk_table;
+#endif /* MULTIBYTE */
+
+ /* Get to first foreign key */
+ fkey_ptr = trig_args;
+ for (k = 0; k < 4; k++)
+ fkey_ptr += strlen(fkey_ptr) + 1;
+
+ for (k = 0; k < num_keys; k++)
+ {
+#ifdef MULTIBYTE
+ pkey_text = getClientColumnName(conn, pk_table, pkey_ptr, &pkey_alloced);
+ fkey_text = getClientColumnName(conn, fk_table, fkey_ptr, &fkey_alloced);
+#else
+ pkey_text = pkey_ptr;
+ fkey_text = fkey_ptr;
+#endif /* MULTIBYTE */
+ mylog("pkey_ptr = '%s', fk_table = '%s', fkey_ptr = '%s'\n", pkey_text, fkt_text, fkey_text);
+
+ row = (TupleNode *) malloc(sizeof(TupleNode) + (result_cols - 1) *sizeof(TupleField));
+
+ mylog("pk_table_needed = '%s', pkey_ptr = '%s'\n", pk_table_needed, pkey_text);
+ set_tuplefield_null(&row->tuple[0]);
+ set_tuplefield_string(&row->tuple[1], "");
+ set_tuplefield_string(&row->tuple[2], pk_table_needed);
+ set_tuplefield_string(&row->tuple[3], pkey_text);
+
+ mylog("fk_table = '%s', fkey_ptr = '%s'\n", fkt_text, fkey_text);
+ set_tuplefield_null(&row->tuple[4]);
+ set_tuplefield_string(&row->tuple[5], "");
+ set_tuplefield_string(&row->tuple[6], fkt_text);
+ set_tuplefield_string(&row->tuple[7], fkey_text);
+
+ set_tuplefield_int2(&row->tuple[8], (Int2) (k + 1));
+
+ mylog("upd_rule = %d, del_rule= %d", upd_rule_type, del_rule_type);
+ set_nullfield_int2(&row->tuple[9], (Int2) upd_rule_type);
+ set_nullfield_int2(&row->tuple[10], (Int2) del_rule_type);
+
+ set_tuplefield_null(&row->tuple[11]);
+ set_tuplefield_null(&row->tuple[12]);
+
+ set_tuplefield_string(&row->tuple[13], trig_args);
+
+#if (ODBCVER >= 0x0300)
+ mylog("defer_type = '%s'", defer_type);
+ set_tuplefield_int2(&row->tuple[14], defer_type);
+#endif /* ODBCVER >= 0x0300 */
+
+ QR_add_tuple(stmt->result, row);
+#ifdef MULTIBYTE
+ if (pkey_alloced)
+ free(pkey_text);
+ pkey_alloced = FALSE;
+ if (fkey_alloced)
+ free(fkey_text);
+ fkey_alloced = FALSE;
+#endif /* MULTIBYTE */
+
+ /* next primary/foreign key */
+ for (j = 0; j < 2; j++)
+ {
+ pkey_ptr += strlen(pkey_ptr) + 1;
+ fkey_ptr += strlen(fkey_ptr) + 1;
+ }
+ }
+#ifdef MULTIBYTE
+ if (fkt_alloced)
+ free(fkt_text);
+ fkt_alloced = FALSE;
+#endif /* MULTIBYTE */
+ result = PGAPI_Fetch(htbl_stmt);
+ }
+ }
+ else
+ {
+ stmt->errormsg = "No tables specified to PGAPI_ForeignKeys.";
+ stmt->errornumber = STMT_INTERNAL_ERROR;
+ SC_log_error(func, "", stmt);
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+ return SQL_ERROR;
+ }
+#ifdef MULTIBYTE
+ if (pkt_alloced)
+ free(pkt_text);
+ if (pkey_alloced)
+ free(pkey_text);
+ if (fkt_alloced)
+ free(fkt_text);
+ if (fkey_alloced)
+ free(fkey_text);
+#endif /* MULTIBYTE */
+
+ PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
+
+ mylog("PGAPI_ForeignKeys(): EXIT, stmt=%u\n", stmt);
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_ProcedureColumns(
+ HSTMT hstmt,
+ UCHAR FAR * szProcQualifier,
+ SWORD cbProcQualifier,
+ UCHAR FAR * szProcOwner,
+ SWORD cbProcOwner,
+ UCHAR FAR * szProcName,
+ SWORD cbProcName,
+ UCHAR FAR * szColumnName,
+ SWORD cbColumnName)
+{
+ static char *func = "PGAPI_ProcedureColumns";
+
+ mylog("%s: entering...\n", func);
+
+ SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
+ return SQL_ERROR;
+}
+
+
+RETCODE SQL_API
+PGAPI_Procedures(
+ HSTMT hstmt,
+ UCHAR FAR * szProcQualifier,
+ SWORD cbProcQualifier,
+ UCHAR FAR * szProcOwner,
+ SWORD cbProcOwner,
+ UCHAR FAR * szProcName,
+ SWORD cbProcName)
+{
+ static char *func = "PGAPI_Procedures";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ ConnectionClass *conn = SC_get_conn(stmt);
+ char proc_query[INFO_INQUIRY_LEN];
+ QResultClass *res;
+
+ mylog("%s: entering...\n", func);
+
+ if (PG_VERSION_LT(conn, 6.5))
+ {
+ stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+ stmt->errormsg = "Version is too old";
+ SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
+ return SQL_ERROR;
+ }
+ if (!SC_recycle_statement(stmt))
+ return SQL_ERROR;
+
+ /*
+ * The following seems the simplest implementation
+ */
+ strcpy(proc_query, "select '' as " "PROCEDURE_CAT" ", '' as " "PROCEDURE_SCHEM" ","
+ " proname as " "PROCEDURE_NAME" ", '' as " "NUM_INPUT_PARAMS" ","
+ " '' as " "NUM_OUTPUT_PARAMS" ", '' as " "NUM_RESULT_SETS" ","
+ " '' as " "REMARKS" ","
+ " case when prorettype =0 then 1::int2 else 2::int2 end as " "PROCEDURE_TYPE" " from pg_proc");
+ my_strcat(proc_query, " where proname like '%.*s'", szProcName, cbProcName);
+
+ res = CC_send_query(conn, proc_query, NULL);
+ if (!res || QR_aborted(res))
+ {
+ if (res)
+ QR_Destructor(res);
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "PGAPI_Procedures query error";
+ return SQL_ERROR;
+ }
+ stmt->result = res;
+
+ /*
+ * also, things need to think that this statement is finished so the
+ * results can be retrieved.
+ */
+ stmt->status = STMT_FINISHED;
+ extend_bindings(stmt, 8);
+ /* set up the current tuple pointer for SQLFetch */
+ stmt->currTuple = -1;
+ stmt->rowset_start = -1;
+ stmt->current_col = -1;
+
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_TablePrivileges(
+ HSTMT hstmt,
+ UCHAR FAR * szTableQualifier,
+ SWORD cbTableQualifier,
+ UCHAR FAR * szTableOwner,
+ SWORD cbTableOwner,
+ UCHAR FAR * szTableName,
+ SWORD cbTableName)
+{
+ StatementClass *stmt = (StatementClass *) hstmt;
+ static char *func = "PGAPI_TablePrivileges";
+ Int2 result_cols;
+
+ mylog("%s: entering...\n", func);
+
+ /*
+ * a statement is actually executed, so we'll have to do this
+ * ourselves.
+ */
+ result_cols = 7;
+ extend_bindings(stmt, result_cols);
+
+ /* set the field names */
+ QR_set_num_fields(stmt->result, result_cols);
+ QR_set_field_info(stmt->result, 0, "TABLE_CAT", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 1, "TABLE_SCHEM", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 3, "GRANTOR", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 4, "GRANTEE", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 5, "PRIVILEGE", PG_TYPE_TEXT, MAX_INFO_STRING);
+ QR_set_field_info(stmt->result, 6, "IS_GRANTABLE", PG_TYPE_TEXT, MAX_INFO_STRING);
+
+ SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
+ return SQL_ERROR;
+}
--- /dev/null
+#ifndef _IODBC_H
+#define _IODBC_H
+
+#if !defined(WIN32) && !defined(WIN32_SYSTEM)
+#define _UNIX_
+
+#include <stdlib.h>
+#include <sys/types.h>
+
+#define MEM_ALLOC(size) (malloc((size_t)(size)))
+#define MEM_FREE(ptr) \
+do { \
+ if(ptr) \
+ free(ptr); \
+} while (0)
+
+#define STRCPY(t, s) (strcpy((char*)(t), (char*)(s)))
+#define STRNCPY(t,s,n) (strncpy((char*)(t), (char*)(s), (size_t)(n)))
+#define STRCAT(t, s) (strcat((char*)(t), (char*)(s)))
+#define STRNCAT(t,s,n) (strncat((char*)(t), (char*)(s), (size_t)(n)))
+#define STREQ(a, b) (strcmp((char*)(a), (char*)(b)) == 0)
+#define STRLEN(str) ((str)? strlen((char*)(str)):0)
+
+#define EXPORT
+#define CALLBACK
+#define FAR
+
+typedef signed short SSHOR;
+typedef short WORD;
+typedef long DWORD;
+
+typedef WORD WPARAM;
+typedef DWORD LPARAM;
+typedef void *HWND;
+typedef int BOOL;
+#endif /* _UNIX_ */
+
+#if defined(WIN32) || defined(WIN32_SYSTEM)
+
+#include <windows.h>
+#include <windowsx.h>
+
+#ifdef _MSVC_
+#define MEM_ALLOC(size) (fmalloc((size_t)(size)))
+#define MEM_FREE(ptr) ((ptr)? ffree((PTR)(ptr)):0))
+#define STRCPY(t, s) (fstrcpy((char FAR*)(t), (char FAR*)(s)))
+#define STRNCPY(t,s,n) (fstrncpy((char FAR*)(t), (char FAR*)(s), (size_t)(n)))
+#define STRLEN(str) ((str)? fstrlen((char FAR*)(str)):0)
+#define STREQ(a, b) (fstrcmp((char FAR*)(a), (char FAR*)(b) == 0)
+#endif
+
+#ifdef _BORLAND_
+#define MEM_ALLOC(size) (farmalloc((unsigned long)(size))
+#define MEM_FREE(ptr) ((ptr)? farfree((void far*)(ptr)):0)
+#define STRCPY(t, s) (_fstrcpy((char FAR*)(t), (char FAR*)(s)))
+#define STRNCPY(t,s,n) (_fstrncpy((char FAR*)(t), (char FAR*)(s), (size_t)(n)))
+#define STRLEN(str) ((str)? _fstrlen((char FAR*)(str)):0)
+#define STREQ(a, b) (_fstrcmp((char FAR*)(a), (char FAR*)(b) == 0)
+#endif
+#endif /* WIN32 */
+
+#define SYSERR (-1)
+
+#ifndef NULL
+#define NULL ((void FAR*)0UL)
+#endif
+
+#endif
--- /dev/null
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+
+ Version 2, June 1991
+
+
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+ 675 Mass Ave, Cambridge, MA 02139, USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+
+ of this license document, but changing it is not allowed.
+
+
+
+[This is the first released version of the library GPL. It is
+
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+
+
+ Preamble
+
+
+
+ The licenses for most software are designed to take away your
+
+freedom to share and change it. By contrast, the GNU General Public
+
+Licenses are intended to guarantee your freedom to share and change
+
+free software--to make sure the software is free for all its users.
+
+
+
+ This license, the Library General Public License, applies to some
+
+specially designated Free Software Foundation software, and to any
+
+other libraries whose authors decide to use it. You can use it for
+
+your libraries, too.
+
+
+
+ When we speak of free software, we are referring to freedom, not
+
+price. Our General Public Licenses are designed to make sure that you
+
+have the freedom to distribute copies of free software (and charge for
+
+this service if you wish), that you receive source code or can get it
+
+if you want it, that you can change the software or use pieces of it
+
+in new free programs; and that you know you can do these things.
+
+
+
+ To protect your rights, we need to make restrictions that forbid
+
+anyone to deny you these rights or to ask you to surrender the rights.
+
+These restrictions translate to certain responsibilities for you if
+
+you distribute copies of the library, or if you modify it.
+
+
+
+ For example, if you distribute copies of the library, whether gratis
+
+or for a fee, you must give the recipients all the rights that we gave
+
+you. You must make sure that they, too, receive or can get the source
+
+code. If you link a program with the library, you must provide
+
+complete object files to the recipients so that they can relink them
+
+with the library, after making changes to the library and recompiling
+
+it. And you must show them these terms so they know their rights.
+
+
+
+ Our method of protecting your rights has two steps: (1) copyright
+
+the library, and (2) offer you this license which gives you legal
+
+permission to copy, distribute and/or modify the library.
+
+
+
+ Also, for each distributor's protection, we want to make certain
+
+that everyone understands that there is no warranty for this free
+
+library. If the library is modified by someone else and passed on, we
+
+want its recipients to know that what they have is not the original
+
+version, so that any problems introduced by others will not reflect on
+
+the original authors' reputations.
+
+\f
+
+ Finally, any free program is threatened constantly by software
+
+patents. We wish to avoid the danger that companies distributing free
+
+software will individually obtain patent licenses, thus in effect
+
+transforming the program into proprietary software. To prevent this,
+
+we have made it clear that any patent must be licensed for everyone's
+
+free use or not licensed at all.
+
+
+
+ Most GNU software, including some libraries, is covered by the ordinary
+
+GNU General Public License, which was designed for utility programs. This
+
+license, the GNU Library General Public License, applies to certain
+
+designated libraries. This license is quite different from the ordinary
+
+one; be sure to read it in full, and don't assume that anything in it is
+
+the same as in the ordinary license.
+
+
+
+ The reason we have a separate public license for some libraries is that
+
+they blur the distinction we usually make between modifying or adding to a
+
+program and simply using it. Linking a program with a library, without
+
+changing the library, is in some sense simply using the library, and is
+
+analogous to running a utility program or application program. However, in
+
+a textual and legal sense, the linked executable is a combined work, a
+
+derivative of the original library, and the ordinary General Public License
+
+treats it as such.
+
+
+
+ Because of this blurred distinction, using the ordinary General
+
+Public License for libraries did not effectively promote software
+
+sharing, because most developers did not use the libraries. We
+
+concluded that weaker conditions might promote sharing better.
+
+
+
+ However, unrestricted linking of non-free programs would deprive the
+
+users of those programs of all benefit from the free status of the
+
+libraries themselves. This Library General Public License is intended to
+
+permit developers of non-free programs to use free libraries, while
+
+preserving your freedom as a user of such programs to change the free
+
+libraries that are incorporated in them. (We have not seen how to achieve
+
+this as regards changes in header files, but we have achieved it as regards
+
+changes in the actual functions of the Library.) The hope is that this
+
+will lead to faster development of free libraries.
+
+
+
+ The precise terms and conditions for copying, distribution and
+
+modification follow. Pay close attention to the difference between a
+
+"work based on the library" and a "work that uses the library". The
+
+former contains code derived from the library, while the latter only
+
+works together with the library.
+
+
+
+ Note that it is possible for a library to be covered by the ordinary
+
+General Public License rather than by this special one.
+
+\f
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+
+
+ 0. This License Agreement applies to any software library which
+
+contains a notice placed by the copyright holder or other authorized
+
+party saying it may be distributed under the terms of this Library
+
+General Public License (also called "this License"). Each licensee is
+
+addressed as "you".
+
+
+
+ A "library" means a collection of software functions and/or data
+
+prepared so as to be conveniently linked with application programs
+
+(which use some of those functions and data) to form executables.
+
+
+
+ The "Library", below, refers to any such software library or work
+
+which has been distributed under these terms. A "work based on the
+
+Library" means either the Library or any derivative work under
+
+copyright law: that is to say, a work containing the Library or a
+
+portion of it, either verbatim or with modifications and/or translated
+
+straightforwardly into another language. (Hereinafter, translation is
+
+included without limitation in the term "modification".)
+
+
+
+ "Source code" for a work means the preferred form of the work for
+
+making modifications to it. For a library, complete source code means
+
+all the source code for all modules it contains, plus any associated
+
+interface definition files, plus the scripts used to control compilation
+
+and installation of the library.
+
+
+
+ Activities other than copying, distribution and modification are not
+
+covered by this License; they are outside its scope. The act of
+
+running a program using the Library is not restricted, and output from
+
+such a program is covered only if its contents constitute a work based
+
+on the Library (independent of the use of the Library in a tool for
+
+writing it). Whether that is true depends on what the Library does
+
+and what the program that uses the Library does.
+
+
+
+ 1. You may copy and distribute verbatim copies of the Library's
+
+complete source code as you receive it, in any medium, provided that
+
+you conspicuously and appropriately publish on each copy an
+
+appropriate copyright notice and disclaimer of warranty; keep intact
+
+all the notices that refer to this License and to the absence of any
+
+warranty; and distribute a copy of this License along with the
+
+Library.
+
+
+
+ You may charge a fee for the physical act of transferring a copy,
+
+and you may at your option offer warranty protection in exchange for a
+
+fee.
+
+\f
+
+ 2. You may modify your copy or copies of the Library or any portion
+
+of it, thus forming a work based on the Library, and copy and
+
+distribute such modifications or work under the terms of Section 1
+
+above, provided that you also meet all of these conditions:
+
+
+
+ a) The modified work must itself be a software library.
+
+
+
+ b) You must cause the files modified to carry prominent notices
+
+ stating that you changed the files and the date of any change.
+
+
+
+ c) You must cause the whole of the work to be licensed at no
+
+ charge to all third parties under the terms of this License.
+
+
+
+ d) If a facility in the modified Library refers to a function or a
+
+ table of data to be supplied by an application program that uses
+
+ the facility, other than as an argument passed when the facility
+
+ is invoked, then you must make a good faith effort to ensure that,
+
+ in the event an application does not supply such function or
+
+ table, the facility still operates, and performs whatever part of
+
+ its purpose remains meaningful.
+
+
+
+ (For example, a function in a library to compute square roots has
+
+ a purpose that is entirely well-defined independent of the
+
+ application. Therefore, Subsection 2d requires that any
+
+ application-supplied function or table used by this function must
+
+ be optional: if the application does not supply it, the square
+
+ root function must still compute square roots.)
+
+
+
+These requirements apply to the modified work as a whole. If
+
+identifiable sections of that work are not derived from the Library,
+
+and can be reasonably considered independent and separate works in
+
+themselves, then this License, and its terms, do not apply to those
+
+sections when you distribute them as separate works. But when you
+
+distribute the same sections as part of a whole which is a work based
+
+on the Library, the distribution of the whole must be on the terms of
+
+this License, whose permissions for other licensees extend to the
+
+entire whole, and thus to each and every part regardless of who wrote
+
+it.
+
+
+
+Thus, it is not the intent of this section to claim rights or contest
+
+your rights to work written entirely by you; rather, the intent is to
+
+exercise the right to control the distribution of derivative or
+
+collective works based on the Library.
+
+
+
+In addition, mere aggregation of another work not based on the Library
+
+with the Library (or with a work based on the Library) on a volume of
+
+a storage or distribution medium does not bring the other work under
+
+the scope of this License.
+
+
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+
+License instead of this License to a given copy of the Library. To do
+
+this, you must alter all the notices that refer to this License, so
+
+that they refer to the ordinary GNU General Public License, version 2,
+
+instead of to this License. (If a newer version than version 2 of the
+
+ordinary GNU General Public License has appeared, then you can specify
+
+that version instead if you wish.) Do not make any other change in
+
+these notices.
+
+\f
+
+ Once this change is made in a given copy, it is irreversible for
+
+that copy, so the ordinary GNU General Public License applies to all
+
+subsequent copies and derivative works made from that copy.
+
+
+
+ This option is useful when you wish to copy part of the code of
+
+the Library into a program that is not a library.
+
+
+
+ 4. You may copy and distribute the Library (or a portion or
+
+derivative of it, under Section 2) in object code or executable form
+
+under the terms of Sections 1 and 2 above provided that you accompany
+
+it with the complete corresponding machine-readable source code, which
+
+must be distributed under the terms of Sections 1 and 2 above on a
+
+medium customarily used for software interchange.
+
+
+
+ If distribution of object code is made by offering access to copy
+
+from a designated place, then offering equivalent access to copy the
+
+source code from the same place satisfies the requirement to
+
+distribute the source code, even though third parties are not
+
+compelled to copy the source along with the object code.
+
+
+
+ 5. A program that contains no derivative of any portion of the
+
+Library, but is designed to work with the Library by being compiled or
+
+linked with it, is called a "work that uses the Library". Such a
+
+work, in isolation, is not a derivative work of the Library, and
+
+therefore falls outside the scope of this License.
+
+
+
+ However, linking a "work that uses the Library" with the Library
+
+creates an executable that is a derivative of the Library (because it
+
+contains portions of the Library), rather than a "work that uses the
+
+library". The executable is therefore covered by this License.
+
+Section 6 states terms for distribution of such executables.
+
+
+
+ When a "work that uses the Library" uses material from a header file
+
+that is part of the Library, the object code for the work may be a
+
+derivative work of the Library even though the source code is not.
+
+Whether this is true is especially significant if the work can be
+
+linked without the Library, or if the work is itself a library. The
+
+threshold for this to be true is not precisely defined by law.
+
+
+
+ If such an object file uses only numerical parameters, data
+
+structure layouts and accessors, and small macros and small inline
+
+functions (ten lines or less in length), then the use of the object
+
+file is unrestricted, regardless of whether it is legally a derivative
+
+work. (Executables containing this object code plus portions of the
+
+Library will still fall under Section 6.)
+
+
+
+ Otherwise, if the work is a derivative of the Library, you may
+
+distribute the object code for the work under the terms of Section 6.
+
+Any executables containing that work also fall under Section 6,
+
+whether or not they are linked directly with the Library itself.
+
+\f
+
+ 6. As an exception to the Sections above, you may also compile or
+
+link a "work that uses the Library" with the Library to produce a
+
+work containing portions of the Library, and distribute that work
+
+under terms of your choice, provided that the terms permit
+
+modification of the work for the customer's own use and reverse
+
+engineering for debugging such modifications.
+
+
+
+ You must give prominent notice with each copy of the work that the
+
+Library is used in it and that the Library and its use are covered by
+
+this License. You must supply a copy of this License. If the work
+
+during execution displays copyright notices, you must include the
+
+copyright notice for the Library among them, as well as a reference
+
+directing the user to the copy of this License. Also, you must do one
+
+of these things:
+
+
+
+ a) Accompany the work with the complete corresponding
+
+ machine-readable source code for the Library including whatever
+
+ changes were used in the work (which must be distributed under
+
+ Sections 1 and 2 above); and, if the work is an executable linked
+
+ with the Library, with the complete machine-readable "work that
+
+ uses the Library", as object code and/or source code, so that the
+
+ user can modify the Library and then relink to produce a modified
+
+ executable containing the modified Library. (It is understood
+
+ that the user who changes the contents of definitions files in the
+
+ Library will not necessarily be able to recompile the application
+
+ to use the modified definitions.)
+
+
+
+ b) Accompany the work with a written offer, valid for at
+
+ least three years, to give the same user the materials
+
+ specified in Subsection 6a, above, for a charge no more
+
+ than the cost of performing this distribution.
+
+
+
+ c) If distribution of the work is made by offering access to copy
+
+ from a designated place, offer equivalent access to copy the above
+
+ specified materials from the same place.
+
+
+
+ d) Verify that the user has already received a copy of these
+
+ materials or that you have already sent this user a copy.
+
+
+
+ For an executable, the required form of the "work that uses the
+
+Library" must include any data and utility programs needed for
+
+reproducing the executable from it. However, as a special exception,
+
+the source code distributed need not include anything that is normally
+
+distributed (in either source or binary form) with the major
+
+components (compiler, kernel, and so on) of the operating system on
+
+which the executable runs, unless that component itself accompanies
+
+the executable.
+
+
+
+ It may happen that this requirement contradicts the license
+
+restrictions of other proprietary libraries that do not normally
+
+accompany the operating system. Such a contradiction means you cannot
+
+use both them and the Library together in an executable that you
+
+distribute.
+
+\f
+
+ 7. You may place library facilities that are a work based on the
+
+Library side-by-side in a single library together with other library
+
+facilities not covered by this License, and distribute such a combined
+
+library, provided that the separate distribution of the work based on
+
+the Library and of the other library facilities is otherwise
+
+permitted, and provided that you do these two things:
+
+
+
+ a) Accompany the combined library with a copy of the same work
+
+ based on the Library, uncombined with any other library
+
+ facilities. This must be distributed under the terms of the
+
+ Sections above.
+
+
+
+ b) Give prominent notice with the combined library of the fact
+
+ that part of it is a work based on the Library, and explaining
+
+ where to find the accompanying uncombined form of the same work.
+
+
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+
+the Library except as expressly provided under this License. Any
+
+attempt otherwise to copy, modify, sublicense, link with, or
+
+distribute the Library is void, and will automatically terminate your
+
+rights under this License. However, parties who have received copies,
+
+or rights, from you under this License will not have their licenses
+
+terminated so long as such parties remain in full compliance.
+
+
+
+ 9. You are not required to accept this License, since you have not
+
+signed it. However, nothing else grants you permission to modify or
+
+distribute the Library or its derivative works. These actions are
+
+prohibited by law if you do not accept this License. Therefore, by
+
+modifying or distributing the Library (or any work based on the
+
+Library), you indicate your acceptance of this License to do so, and
+
+all its terms and conditions for copying, distributing or modifying
+
+the Library or works based on it.
+
+
+
+ 10. Each time you redistribute the Library (or any work based on the
+
+Library), the recipient automatically receives a license from the
+
+original licensor to copy, distribute, link with or modify the Library
+
+subject to these terms and conditions. You may not impose any further
+
+restrictions on the recipients' exercise of the rights granted herein.
+
+You are not responsible for enforcing compliance by third parties to
+
+this License.
+
+\f
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+
+infringement or for any other reason (not limited to patent issues),
+
+conditions are imposed on you (whether by court order, agreement or
+
+otherwise) that contradict the conditions of this License, they do not
+
+excuse you from the conditions of this License. If you cannot
+
+distribute so as to satisfy simultaneously your obligations under this
+
+License and any other pertinent obligations, then as a consequence you
+
+may not distribute the Library at all. For example, if a patent
+
+license would not permit royalty-free redistribution of the Library by
+
+all those who receive copies directly or indirectly through you, then
+
+the only way you could satisfy both it and this License would be to
+
+refrain entirely from distribution of the Library.
+
+
+
+If any portion of this section is held invalid or unenforceable under any
+
+particular circumstance, the balance of the section is intended to apply,
+
+and the section as a whole is intended to apply in other circumstances.
+
+
+
+It is not the purpose of this section to induce you to infringe any
+
+patents or other property right claims or to contest validity of any
+
+such claims; this section has the sole purpose of protecting the
+
+integrity of the free software distribution system which is
+
+implemented by public license practices. Many people have made
+
+generous contributions to the wide range of software distributed
+
+through that system in reliance on consistent application of that
+
+system; it is up to the author/donor to decide if he or she is willing
+
+to distribute software through any other system and a licensee cannot
+
+impose that choice.
+
+
+
+This section is intended to make thoroughly clear what is believed to
+
+be a consequence of the rest of this License.
+
+
+
+ 12. If the distribution and/or use of the Library is restricted in
+
+certain countries either by patents or by copyrighted interfaces, the
+
+original copyright holder who places the Library under this License may add
+
+an explicit geographical distribution limitation excluding those countries,
+
+so that distribution is permitted only in or among countries not thus
+
+excluded. In such case, this License incorporates the limitation as if
+
+written in the body of this License.
+
+
+
+ 13. The Free Software Foundation may publish revised and/or new
+
+versions of the Library General Public License from time to time.
+
+Such new versions will be similar in spirit to the present version,
+
+but may differ in detail to address new problems or concerns.
+
+
+
+Each version is given a distinguishing version number. If the Library
+
+specifies a version number of this License which applies to it and
+
+"any later version", you have the option of following the terms and
+
+conditions either of that version or of any later version published by
+
+the Free Software Foundation. If the Library does not specify a
+
+license version number, you may choose any version ever published by
+
+the Free Software Foundation.
+
+\f
+
+ 14. If you wish to incorporate parts of the Library into other free
+
+programs whose distribution conditions are incompatible with these,
+
+write to the author to ask for permission. For software which is
+
+copyrighted by the Free Software Foundation, write to the Free
+
+Software Foundation; we sometimes make exceptions for this. Our
+
+decision will be guided by the two goals of preserving the free status
+
+of all derivatives of our free software and of promoting the sharing
+
+and reuse of software generally.
+
+
+
+ NO WARRANTY
+
+
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+
+DAMAGES.
+
+
+
+ END OF TERMS AND CONDITIONS
+
+\f
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+
+
+ If you develop a new library, and you want it to be of the greatest
+
+possible use to the public, we recommend making it free software that
+
+everyone can redistribute and change. You can do so by permitting
+
+redistribution under these terms (or, alternatively, under the terms of the
+
+ordinary General Public License).
+
+
+
+ To apply these terms, attach the following notices to the library. It is
+
+safest to attach them to the start of each source file to most effectively
+
+convey the exclusion of warranty; and each file should have at least the
+
+"copyright" line and a pointer to where the full notice is found.
+
+
+
+ <one line to give the library's name and a brief idea of what it does.>
+
+ Copyright (C) <year> <name of author>
+
+
+
+ This library is free software; you can redistribute it and/or
+
+ modify it under the terms of the GNU Library General Public
+
+ License as published by the Free Software Foundation; either
+
+ version 2 of the License, or (at your option) any later version.
+
+
+
+ This library is distributed in the hope that it will be useful,
+
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+
+ Library General Public License for more details.
+
+
+
+ You should have received a copy of the GNU Library General Public
+
+ License along with this library; if not, write to the Free
+
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+
+
+You should also get your employer (if you work as a programmer) or your
+
+school, if any, to sign a "copyright disclaimer" for the library, if
+
+necessary. Here is a sample; alter the names:
+
+
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+
+
+ <signature of Ty Coon>, 1 April 1990
+
+ Ty Coon, President of Vice
+
+
+
+That's all there is to it!
+
--- /dev/null
+/*--------
+ * Module: lobj.c
+ *
+ * Description: This module contains routines related to manipulating
+ * large objects.
+ *
+ * Classes: none
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *--------
+ */
+
+#include "lobj.h"
+
+#include "connection.h"
+
+
+Oid
+lo_creat(ConnectionClass *conn, int mode)
+{
+ LO_ARG argv[1];
+ int retval,
+ result_len;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = mode;
+
+ if (!CC_send_function(conn, LO_CREAT, &retval, &result_len, 1, argv, 1))
+ return 0; /* invalid oid */
+ else
+ return retval;
+}
+
+
+int
+lo_open(ConnectionClass *conn, int lobjId, int mode)
+{
+ int fd;
+ int result_len;
+ LO_ARG argv[2];
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = lobjId;
+
+ argv[1].isint = 1;
+ argv[1].len = 4;
+ argv[1].u.integer = mode;
+
+ if (!CC_send_function(conn, LO_OPEN, &fd, &result_len, 1, argv, 2))
+ return -1;
+
+ if (fd >= 0 && lo_lseek(conn, fd, 0L, SEEK_SET) < 0)
+ return -1;
+
+ return fd;
+}
+
+
+int
+lo_close(ConnectionClass *conn, int fd)
+{
+ LO_ARG argv[1];
+ int retval,
+ result_len;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ if (!CC_send_function(conn, LO_CLOSE, &retval, &result_len, 1, argv, 1))
+ return -1;
+ else
+ return retval;
+}
+
+
+int
+lo_read(ConnectionClass *conn, int fd, char *buf, int len)
+{
+ LO_ARG argv[2];
+ int result_len;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ argv[1].isint = 1;
+ argv[1].len = 4;
+ argv[1].u.integer = len;
+
+ if (!CC_send_function(conn, LO_READ, (int *) buf, &result_len, 0, argv, 2))
+ return -1;
+ else
+ return result_len;
+}
+
+
+int
+lo_write(ConnectionClass *conn, int fd, char *buf, int len)
+{
+ LO_ARG argv[2];
+ int retval,
+ result_len;
+
+ if (len <= 0)
+ return 0;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ argv[1].isint = 0;
+ argv[1].len = len;
+ argv[1].u.ptr = (char *) buf;
+
+ if (!CC_send_function(conn, LO_WRITE, &retval, &result_len, 1, argv, 2))
+ return -1;
+ else
+ return retval;
+}
+
+
+int
+lo_lseek(ConnectionClass *conn, int fd, int offset, int whence)
+{
+ LO_ARG argv[3];
+ int retval,
+ result_len;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ argv[1].isint = 1;
+ argv[1].len = 4;
+ argv[1].u.integer = offset;
+
+ argv[2].isint = 1;
+ argv[2].len = 4;
+ argv[2].u.integer = whence;
+
+ if (!CC_send_function(conn, LO_LSEEK, &retval, &result_len, 1, argv, 3))
+ return -1;
+ else
+ return retval;
+}
+
+
+int
+lo_tell(ConnectionClass *conn, int fd)
+{
+ LO_ARG argv[1];
+ int retval,
+ result_len;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ if (!CC_send_function(conn, LO_TELL, &retval, &result_len, 1, argv, 1))
+ return -1;
+ else
+ return retval;
+}
+
+
+int
+lo_unlink(ConnectionClass *conn, Oid lobjId)
+{
+ LO_ARG argv[1];
+ int retval,
+ result_len;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = lobjId;
+
+ if (!CC_send_function(conn, LO_UNLINK, &retval, &result_len, 1, argv, 1))
+ return -1;
+ else
+ return retval;
+}
--- /dev/null
+/* File: lobj.h
+ *
+ * Description: See "lobj.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __LOBJ_H__
+#define __LOBJ_H__
+
+
+#include "psqlodbc.h"
+
+struct lo_arg
+{
+ int isint;
+ int len;
+ union
+ {
+ int integer;
+ char *ptr;
+ } u;
+};
+
+#define LO_CREAT 957
+#define LO_OPEN 952
+#define LO_CLOSE 953
+#define LO_READ 954
+#define LO_WRITE 955
+#define LO_LSEEK 956
+#define LO_TELL 958
+#define LO_UNLINK 964
+
+#define INV_WRITE 0x00020000
+#define INV_READ 0x00040000
+
+Oid lo_creat(ConnectionClass *conn, int mode);
+int lo_open(ConnectionClass *conn, int lobjId, int mode);
+int lo_close(ConnectionClass *conn, int fd);
+int lo_read(ConnectionClass *conn, int fd, char *buf, int len);
+int lo_write(ConnectionClass *conn, int fd, char *buf, int len);
+int lo_lseek(ConnectionClass *conn, int fd, int offset, int len);
+int lo_tell(ConnectionClass *conn, int fd);
+int lo_unlink(ConnectionClass *conn, Oid lobjId);
+
+#endif
--- /dev/null
+/*
+ * md5.c
+ *
+ * Implements the MD5 Message-Digest Algorithm as specified in
+ * RFC 1321. This implementation is a simple one, in that it
+ * needs every input byte to be buffered before doing any
+ * calculations. I do not expect this file to be used for
+ * general purpose MD5'ing of large amounts of data, only for
+ * generating hashed passwords from limited input.
+ *
+ * Sverre H. Huseby <sverrehu@online.no>
+ *
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/odbc/windev/Attic/md5.c,v 1.1 2002/01/11 02:50:01 inoue Exp $
+ */
+
+
+/*
+ * NOTE:
+ *
+ * There are two copies of this file, one in backend/libpq and another
+ * in interfaces/odbc. They should be identical. This is done so ODBC
+ * can be compiled stand-alone.
+ */
+
+#ifndef MD5_ODBC
+#include "postgres.h"
+#include "libpq/crypt.h"
+#else
+#include "md5.h"
+#endif
+
+#ifdef FRONTEND
+#undef palloc
+#define palloc malloc
+#undef pfree
+#define pfree free
+#endif
+
+
+/*
+ * PRIVATE FUNCTIONS
+ */
+
+
+/*
+ * The returned array is allocated using malloc. the caller should free it
+ * when it is no longer needed.
+ */
+static uint8 *
+createPaddedCopyWithLength(uint8 *b, uint32 *l)
+{
+ uint8 *ret;
+ uint32 q;
+ uint32 len,
+ newLen448;
+ uint32 len_high,
+ len_low; /* 64-bit value split into 32-bit sections */
+
+ len = ((b == NULL) ? 0 : *l);
+ newLen448 = len + 64 - (len % 64) - 8;
+ if (newLen448 <= len)
+ newLen448 += 64;
+
+ *l = newLen448 + 8;
+ if ((ret = (uint8 *) malloc(sizeof(uint8) * *l)) == NULL)
+ return NULL;
+
+ if (b != NULL)
+ memcpy(ret, b, sizeof(uint8) * len);
+
+ /* pad */
+ ret[len] = 0x80;
+ for (q = len + 1; q < newLen448; q++)
+ ret[q] = 0x00;
+
+ /* append length as a 64 bit bitcount */
+ len_low = len;
+ /* split into two 32-bit values */
+ /* we only look at the bottom 32-bits */
+ len_high = len >> 29;
+ len_low <<= 3;
+ q = newLen448;
+ ret[q++] = (len_low & 0xff);
+ len_low >>= 8;
+ ret[q++] = (len_low & 0xff);
+ len_low >>= 8;
+ ret[q++] = (len_low & 0xff);
+ len_low >>= 8;
+ ret[q++] = (len_low & 0xff);
+ ret[q++] = (len_high & 0xff);
+ len_high >>= 8;
+ ret[q++] = (len_high & 0xff);
+ len_high >>= 8;
+ ret[q++] = (len_high & 0xff);
+ len_high >>= 8;
+ ret[q] = (len_high & 0xff);
+
+ return ret;
+}
+
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define ROT_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+static void
+doTheRounds(uint32 X[16], uint32 state[4])
+{
+ uint32 a,
+ b,
+ c,
+ d;
+
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+
+ /* round 1 */
+ a = b + ROT_LEFT((a + F(b, c, d) + X[0] + 0xd76aa478), 7); /* 1 */
+ d = a + ROT_LEFT((d + F(a, b, c) + X[1] + 0xe8c7b756), 12); /* 2 */
+ c = d + ROT_LEFT((c + F(d, a, b) + X[2] + 0x242070db), 17); /* 3 */
+ b = c + ROT_LEFT((b + F(c, d, a) + X[3] + 0xc1bdceee), 22); /* 4 */
+ a = b + ROT_LEFT((a + F(b, c, d) + X[4] + 0xf57c0faf), 7); /* 5 */
+ d = a + ROT_LEFT((d + F(a, b, c) + X[5] + 0x4787c62a), 12); /* 6 */
+ c = d + ROT_LEFT((c + F(d, a, b) + X[6] + 0xa8304613), 17); /* 7 */
+ b = c + ROT_LEFT((b + F(c, d, a) + X[7] + 0xfd469501), 22); /* 8 */
+ a = b + ROT_LEFT((a + F(b, c, d) + X[8] + 0x698098d8), 7); /* 9 */
+ d = a + ROT_LEFT((d + F(a, b, c) + X[9] + 0x8b44f7af), 12); /* 10 */
+ c = d + ROT_LEFT((c + F(d, a, b) + X[10] + 0xffff5bb1), 17); /* 11 */
+ b = c + ROT_LEFT((b + F(c, d, a) + X[11] + 0x895cd7be), 22); /* 12 */
+ a = b + ROT_LEFT((a + F(b, c, d) + X[12] + 0x6b901122), 7); /* 13 */
+ d = a + ROT_LEFT((d + F(a, b, c) + X[13] + 0xfd987193), 12); /* 14 */
+ c = d + ROT_LEFT((c + F(d, a, b) + X[14] + 0xa679438e), 17); /* 15 */
+ b = c + ROT_LEFT((b + F(c, d, a) + X[15] + 0x49b40821), 22); /* 16 */
+
+ /* round 2 */
+ a = b + ROT_LEFT((a + G(b, c, d) + X[1] + 0xf61e2562), 5); /* 17 */
+ d = a + ROT_LEFT((d + G(a, b, c) + X[6] + 0xc040b340), 9); /* 18 */
+ c = d + ROT_LEFT((c + G(d, a, b) + X[11] + 0x265e5a51), 14); /* 19 */
+ b = c + ROT_LEFT((b + G(c, d, a) + X[0] + 0xe9b6c7aa), 20); /* 20 */
+ a = b + ROT_LEFT((a + G(b, c, d) + X[5] + 0xd62f105d), 5); /* 21 */
+ d = a + ROT_LEFT((d + G(a, b, c) + X[10] + 0x02441453), 9); /* 22 */
+ c = d + ROT_LEFT((c + G(d, a, b) + X[15] + 0xd8a1e681), 14); /* 23 */
+ b = c + ROT_LEFT((b + G(c, d, a) + X[4] + 0xe7d3fbc8), 20); /* 24 */
+ a = b + ROT_LEFT((a + G(b, c, d) + X[9] + 0x21e1cde6), 5); /* 25 */
+ d = a + ROT_LEFT((d + G(a, b, c) + X[14] + 0xc33707d6), 9); /* 26 */
+ c = d + ROT_LEFT((c + G(d, a, b) + X[3] + 0xf4d50d87), 14); /* 27 */
+ b = c + ROT_LEFT((b + G(c, d, a) + X[8] + 0x455a14ed), 20); /* 28 */
+ a = b + ROT_LEFT((a + G(b, c, d) + X[13] + 0xa9e3e905), 5); /* 29 */
+ d = a + ROT_LEFT((d + G(a, b, c) + X[2] + 0xfcefa3f8), 9); /* 30 */
+ c = d + ROT_LEFT((c + G(d, a, b) + X[7] + 0x676f02d9), 14); /* 31 */
+ b = c + ROT_LEFT((b + G(c, d, a) + X[12] + 0x8d2a4c8a), 20); /* 32 */
+
+ /* round 3 */
+ a = b + ROT_LEFT((a + H(b, c, d) + X[5] + 0xfffa3942), 4); /* 33 */
+ d = a + ROT_LEFT((d + H(a, b, c) + X[8] + 0x8771f681), 11); /* 34 */
+ c = d + ROT_LEFT((c + H(d, a, b) + X[11] + 0x6d9d6122), 16); /* 35 */
+ b = c + ROT_LEFT((b + H(c, d, a) + X[14] + 0xfde5380c), 23); /* 36 */
+ a = b + ROT_LEFT((a + H(b, c, d) + X[1] + 0xa4beea44), 4); /* 37 */
+ d = a + ROT_LEFT((d + H(a, b, c) + X[4] + 0x4bdecfa9), 11); /* 38 */
+ c = d + ROT_LEFT((c + H(d, a, b) + X[7] + 0xf6bb4b60), 16); /* 39 */
+ b = c + ROT_LEFT((b + H(c, d, a) + X[10] + 0xbebfbc70), 23); /* 40 */
+ a = b + ROT_LEFT((a + H(b, c, d) + X[13] + 0x289b7ec6), 4); /* 41 */
+ d = a + ROT_LEFT((d + H(a, b, c) + X[0] + 0xeaa127fa), 11); /* 42 */
+ c = d + ROT_LEFT((c + H(d, a, b) + X[3] + 0xd4ef3085), 16); /* 43 */
+ b = c + ROT_LEFT((b + H(c, d, a) + X[6] + 0x04881d05), 23); /* 44 */
+ a = b + ROT_LEFT((a + H(b, c, d) + X[9] + 0xd9d4d039), 4); /* 45 */
+ d = a + ROT_LEFT((d + H(a, b, c) + X[12] + 0xe6db99e5), 11); /* 46 */
+ c = d + ROT_LEFT((c + H(d, a, b) + X[15] + 0x1fa27cf8), 16); /* 47 */
+ b = c + ROT_LEFT((b + H(c, d, a) + X[2] + 0xc4ac5665), 23); /* 48 */
+
+ /* round 4 */
+ a = b + ROT_LEFT((a + I(b, c, d) + X[0] + 0xf4292244), 6); /* 49 */
+ d = a + ROT_LEFT((d + I(a, b, c) + X[7] + 0x432aff97), 10); /* 50 */
+ c = d + ROT_LEFT((c + I(d, a, b) + X[14] + 0xab9423a7), 15); /* 51 */
+ b = c + ROT_LEFT((b + I(c, d, a) + X[5] + 0xfc93a039), 21); /* 52 */
+ a = b + ROT_LEFT((a + I(b, c, d) + X[12] + 0x655b59c3), 6); /* 53 */
+ d = a + ROT_LEFT((d + I(a, b, c) + X[3] + 0x8f0ccc92), 10); /* 54 */
+ c = d + ROT_LEFT((c + I(d, a, b) + X[10] + 0xffeff47d), 15); /* 55 */
+ b = c + ROT_LEFT((b + I(c, d, a) + X[1] + 0x85845dd1), 21); /* 56 */
+ a = b + ROT_LEFT((a + I(b, c, d) + X[8] + 0x6fa87e4f), 6); /* 57 */
+ d = a + ROT_LEFT((d + I(a, b, c) + X[15] + 0xfe2ce6e0), 10); /* 58 */
+ c = d + ROT_LEFT((c + I(d, a, b) + X[6] + 0xa3014314), 15); /* 59 */
+ b = c + ROT_LEFT((b + I(c, d, a) + X[13] + 0x4e0811a1), 21); /* 60 */
+ a = b + ROT_LEFT((a + I(b, c, d) + X[4] + 0xf7537e82), 6); /* 61 */
+ d = a + ROT_LEFT((d + I(a, b, c) + X[11] + 0xbd3af235), 10); /* 62 */
+ c = d + ROT_LEFT((c + I(d, a, b) + X[2] + 0x2ad7d2bb), 15); /* 63 */
+ b = c + ROT_LEFT((b + I(c, d, a) + X[9] + 0xeb86d391), 21); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+}
+
+static int
+calculateDigestFromBuffer(uint8 *b, uint32 len, uint8 sum[16])
+{
+ register uint32 i,
+ j,
+ k,
+ newI;
+ uint32 l;
+ uint8 *input;
+ register uint32 *wbp;
+ uint32 workBuff[16],
+ state[4];
+
+ l = len;
+
+ state[0] = 0x67452301;
+ state[1] = 0xEFCDAB89;
+ state[2] = 0x98BADCFE;
+ state[3] = 0x10325476;
+
+ if ((input = createPaddedCopyWithLength(b, &l)) == NULL)
+ return 0;
+
+ for (i = 0;;)
+ {
+ if ((newI = i + 16 * 4) > l)
+ break;
+ k = i + 3;
+ for (j = 0; j < 16; j++)
+ {
+ wbp = (workBuff + j);
+ *wbp = input[k--];
+ *wbp <<= 8;
+ *wbp |= input[k--];
+ *wbp <<= 8;
+ *wbp |= input[k--];
+ *wbp <<= 8;
+ *wbp |= input[k];
+ k += 7;
+ }
+ doTheRounds(workBuff, state);
+ i = newI;
+ }
+ free(input);
+
+ j = 0;
+ for (i = 0; i < 4; i++)
+ {
+ k = state[i];
+ sum[j++] = (k & 0xff);
+ k >>= 8;
+ sum[j++] = (k & 0xff);
+ k >>= 8;
+ sum[j++] = (k & 0xff);
+ k >>= 8;
+ sum[j++] = (k & 0xff);
+ }
+ return 1;
+}
+
+static void
+bytesToHex(uint8 b[16], char *s)
+{
+ static char *hex = "0123456789abcdef";
+ int q,
+ w;
+
+ for (q = 0, w = 0; q < 16; q++)
+ {
+ s[w++] = hex[(b[q] >> 4) & 0x0F];
+ s[w++] = hex[b[q] & 0x0F];
+ }
+ s[w] = '\0';
+}
+
+/*
+ * PUBLIC FUNCTIONS
+ */
+
+/*
+ * md5_hash
+ *
+ * Calculates the MD5 sum of the bytes in a buffer.
+ *
+ * SYNOPSIS #include "crypt.h"
+ * int md5_hash(const void *buff, size_t len, char *hexsum)
+ *
+ * INPUT buff the buffer containing the bytes that you want
+ * the MD5 sum of.
+ * len number of bytes in the buffer.
+ *
+ * OUTPUT hexsum the MD5 sum as a '\0'-terminated string of
+ * hexadecimal digits. an MD5 sum is 16 bytes long.
+ * each byte is represented by two heaxadecimal
+ * characters. you thus need to provide an array
+ * of 33 characters, including the trailing '\0'.
+ *
+ * RETURNS 0 on failure (out of memory for internal buffers) or
+ * non-zero on success.
+ *
+ * STANDARDS MD5 is described in RFC 1321.
+ *
+ * AUTHOR Sverre H. Huseby <sverrehu@online.no>
+ *
+ */
+bool
+md5_hash(const void *buff, size_t len, char *hexsum)
+{
+ uint8 sum[16];
+
+ if (!calculateDigestFromBuffer((uint8 *) buff, len, sum))
+ return false;
+
+ bytesToHex(sum, hexsum);
+ return true;
+}
+
+
+
+/*
+ * Computes MD5 checksum of "passwd" (a null-terminated string) followed
+ * by "salt" (which need not be null-terminated).
+ *
+ * Output format is "md5" followed by a 32-hex-digit MD5 checksum.
+ * Hence, the output buffer "buf" must be at least 36 bytes long.
+ *
+ * Returns TRUE if okay, FALSE on error (out of memory).
+ */
+bool
+EncryptMD5(const char *passwd, const char *salt, size_t salt_len,
+ char *buf)
+{
+ size_t passwd_len = strlen(passwd);
+ char *crypt_buf = palloc(passwd_len + salt_len);
+ bool ret;
+
+ /*
+ * Place salt at the end because it may be known by users trying to
+ * crack the MD5 output.
+ */
+ strcpy(crypt_buf, passwd);
+ memcpy(crypt_buf + passwd_len, salt, salt_len);
+
+ strcpy(buf, "md5");
+ ret = md5_hash(crypt_buf, passwd_len + salt_len, buf + 3);
+
+ pfree(crypt_buf);
+
+ return ret;
+}
--- /dev/null
+/* File: md5.h
+ *
+ * Description: See "md5.h"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __MD5_H__
+#define __MD5_H__
+
+#include "psqlodbc.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define MD5_PASSWD_LEN 35
+
+/* From c.h */
+#ifndef __BEOS__
+
+#ifndef __cplusplus
+
+#ifndef bool
+typedef char bool;
+#endif
+
+#ifndef true
+#define true ((bool) 1)
+#endif
+
+#ifndef false
+#define false ((bool) 0)
+#endif
+#endif /* not C++ */
+#endif /* __BEOS__ */
+
+/* Also defined in include/c.h */
+#ifndef HAVE_UINT8
+typedef unsigned char uint8; /* == 8 bits */
+typedef unsigned short uint16; /* == 16 bits */
+typedef unsigned int uint32; /* == 32 bits */
+#endif /* not HAVE_UINT8 */
+
+extern bool md5_hash(const void *buff, size_t len, char *hexsum);
+extern bool EncryptMD5(const char *passwd, const char *salt,
+ size_t salt_len, char *buf);
+
+#endif
--- /dev/null
+/*-------
+ * Module: misc.c
+ *
+ * Description: This module contains miscellaneous routines
+ * such as for debugging/logging and string functions.
+ *
+ * Classes: n/a
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "psqlodbc.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#ifndef WIN32
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+#include <sys/types.h>
+#include <unistd.h>
+#else
+#include <process.h> /* Byron: is this where Windows keeps def.
+ * of getpid ? */
+#endif
+
+extern GLOBAL_VALUES globals;
+void generate_filename(const char *, const char *, char *);
+
+
+void
+generate_filename(const char *dirname, const char *prefix, char *filename)
+{
+ int pid = 0;
+
+#ifndef WIN32
+ struct passwd *ptr = 0;
+
+ ptr = getpwuid(getuid());
+#endif
+ pid = getpid();
+ if (dirname == 0 || filename == 0)
+ return;
+
+ strcpy(filename, dirname);
+ strcat(filename, DIRSEPARATOR);
+ if (prefix != 0)
+ strcat(filename, prefix);
+#ifndef WIN32
+ strcat(filename, ptr->pw_name);
+#endif
+ sprintf(filename, "%s%u%s", filename, pid, ".log");
+ return;
+}
+
+static int mylog_on = 0,
+ qlog_on = 0;
+void
+logs_on_off(int cnopen, int mylog_onoff, int qlog_onoff)
+{
+ static int mylog_on_count = 0,
+ mylog_off_count = 0,
+ qlog_on_count = 0,
+ qlog_off_count = 0;
+
+ if (mylog_onoff)
+ mylog_on_count += cnopen;
+ else
+ mylog_off_count += cnopen;
+ if (mylog_on_count > 0)
+ mylog_on = 1;
+ else if (mylog_off_count > 0)
+ mylog_on = 0;
+ else
+ mylog_on = globals.debug;
+ if (qlog_onoff)
+ qlog_on_count += cnopen;
+ else
+ qlog_off_count += cnopen;
+ if (qlog_on_count > 0)
+ qlog_on = 1;
+ else if (qlog_off_count > 0)
+ qlog_on = 0;
+ else
+ qlog_on = globals.commlog;
+}
+
+#ifdef MY_LOG
+void
+mylog(char *fmt,...)
+{
+ va_list args;
+ char filebuf[80];
+ static FILE *LOGFP = NULL;
+
+ if (mylog_on)
+ {
+ va_start(args, fmt);
+
+ if (!LOGFP)
+ {
+ generate_filename(MYLOGDIR, MYLOGFILE, filebuf);
+ LOGFP = fopen(filebuf, PG_BINARY_W);
+ setbuf(LOGFP, NULL);
+ }
+
+ if (LOGFP)
+ vfprintf(LOGFP, fmt, args);
+
+ va_end(args);
+ }
+}
+#endif
+
+
+#ifdef Q_LOG
+void
+qlog(char *fmt,...)
+{
+ va_list args;
+ char filebuf[80];
+ static FILE *LOGFP = NULL;
+
+ if (qlog_on)
+ {
+ va_start(args, fmt);
+
+ if (!LOGFP)
+ {
+ generate_filename(QLOGDIR, QLOGFILE, filebuf);
+ LOGFP = fopen(filebuf, PG_BINARY_W);
+ setbuf(LOGFP, NULL);
+ }
+
+ if (LOGFP)
+ vfprintf(LOGFP, fmt, args);
+
+ va_end(args);
+ }
+}
+#endif
+
+
+/*
+ * returns STRCPY_FAIL, STRCPY_TRUNCATED, or #bytes copied
+ * (not including null term)
+ */
+int
+my_strcpy(char *dst, int dst_len, const char *src, int src_len)
+{
+ if (dst_len <= 0)
+ return STRCPY_FAIL;
+
+ if (src_len == SQL_NULL_DATA)
+ {
+ dst[0] = '\0';
+ return STRCPY_NULL;
+ }
+ else if (src_len == SQL_NTS)
+ src_len = strlen(src);
+
+ if (src_len <= 0)
+ return STRCPY_FAIL;
+ else
+ {
+ if (src_len < dst_len)
+ {
+ memcpy(dst, src, src_len);
+ dst[src_len] = '\0';
+ }
+ else
+ {
+ memcpy(dst, src, dst_len - 1);
+ dst[dst_len - 1] = '\0'; /* truncated */
+ return STRCPY_TRUNCATED;
+ }
+ }
+
+ return strlen(dst);
+}
+
+
+/*
+ * strncpy copies up to len characters, and doesn't terminate
+ * the destination string if src has len characters or more.
+ * instead, I want it to copy up to len-1 characters and always
+ * terminate the destination string.
+ */
+char *
+strncpy_null(char *dst, const char *src, int len)
+{
+ int i;
+
+
+ if (NULL != dst)
+ {
+ /* Just in case, check for special lengths */
+ if (len == SQL_NULL_DATA)
+ {
+ dst[0] = '\0';
+ return NULL;
+ }
+ else if (len == SQL_NTS)
+ len = strlen(src) + 1;
+
+ for (i = 0; src[i] && i < len - 1; i++)
+ dst[i] = src[i];
+
+ if (len > 0)
+ dst[i] = '\0';
+ }
+ return dst;
+}
+
+
+/*------
+ * Create a null terminated string (handling the SQL_NTS thing):
+ * 1. If buf is supplied, place the string in there
+ * (assumes enough space) and return buf.
+ * 2. If buf is not supplied, malloc space and return this string
+ *------
+ */
+char *
+make_string(const char *s, int len, char *buf)
+{
+ int length;
+ char *str;
+
+ if (s && (len > 0 || (len == SQL_NTS && strlen(s) > 0)))
+ {
+ length = (len > 0) ? len : strlen(s);
+
+ if (buf)
+ {
+ strncpy_null(buf, s, length + 1);
+ return buf;
+ }
+
+ str = malloc(length + 1);
+ if (!str)
+ return NULL;
+
+ strncpy_null(str, s, length + 1);
+ return str;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Concatenate a single formatted argument to a given buffer handling the SQL_NTS thing.
+ * "fmt" must contain somewhere in it the single form '%.*s'.
+ * This is heavily used in creating queries for info routines (SQLTables, SQLColumns).
+ * This routine could be modified to use vsprintf() to handle multiple arguments.
+ */
+char *
+my_strcat(char *buf, const char *fmt, const char *s, int len)
+{
+ if (s && (len > 0 || (len == SQL_NTS && strlen(s) > 0)))
+ {
+ int length = (len > 0) ? len : strlen(s);
+
+ int pos = strlen(buf);
+
+ sprintf(&buf[pos], fmt, length, s);
+ return buf;
+ }
+ return NULL;
+}
+
+
+void
+remove_newlines(char *string)
+{
+ unsigned int i;
+
+ for (i = 0; i < strlen(string); i++)
+ {
+ if ((string[i] == '\n') ||
+ (string[i] == '\r'))
+ string[i] = ' ';
+ }
+}
+
+
+char *
+trim(char *s)
+{
+ int i;
+
+ for (i = strlen(s) - 1; i >= 0; i--)
+ {
+ if (s[i] == ' ')
+ s[i] = '\0';
+ else
+ break;
+ }
+
+ return s;
+}
--- /dev/null
+/* File: misc.h
+ *
+ * Description: See "misc.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __MISC_H__
+#define __MISC_H__
+
+#include "psqlodbc.h"
+
+#include <stdio.h>
+
+/* Uncomment MY_LOG define to compile in the mylog() statements.
+ Then, debug logging will occur if 'Debug' is set to 1 in the ODBCINST.INI
+ portion of the registry. You may have to manually add this key.
+ This logfile is intended for development use, not for an end user!
+*/
+#define MY_LOG
+
+
+/* Uncomment Q_LOG to compile in the qlog() statements (Communications log, i.e. CommLog).
+ This logfile contains serious log statements that are intended for an
+ end user to be able to read and understand. It is controlled by the
+ 'CommLog' flag in the ODBCINST.INI portion of the registry (see above),
+ which is manipulated on the setup/connection dialog boxes.
+*/
+#define Q_LOG
+
+
+#ifdef MY_LOG
+#define MYLOGFILE "mylog_"
+#ifndef WIN32
+#define MYLOGDIR "/tmp"
+#else
+#define MYLOGDIR "c:"
+#endif
+extern void mylog(char *fmt,...);
+
+#else
+#ifndef WIN32
+#define mylog(args...) /* GNU convention for variable arguments */
+#else
+#define mylog /* mylog */
+#endif
+#endif
+
+#ifdef Q_LOG
+#define QLOGFILE "psqlodbc_"
+#ifndef WIN32
+#define QLOGDIR "/tmp"
+#else
+#define QLOGDIR "c:"
+#endif
+extern void qlog(char *fmt,...);
+
+#else
+#ifndef WIN32
+#define qlog(args...) /* GNU convention for variable arguments */
+#else
+#define qlog /* qlog */
+#endif
+#endif
+
+#ifndef WIN32
+#define DIRSEPARATOR "/"
+#else
+#define DIRSEPARATOR "\\"
+#endif
+
+#ifdef WIN32
+#define PG_BINARY O_BINARY
+#define PG_BINARY_R "rb"
+#define PG_BINARY_W "wb"
+#else
+#define PG_BINARY 0
+#define PG_BINARY_R "r"
+#define PG_BINARY_W "w"
+#endif
+
+
+void remove_newlines(char *string);
+char *strncpy_null(char *dst, const char *src, int len);
+char *trim(char *string);
+char *make_string(const char *s, int len, char *buf);
+char *my_strcat(char *buf, const char *fmt, const char *s, int len);
+
+/* defines for return value of my_strcpy */
+#define STRCPY_SUCCESS 1
+#define STRCPY_FAIL 0
+#define STRCPY_TRUNCATED (-1)
+#define STRCPY_NULL (-2)
+
+int my_strcpy(char *dst, int dst_len, const char *src, int src_len);
+
+#endif
--- /dev/null
+/*--------
+ * Module : multibyte.c
+ *
+ * Description: Mlutibyte related additional function.
+ *
+ * Create 2001-03-03 Eiji Tokuya
+ *--------
+ */
+
+#include "multibyte.h"
+#include <string.h>
+
+int multibyte_client_encoding; /* Multibyte Client Encoding. */
+int multibyte_status; /* Multibyte Odds and ends character. */
+
+
+unsigned char *
+multibyte_strchr(unsigned char *s, unsigned char c)
+{
+ int mb_st = 0,
+ i = 0;
+
+ while (!(mb_st == 0 && (s[i] == c || s[i] == 0)))
+ {
+ if (s[i] == 0)
+ return (0);
+ switch (multibyte_client_encoding)
+ {
+ case SJIS:
+ {
+ if (mb_st < 2 && s[i] > 0x80 && !(s[i] > 0x9f && s[i] < 0xe0))
+ mb_st = 2;
+ else if (mb_st == 2)
+ mb_st = 1;
+ else
+ mb_st = 0;
+ }
+ break;
+
+/* Chinese Big5 Support. */
+ case BIG5:
+ {
+ if (mb_st < 2 && s[i] > 0xA0)
+ mb_st = 2;
+ else if (mb_st == 2)
+ mb_st = 1;
+ else
+ mb_st = 0;
+ }
+ break;
+ default:
+ mb_st = 0;
+ }
+ i++;
+ }
+#ifdef _DEBUG
+ qlog("i = %d\n", i);
+#endif
+ return (s + i);
+}
+
+
+void
+multibyte_init(void)
+{
+ multibyte_status = 0;
+}
+
+
+unsigned char *
+check_client_encoding(unsigned char *str)
+{
+ if (strstr(str, "%27SJIS%27") ||
+ strstr(str, "%27Shift_JIS%27") ||
+ strstr(str, "'SJIS'") ||
+ strstr(str, "'sjis'") ||
+ strstr(str, "'Shift_JIS'"))
+ {
+ multibyte_client_encoding = SJIS;
+ return ("SJIS");
+ }
+ if (strstr(str, "%27BIG5%27") ||
+ strstr(str, "%27Big5%27") ||
+ strstr(str, "'BIG5'") ||
+ strstr(str, "'big5'") ||
+ strstr(str, "'Big5'"))
+ {
+ multibyte_client_encoding = BIG5;
+ return ("BIG5");
+ }
+ return ("OTHER");
+}
+
+
+/*--------
+ * Multibyte Status Function.
+ * Input char
+ * Output 0 : 1 Byte Character.
+ * 1 : MultibyteCharacter Last Byte.
+ * N : MultibyteCharacter Fast or Middle Byte.
+ *--------
+ */
+int
+multibyte_char_check(unsigned char s)
+{
+ switch (multibyte_client_encoding)
+ {
+ /* Japanese Shift-JIS(CP932) Support. */
+ case SJIS:
+ {
+ if (multibyte_status < 2 && s > 0x80 && !(s > 0x9f && s < 0xE0))
+ multibyte_status = 2;
+ else if (multibyte_status == 2)
+ multibyte_status = 1;
+ else
+ multibyte_status = 0;
+ }
+ break;
+
+ /* Chinese Big5(CP950) Support. */
+ case BIG5:
+ {
+ if (multibyte_status < 2 && s > 0xA0)
+ multibyte_status = 2;
+ else if (multibyte_status == 2)
+ multibyte_status = 1;
+ else
+ multibyte_status = 0;
+ }
+ break;
+ default:
+ multibyte_status = 0;
+ }
+#ifdef _DEBUG
+ qlog("multibyte_client_encoding = %d s = 0x%02X multibyte_stat = %d\n", multibyte_client_encoding, s, multibyte_status);
+#endif
+ return (multibyte_status);
+}
--- /dev/null
+/*
+ *
+ * Multibyte library header ( psqlODBC Only )
+ *
+ */
+#include "psqlodbc.h"
+
+/* PostgreSQL client encoding */
+#define SQL_ASCII 0 /* SQL/ASCII */
+#define EUC_JP 1 /* EUC for Japanese */
+#define EUC_CN 2 /* EUC for Chinese */
+#define EUC_KR 3 /* EUC for Korean */
+#define EUC_TW 4 /* EUC for Taiwan */
+#define UNICODE 5 /* Unicode UTF-8 */
+#define MULE_INTERNAL 6 /* Mule internal code */
+#define LATIN1 7 /* ISO-8859 Latin 1 */
+#define LATIN2 8 /* ISO-8859 Latin 2 */
+#define LATIN3 9 /* ISO-8859 Latin 3 */
+#define LATIN4 10 /* ISO-8859 Latin 4 */
+#define LATIN5 11 /* ISO-8859 Latin 5 */
+#define LATIN6 12 /* ISO-8859 Latin 6 */
+#define LATIN7 13 /* ISO-8859 Latin 7 */
+#define LATIN8 14 /* ISO-8859 Latin 8 */
+#define LATIN9 15 /* ISO-8859 Latin 9 */
+#define KOI8 16 /* KOI8-R/U */
+#define WIN 17 /* windows-1251 */
+#define ALT 18 /* Alternativny Variant (MS-DOS CP866) */
+#define SJIS 32 /* Shift JIS */
+#define BIG5 33 /* Big5 */
+#define WIN1250 34 /* windows-1250 */
+
+
+extern int multibyte_client_encoding; /* Multibyte client encoding. */
+extern int multibyte_status; /* Multibyte charcter status. */
+
+void multibyte_init(void);
+unsigned char *check_client_encoding(unsigned char *str);
+int multibyte_char_check(unsigned char s);
+unsigned char *multibyte_strchr(unsigned char *s, unsigned char c);
--- /dev/null
+
+/********************************************************************
+
+ PSQLODBC.DLL - A library to talk to the PostgreSQL DBMS using ODBC.
+
+
+ Copyright (C) 1998; Insight Distribution Systems
+
+ The code contained in this library is based on code written by
+ Christian Czezatke and Dan McGuirk, (C) 1996.
+
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library (see "license.txt"); if not, write to
+ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
+ 02139, USA.
+
+
+ How to contact the author:
+
+ email: byronn@insightdist.com (Byron Nikolaidis)
+
+
+***********************************************************************/
+
--- /dev/null
+/*-------
+ * Module: odbcapi.c
+ *
+ * Description: This module contains routines related to
+ * preparing and executing an SQL statement.
+ *
+ * Classes: n/a
+ *
+ * API functions: SQLAllocConnect, SQLAllocEnv, SQLAllocStmt,
+ SQLBindCol, SQLCancel, SQLColumns, SQLConnect,
+ SQLDataSources, SQLDescribeCol, SQLDisconnect,
+ SQLError, SQLExecDirect, SQLExecute, SQLFetch,
+ SQLFreeConnect, SQLFreeEnv, SQLFreeStmt,
+ SQLGetConnectOption, SQLGetCursorName, SQLGetData,
+ SQLGetFunctions, SQLGetInfo, SQLGetStmtOption,
+ SQLGetTypeInfo, SQLNumResultCols, SQLParamData,
+ SQLPrepare, SQLPutData, SQLRowCount,
+ SQLSetConnectOption, SQLSetCursorName, SQLSetParam,
+ SQLSetStmtOption, SQLSpecialColumns, SQLStatistics,
+ SQLTables, SQLTransact, SQLColAttributes,
+ SQLColumnPrivileges, SQLDescribeParam, SQLExtendedFetch,
+ SQLForeignKeys, SQLMoreResults, SQLNativeSql,
+ SQLNumParams, SQLParamOptions, SQLPrimaryKeys,
+ SQLProcedureColumns, SQLProcedures, SQLSetPos,
+ SQLTablePrivileges, SQLBindParameter
+ *-------
+ */
+
+#include "psqlodbc.h"
+#include <stdio.h>
+#include <string.h>
+
+#include "pgapifunc.h"
+#include "connection.h"
+#include "statement.h"
+
+RETCODE SQL_API
+SQLAllocConnect(HENV EnvironmentHandle,
+ HDBC FAR * ConnectionHandle)
+{
+ mylog("[SQLAllocConnect]");
+ return PGAPI_AllocConnect(EnvironmentHandle, ConnectionHandle);
+}
+
+RETCODE SQL_API
+SQLAllocEnv(HENV FAR * EnvironmentHandle)
+{
+ mylog("[SQLAllocEnv]");
+ return PGAPI_AllocEnv(EnvironmentHandle);
+}
+
+RETCODE SQL_API
+SQLAllocStmt(HDBC ConnectionHandle,
+ HSTMT *StatementHandle)
+{
+ mylog("[SQLAllocStmt]");
+ return PGAPI_AllocStmt(ConnectionHandle, StatementHandle);
+}
+
+RETCODE SQL_API
+SQLBindCol(HSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
+ PTR TargetValue, SQLINTEGER BufferLength,
+ SQLINTEGER *StrLen_or_Ind)
+{
+ mylog("[SQLBindCol]");
+ return PGAPI_BindCol(StatementHandle, ColumnNumber,
+ TargetType, TargetValue, BufferLength, StrLen_or_Ind);
+}
+
+RETCODE SQL_API
+SQLCancel(HSTMT StatementHandle)
+{
+ mylog("[SQLCancel]");
+ return PGAPI_Cancel(StatementHandle);
+}
+
+RETCODE SQL_API
+SQLColumns(HSTMT StatementHandle,
+ SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
+ SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
+ SQLCHAR *TableName, SQLSMALLINT NameLength3,
+ SQLCHAR *ColumnName, SQLSMALLINT NameLength4)
+{
+ mylog("[SQLColumns]");
+ return PGAPI_Columns(StatementHandle, CatalogName, NameLength1,
+ SchemaName, NameLength2, TableName, NameLength3,
+ ColumnName, NameLength4);
+}
+
+
+RETCODE SQL_API
+SQLConnect(HDBC ConnectionHandle,
+ SQLCHAR *ServerName, SQLSMALLINT NameLength1,
+ SQLCHAR *UserName, SQLSMALLINT NameLength2,
+ SQLCHAR *Authentication, SQLSMALLINT NameLength3)
+{
+ mylog("[SQLConnect]");
+ return PGAPI_Connect(ConnectionHandle, ServerName, NameLength1,
+ UserName, NameLength2, Authentication, NameLength3);
+}
+
+RETCODE SQL_API
+SQLDriverConnect(HDBC hdbc,
+ HWND hwnd,
+ UCHAR FAR * szConnStrIn,
+ SWORD cbConnStrIn,
+ UCHAR FAR * szConnStrOut,
+ SWORD cbConnStrOutMax,
+ SWORD FAR * pcbConnStrOut,
+ UWORD fDriverCompletion)
+{
+ mylog("[SQLDriverConnect]");
+ return PGAPI_DriverConnect(hdbc, hwnd, szConnStrIn, cbConnStrIn,
+ szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion);
+}
+RETCODE SQL_API
+SQLBrowseConnect(
+ HDBC hdbc,
+ SQLCHAR *szConnStrIn,
+ SQLSMALLINT cbConnStrIn,
+ SQLCHAR *szConnStrOut,
+ SQLSMALLINT cbConnStrOutMax,
+ SQLSMALLINT *pcbConnStrOut)
+{
+ mylog("[SQLBrowseConnect]");
+ return PGAPI_BrowseConnect(hdbc, szConnStrIn, cbConnStrIn,
+ szConnStrOut, cbConnStrOutMax, pcbConnStrOut);
+}
+
+RETCODE SQL_API
+SQLDataSources(HENV EnvironmentHandle,
+ SQLUSMALLINT Direction, SQLCHAR *ServerName,
+ SQLSMALLINT BufferLength1, SQLSMALLINT *NameLength1,
+ SQLCHAR *Description, SQLSMALLINT BufferLength2,
+ SQLSMALLINT *NameLength2)
+{
+ mylog("[SQLDataSources]");
+
+ /*
+ * return PGAPI_DataSources(EnvironmentHandle, Direction, ServerName,
+ * BufferLength1, NameLength1, Description, BufferLength2,
+ * NameLength2);
+ */
+ return SQL_ERROR;
+}
+
+RETCODE SQL_API
+SQLDescribeCol(HSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName,
+ SQLSMALLINT BufferLength, SQLSMALLINT *NameLength,
+ SQLSMALLINT *DataType, SQLUINTEGER *ColumnSize,
+ SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable)
+{
+ mylog("[SQLDescribeCol]");
+ return PGAPI_DescribeCol(StatementHandle, ColumnNumber,
+ ColumnName, BufferLength, NameLength,
+ DataType, ColumnSize, DecimalDigits, Nullable);
+}
+
+RETCODE SQL_API
+SQLDisconnect(HDBC ConnectionHandle)
+{
+ mylog("[SQLDisconnect]");
+ return PGAPI_Disconnect(ConnectionHandle);
+}
+
+RETCODE SQL_API
+SQLError(HENV EnvironmentHandle,
+ HDBC ConnectionHandle, HSTMT StatementHandle,
+ SQLCHAR *Sqlstate, SQLINTEGER *NativeError,
+ SQLCHAR *MessageText, SQLSMALLINT BufferLength,
+ SQLSMALLINT *TextLength)
+{
+ mylog("[SQLError]");
+ return PGAPI_Error(EnvironmentHandle, ConnectionHandle, StatementHandle,
+ Sqlstate, NativeError, MessageText, BufferLength, TextLength);
+}
+
+RETCODE SQL_API
+SQLExecDirect(HSTMT StatementHandle,
+ SQLCHAR *StatementText, SQLINTEGER TextLength)
+{
+ mylog("[SQLExecDirect]");
+ return PGAPI_ExecDirect(StatementHandle, StatementText, TextLength);
+}
+
+RETCODE SQL_API
+SQLExecute(HSTMT StatementHandle)
+{
+ mylog("[SQLExecute]");
+ return PGAPI_Execute(StatementHandle);
+}
+
+RETCODE SQL_API
+SQLFetch(HSTMT StatementHandle)
+{
+ static char *func = "SQLFetch";
+
+#if (ODBCVER >= 0x0300)
+ StatementClass *stmt = (StatementClass *) StatementHandle;
+ ConnectionClass *conn = SC_get_conn(stmt);
+
+ if (conn->driver_version >= 0x0300)
+ {
+ SQLUSMALLINT *rowStatusArray = stmt->options.rowStatusArray;
+ SQLINTEGER *pcRow = stmt->options.rowsFetched;
+
+ mylog("[[%s]]", func);
+ return PGAPI_ExtendedFetch(StatementHandle, SQL_FETCH_NEXT, 0,
+ pcRow, rowStatusArray);
+ }
+#endif
+ mylog("[%s]", func);
+ return PGAPI_Fetch(StatementHandle);
+}
+
+RETCODE SQL_API
+SQLFreeConnect(HDBC ConnectionHandle)
+{
+ mylog("[SQLFreeStmt]");
+ return PGAPI_FreeConnect(ConnectionHandle);
+}
+
+RETCODE SQL_API
+SQLFreeEnv(HENV EnvironmentHandle)
+{
+ mylog("[SQLFreeEnv]");
+ return PGAPI_FreeEnv(EnvironmentHandle);
+}
+
+RETCODE SQL_API
+SQLFreeStmt(HSTMT StatementHandle,
+ SQLUSMALLINT Option)
+{
+ mylog("[SQLFreeStmt]");
+ return PGAPI_FreeStmt(StatementHandle, Option);
+}
+
+RETCODE SQL_API
+SQLGetConnectOption(HDBC ConnectionHandle,
+ SQLUSMALLINT Option, PTR Value)
+{
+ mylog("[SQLGetConnectOption]");
+ return PGAPI_GetConnectOption(ConnectionHandle, Option, Value);
+}
+RETCODE SQL_API
+SQLGetCursorName(HSTMT StatementHandle,
+ SQLCHAR *CursorName, SQLSMALLINT BufferLength,
+ SQLSMALLINT *NameLength)
+{
+ mylog("[SQLGetCursorName]");
+ return PGAPI_GetCursorName(StatementHandle, CursorName, BufferLength,
+ NameLength);
+}
+
+RETCODE SQL_API
+SQLGetData(HSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
+ PTR TargetValue, SQLINTEGER BufferLength,
+ SQLINTEGER *StrLen_or_Ind)
+{
+ mylog("[SQLGetData]");
+ return PGAPI_GetData(StatementHandle, ColumnNumber, TargetType,
+ TargetValue, BufferLength, StrLen_or_Ind);
+}
+
+RETCODE SQL_API
+SQLGetFunctions(HDBC ConnectionHandle,
+ SQLUSMALLINT FunctionId, SQLUSMALLINT *Supported)
+{
+ mylog("[SQLGetFunctions]");
+#if (ODBCVER >= 0x0300)
+ if (FunctionId == SQL_API_ODBC3_ALL_FUNCTIONS)
+ return PGAPI_GetFunctions30(ConnectionHandle, FunctionId, Supported);
+#endif
+ return PGAPI_GetFunctions(ConnectionHandle, FunctionId, Supported);
+}
+RETCODE SQL_API
+SQLGetInfo(HDBC ConnectionHandle,
+ SQLUSMALLINT InfoType, PTR InfoValue,
+ SQLSMALLINT BufferLength, SQLSMALLINT *StringLength)
+{
+#if (ODBCVER >= 0x0300)
+ RETCODE ret;
+
+ mylog("[SQLGetInfo(30)]");
+ if ((ret = PGAPI_GetInfo(ConnectionHandle, InfoType, InfoValue,
+ BufferLength, StringLength)) == SQL_ERROR)
+ {
+ if (((ConnectionClass *) ConnectionHandle)->driver_version >= 0x0300)
+ return PGAPI_GetInfo30(ConnectionHandle, InfoType, InfoValue,
+ BufferLength, StringLength);
+ }
+ return ret;
+#else
+ mylog("[SQLGetInfo]");
+ return PGAPI_GetInfo(ConnectionHandle, InfoType, InfoValue,
+ BufferLength, StringLength);
+#endif
+}
+
+RETCODE SQL_API
+SQLGetStmtOption(HSTMT StatementHandle,
+ SQLUSMALLINT Option, PTR Value)
+{
+ mylog("[SQLGetStmtOption]");
+ return PGAPI_GetStmtOption(StatementHandle, Option, Value);
+}
+
+RETCODE SQL_API
+SQLGetTypeInfo(HSTMT StatementHandle,
+ SQLSMALLINT DataType)
+{
+ mylog("[SQLGetTypeInfo]");
+ return PGAPI_GetTypeInfo(StatementHandle, DataType);
+}
+
+RETCODE SQL_API
+SQLNumResultCols(HSTMT StatementHandle,
+ SQLSMALLINT *ColumnCount)
+{
+ mylog("[SQLNumResultCols]");
+ return PGAPI_NumResultCols(StatementHandle, ColumnCount);
+}
+
+RETCODE SQL_API
+SQLParamData(HSTMT StatementHandle,
+ PTR *Value)
+{
+ mylog("[SQLParamData]");
+ return PGAPI_ParamData(StatementHandle, Value);
+}
+
+RETCODE SQL_API
+SQLPrepare(HSTMT StatementHandle,
+ SQLCHAR *StatementText, SQLINTEGER TextLength)
+{
+ mylog("[SQLPrepare]");
+ return PGAPI_Prepare(StatementHandle, StatementText, TextLength);
+}
+
+RETCODE SQL_API
+SQLPutData(HSTMT StatementHandle,
+ PTR Data, SQLINTEGER StrLen_or_Ind)
+{
+ mylog("[SQLPutData]");
+ return PGAPI_PutData(StatementHandle, Data, StrLen_or_Ind);
+}
+
+RETCODE SQL_API
+SQLRowCount(HSTMT StatementHandle,
+ SQLINTEGER *RowCount)
+{
+ mylog("[SQLRowCount]");
+ return PGAPI_RowCount(StatementHandle, RowCount);
+}
+
+RETCODE SQL_API
+SQLSetConnectOption(HDBC ConnectionHandle,
+ SQLUSMALLINT Option, SQLUINTEGER Value)
+{
+ mylog("[SQLSetConnectionOption]");
+ return PGAPI_SetConnectOption(ConnectionHandle, Option, Value);
+}
+
+RETCODE SQL_API
+SQLSetCursorName(HSTMT StatementHandle,
+ SQLCHAR *CursorName, SQLSMALLINT NameLength)
+{
+ mylog("[SQLSetCursorName]");
+ return PGAPI_SetCursorName(StatementHandle, CursorName, NameLength);
+}
+
+RETCODE SQL_API
+SQLSetParam(HSTMT StatementHandle,
+ SQLUSMALLINT ParameterNumber, SQLSMALLINT ValueType,
+ SQLSMALLINT ParameterType, SQLUINTEGER LengthPrecision,
+ SQLSMALLINT ParameterScale, PTR ParameterValue,
+ SQLINTEGER *StrLen_or_Ind)
+{
+ mylog("[SQLSetParam]");
+
+ /*
+ * return PGAPI_SetParam(StatementHandle, ParameterNumber, ValueType,
+ * ParameterType, LengthPrecision, ParameterScale, ParameterValue,
+ * StrLen_or_Ind);
+ */
+ return SQL_ERROR;
+}
+
+RETCODE SQL_API
+SQLSetStmtOption(HSTMT StatementHandle,
+ SQLUSMALLINT Option, SQLUINTEGER Value)
+{
+ mylog("[SQLSetStmtOption]");
+ return PGAPI_SetStmtOption(StatementHandle, Option, Value);
+}
+
+RETCODE SQL_API
+SQLSpecialColumns(HSTMT StatementHandle,
+ SQLUSMALLINT IdentifierType, SQLCHAR *CatalogName,
+ SQLSMALLINT NameLength1, SQLCHAR *SchemaName,
+ SQLSMALLINT NameLength2, SQLCHAR *TableName,
+ SQLSMALLINT NameLength3, SQLUSMALLINT Scope,
+ SQLUSMALLINT Nullable)
+{
+ mylog("[SQLSpecialColumns]");
+ return PGAPI_SpecialColumns(StatementHandle, IdentifierType, CatalogName,
+ NameLength1, SchemaName, NameLength2, TableName, NameLength3,
+ Scope, Nullable);
+}
+
+RETCODE SQL_API
+SQLStatistics(HSTMT StatementHandle,
+ SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
+ SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
+ SQLCHAR *TableName, SQLSMALLINT NameLength3,
+ SQLUSMALLINT Unique, SQLUSMALLINT Reserved)
+{
+ mylog("[SQLStatistics]");
+ return PGAPI_Statistics(StatementHandle, CatalogName, NameLength1,
+ SchemaName, NameLength2, TableName, NameLength3, Unique,
+ Reserved);
+}
+
+RETCODE SQL_API
+SQLTables(HSTMT StatementHandle,
+ SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
+ SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
+ SQLCHAR *TableName, SQLSMALLINT NameLength3,
+ SQLCHAR *TableType, SQLSMALLINT NameLength4)
+{
+ mylog("[SQLTables]");
+ return PGAPI_Tables(StatementHandle, CatalogName, NameLength1,
+ SchemaName, NameLength2, TableName, NameLength3,
+ TableType, NameLength4);
+}
+
+RETCODE SQL_API
+SQLTransact(HENV EnvironmentHandle,
+ HDBC ConnectionHandle, SQLUSMALLINT CompletionType)
+{
+ mylog("[SQLTransact]");
+ return PGAPI_Transact(EnvironmentHandle, ConnectionHandle, CompletionType);
+}
+
+RETCODE SQL_API
+SQLColAttributes(
+ HSTMT hstmt,
+ SQLUSMALLINT icol,
+ SQLUSMALLINT fDescType,
+ PTR rgbDesc,
+ SQLSMALLINT cbDescMax,
+ SQLSMALLINT *pcbDesc,
+ SQLINTEGER *pfDesc)
+{
+ mylog("[SQLColAttributes]");
+ return PGAPI_ColAttributes(hstmt, icol, fDescType, rgbDesc,
+ cbDescMax, pcbDesc, pfDesc);
+}
+
+RETCODE SQL_API
+SQLColumnPrivileges(
+ HSTMT hstmt,
+ SQLCHAR *szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR *szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR *szTableName,
+ SQLSMALLINT cbTableName,
+ SQLCHAR *szColumnName,
+ SQLSMALLINT cbColumnName)
+{
+ mylog("[SQLColumnPrivileges]");
+ return PGAPI_ColumnPrivileges(hstmt, szCatalogName, cbCatalogName,
+ szSchemaName, cbSchemaName, szTableName, cbTableName,
+ szColumnName, cbColumnName);
+}
+
+RETCODE SQL_API
+SQLDescribeParam(
+ HSTMT hstmt,
+ SQLUSMALLINT ipar,
+ SQLSMALLINT *pfSqlType,
+ SQLUINTEGER *pcbParamDef,
+ SQLSMALLINT *pibScale,
+ SQLSMALLINT *pfNullable)
+{
+ mylog("[SQLDescribeParam]");
+ return PGAPI_DescribeParam(hstmt, ipar, pfSqlType, pcbParamDef,
+ pibScale, pfNullable);
+}
+
+RETCODE SQL_API
+SQLExtendedFetch(
+ HSTMT hstmt,
+ SQLUSMALLINT fFetchType,
+ SQLINTEGER irow,
+ SQLUINTEGER *pcrow,
+ SQLUSMALLINT *rgfRowStatus)
+{
+ mylog("[SQLExtendedFetch]");
+ return PGAPI_ExtendedFetch(hstmt, fFetchType, irow, pcrow, rgfRowStatus);
+}
+
+RETCODE SQL_API
+SQLForeignKeys(
+ HSTMT hstmt,
+ SQLCHAR *szPkCatalogName,
+ SQLSMALLINT cbPkCatalogName,
+ SQLCHAR *szPkSchemaName,
+ SQLSMALLINT cbPkSchemaName,
+ SQLCHAR *szPkTableName,
+ SQLSMALLINT cbPkTableName,
+ SQLCHAR *szFkCatalogName,
+ SQLSMALLINT cbFkCatalogName,
+ SQLCHAR *szFkSchemaName,
+ SQLSMALLINT cbFkSchemaName,
+ SQLCHAR *szFkTableName,
+ SQLSMALLINT cbFkTableName)
+{
+ mylog("[SQLForeignKeys]");
+ return PGAPI_ForeignKeys(hstmt, szPkCatalogName, cbPkCatalogName,
+ szPkSchemaName, cbPkSchemaName, szPkTableName,
+ cbPkTableName, szFkCatalogName, cbFkCatalogName,
+ szFkSchemaName, cbFkSchemaName, szFkTableName, cbFkTableName);
+}
+
+RETCODE SQL_API
+SQLMoreResults(HSTMT hstmt)
+{
+ mylog("[SQLMoreResults]");
+ return PGAPI_MoreResults(hstmt);
+}
+
+RETCODE SQL_API
+SQLNativeSql(
+ HDBC hdbc,
+ SQLCHAR *szSqlStrIn,
+ SQLINTEGER cbSqlStrIn,
+ SQLCHAR *szSqlStr,
+ SQLINTEGER cbSqlStrMax,
+ SQLINTEGER *pcbSqlStr)
+{
+ mylog("[SQLNativeSql]");
+ return PGAPI_NativeSql(hdbc, szSqlStrIn, cbSqlStrIn, szSqlStr,
+ cbSqlStrMax, pcbSqlStr);
+}
+
+RETCODE SQL_API
+SQLNumParams(
+ HSTMT hstmt,
+ SQLSMALLINT *pcpar)
+{
+ mylog("[SQLNumParams]");
+ return PGAPI_NumParams(hstmt, pcpar);
+}
+
+RETCODE SQL_API
+SQLParamOptions(
+ HSTMT hstmt,
+ SQLUINTEGER crow,
+ SQLUINTEGER *pirow)
+{
+ mylog("[SQLParamOptions]");
+ return PGAPI_ParamOptions(hstmt, crow, pirow);
+}
+
+RETCODE SQL_API
+SQLPrimaryKeys(
+ HSTMT hstmt,
+ SQLCHAR *szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR *szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR *szTableName,
+ SQLSMALLINT cbTableName)
+{
+ mylog("[SQLPrimaryKeys]");
+ return PGAPI_PrimaryKeys(hstmt, szCatalogName, cbCatalogName,
+ szSchemaName, cbSchemaName, szTableName, cbTableName);
+}
+
+RETCODE SQL_API
+SQLProcedureColumns(
+ HSTMT hstmt,
+ SQLCHAR *szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR *szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR *szProcName,
+ SQLSMALLINT cbProcName,
+ SQLCHAR *szColumnName,
+ SQLSMALLINT cbColumnName)
+{
+ mylog("[SQLProcedureColumns]");
+ return PGAPI_ProcedureColumns(hstmt, szCatalogName, cbCatalogName,
+ szSchemaName, cbSchemaName, szProcName, cbProcName,
+ szColumnName, cbColumnName);
+}
+
+RETCODE SQL_API
+SQLProcedures(
+ HSTMT hstmt,
+ SQLCHAR *szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR *szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR *szProcName,
+ SQLSMALLINT cbProcName)
+{
+ mylog("[SQLProcedures]");
+ return PGAPI_Procedures(hstmt, szCatalogName, cbCatalogName,
+ szSchemaName, cbSchemaName, szProcName, cbProcName);
+}
+
+RETCODE SQL_API
+SQLSetPos(
+ HSTMT hstmt,
+ SQLUSMALLINT irow,
+ SQLUSMALLINT fOption,
+ SQLUSMALLINT fLock)
+{
+ mylog("[SQLSetPos]");
+ return PGAPI_SetPos(hstmt, irow, fOption, fLock);
+}
+
+RETCODE SQL_API
+SQLTablePrivileges(
+ HSTMT hstmt,
+ SQLCHAR *szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR *szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR *szTableName,
+ SQLSMALLINT cbTableName)
+{
+ mylog("[SQLTablePrivileges]");
+ return PGAPI_TablePrivileges(hstmt, szCatalogName, cbCatalogName,
+ szSchemaName, cbSchemaName, szTableName, cbTableName);
+}
+
+RETCODE SQL_API
+SQLBindParameter(
+ HSTMT hstmt,
+ SQLUSMALLINT ipar,
+ SQLSMALLINT fParamType,
+ SQLSMALLINT fCType,
+ SQLSMALLINT fSqlType,
+ SQLUINTEGER cbColDef,
+ SQLSMALLINT ibScale,
+ PTR rgbValue,
+ SQLINTEGER cbValueMax,
+ SQLINTEGER *pcbValue)
+{
+ mylog("[SQLBindParameter]");
+ return PGAPI_BindParameter(hstmt, ipar, fParamType, fCType,
+ fSqlType, cbColDef, ibScale, rgbValue, cbValueMax,
+ pcbValue);
+}
--- /dev/null
+/*-------
+ * Module: odbcapi30.c
+ *
+ * Description: This module contains routines related to ODBC 3.0
+ * most of their implementations are temporary
+ * and must be rewritten properly.
+ * 2001/07/23 inoue
+ *
+ * Classes: n/a
+ *
+ * API functions: SQLAllocHandle, SQLBindParam, SQLCloseCursor,
+ SQLColAttribute, SQLCopyDesc, SQLEndTran,
+ SQLFetchScroll, SQLFreeHandle, SQLGetDescField,
+ SQLGetDescRec, SQLGetDiagField, SQLGetDiagRec,
+ SQLGetEnvAttr, SQLGetConnectAttr, SQLGetStmtAttr,
+ SQLSetConnectAttr, SQLSetDescField, SQLSetDescRec,
+ SQLSetEnvAttr, SQLSetStmtAttr, SQLBulkOperations
+ *-------
+ */
+
+#ifndef ODBCVER
+#define ODBCVER 0x0300
+#endif
+#include "psqlodbc.h"
+#include <stdio.h>
+#include <string.h>
+
+#include "environ.h"
+#include "connection.h"
+#include "statement.h"
+#include "pgapifunc.h"
+
+/* SQLAllocConnect/SQLAllocEnv/SQLAllocStmt -> SQLAllocHandle */
+RETCODE SQL_API
+SQLAllocHandle(SQLSMALLINT HandleType,
+ SQLHANDLE InputHandle, SQLHANDLE * OutputHandle)
+{
+ mylog("[[SQLAllocHandle]]");
+ switch (HandleType)
+ {
+ case SQL_HANDLE_ENV:
+ return PGAPI_AllocEnv(OutputHandle);
+ case SQL_HANDLE_DBC:
+ return PGAPI_AllocConnect(InputHandle, OutputHandle);
+ case SQL_HANDLE_STMT:
+ return PGAPI_AllocStmt(InputHandle, OutputHandle);
+ default:
+ break;
+ }
+ return SQL_ERROR;
+}
+
+/* SQLBindParameter/SQLSetParam -> SQLBindParam */
+RETCODE SQL_API
+SQLBindParam(HSTMT StatementHandle,
+ SQLUSMALLINT ParameterNumber, SQLSMALLINT ValueType,
+ SQLSMALLINT ParameterType, SQLUINTEGER LengthPrecision,
+ SQLSMALLINT ParameterScale, PTR ParameterValue,
+ SQLINTEGER *StrLen_or_Ind)
+{
+ int BufferLength = 512; /* Is it OK ? */
+
+ mylog("[[SQLBindParam]]");
+ return PGAPI_BindParameter(StatementHandle, ParameterNumber, SQL_PARAM_INPUT, ValueType, ParameterType, LengthPrecision, ParameterScale, ParameterValue, BufferLength, StrLen_or_Ind);
+}
+
+/* New function */
+RETCODE SQL_API
+SQLCloseCursor(HSTMT StatementHandle)
+{
+ mylog("[[SQLCloseCursor]]");
+ return PGAPI_FreeStmt(StatementHandle, SQL_CLOSE);
+}
+
+/* SQLColAttributes -> SQLColAttribute */
+RETCODE SQL_API
+SQLColAttribute(HSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier,
+ PTR CharacterAttribute, SQLSMALLINT BufferLength,
+ SQLSMALLINT *StringLength, PTR NumericAttribute)
+{
+ mylog("[[SQLColAttribute]]");
+ return PGAPI_ColAttributes(StatementHandle, ColumnNumber,
+ FieldIdentifier, CharacterAttribute, BufferLength,
+ StringLength, NumericAttribute);
+}
+
+/* new function */
+RETCODE SQL_API
+SQLCopyDesc(SQLHDESC SourceDescHandle,
+ SQLHDESC TargetDescHandle)
+{
+ mylog("[[SQLCopyDesc]]\n");
+ return SQL_ERROR;
+}
+
+/* SQLTransact -> SQLEndTran */
+RETCODE SQL_API
+SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle,
+ SQLSMALLINT CompletionType)
+{
+ mylog("[[SQLEndTran]]");
+ switch (HandleType)
+ {
+ case SQL_HANDLE_ENV:
+ return PGAPI_Transact(Handle, SQL_NULL_HDBC, CompletionType);
+ case SQL_HANDLE_DBC:
+ return PGAPI_Transact(SQL_NULL_HENV, Handle, CompletionType);
+ default:
+ break;
+ }
+ return SQL_ERROR; /* SQLSTATE HY092 ("Invalid
+ * attribute/option identifier") */
+
+}
+
+/* SQLExtendedFetch -> SQLFetchScroll */
+RETCODE SQL_API
+SQLFetchScroll(HSTMT StatementHandle,
+ SQLSMALLINT FetchOrientation, SQLINTEGER FetchOffset)
+{
+ static char *func = "SQLFetchScroll";
+ StatementClass *stmt = (StatementClass *) StatementHandle;
+ RETCODE ret;
+ SQLUSMALLINT *rowStatusArray = stmt->options.rowStatusArray;
+ SQLINTEGER *pcRow = stmt->options.rowsFetched;
+
+ mylog("[[%s]] %d,%d\n", func, FetchOrientation, FetchOffset);
+ if (FetchOrientation == SQL_FETCH_BOOKMARK)
+ {
+ if (stmt->options.bookmark_ptr)
+ FetchOffset += *((Int4 *) stmt->options.bookmark_ptr);
+ else
+ {
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ stmt->errormsg = "Bookmark isn't specifed yet";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+ ret = PGAPI_ExtendedFetch(StatementHandle, FetchOrientation, FetchOffset,
+ pcRow, rowStatusArray);
+ if (ret != SQL_SUCCESS)
+ mylog("%s return = %d\n", func, ret);
+ return ret;
+}
+
+/* SQLFree(Connect/Env/Stmt) -> SQLFreeHandle */
+RETCODE SQL_API
+SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle)
+{
+ mylog("[[SQLFreeHandle]]");
+ switch (HandleType)
+ {
+ case SQL_HANDLE_ENV:
+ return PGAPI_FreeEnv(Handle);
+ case SQL_HANDLE_DBC:
+ return PGAPI_FreeConnect(Handle);
+ case SQL_HANDLE_STMT:
+ return PGAPI_FreeStmt(Handle, SQL_DROP);
+ default:
+ break;
+ }
+ return SQL_ERROR;
+}
+
+/* new function */
+RETCODE SQL_API
+SQLGetDescField(SQLHDESC DescriptorHandle,
+ SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier,
+ PTR Value, SQLINTEGER BufferLength,
+ SQLINTEGER *StringLength)
+{
+ mylog("[[SQLGetDescField]]\n");
+ return SQL_ERROR;
+}
+
+/* new function */
+RETCODE SQL_API
+SQLGetDescRec(SQLHDESC DescriptorHandle,
+ SQLSMALLINT RecNumber, SQLCHAR *Name,
+ SQLSMALLINT BufferLength, SQLSMALLINT *StringLength,
+ SQLSMALLINT *Type, SQLSMALLINT *SubType,
+ SQLINTEGER *Length, SQLSMALLINT *Precision,
+ SQLSMALLINT *Scale, SQLSMALLINT *Nullable)
+{
+ mylog("[[SQLGetDescRec]]\n");
+ return SQL_ERROR;
+}
+
+/* new function */
+RETCODE SQL_API
+SQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle,
+ SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier,
+ PTR DiagInfo, SQLSMALLINT BufferLength,
+ SQLSMALLINT *StringLength)
+{
+ mylog("[[SQLGetDiagField]]\n");
+ return SQL_ERROR;
+}
+
+/* SQLError -> SQLDiagRec */
+RETCODE SQL_API
+SQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle,
+ SQLSMALLINT RecNumber, SQLCHAR *Sqlstate,
+ SQLINTEGER *NativeError, SQLCHAR *MessageText,
+ SQLSMALLINT BufferLength, SQLSMALLINT *TextLength)
+{
+ RETCODE ret;
+
+ mylog("[[SQLGetDiagRec]]\n");
+ switch (HandleType)
+ {
+ case SQL_HANDLE_ENV:
+ ret = PGAPI_Error(Handle, NULL, NULL, Sqlstate, NativeError,
+ MessageText, BufferLength, TextLength);
+ break;
+ case SQL_HANDLE_DBC:
+ ret = PGAPI_Error(NULL, Handle, NULL, Sqlstate, NativeError,
+ MessageText, BufferLength, TextLength);
+ break;
+ case SQL_HANDLE_STMT:
+ ret = PGAPI_Error(NULL, NULL, Handle, Sqlstate, NativeError,
+ MessageText, BufferLength, TextLength);
+ break;
+ default:
+ ret = SQL_ERROR;
+ }
+ if (ret == SQL_SUCCESS_WITH_INFO &&
+ BufferLength == 0 &&
+ *TextLength)
+ {
+ SQLSMALLINT BufferLength = *TextLength + 4;
+ SQLCHAR *MessageText = malloc(BufferLength);
+
+ ret = SQLGetDiagRec(HandleType, Handle, RecNumber, Sqlstate,
+ NativeError, MessageText, BufferLength,
+ TextLength);
+ free(MessageText);
+ }
+ return ret;
+}
+
+/* new function */
+RETCODE SQL_API
+SQLGetEnvAttr(HENV EnvironmentHandle,
+ SQLINTEGER Attribute, PTR Value,
+ SQLINTEGER BufferLength, SQLINTEGER *StringLength)
+{
+ EnvironmentClass *env = (EnvironmentClass *) EnvironmentHandle;
+
+ mylog("[[SQLGetEnvAttr]] %d\n", Attribute);
+ switch (Attribute)
+ {
+ case SQL_ATTR_CONNECTION_POOLING:
+ *((unsigned int *) Value) = SQL_CP_OFF;
+ break;
+ case SQL_ATTR_CP_MATCH:
+ *((unsigned int *) Value) = SQL_CP_RELAXED_MATCH;
+ break;
+ case SQL_ATTR_ODBC_VERSION:
+ *((unsigned int *) Value) = SQL_OV_ODBC3;
+ break;
+ case SQL_ATTR_OUTPUT_NTS:
+ *((unsigned int *) Value) = SQL_TRUE;
+ break;
+ default:
+ env->errornumber = CONN_INVALID_ARGUMENT_NO;
+ return SQL_ERROR;
+ }
+ return SQL_SUCCESS;
+}
+
+/* SQLGetConnectOption -> SQLGetconnectAttr */
+RETCODE SQL_API
+SQLGetConnectAttr(HDBC ConnectionHandle,
+ SQLINTEGER Attribute, PTR Value,
+ SQLINTEGER BufferLength, SQLINTEGER *StringLength)
+{
+ ConnectionClass *conn = (ConnectionClass *) ConnectionHandle;
+
+ mylog("[[SQLGetConnectAttr]] %d\n", Attribute);
+ switch (Attribute)
+ {
+ case SQL_ATTR_ASYNC_ENABLE:
+ case SQL_ATTR_AUTO_IPD:
+ case SQL_ATTR_CONNECTION_DEAD:
+ case SQL_ATTR_CONNECTION_TIMEOUT:
+ case SQL_ATTR_METADATA_ID:
+ conn->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
+ conn->errormsg = "Unsupported connection option (Set)";
+ return SQL_ERROR;
+ }
+ return PGAPI_GetConnectOption(ConnectionHandle, (UWORD) Attribute, Value);
+}
+
+/* SQLGetStmtOption -> SQLGetStmtAttr */
+RETCODE SQL_API
+SQLGetStmtAttr(HSTMT StatementHandle,
+ SQLINTEGER Attribute, PTR Value,
+ SQLINTEGER BufferLength, SQLINTEGER *StringLength)
+{
+ static char *func = "SQLGetStmtAttr";
+ StatementClass *stmt = (StatementClass *) StatementHandle;
+ RETCODE ret = SQL_SUCCESS;
+ int len = 0;
+
+ mylog("[[%s]] %d\n", func, Attribute);
+ switch (Attribute)
+ {
+ case SQL_ATTR_FETCH_BOOKMARK_PTR: /* 16 */
+ Value = stmt->options.bookmark_ptr;
+
+ len = 4;
+ break;
+ case SQL_ATTR_ROW_STATUS_PTR: /* 25 */
+ Value = stmt->options.rowStatusArray;
+
+ len = 4;
+ break;
+ case SQL_ATTR_ROWS_FETCHED_PTR: /* 26 */
+ Value = stmt->options.rowsFetched;
+
+ len = 4;
+ break;
+ case SQL_ATTR_ROW_ARRAY_SIZE: /* 27 */
+ *((SQLUINTEGER *) Value) = stmt->options.rowset_size;
+ len = 4;
+ break;
+ case SQL_ATTR_APP_ROW_DESC: /* 10010 */
+ *((HSTMT *) Value) = StatementHandle; /* this is useless */
+ len = 4;
+ break;
+ case SQL_ATTR_APP_PARAM_DESC: /* 10011 */
+ *((HSTMT *) Value) = StatementHandle; /* this is useless */
+ len = 4;
+ break;
+ case SQL_ATTR_IMP_ROW_DESC: /* 10012 */
+ *((HSTMT *) Value) = StatementHandle; /* this is useless */
+ len = 4;
+ break;
+ case SQL_ATTR_IMP_PARAM_DESC: /* 10013 */
+ *((HSTMT *) Value) = StatementHandle; /* this is useless */
+ len = 4;
+ break;
+ case SQL_ATTR_AUTO_IPD: /* 10001 */
+ /* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */
+ case SQL_ATTR_PARAMSET_SIZE: /* 22 */
+ case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */
+ case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */
+
+ case SQL_ATTR_CURSOR_SCROLLABLE: /* -1 */
+ case SQL_ATTR_CURSOR_SENSITIVITY: /* -2 */
+
+ case SQL_ATTR_ENABLE_AUTO_IPD: /* 15 */
+ case SQL_ATTR_METADATA_ID: /* 10014 */
+
+ /*
+ * case SQL_ATTR_PREDICATE_PTR: case
+ * SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR:
+ */
+ case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */
+ case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */
+ case SQL_ATTR_PARAM_OPERATION_PTR: /* 19 */
+ case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */
+ case SQL_ATTR_ROW_OPERATION_PTR: /* 24 */
+ stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
+ stmt->errormsg = "Unsupported statement option (Get)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ default:
+ len = 4;
+ ret = PGAPI_GetStmtOption(StatementHandle, (UWORD) Attribute, Value);
+ }
+ if (ret == SQL_SUCCESS && StringLength)
+ *StringLength = len;
+ return ret;
+}
+
+/* SQLSetConnectOption -> SQLSetConnectAttr */
+RETCODE SQL_API
+SQLSetConnectAttr(HDBC ConnectionHandle,
+ SQLINTEGER Attribute, PTR Value,
+ SQLINTEGER StringLength)
+{
+ ConnectionClass *conn = (ConnectionClass *) ConnectionHandle;
+
+ mylog("[[SQLSetConnectAttr]] %d\n", Attribute);
+ switch (Attribute)
+ {
+ case SQL_ATTR_ASYNC_ENABLE:
+ case SQL_ATTR_AUTO_IPD:
+ case SQL_ATTR_CONNECTION_DEAD:
+ case SQL_ATTR_CONNECTION_TIMEOUT:
+ case SQL_ATTR_METADATA_ID:
+ conn->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
+ conn->errormsg = "Unsupported connection option (Set)";
+ return SQL_ERROR;
+ }
+ return PGAPI_SetConnectOption(ConnectionHandle, (UWORD) Attribute, (UDWORD) Value);
+}
+
+/* new function */
+RETCODE SQL_API
+SQLSetDescField(SQLHDESC DescriptorHandle,
+ SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier,
+ PTR Value, SQLINTEGER BufferLength)
+{
+ mylog("[[SQLSetDescField]]\n");
+ return SQL_ERROR;
+}
+
+/* new fucntion */
+RETCODE SQL_API
+SQLSetDescRec(SQLHDESC DescriptorHandle,
+ SQLSMALLINT RecNumber, SQLSMALLINT Type,
+ SQLSMALLINT SubType, SQLINTEGER Length,
+ SQLSMALLINT Precision, SQLSMALLINT Scale,
+ PTR Data, SQLINTEGER *StringLength,
+ SQLINTEGER *Indicator)
+{
+ mylog("[[SQLsetDescRec]]\n");
+ return SQL_ERROR;
+}
+
+/* new function */
+RETCODE SQL_API
+SQLSetEnvAttr(HENV EnvironmentHandle,
+ SQLINTEGER Attribute, PTR Value,
+ SQLINTEGER StringLength)
+{
+ EnvironmentClass *env = (EnvironmentClass *) EnvironmentHandle;
+
+ mylog("[[SQLSetEnvAttr]] att=%d,%u\n", Attribute, Value);
+ switch (Attribute)
+ {
+ case SQL_ATTR_CONNECTION_POOLING:
+ if ((SQLUINTEGER) Value == SQL_CP_OFF)
+ return SQL_SUCCESS;
+ break;
+ case SQL_ATTR_CP_MATCH:
+ /* *((unsigned int *) Value) = SQL_CP_RELAXED_MATCH; */
+ return SQL_SUCCESS;
+ case SQL_ATTR_ODBC_VERSION:
+ if ((SQLUINTEGER) Value == SQL_OV_ODBC2)
+ return SQL_SUCCESS;
+ break;
+ case SQL_ATTR_OUTPUT_NTS:
+ if ((SQLUINTEGER) Value == SQL_TRUE)
+ return SQL_SUCCESS;
+ break;
+ default:
+ env->errornumber = CONN_INVALID_ARGUMENT_NO;
+ return SQL_ERROR;
+ }
+ env->errornumber = CONN_OPTION_VALUE_CHANGED;
+ env->errormsg = "SetEnv changed to ";
+ return SQL_SUCCESS_WITH_INFO;
+}
+
+/* SQLSet(Param/Scroll/Stmt)Option -> SQLSetStmtAttr */
+RETCODE SQL_API
+SQLSetStmtAttr(HSTMT StatementHandle,
+ SQLINTEGER Attribute, PTR Value,
+ SQLINTEGER StringLength)
+{
+ static char *func = "SQLSetStmtAttr";
+ StatementClass *stmt = (StatementClass *) StatementHandle;
+ UDWORD rowcount;
+
+ mylog("[[%s]] %d,%u\n", func, Attribute, Value);
+ switch (Attribute)
+ {
+ case SQL_ATTR_PARAMSET_SIZE: /* 22 */
+ return PGAPI_ParamOptions(StatementHandle, (UWORD) Value, &rowcount);
+ case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */
+ case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */
+
+ case SQL_ATTR_CURSOR_SCROLLABLE: /* -1 */
+ case SQL_ATTR_CURSOR_SENSITIVITY: /* -2 */
+
+ case SQL_ATTR_ENABLE_AUTO_IPD: /* 15 */
+
+ case SQL_ATTR_APP_ROW_DESC: /* 10010 */
+ case SQL_ATTR_APP_PARAM_DESC: /* 10011 */
+ case SQL_ATTR_AUTO_IPD: /* 10001 */
+ /* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */
+ case SQL_ATTR_IMP_ROW_DESC: /* 10012 */
+ case SQL_ATTR_IMP_PARAM_DESC: /* 10013 */
+ case SQL_ATTR_METADATA_ID: /* 10014 */
+
+ /*
+ * case SQL_ATTR_PREDICATE_PTR: case
+ * SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR:
+ */
+ case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */
+ case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */
+ case SQL_ATTR_PARAM_OPERATION_PTR: /* 19 */
+ case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */
+ case SQL_ATTR_ROW_OPERATION_PTR: /* 24 */
+ stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
+ stmt->errormsg = "Unsupported statement option (Set)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+
+ case SQL_ATTR_FETCH_BOOKMARK_PTR: /* 16 */
+ stmt->options.bookmark_ptr = Value;
+
+ break;
+ case SQL_ATTR_ROW_STATUS_PTR: /* 25 */
+ stmt->options.rowStatusArray = (SQLUSMALLINT *) Value;
+
+ break;
+ case SQL_ATTR_ROWS_FETCHED_PTR: /* 26 */
+ stmt->options.rowsFetched = (SQLUINTEGER *) Value;
+
+ break;
+ case SQL_ATTR_ROW_ARRAY_SIZE: /* 27 */
+ stmt->options.rowset_size = (SQLUINTEGER) Value;
+
+ break;
+ default:
+ return PGAPI_SetStmtOption(StatementHandle, (UWORD) Attribute, (UDWORD) Value);
+ }
+ return SQL_SUCCESS;
+}
+
+#define SQL_FUNC_ESET(pfExists, uwAPI) \
+ (*(((UWORD*) (pfExists)) + ((uwAPI) >> 4)) \
+ |= (1 << ((uwAPI) & 0x000F)) \
+ )
+RETCODE SQL_API
+PGAPI_GetFunctions30(HDBC hdbc, UWORD fFunction, UWORD FAR * pfExists)
+{
+ if (fFunction != SQL_API_ODBC3_ALL_FUNCTIONS)
+ return SQL_ERROR;
+ memset(pfExists, 0, sizeof(UWORD) * SQL_API_ODBC3_ALL_FUNCTIONS_SIZE);
+
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLALLOCCONNECT); 1 deprecated */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLALLOCENV); 2 deprecated */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLALLOCSTMT); 3 deprecated */
+
+ /*
+ * for (i = SQL_API_SQLBINDCOL; i <= 23; i++) SQL_FUNC_ESET(pfExists,
+ * i);
+ */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLBINDCOL); /* 4 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLCANCEL); /* 5 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLCOLATTRIBUTE); /* 6 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLCONNECT); /* 7 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLDESCRIBECOL); /* 8 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLDISCONNECT); /* 9 */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLERROR); 10 deprecated */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLEXECDIRECT); /* 11 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLEXECUTE); /* 12 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLFETCH); /* 13 */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLFREECONNECT); 14 deprecated */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLFREEENV); 15 deprecated */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLFREESTMT); /* 16 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETCURSORNAME); /* 17 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLNUMRESULTCOLS); /* 18 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLPREPARE); /* 19 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLROWCOUNT); /* 20 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETCURSORNAME); /* 21 */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLSETPARAM); 22 deprecated */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLTRANSACT); 23 deprecated */
+
+ /*
+ * for (i = 40; i < SQL_API_SQLEXTENDEDFETCH; i++)
+ * SQL_FUNC_ESET(pfExists, i);
+ */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLCOLUMNS); /* 40 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLDRIVERCONNECT); /* 41 */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLGETCONNECTOPTION); 42 deprecated */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDATA); /* 43 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETFUNCTIONS); /* 44 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETINFO); /* 45 */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLGETSTMTOPTION); 46 deprecated */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETTYPEINFO); /* 47 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLPARAMDATA); /* 48 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLPUTDATA); /* 49 */
+
+ /*
+ * SQL_FUNC_ESET(pfExists, SQL_API_SQLSETCONNECTIONOPTION); 50
+ * deprecated
+ */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLSETSTMTOPTION); 51 deprecated */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLSPECIALCOLUMNS); /* 52 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLSTATISTICS); /* 53 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLTABLES); /* 54 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLBROWSECONNECT); /* 55 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLCOLUMNPRIVILEGES); /* 56 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLDATASOURCES); /* 57 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLDESCRIBEPARAM); /* 58 */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLEXTENDEDFETCH); 59 deprecated */
+
+ /*
+ * for (++i; i < SQL_API_SQLBINDPARAMETER; i++)
+ * SQL_FUNC_ESET(pfExists, i);
+ */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLFOREIGNKEYS); /* 60 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLMORERESULTS); /* 61 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLNATIVESQL); /* 62 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLNUMPARAMS); /* 63 */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLPARAMOPTIONS); 64 deprecated */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLPRIMARYKEYS); /* 65 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLPROCEDURECOLUMNS); /* 66 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLPROCEDURES); /* 67 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETPOS); /* 68 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETSCROLLOPTIONS); /* 69 deprecated */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLTABLEPRIVILEGES); /* 70 */
+ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLDRIVERS); */ /* 71 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLBINDPARAMETER); /* 72 */
+
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLALLOCHANDLE); /* 1001 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLBINDPARAM); /* 1002 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLCLOSECURSOR); /* 1003 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLCOPYDESC); /* 1004 not implemented
+ * yet */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLENDTRAN); /* 1005 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLFREEHANDLE); /* 1006 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETCONNECTATTR); /* 1007 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDESCFIELD); /* 1008 not implemented
+ * yet */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDESCREC); /* 1009 not implemented
+ * yet */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDIAGFIELD); /* 1010 not implemented
+ * yet */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDIAGREC); /* 1011 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETENVATTR); /* 1012 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETSTMTATTR); /* 1014 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETCONNECTATTR); /* 1016 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETDESCFIELD); /* 1017 not implemeted
+ * yet */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETDESCREC); /* 1018 not implemented
+ * yet */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETENVATTR); /* 1019 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETSTMTATTR); /* 1020 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLFETCHSCROLL); /* 1021 */
+ SQL_FUNC_ESET(pfExists, SQL_API_SQLBULKOPERATIONS); /* 24 not implemented
+ * yet */
+
+ return SQL_SUCCESS;
+}
+RETCODE SQL_API
+PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue,
+ SWORD cbInfoValueMax, SWORD FAR * pcbInfoValue)
+{
+ static char *func = "PGAPI_GetInfo30";
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ char *p = NULL;
+ int len = 0,
+ value = 0;
+ RETCODE result;
+
+ switch (fInfoType)
+ {
+ case SQL_DYNAMIC_CURSOR_ATTRIBUTES1:
+ len = 4;
+ value = 0;
+ break;
+ case SQL_DYNAMIC_CURSOR_ATTRIBUTES2:
+ len = 4;
+ value = 0;
+ break;
+
+ case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1:
+ len = 4;
+ value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE |
+ SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK;
+ break;
+ case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2:
+ len = 4;
+ value = 0;
+ break;
+ case SQL_KEYSET_CURSOR_ATTRIBUTES1:
+ len = 4;
+ value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE
+ | SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK
+ | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION
+ | SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE
+ | SQL_CA1_POS_REFRESH
+ | SQL_CA1_BULK_ADD
+ | SQL_CA1_BULK_UPDATE_BY_BOOKMARK
+ | SQL_CA1_BULK_DELETE_BY_BOOKMARK
+ | SQL_CA1_BULK_FETCH_BY_BOOKMARK
+ ;
+ break;
+ case SQL_KEYSET_CURSOR_ATTRIBUTES2:
+ len = 4;
+ value = SQL_CA2_OPT_ROWVER_CONCURRENCY |
+ SQL_CA2_SENSITIVITY_ADDITIONS |
+ SQL_CA2_SENSITIVITY_DELETIONS |
+ SQL_CA2_SENSITIVITY_UPDATES;
+ break;
+
+ case SQL_STATIC_CURSOR_ATTRIBUTES1:
+ len = 4;
+ value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE |
+ SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK |
+ SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION |
+ SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE |
+ SQL_CA1_POS_REFRESH;
+ break;
+ case SQL_STATIC_CURSOR_ATTRIBUTES2:
+ len = 4;
+ value = SQL_CA2_OPT_ROWVER_CONCURRENCY |
+ SQL_CA2_SENSITIVITY_ADDITIONS |
+ SQL_CA2_SENSITIVITY_DELETIONS |
+ SQL_CA2_SENSITIVITY_UPDATES;
+ break;
+ default:
+ /* unrecognized key */
+ conn->errormsg = "Unrecognized key passed to SQLGetInfo.";
+ conn->errornumber = CONN_NOT_IMPLEMENTED_ERROR;
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+ result = SQL_SUCCESS;
+ if (p)
+ {
+ /* char/binary data */
+ len = strlen(p);
+
+ if (rgbInfoValue)
+ {
+ strncpy_null((char *) rgbInfoValue, p, (size_t) cbInfoValueMax);
+
+ if (len >= cbInfoValueMax)
+ {
+ result = SQL_SUCCESS_WITH_INFO;
+ conn->errornumber = STMT_TRUNCATED;
+ conn->errormsg = "The buffer was too small for tthe InfoValue.";
+ }
+ }
+ }
+ else
+ {
+ /* numeric data */
+ if (rgbInfoValue)
+ {
+ if (len == 2)
+ *((WORD *) rgbInfoValue) = (WORD) value;
+ else if (len == 4)
+ *((DWORD *) rgbInfoValue) = (DWORD) value;
+ }
+ }
+
+ if (pcbInfoValue)
+ *pcbInfoValue = len;
+ return result;
+}
--- /dev/null
+/*--------
+ * Module: options.c
+ *
+ * Description: This module contains routines for getting/setting
+ * connection and statement options.
+ *
+ * Classes: n/a
+ *
+ * API functions: SQLSetConnectOption, SQLSetStmtOption, SQLGetConnectOption,
+ * SQLGetStmtOption
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *--------
+ */
+
+#include "psqlodbc.h"
+#include <string.h>
+
+#include "environ.h"
+#include "connection.h"
+#include "statement.h"
+#include "qresult.h"
+#include "pgapifunc.h"
+
+
+
+RETCODE set_statement_option(ConnectionClass *conn,
+ StatementClass *stmt,
+ UWORD fOption,
+ UDWORD vParam);
+
+
+RETCODE
+set_statement_option(ConnectionClass *conn,
+ StatementClass *stmt,
+ UWORD fOption,
+ UDWORD vParam)
+{
+ static char *func = "set_statement_option";
+ char changed = FALSE;
+ ConnInfo *ci = NULL;
+
+ if (conn)
+ ci = &(conn->connInfo);
+ else if (stmt)
+ ci = &(SC_get_conn(stmt)->connInfo);
+ switch (fOption)
+ {
+ case SQL_ASYNC_ENABLE: /* ignored */
+ break;
+
+ case SQL_BIND_TYPE:
+ /* now support multi-column and multi-row binding */
+ if (conn)
+ conn->stmtOptions.bind_size = vParam;
+ if (stmt)
+ stmt->options.bind_size = vParam;
+ break;
+
+ case SQL_CONCURRENCY:
+
+ /*
+ * positioned update isn't supported so cursor concurrency is
+ * read-only
+ */
+ mylog("SetStmtOption(): SQL_CONCURRENCY = %d\n", vParam);
+ if (ci->drivers.lie || vParam == SQL_CONCUR_READ_ONLY || vParam == SQL_CONCUR_ROWVER)
+ {
+ if (conn)
+ conn->stmtOptions.scroll_concurrency = vParam;
+ if (stmt)
+ stmt->options.scroll_concurrency = vParam;
+ }
+ else
+ {
+ if (conn)
+ conn->stmtOptions.scroll_concurrency = SQL_CONCUR_ROWVER;
+ if (stmt)
+ stmt->options.scroll_concurrency = SQL_CONCUR_ROWVER;
+ changed = TRUE;
+ }
+ break;
+
+ case SQL_CURSOR_TYPE:
+
+ /*
+ * if declare/fetch, then type can only be forward. otherwise,
+ * it can only be forward or static.
+ */
+ mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam);
+
+ if (ci->drivers.lie)
+ {
+ if (conn)
+ conn->stmtOptions.cursor_type = vParam;
+ if (stmt)
+ stmt->options.cursor_type = vParam;
+ }
+ else
+ {
+ if (ci->drivers.use_declarefetch)
+ {
+ if (conn)
+ conn->stmtOptions.cursor_type = SQL_CURSOR_FORWARD_ONLY;
+ if (stmt)
+ stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
+
+ if (vParam != SQL_CURSOR_FORWARD_ONLY)
+ changed = TRUE;
+ }
+ else
+ {
+ if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC)
+ {
+ if (conn)
+ conn->stmtOptions.cursor_type = vParam; /* valid type */
+ if (stmt)
+ stmt->options.cursor_type = vParam; /* valid type */
+ }
+ else
+ {
+ if (conn)
+ conn->stmtOptions.cursor_type = SQL_CURSOR_STATIC;
+ if (stmt)
+ stmt->options.cursor_type = SQL_CURSOR_STATIC;
+
+ changed = TRUE;
+ }
+ }
+ }
+ break;
+
+ case SQL_KEYSET_SIZE: /* ignored, but saved and returned */
+ mylog("SetStmtOption(): SQL_KEYSET_SIZE, vParam = %d\n", vParam);
+
+ if (conn)
+ conn->stmtOptions.keyset_size = vParam;
+ if (stmt)
+ stmt->options.keyset_size = vParam;
+
+ break;
+
+ /*-------
+ * if (ci->drivers.lie)
+ * stmt->keyset_size = vParam;
+ * else
+ * {
+ * stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+ * stmt->errormsg = "Driver does not support keyset size option";
+ * SC_log_error(func, "", stmt);
+ * return SQL_ERROR;
+ * }
+ *-------
+ */
+
+ case SQL_MAX_LENGTH: /* ignored, but saved */
+ mylog("SetStmtOption(): SQL_MAX_LENGTH, vParam = %d\n", vParam);
+ if (conn)
+ conn->stmtOptions.maxLength = vParam;
+ if (stmt)
+ stmt->options.maxLength = vParam;
+ break;
+
+ case SQL_MAX_ROWS: /* ignored, but saved */
+ mylog("SetStmtOption(): SQL_MAX_ROWS, vParam = %d\n", vParam);
+ if (conn)
+ conn->stmtOptions.maxRows = vParam;
+ if (stmt)
+ stmt->options.maxRows = vParam;
+ break;
+
+ case SQL_NOSCAN: /* ignored */
+ mylog("SetStmtOption: SQL_NOSCAN, vParam = %d\n", vParam);
+ break;
+
+ case SQL_QUERY_TIMEOUT: /* ignored */
+ mylog("SetStmtOption: SQL_QUERY_TIMEOUT, vParam = %d\n", vParam);
+ /* "0" returned in SQLGetStmtOption */
+ break;
+
+ case SQL_RETRIEVE_DATA:
+ mylog("SetStmtOption(): SQL_RETRIEVE_DATA, vParam = %d\n", vParam);
+ if (conn)
+ conn->stmtOptions.retrieve_data = vParam;
+ if (stmt)
+ stmt->options.retrieve_data = vParam;
+ break;
+
+ case SQL_ROWSET_SIZE:
+ mylog("SetStmtOption(): SQL_ROWSET_SIZE, vParam = %d\n", vParam);
+
+ /*
+ * Save old rowset size for SQLExtendedFetch purposes If the
+ * rowset_size is being changed since the last call to fetch
+ * rows.
+ */
+
+ if (stmt && stmt->save_rowset_size <= 0 && stmt->last_fetch_count > 0)
+ stmt->save_rowset_size = stmt->options.rowset_size;
+
+ if (vParam < 1)
+ {
+ vParam = 1;
+ changed = TRUE;
+ }
+
+ if (conn)
+ conn->stmtOptions.rowset_size = vParam;
+ if (stmt)
+ stmt->options.rowset_size = vParam;
+ break;
+
+ case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */
+ if (stmt)
+ {
+ stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+ stmt->errormsg = "Simulated positioned update/delete not supported. Use the cursor library.";
+ SC_log_error(func, "", stmt);
+ }
+ if (conn)
+ {
+ conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+ conn->errormsg = "Simulated positioned update/delete not supported. Use the cursor library.";
+ CC_log_error(func, "", conn);
+ }
+ return SQL_ERROR;
+
+ case SQL_USE_BOOKMARKS:
+ if (stmt)
+ stmt->options.use_bookmarks = vParam;
+ if (conn)
+ conn->stmtOptions.use_bookmarks = vParam;
+ break;
+
+ default:
+ {
+ char option[64];
+
+ if (stmt)
+ {
+ stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+ stmt->errormsg = "Unknown statement option (Set)";
+ sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
+ SC_log_error(func, option, stmt);
+ }
+ if (conn)
+ {
+ conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+ conn->errormsg = "Unknown statement option (Set)";
+ sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
+ CC_log_error(func, option, conn);
+ }
+
+ return SQL_ERROR;
+ }
+ }
+
+ if (changed)
+ {
+ if (stmt)
+ {
+ stmt->errormsg = "Requested value changed.";
+ stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
+ }
+ if (conn)
+ {
+ conn->errormsg = "Requested value changed.";
+ conn->errornumber = STMT_OPTION_VALUE_CHANGED;
+ }
+ return SQL_SUCCESS_WITH_INFO;
+ }
+ else
+ return SQL_SUCCESS;
+}
+
+
+/* Implements only SQL_AUTOCOMMIT */
+RETCODE SQL_API
+PGAPI_SetConnectOption(
+ HDBC hdbc,
+ UWORD fOption,
+ UDWORD vParam)
+{
+ static char *func = "PGAPI_SetConnectOption";
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ char changed = FALSE;
+ RETCODE retval;
+ int i;
+
+ mylog("%s: entering fOption = %d vParam = %d\n", func, fOption, vParam);
+ if (!conn)
+ {
+ CC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ switch (fOption)
+ {
+ /*
+ * Statement Options (apply to all stmts on the connection and
+ * become defaults for new stmts)
+ */
+ case SQL_ASYNC_ENABLE:
+ case SQL_BIND_TYPE:
+ case SQL_CONCURRENCY:
+ case SQL_CURSOR_TYPE:
+ case SQL_KEYSET_SIZE:
+ case SQL_MAX_LENGTH:
+ case SQL_MAX_ROWS:
+ case SQL_NOSCAN:
+ case SQL_QUERY_TIMEOUT:
+ case SQL_RETRIEVE_DATA:
+ case SQL_ROWSET_SIZE:
+ case SQL_SIMULATE_CURSOR:
+ case SQL_USE_BOOKMARKS:
+
+ /* Affect all current Statements */
+ for (i = 0; i < conn->num_stmts; i++)
+ {
+ if (conn->stmts[i])
+ set_statement_option(NULL, conn->stmts[i], fOption, vParam);
+ }
+
+ /*
+ * Become the default for all future statements on this
+ * connection
+ */
+ retval = set_statement_option(conn, NULL, fOption, vParam);
+
+ if (retval == SQL_SUCCESS_WITH_INFO)
+ changed = TRUE;
+ else if (retval == SQL_ERROR)
+ return SQL_ERROR;
+
+ break;
+
+ /*
+ * Connection Options
+ */
+
+ case SQL_ACCESS_MODE: /* ignored */
+ break;
+
+ case SQL_AUTOCOMMIT:
+ if (CC_is_in_trans(conn))
+ {
+ conn->errormsg = "Cannot switch commit mode while a transaction is in progress";
+ conn->errornumber = CONN_TRANSACT_IN_PROGRES;
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+
+ mylog("PGAPI_SetConnectOption: AUTOCOMMIT: transact_status=%d, vparam=%d\n", conn->transact_status, vParam);
+
+ switch (vParam)
+ {
+ case SQL_AUTOCOMMIT_OFF:
+ CC_set_autocommit_off(conn);
+ break;
+
+ case SQL_AUTOCOMMIT_ON:
+ CC_set_autocommit_on(conn);
+ break;
+
+ default:
+ conn->errormsg = "Illegal parameter value for SQL_AUTOCOMMIT";
+ conn->errornumber = CONN_INVALID_ARGUMENT_NO;
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+ break;
+
+ case SQL_CURRENT_QUALIFIER: /* ignored */
+ break;
+
+ case SQL_LOGIN_TIMEOUT: /* ignored */
+ break;
+
+ case SQL_PACKET_SIZE: /* ignored */
+ break;
+
+ case SQL_QUIET_MODE: /* ignored */
+ break;
+
+ case SQL_TXN_ISOLATION: /* ignored */
+ break;
+
+ /* These options should be handled by driver manager */
+ case SQL_ODBC_CURSORS:
+ case SQL_OPT_TRACE:
+ case SQL_OPT_TRACEFILE:
+ case SQL_TRANSLATE_DLL:
+ case SQL_TRANSLATE_OPTION:
+ CC_log_error(func, "This connect option (Set) is only used by the Driver Manager", conn);
+ break;
+
+ default:
+ {
+ char option[64];
+
+ conn->errormsg = "Unknown connect option (Set)";
+ conn->errornumber = CONN_UNSUPPORTED_OPTION;
+ sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
+ if (fOption == 30002 && vParam)
+ {
+ if (strcmp((char *) vParam, "Microsoft Jet") == 0)
+ {
+ conn->errornumber = 0;
+ conn->ms_jet = 1;
+ return SQL_SUCCESS;
+ }
+ }
+ CC_log_error(func, option, conn);
+ return SQL_ERROR;
+ }
+ }
+
+ if (changed)
+ {
+ conn->errornumber = CONN_OPTION_VALUE_CHANGED;
+ conn->errormsg = "Requested value changed.";
+ return SQL_SUCCESS_WITH_INFO;
+ }
+ else
+ return SQL_SUCCESS;
+}
+
+
+/* This function just can tell you whether you are in Autcommit mode or not */
+RETCODE SQL_API
+PGAPI_GetConnectOption(
+ HDBC hdbc,
+ UWORD fOption,
+ PTR pvParam)
+{
+ static char *func = "PGAPI_GetConnectOption";
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ ConnInfo *ci = &(conn->connInfo);
+
+ mylog("%s: entering...\n", func);
+
+ if (!conn)
+ {
+ CC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ switch (fOption)
+ {
+ case SQL_ACCESS_MODE: /* NOT SUPPORTED */
+ *((UDWORD *) pvParam) = SQL_MODE_READ_WRITE;
+ break;
+
+ case SQL_AUTOCOMMIT:
+ *((UDWORD *) pvParam) = (UDWORD) (CC_is_in_autocommit(conn) ?
+ SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
+ break;
+
+ case SQL_CURRENT_QUALIFIER: /* don't use qualifiers */
+ if (pvParam)
+ strcpy(pvParam, "");
+
+ break;
+
+ case SQL_LOGIN_TIMEOUT: /* NOT SUPPORTED */
+ *((UDWORD *) pvParam) = 0;
+ break;
+
+ case SQL_PACKET_SIZE: /* NOT SUPPORTED */
+ *((UDWORD *) pvParam) = ci->drivers.socket_buffersize;
+ break;
+
+ case SQL_QUIET_MODE: /* NOT SUPPORTED */
+ *((UDWORD *) pvParam) = (UDWORD) NULL;
+ break;
+
+ case SQL_TXN_ISOLATION: /* NOT SUPPORTED */
+ *((UDWORD *) pvParam) = SQL_TXN_SERIALIZABLE;
+ break;
+
+ /* These options should be handled by driver manager */
+ case SQL_ODBC_CURSORS:
+ case SQL_OPT_TRACE:
+ case SQL_OPT_TRACEFILE:
+ case SQL_TRANSLATE_DLL:
+ case SQL_TRANSLATE_OPTION:
+ CC_log_error(func, "This connect option (Get) is only used by the Driver Manager", conn);
+ break;
+
+ default:
+ {
+ char option[64];
+
+ conn->errormsg = "Unknown connect option (Get)";
+ conn->errornumber = CONN_UNSUPPORTED_OPTION;
+ sprintf(option, "fOption=%d", fOption);
+ CC_log_error(func, option, conn);
+ return SQL_ERROR;
+ break;
+ }
+ }
+
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_SetStmtOption(
+ HSTMT hstmt,
+ UWORD fOption,
+ UDWORD vParam)
+{
+ static char *func = "PGAPI_SetStmtOption";
+ StatementClass *stmt = (StatementClass *) hstmt;
+
+ mylog("%s: entering...\n", func);
+
+ /*
+ * Though we could fake Access out by just returning SQL_SUCCESS all
+ * the time, but it tries to set a huge value for SQL_MAX_LENGTH and
+ * expects the driver to reduce it to the real value.
+ */
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ return set_statement_option(NULL, stmt, fOption, vParam);
+}
+
+
+RETCODE SQL_API
+PGAPI_GetStmtOption(
+ HSTMT hstmt,
+ UWORD fOption,
+ PTR pvParam)
+{
+ static char *func = "PGAPI_GetStmtOption";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ QResultClass *res;
+ ConnInfo *ci = &(SC_get_conn(stmt)->connInfo);
+
+ mylog("%s: entering...\n", func);
+
+ /*
+ * thought we could fake Access out by just returning SQL_SUCCESS all
+ * the time, but it tries to set a huge value for SQL_MAX_LENGTH and
+ * expects the driver to reduce it to the real value
+ */
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ switch (fOption)
+ {
+ case SQL_GET_BOOKMARK:
+ case SQL_ROW_NUMBER:
+
+ res = stmt->result;
+
+ if (stmt->manual_result || !ci->drivers.use_declarefetch)
+ {
+ /* make sure we're positioned on a valid row */
+ if ((stmt->currTuple < 0) ||
+ (stmt->currTuple >= QR_get_num_tuples(res)))
+ {
+ stmt->errormsg = "Not positioned on a valid row.";
+ stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+ else
+ {
+ if (stmt->currTuple == -1 || !res || !res->tupleField)
+ {
+ stmt->errormsg = "Not positioned on a valid row.";
+ stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+
+ if (fOption == SQL_GET_BOOKMARK && stmt->options.use_bookmarks == SQL_UB_OFF)
+ {
+ stmt->errormsg = "Operation invalid because use bookmarks not enabled.";
+ stmt->errornumber = STMT_OPERATION_INVALID;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ *((UDWORD *) pvParam) = SC_get_bookmark(stmt);
+
+ break;
+
+ case SQL_ASYNC_ENABLE: /* NOT SUPPORTED */
+ *((SDWORD *) pvParam) = SQL_ASYNC_ENABLE_OFF;
+ break;
+
+ case SQL_BIND_TYPE:
+ *((SDWORD *) pvParam) = stmt->options.bind_size;
+ break;
+
+ case SQL_CONCURRENCY: /* NOT REALLY SUPPORTED */
+ mylog("GetStmtOption(): SQL_CONCURRENCY\n");
+ *((SDWORD *) pvParam) = stmt->options.scroll_concurrency;
+ break;
+
+ case SQL_CURSOR_TYPE: /* PARTIAL SUPPORT */
+ mylog("GetStmtOption(): SQL_CURSOR_TYPE\n");
+ *((SDWORD *) pvParam) = stmt->options.cursor_type;
+ break;
+
+ case SQL_KEYSET_SIZE: /* NOT SUPPORTED, but saved */
+ mylog("GetStmtOption(): SQL_KEYSET_SIZE\n");
+ *((SDWORD *) pvParam) = stmt->options.keyset_size;
+ break;
+
+ case SQL_MAX_LENGTH: /* NOT SUPPORTED, but saved */
+ *((SDWORD *) pvParam) = stmt->options.maxLength;
+ break;
+
+ case SQL_MAX_ROWS: /* NOT SUPPORTED, but saved */
+ *((SDWORD *) pvParam) = stmt->options.maxRows;
+ mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->options.maxRows);
+ break;
+
+ case SQL_NOSCAN: /* NOT SUPPORTED */
+ *((SDWORD *) pvParam) = SQL_NOSCAN_ON;
+ break;
+
+ case SQL_QUERY_TIMEOUT: /* NOT SUPPORTED */
+ *((SDWORD *) pvParam) = 0;
+ break;
+
+ case SQL_RETRIEVE_DATA:
+ *((SDWORD *) pvParam) = stmt->options.retrieve_data;
+ break;
+
+ case SQL_ROWSET_SIZE:
+ *((SDWORD *) pvParam) = stmt->options.rowset_size;
+ break;
+
+ case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */
+ *((SDWORD *) pvParam) = SQL_SC_NON_UNIQUE;
+ break;
+
+ case SQL_USE_BOOKMARKS:
+ *((SDWORD *) pvParam) = stmt->options.use_bookmarks;
+ break;
+
+ default:
+ {
+ char option[64];
+
+ stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+ stmt->errormsg = "Unknown statement option (Get)";
+ sprintf(option, "fOption=%d", fOption);
+ SC_log_error(func, option, stmt);
+ return SQL_ERROR;
+ }
+ }
+
+ return SQL_SUCCESS;
+}
--- /dev/null
+/*--------
+ * Module: parse.c
+ *
+ * Description: This module contains routines related to parsing SQL
+ * statements. This can be useful for two reasons:
+ *
+ * 1. So the query does not actually have to be executed
+ * to return data about it
+ *
+ * 2. To be able to return information about precision,
+ * nullability, aliases, etc. in the functions
+ * SQLDescribeCol and SQLColAttributes. Currently,
+ * Postgres doesn't return any information about
+ * these things in a query.
+ *
+ * Classes: none
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *--------
+ */
+/* Multibyte support Eiji Tokuya 2001-03-15 */
+
+#include "psqlodbc.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "statement.h"
+#include "connection.h"
+#include "qresult.h"
+#include "pgtypes.h"
+#include "pgapifunc.h"
+
+#ifdef MULTIBYTE
+#include "multibyte.h"
+#endif
+
+#define FLD_INCR 32
+#define TAB_INCR 8
+#define COL_INCR 16
+
+char *getNextToken(char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric);
+void getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k);
+char searchColInfo(COL_INFO *col_info, FIELD_INFO *fi);
+
+
+char *
+getNextToken(char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric)
+{
+ int i = 0;
+ int out = 0;
+ char qc,
+ in_escape = FALSE;
+
+ if (smax <= 1)
+ return NULL;
+
+ smax--;
+
+ /* skip leading delimiters */
+ while (isspace((unsigned char) s[i]) || s[i] == ',')
+ {
+ /* mylog("skipping '%c'\n", s[i]); */
+ i++;
+ }
+
+ if (s[i] == '\0')
+ {
+ token[0] = '\0';
+ return NULL;
+ }
+
+ if (quote)
+ *quote = FALSE;
+ if (dquote)
+ *dquote = FALSE;
+ if (numeric)
+ *numeric = FALSE;
+
+ /* get the next token */
+ while (!isspace((unsigned char) s[i]) && s[i] != ',' &&
+ s[i] != '\0' && out != smax)
+ {
+#ifdef MULTIBYTE
+ if (multibyte_char_check(s[i]) != 0)
+ {
+ token[out++] = s[i++];
+ continue;
+ }
+#endif
+ /* Handle quoted stuff */
+ if (out == 0 && (s[i] == '\"' || s[i] == '\''))
+ {
+ qc = s[i];
+ if (qc == '\"')
+ {
+ if (dquote)
+ *dquote = TRUE;
+ }
+ if (qc == '\'')
+ {
+ if (quote)
+ *quote = TRUE;
+ }
+
+ i++; /* dont return the quote */
+ while (s[i] != '\0' && out != smax)
+ {
+#ifdef MULTIBYTE
+ if (multibyte_char_check(s[i]) != 0)
+ {
+ token[out++] = s[i++];
+ continue;
+ }
+#endif
+ if (s[i] == qc && !in_escape)
+ break;
+ if (s[i] == '\\' && !in_escape)
+ in_escape = TRUE;
+ else
+ {
+ in_escape = FALSE;
+ token[out++] = s[i];
+ }
+ i++;
+ }
+ if (s[i] == qc)
+ i++;
+ break;
+ }
+
+ /* Check for numeric literals */
+ if (out == 0 && isdigit((unsigned char) s[i]))
+ {
+ if (numeric)
+ *numeric = TRUE;
+ token[out++] = s[i++];
+ while (isalnum((unsigned char) s[i]) || s[i] == '.')
+ token[out++] = s[i++];
+
+ break;
+ }
+
+ if (ispunct((unsigned char) s[i]) && s[i] != '_')
+ {
+ mylog("got ispunct: s[%d] = '%c'\n", i, s[i]);
+
+ if (out == 0)
+ {
+ token[out++] = s[i++];
+ break;
+ }
+ else
+ break;
+ }
+
+ if (out != smax)
+ token[out++] = s[i];
+
+ i++;
+ }
+
+ /* mylog("done -- s[%d] = '%c'\n", i, s[i]); */
+
+ token[out] = '\0';
+
+ /* find the delimiter */
+ while (isspace((unsigned char) s[i]))
+ i++;
+
+ /* return the most priority delimiter */
+ if (s[i] == ',')
+ {
+ if (delim)
+ *delim = s[i];
+ }
+ else if (s[i] == '\0')
+ {
+ if (delim)
+ *delim = '\0';
+ }
+ else
+ {
+ if (delim)
+ *delim = ' ';
+ }
+
+ /* skip trailing blanks */
+ while (isspace((unsigned char) s[i]))
+ i++;
+
+ return &s[i];
+}
+
+
+#if 0
+QR_set_num_fields(stmt->result, 14);
+QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
+QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
+QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+QR_set_field_info(stmt->result, 3, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+QR_set_field_info(stmt->result, 4, "DATA_TYPE", PG_TYPE_INT2, 2);
+QR_set_field_info(stmt->result, 5, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
+QR_set_field_info(stmt->result, 6, "PRECISION", PG_TYPE_INT4, 4);
+QR_set_field_info(stmt->result, 7, "LENGTH", PG_TYPE_INT4, 4);
+QR_set_field_info(stmt->result, 8, "SCALE", PG_TYPE_INT2, 2);
+QR_set_field_info(stmt->result, 9, "RADIX", PG_TYPE_INT2, 2);
+QR_set_field_info(stmt->result, 10, "NULLABLE", PG_TYPE_INT2, 2);
+QR_set_field_info(stmt->result, 11, "REMARKS", PG_TYPE_TEXT, 254);
+/* User defined fields */
+QR_set_field_info(stmt->result, 12, "DISPLAY_SIZE", PG_TYPE_INT4, 4);
+QR_set_field_info(stmt->result, 13, "FIELD_TYPE", PG_TYPE_INT4, 4);
+#endif
+
+void
+getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k)
+{
+ char *str;
+
+ if (fi->name[0] == '\0')
+ strcpy(fi->name, QR_get_value_manual(col_info->result, k, 3));
+
+ fi->type = atoi(QR_get_value_manual(col_info->result, k, 13));
+ fi->precision = atoi(QR_get_value_manual(col_info->result, k, 6));
+ fi->length = atoi(QR_get_value_manual(col_info->result, k, 7));
+ if (str = QR_get_value_manual(col_info->result, k, 8), str)
+ fi->scale = atoi(str);
+ else
+ fi->scale = -1;
+ fi->nullable = atoi(QR_get_value_manual(col_info->result, k, 10));
+ fi->display_size = atoi(QR_get_value_manual(col_info->result, k, 12));
+}
+
+
+char
+searchColInfo(COL_INFO *col_info, FIELD_INFO *fi)
+{
+ int k,
+ cmp;
+ char *col;
+
+ for (k = 0; k < QR_get_num_tuples(col_info->result); k++)
+ {
+ col = QR_get_value_manual(col_info->result, k, 3);
+ if (fi->dquote)
+ cmp = strcmp(col, fi->name);
+ else
+ cmp = stricmp(col, fi->name);
+ if (!cmp)
+ {
+ if (!fi->dquote)
+ strcpy(fi->name, col);
+ getColInfo(col_info, fi, k);
+
+ mylog("PARSE: searchColInfo: \n");
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+char
+parse_statement(StatementClass *stmt)
+{
+ static char *func = "parse_statement";
+ char token[256];
+ char delim,
+ quote,
+ dquote,
+ numeric,
+ unquoted;
+ char *ptr,
+ *pptr = NULL;
+ char in_select = FALSE,
+ in_distinct = FALSE,
+ in_on = FALSE,
+ in_from = FALSE,
+ from_found = FALSE,
+ in_where = FALSE,
+ in_table = FALSE;
+ char in_field = FALSE,
+ in_expr = FALSE,
+ in_func = FALSE,
+ in_dot = FALSE,
+ in_as = FALSE;
+ int j,
+ i,
+ k = 0,
+ n,
+ first_where = 0,
+ blevel = 0;
+ FIELD_INFO **fi;
+ TABLE_INFO **ti;
+ char parse;
+ ConnectionClass *conn = stmt->hdbc;
+ HSTMT hcol_stmt;
+ StatementClass *col_stmt;
+ RETCODE result;
+
+ mylog("%s: entering...\n", func);
+
+ ptr = stmt->statement;
+ fi = stmt->fi;
+ ti = stmt->ti;
+
+ stmt->nfld = 0;
+ stmt->ntab = 0;
+
+#ifdef MULTIBYTE
+ multibyte_init();
+#endif
+ while (pptr = ptr, (ptr = getNextToken(pptr, token, sizeof(token), &delim, "e, &dquote, &numeric)) != NULL)
+ {
+ unquoted = !(quote || dquote);
+
+ mylog("unquoted=%d, quote=%d, dquote=%d, numeric=%d, delim='%c', token='%s', ptr='%s'\n", unquoted, quote, dquote, numeric, delim, token, ptr);
+
+ if (in_select && unquoted && blevel == 0)
+ {
+ if (!stricmp(token, "distinct"))
+ {
+ in_distinct = TRUE;
+
+ mylog("DISTINCT\n");
+ continue;
+ }
+ if (!stricmp(token, "into"))
+ {
+ in_select = FALSE;
+ mylog("INTO\n");
+ stmt->statement_type = STMT_TYPE_CREATE;
+ stmt->parse_status = STMT_PARSE_FATAL;
+ return FALSE;
+ }
+ if (!stricmp(token, "from"))
+ {
+ in_select = FALSE;
+ in_from = TRUE;
+ if (!from_found &&
+ (!strnicmp(pptr, "from", 4)))
+ {
+ mylog("First ");
+ from_found = TRUE;
+ }
+
+ mylog("FROM\n");
+ continue;
+ }
+ }
+ if (unquoted && blevel == 0)
+ {
+ if ((!stricmp(token, "where") ||
+ !stricmp(token, "union") ||
+ !stricmp(token, "intersect") ||
+ !stricmp(token, "except") ||
+ !stricmp(token, "order") ||
+ !stricmp(token, "group") ||
+ !stricmp(token, "having")))
+ {
+ in_select = FALSE;
+ in_from = FALSE;
+ in_where = TRUE;
+
+ if (!first_where &&
+ (!stricmp(token, "where")))
+ first_where = ptr - stmt->statement;
+
+ mylog("WHERE...\n");
+ break;
+ }
+ }
+ if (in_select && (in_expr || in_func))
+ {
+ /* just eat the expression */
+ mylog("in_expr=%d or func=%d\n", in_expr, in_func);
+
+ if (unquoted)
+ {
+ if (token[0] == '(')
+ {
+ blevel++;
+ mylog("blevel++ = %d\n", blevel);
+ }
+ else if (token[0] == ')')
+ {
+ blevel--;
+ mylog("blevel-- = %d\n", blevel);
+ }
+ }
+ if (blevel == 0)
+ {
+ if (delim == ',')
+ {
+ mylog("**** Got comma in_expr/func\n");
+ in_func = FALSE;
+ in_expr = FALSE;
+ in_field = FALSE;
+ }
+ else if (unquoted && !stricmp(token, "as"))
+ {
+ mylog("got AS in_expr\n");
+ in_func = FALSE;
+ in_expr = FALSE;
+ in_as = TRUE;
+ in_field = TRUE;
+ }
+ }
+ continue;
+ }
+
+ if (unquoted && !stricmp(token, "select"))
+ {
+ in_select = TRUE;
+
+ mylog("SELECT\n");
+ continue;
+ }
+ if (in_select)
+ {
+ if (in_distinct)
+ {
+ mylog("in distinct\n");
+
+ if (unquoted && !stricmp(token, "on"))
+ {
+ in_on = TRUE;
+ mylog("got on\n");
+ continue;
+ }
+ if (in_on)
+ {
+ in_distinct = FALSE;
+ in_on = FALSE;
+ continue; /* just skip the unique on field */
+ }
+ mylog("done distinct\n");
+ in_distinct = FALSE;
+ }
+
+ if (!in_field)
+ {
+ if (!token[0])
+ continue;
+
+ if (!(stmt->nfld % FLD_INCR))
+ {
+ mylog("reallocing at nfld=%d\n", stmt->nfld);
+ fi = (FIELD_INFO **) realloc(fi, (stmt->nfld + FLD_INCR) * sizeof(FIELD_INFO *));
+ if (!fi)
+ {
+ stmt->parse_status = STMT_PARSE_FATAL;
+ return FALSE;
+ }
+ stmt->fi = fi;
+ }
+
+ fi[stmt->nfld] = (FIELD_INFO *) malloc(sizeof(FIELD_INFO));
+ if (fi[stmt->nfld] == NULL)
+ {
+ stmt->parse_status = STMT_PARSE_FATAL;
+ return FALSE;
+ }
+
+ /* Initialize the field info */
+ memset(fi[stmt->nfld], 0, sizeof(FIELD_INFO));
+
+ /* double quotes are for qualifiers */
+ if (dquote)
+ fi[stmt->nfld]->dquote = TRUE;
+
+ if (quote)
+ {
+ fi[stmt->nfld]->quote = TRUE;
+ fi[stmt->nfld]->precision = strlen(token);
+ }
+ else if (numeric)
+ {
+ mylog("**** got numeric: nfld = %d\n", stmt->nfld);
+ fi[stmt->nfld]->numeric = TRUE;
+ }
+ else if (token[0] == '(')
+ { /* expression */
+ mylog("got EXPRESSION\n");
+ fi[stmt->nfld++]->expr = TRUE;
+ in_expr = TRUE;
+ blevel = 1;
+ continue;
+ }
+ else
+ {
+ strcpy(fi[stmt->nfld]->name, token);
+ fi[stmt->nfld]->dot[0] = '\0';
+ }
+ mylog("got field='%s', dot='%s'\n", fi[stmt->nfld]->name, fi[stmt->nfld]->dot);
+
+ if (delim == ',')
+ mylog("comma (1)\n");
+ else
+ in_field = TRUE;
+ stmt->nfld++;
+ continue;
+ }
+
+ /*
+ * We are in a field now
+ */
+ if (in_dot)
+ {
+ stmt->nfld--;
+ strcpy(fi[stmt->nfld]->dot, fi[stmt->nfld]->name);
+ strcpy(fi[stmt->nfld]->name, token);
+ stmt->nfld++;
+ in_dot = FALSE;
+
+ if (delim == ',')
+ {
+ mylog("in_dot: got comma\n");
+ in_field = FALSE;
+ }
+ continue;
+ }
+
+ if (in_as)
+ {
+ stmt->nfld--;
+ strcpy(fi[stmt->nfld]->alias, token);
+ mylog("alias for field '%s' is '%s'\n", fi[stmt->nfld]->name, fi[stmt->nfld]->alias);
+ in_as = FALSE;
+ in_field = FALSE;
+
+ stmt->nfld++;
+
+ if (delim == ',')
+ mylog("comma(2)\n");
+ continue;
+ }
+
+ /* Function */
+ if (token[0] == '(')
+ {
+ in_func = TRUE;
+ blevel = 1;
+ fi[stmt->nfld - 1]->func = TRUE;
+
+ /*
+ * name will have the function name -- maybe useful some
+ * day
+ */
+ mylog("**** got function = '%s'\n", fi[stmt->nfld - 1]->name);
+ continue;
+ }
+
+ if (token[0] == '.')
+ {
+ in_dot = TRUE;
+ mylog("got dot\n");
+ continue;
+ }
+
+ if (!stricmp(token, "as"))
+ {
+ in_as = TRUE;
+ mylog("got AS\n");
+ continue;
+ }
+
+ /* otherwise, it's probably an expression */
+ in_expr = TRUE;
+ fi[stmt->nfld - 1]->expr = TRUE;
+ fi[stmt->nfld - 1]->name[0] = '\0';
+ fi[stmt->nfld - 1]->precision = 0;
+ mylog("*** setting expression\n");
+ }
+
+ if (in_from)
+ {
+ if (!in_table)
+ {
+ if (!token[0])
+ continue;
+
+ if (!(stmt->ntab % TAB_INCR))
+ {
+ ti = (TABLE_INFO **) realloc(ti, (stmt->ntab + TAB_INCR) * sizeof(TABLE_INFO *));
+ if (!ti)
+ {
+ stmt->parse_status = STMT_PARSE_FATAL;
+ return FALSE;
+ }
+ stmt->ti = ti;
+ }
+ ti[stmt->ntab] = (TABLE_INFO *) malloc(sizeof(TABLE_INFO));
+ if (ti[stmt->ntab] == NULL)
+ {
+ stmt->parse_status = STMT_PARSE_FATAL;
+ return FALSE;
+ }
+
+ ti[stmt->ntab]->alias[0] = '\0';
+
+ strcpy(ti[stmt->ntab]->name, token);
+ if (!dquote)
+ {
+ char *ptr;
+
+ /* lower case table name */
+ for (ptr = ti[stmt->ntab]->name; *ptr; ptr++)
+ {
+#ifdef MULTIBYTE
+ if ((unsigned char) *ptr >= 0x80)
+ ptr++;
+ else
+#endif /* MULTIBYTE */
+ *ptr = tolower((unsigned char) *ptr);
+ }
+ }
+ mylog("got table = '%s'\n", ti[stmt->ntab]->name);
+
+ if (delim == ',')
+ mylog("more than 1 tables\n");
+ else
+ in_table = TRUE;
+ stmt->ntab++;
+ continue;
+ }
+
+ strcpy(ti[stmt->ntab - 1]->alias, token);
+ mylog("alias for table '%s' is '%s'\n", ti[stmt->ntab - 1]->name, ti[stmt->ntab - 1]->alias);
+ in_table = FALSE;
+ if (delim == ',')
+ mylog("more than 1 tables\n");
+ }
+ }
+
+ /*
+ * Resolve any possible field names with tables
+ */
+
+ parse = TRUE;
+
+ /* Resolve field names with tables */
+ for (i = 0; i < stmt->nfld; i++)
+ {
+ if (fi[i]->func || fi[i]->expr || fi[i]->numeric)
+ {
+ fi[i]->ti = NULL;
+ fi[i]->type = -1;
+ parse = FALSE;
+ continue;
+ }
+ else if (fi[i]->quote)
+ { /* handle as text */
+ fi[i]->ti = NULL;
+
+ /*
+ * fi[i]->type = PG_TYPE_TEXT; fi[i]->precision = 0; the
+ * following may be better
+ */
+ fi[i]->type = PG_TYPE_UNKNOWN;
+ if (fi[i]->precision == 0)
+ {
+ fi[i]->type = PG_TYPE_VARCHAR;
+ fi[i]->precision = 254;
+ }
+ fi[i]->length = fi[i]->precision;
+ continue;
+ }
+ /* it's a dot, resolve to table or alias */
+ else if (fi[i]->dot[0])
+ {
+ for (k = 0; k < stmt->ntab; k++)
+ {
+ if (!stricmp(ti[k]->name, fi[i]->dot))
+ {
+ fi[i]->ti = ti[k];
+ break;
+ }
+ else if (!stricmp(ti[k]->alias, fi[i]->dot))
+ {
+ fi[i]->ti = ti[k];
+ break;
+ }
+ }
+ }
+ else if (stmt->ntab == 1)
+ fi[i]->ti = ti[0];
+ }
+
+ mylog("--------------------------------------------\n");
+ mylog("nfld=%d, ntab=%d\n", stmt->nfld, stmt->ntab);
+
+ for (i = 0; i < stmt->nfld; i++)
+ {
+ mylog("Field %d: expr=%d, func=%d, quote=%d, dquote=%d, numeric=%d, name='%s', alias='%s', dot='%s'\n", i, fi[i]->expr, fi[i]->func, fi[i]->quote, fi[i]->dquote, fi[i]->numeric, fi[i]->name, fi[i]->alias, fi[i]->dot);
+ if (fi[i]->ti)
+ mylog(" ----> table_name='%s', table_alias='%s'\n", fi[i]->ti->name, fi[i]->ti->alias);
+ }
+
+ for (i = 0; i < stmt->ntab; i++)
+ mylog("Table %d: name='%s', alias='%s'\n", i, ti[i]->name, ti[i]->alias);
+
+
+ /*
+ * Now save the SQLColumns Info for the parse tables
+ */
+
+ /* Call SQLColumns for each table and store the result */
+ for (i = 0; i < stmt->ntab; i++)
+ {
+ /* See if already got it */
+ char found = FALSE;
+
+ for (k = 0; k < conn->ntables; k++)
+ {
+ if (!stricmp(conn->col_info[k]->name, ti[i]->name))
+ {
+ mylog("FOUND col_info table='%s'\n", ti[i]->name);
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ mylog("PARSE: Getting PG_Columns for table[%d]='%s'\n", i, ti[i]->name);
+
+ result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt);
+ if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
+ {
+ stmt->errormsg = "PGAPI_AllocStmt failed in parse_statement for columns.";
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->parse_status = STMT_PARSE_FATAL;
+ return FALSE;
+ }
+
+ col_stmt = (StatementClass *) hcol_stmt;
+ col_stmt->internal = TRUE;
+
+ result = PGAPI_Columns(hcol_stmt, "", 0, "", 0,
+ ti[i]->name, (SWORD) strlen(ti[i]->name), "", 0);
+
+ mylog(" Past PG_Columns\n");
+ if (result == SQL_SUCCESS)
+ {
+ mylog(" Success\n");
+ if (!(conn->ntables % COL_INCR))
+ {
+ mylog("PARSE: Allocing col_info at ntables=%d\n", conn->ntables);
+
+ conn->col_info = (COL_INFO **) realloc(conn->col_info, (conn->ntables + COL_INCR) * sizeof(COL_INFO *));
+ if (!conn->col_info)
+ {
+ stmt->parse_status = STMT_PARSE_FATAL;
+ return FALSE;
+ }
+ }
+
+ mylog("PARSE: malloc at conn->col_info[%d]\n", conn->ntables);
+ conn->col_info[conn->ntables] = (COL_INFO *) malloc(sizeof(COL_INFO));
+ if (!conn->col_info[conn->ntables])
+ {
+ stmt->parse_status = STMT_PARSE_FATAL;
+ return FALSE;
+ }
+
+ /*
+ * Store the table name and the SQLColumns result
+ * structure
+ */
+ strcpy(conn->col_info[conn->ntables]->name, ti[i]->name);
+ conn->col_info[conn->ntables]->result = col_stmt->result;
+
+ /*
+ * The connection will now free the result structures, so
+ * make sure that the statement doesn't free it
+ */
+ col_stmt->result = NULL;
+
+ conn->ntables++;
+
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ mylog("Created col_info table='%s', ntables=%d\n", ti[i]->name, conn->ntables);
+ }
+ else
+ {
+ PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
+ break;
+ }
+ }
+
+ /* Associate a table from the statement with a SQLColumn info */
+ ti[i]->col_info = conn->col_info[k];
+ mylog("associate col_info: i=%d, k=%d\n", i, k);
+ }
+
+ mylog("Done PG_Columns\n");
+
+ /*
+ * Now resolve the fields to point to column info
+ */
+ for (i = 0; i < stmt->nfld;)
+ {
+ /* Dont worry about functions or quotes */
+ if (fi[i]->func || fi[i]->quote || fi[i]->numeric)
+ {
+ i++;
+ continue;
+ }
+
+ /* Stars get expanded to all fields in the table */
+ else if (fi[i]->name[0] == '*')
+ {
+ char do_all_tables;
+ int total_cols,
+ old_alloc,
+ new_size,
+ cols;
+ int increased_cols;
+
+ mylog("expanding field %d\n", i);
+
+ total_cols = 0;
+
+ if (fi[i]->ti) /* The star represents only the qualified
+ * table */
+ total_cols = QR_get_num_tuples(fi[i]->ti->col_info->result);
+
+ else
+ { /* The star represents all tables */
+
+ /* Calculate the total number of columns after expansion */
+ for (k = 0; k < stmt->ntab; k++)
+ total_cols += QR_get_num_tuples(ti[k]->col_info->result);
+ }
+ increased_cols = total_cols - 1;
+
+ /* Allocate some more field pointers if necessary */
+ old_alloc = ((stmt->nfld - 1) / FLD_INCR + 1) * FLD_INCR;
+ new_size = stmt->nfld + increased_cols;
+
+ mylog("k=%d, increased_cols=%d, old_alloc=%d, new_size=%d\n", k, increased_cols, old_alloc, new_size);
+
+ if (new_size > old_alloc)
+ {
+ int new_alloc = ((new_size / FLD_INCR) + 1) * FLD_INCR;
+
+ mylog("need more cols: new_alloc = %d\n", new_alloc);
+ fi = (FIELD_INFO **) realloc(fi, new_alloc * sizeof(FIELD_INFO *));
+ if (!fi)
+ {
+ stmt->parse_status = STMT_PARSE_FATAL;
+ return FALSE;
+ }
+ stmt->fi = fi;
+ }
+
+ /*
+ * copy any other fields (if there are any) up past the
+ * expansion
+ */
+ for (j = stmt->nfld - 1; j > i; j--)
+ {
+ mylog("copying field %d to %d\n", j, increased_cols + j);
+ fi[increased_cols + j] = fi[j];
+ }
+ mylog("done copying fields\n");
+
+ /* Set the new number of fields */
+ stmt->nfld += increased_cols;
+ mylog("stmt->nfld now at %d\n", stmt->nfld);
+
+
+ /* copy the new field info */
+ do_all_tables = (fi[i]->ti ? FALSE : TRUE);
+
+ for (k = 0; k < (do_all_tables ? stmt->ntab : 1); k++)
+ {
+ TABLE_INFO *the_ti = do_all_tables ? ti[k] : fi[i]->ti;
+
+ cols = QR_get_num_tuples(the_ti->col_info->result);
+
+ for (n = 0; n < cols; n++)
+ {
+ mylog("creating field info: n=%d\n", n);
+ /* skip malloc (already did it for the Star) */
+ if (k > 0 || n > 0)
+ {
+ mylog("allocating field info at %d\n", n + i);
+ fi[n + i] = (FIELD_INFO *) malloc(sizeof(FIELD_INFO));
+ if (fi[n + i] == NULL)
+ {
+ stmt->parse_status = STMT_PARSE_FATAL;
+ return FALSE;
+ }
+ }
+ /* Initialize the new space (or the * field) */
+ memset(fi[n + i], 0, sizeof(FIELD_INFO));
+ fi[n + i]->ti = the_ti;
+
+ mylog("about to copy at %d\n", n + i);
+
+ getColInfo(the_ti->col_info, fi[n + i], n);
+
+ mylog("done copying\n");
+ }
+
+ i += cols;
+ mylog("i now at %d\n", i);
+ }
+ }
+
+ /*
+ * We either know which table the field was in because it was
+ * qualified with a table name or alias -OR- there was only 1
+ * table.
+ */
+ else if (fi[i]->ti)
+ {
+ if (!searchColInfo(fi[i]->ti->col_info, fi[i]))
+ parse = FALSE;
+
+ i++;
+ }
+
+ /* Don't know the table -- search all tables in "from" list */
+ else
+ {
+ parse = FALSE;
+ for (k = 0; k < stmt->ntab; k++)
+ {
+ if (searchColInfo(ti[k]->col_info, fi[i]))
+ {
+ fi[i]->ti = ti[k]; /* now know the table */
+ parse = TRUE;
+ break;
+ }
+ }
+ i++;
+ }
+ }
+
+ if (!parse)
+ stmt->parse_status = STMT_PARSE_INCOMPLETE;
+ else
+ stmt->parse_status = STMT_PARSE_COMPLETE;
+
+ mylog("done parse_statement: parse=%d, parse_status=%d\n", parse, stmt->parse_status);
+ return parse;
+}
--- /dev/null
+/*-------
+ * Module: pgapifunc.h
+ *
+ *-------
+ */
+#ifndef _PG_API_FUNC_H__
+#define _PG_API_FUNC_H__
+
+#include "psqlodbc.h"
+#include <stdio.h>
+#include <string.h>
+
+
+RETCODE SQL_API PGAPI_AllocConnect(HENV EnvironmentHandle,
+ HDBC FAR * ConnectionHandle);
+RETCODE SQL_API PGAPI_AllocEnv(HENV FAR * EnvironmentHandle);
+RETCODE SQL_API PGAPI_AllocStmt(HDBC ConnectionHandle,
+ HSTMT *StatementHandle);
+RETCODE SQL_API PGAPI_BindCol(HSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
+ PTR TargetValue, SQLINTEGER BufferLength,
+ SQLINTEGER *StrLen_or_Ind);
+RETCODE SQL_API PGAPI_Cancel(HSTMT StatementHandle);
+RETCODE SQL_API PGAPI_Columns(HSTMT StatementHandle,
+ SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
+ SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
+ SQLCHAR *TableName, SQLSMALLINT NameLength3,
+ SQLCHAR *ColumnName, SQLSMALLINT NameLength4);
+RETCODE SQL_API PGAPI_Connect(HDBC ConnectionHandle,
+ SQLCHAR *ServerName, SQLSMALLINT NameLength1,
+ SQLCHAR *UserName, SQLSMALLINT NameLength2,
+ SQLCHAR *Authentication, SQLSMALLINT NameLength3);
+RETCODE SQL_API PGAPI_DriverConnect(HDBC hdbc, HWND hwnd,
+ UCHAR FAR * szConnStrIn, SWORD cbConnStrIn,
+ UCHAR FAR * szConnStrOut, SWORD cbConnStrOutMax,
+ SWORD FAR * pcbConnStrOut, UWORD fDriverCompletion);
+RETCODE SQL_API PGAPI_BrowseConnect(HDBC hdbc,
+ SQLCHAR *szConnStrIn, SQLSMALLINT cbConnStrIn,
+ SQLCHAR *szConnStrOut, SQLSMALLINT cbConnStrOutMax,
+ SQLSMALLINT *pcbConnStrOut);
+RETCODE SQL_API PGAPI_DataSources(HENV EnvironmentHandle,
+ SQLUSMALLINT Direction, SQLCHAR *ServerName,
+ SQLSMALLINT BufferLength1, SQLSMALLINT *NameLength1,
+ SQLCHAR *Description, SQLSMALLINT BufferLength2,
+ SQLSMALLINT *NameLength2);
+RETCODE SQL_API PGAPI_DescribeCol(HSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName,
+ SQLSMALLINT BufferLength, SQLSMALLINT *NameLength,
+ SQLSMALLINT *DataType, SQLUINTEGER *ColumnSize,
+ SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable);
+RETCODE SQL_API PGAPI_Disconnect(HDBC ConnectionHandle);
+RETCODE SQL_API PGAPI_Error(HENV EnvironmentHandle,
+ HDBC ConnectionHandle, HSTMT StatementHandle,
+ SQLCHAR *Sqlstate, SQLINTEGER *NativeError,
+ SQLCHAR *MessageText, SQLSMALLINT BufferLength,
+ SQLSMALLINT *TextLength);
+RETCODE SQL_API PGAPI_ExecDirect(HSTMT StatementHandle,
+ SQLCHAR *StatementText, SQLINTEGER TextLength);
+RETCODE SQL_API PGAPI_Execute(HSTMT StatementHandle);
+RETCODE SQL_API PGAPI_Fetch(HSTMT StatementHandle);
+RETCODE SQL_API PGAPI_FreeConnect(HDBC ConnectionHandle);
+RETCODE SQL_API PGAPI_FreeEnv(HENV EnvironmentHandle);
+RETCODE SQL_API PGAPI_FreeStmt(HSTMT StatementHandle,
+ SQLUSMALLINT Option);
+RETCODE SQL_API PGAPI_GetConnectOption(HDBC ConnectionHandle,
+ SQLUSMALLINT Option, PTR Value);
+RETCODE SQL_API PGAPI_GetCursorName(HSTMT StatementHandle,
+ SQLCHAR *CursorName, SQLSMALLINT BufferLength,
+ SQLSMALLINT *NameLength);
+RETCODE SQL_API PGAPI_GetData(HSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
+ PTR TargetValue, SQLINTEGER BufferLength,
+ SQLINTEGER *StrLen_or_Ind);
+RETCODE SQL_API PGAPI_GetFunctions(HDBC ConnectionHandle,
+ SQLUSMALLINT FunctionId, SQLUSMALLINT *Supported);
+RETCODE SQL_API PGAPI_GetFunctions30(HDBC ConnectionHandle,
+ SQLUSMALLINT FunctionId, SQLUSMALLINT *Supported);
+RETCODE SQL_API PGAPI_GetInfo(HDBC ConnectionHandle,
+ SQLUSMALLINT InfoType, PTR InfoValue,
+ SQLSMALLINT BufferLength, SQLSMALLINT *StringLength);
+RETCODE SQL_API PGAPI_GetInfo30(HDBC ConnectionHandle,
+ SQLUSMALLINT InfoType, PTR InfoValue,
+ SQLSMALLINT BufferLength, SQLSMALLINT *StringLength);
+RETCODE SQL_API PGAPI_GetStmtOption(HSTMT StatementHandle,
+ SQLUSMALLINT Option, PTR Value);
+RETCODE SQL_API PGAPI_GetTypeInfo(HSTMT StatementHandle,
+ SQLSMALLINT DataType);
+RETCODE SQL_API PGAPI_NumResultCols(HSTMT StatementHandle,
+ SQLSMALLINT *ColumnCount);
+RETCODE SQL_API PGAPI_ParamData(HSTMT StatementHandle,
+ PTR *Value);
+RETCODE SQL_API PGAPI_Prepare(HSTMT StatementHandle,
+ SQLCHAR *StatementText, SQLINTEGER TextLength);
+RETCODE SQL_API PGAPI_PutData(HSTMT StatementHandle,
+ PTR Data, SQLINTEGER StrLen_or_Ind);
+RETCODE SQL_API PGAPI_RowCount(HSTMT StatementHandle,
+ SQLINTEGER *RowCount);
+RETCODE SQL_API PGAPI_SetConnectOption(HDBC ConnectionHandle,
+ SQLUSMALLINT Option, SQLUINTEGER Value);
+RETCODE SQL_API PGAPI_SetCursorName(HSTMT StatementHandle,
+ SQLCHAR *CursorName, SQLSMALLINT NameLength);
+RETCODE SQL_API PGAPI_SetParam(HSTMT StatementHandle,
+ SQLUSMALLINT ParameterNumber, SQLSMALLINT ValueType,
+ SQLSMALLINT ParameterType, SQLUINTEGER LengthPrecision,
+ SQLSMALLINT ParameterScale, PTR ParameterValue,
+ SQLINTEGER *StrLen_or_Ind);
+RETCODE SQL_API PGAPI_SetStmtOption(HSTMT StatementHandle,
+ SQLUSMALLINT Option, SQLUINTEGER Value);
+RETCODE SQL_API PGAPI_SpecialColumns(HSTMT StatementHandle,
+ SQLUSMALLINT IdentifierType, SQLCHAR *CatalogName,
+ SQLSMALLINT NameLength1, SQLCHAR *SchemaName,
+ SQLSMALLINT NameLength2, SQLCHAR *TableName,
+ SQLSMALLINT NameLength3, SQLUSMALLINT Scope,
+ SQLUSMALLINT Nullable);
+RETCODE SQL_API PGAPI_Statistics(HSTMT StatementHandle,
+ SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
+ SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
+ SQLCHAR *TableName, SQLSMALLINT NameLength3,
+ SQLUSMALLINT Unique, SQLUSMALLINT Reserved);
+RETCODE SQL_API PGAPI_Tables(HSTMT StatementHandle,
+ SQLCHAR *CatalogName, SQLSMALLINT NameLength1,
+ SQLCHAR *SchemaName, SQLSMALLINT NameLength2,
+ SQLCHAR *TableName, SQLSMALLINT NameLength3,
+ SQLCHAR *TableType, SQLSMALLINT NameLength4);
+RETCODE SQL_API PGAPI_Transact(HENV EnvironmentHandle,
+ HDBC ConnectionHandle, SQLUSMALLINT CompletionType);
+RETCODE SQL_API PGAPI_ColAttributes(
+ HSTMT hstmt,
+ SQLUSMALLINT icol,
+ SQLUSMALLINT fDescType,
+ PTR rgbDesc,
+ SQLSMALLINT cbDescMax,
+ SQLSMALLINT *pcbDesc,
+ SQLINTEGER *pfDesc);
+RETCODE SQL_API PGAPI_ColumnPrivileges(
+ HSTMT hstmt,
+ SQLCHAR *szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR *szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR *szTableName,
+ SQLSMALLINT cbTableName,
+ SQLCHAR *szColumnName,
+ SQLSMALLINT cbColumnName);
+RETCODE SQL_API PGAPI_DescribeParam(
+ HSTMT hstmt,
+ SQLUSMALLINT ipar,
+ SQLSMALLINT *pfSqlType,
+ SQLUINTEGER *pcbParamDef,
+ SQLSMALLINT *pibScale,
+ SQLSMALLINT *pfNullable);
+RETCODE SQL_API PGAPI_ExtendedFetch(
+ HSTMT hstmt,
+ SQLUSMALLINT fFetchType,
+ SQLINTEGER irow,
+ SQLUINTEGER *pcrow,
+ SQLUSMALLINT *rgfRowStatus);
+RETCODE SQL_API PGAPI_ForeignKeys(
+ HSTMT hstmt,
+ SQLCHAR *szPkCatalogName,
+ SQLSMALLINT cbPkCatalogName,
+ SQLCHAR *szPkSchemaName,
+ SQLSMALLINT cbPkSchemaName,
+ SQLCHAR *szPkTableName,
+ SQLSMALLINT cbPkTableName,
+ SQLCHAR *szFkCatalogName,
+ SQLSMALLINT cbFkCatalogName,
+ SQLCHAR *szFkSchemaName,
+ SQLSMALLINT cbFkSchemaName,
+ SQLCHAR *szFkTableName,
+ SQLSMALLINT cbFkTableName);
+RETCODE SQL_API PGAPI_MoreResults(
+ HSTMT hstmt);
+RETCODE SQL_API PGAPI_NativeSql(
+ HDBC hdbc,
+ SQLCHAR *szSqlStrIn,
+ SQLINTEGER cbSqlStrIn,
+ SQLCHAR *szSqlStr,
+ SQLINTEGER cbSqlStrMax,
+ SQLINTEGER *pcbSqlStr);
+RETCODE SQL_API PGAPI_NumParams(
+ HSTMT hstmt,
+ SQLSMALLINT *pcpar);
+RETCODE SQL_API PGAPI_ParamOptions(
+ HSTMT hstmt,
+ SQLUINTEGER crow,
+ SQLUINTEGER *pirow);
+RETCODE SQL_API PGAPI_PrimaryKeys(
+ HSTMT hstmt,
+ SQLCHAR *szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR *szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR *szTableName,
+ SQLSMALLINT cbTableName);
+RETCODE SQL_API PGAPI_ProcedureColumns(
+ HSTMT hstmt,
+ SQLCHAR *szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR *szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR *szProcName,
+ SQLSMALLINT cbProcName,
+ SQLCHAR *szColumnName,
+ SQLSMALLINT cbColumnName);
+RETCODE SQL_API PGAPI_Procedures(
+ HSTMT hstmt,
+ SQLCHAR *szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR *szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR *szProcName,
+ SQLSMALLINT cbProcName);
+RETCODE SQL_API PGAPI_SetPos(
+ HSTMT hstmt,
+ SQLUSMALLINT irow,
+ SQLUSMALLINT fOption,
+ SQLUSMALLINT fLock);
+RETCODE SQL_API PGAPI_TablePrivileges(
+ HSTMT hstmt,
+ SQLCHAR *szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR *szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR *szTableName,
+ SQLSMALLINT cbTableName);
+RETCODE SQL_API PGAPI_BindParameter(
+ HSTMT hstmt,
+ SQLUSMALLINT ipar,
+ SQLSMALLINT fParamType,
+ SQLSMALLINT fCType,
+ SQLSMALLINT fSqlType,
+ SQLUINTEGER cbColDef,
+ SQLSMALLINT ibScale,
+ PTR rgbValue,
+ SQLINTEGER cbValueMax,
+ SQLINTEGER *pcbValue);
+RETCODE SQL_API PGAPI_SetScrollOptions(
+ HSTMT hstmt,
+ UWORD fConcurrency,
+ SDWORD crowKeyset,
+ UWORD crowRowset);
+
+#endif /* define_PG_API_FUNC_H__ */
--- /dev/null
+/*--------
+ * Module: pgtypes.c
+ *
+ * Description: This module contains routines for getting information
+ * about the supported Postgres data types. Only the
+ * function pgtype_to_sqltype() returns an unknown condition.
+ * All other functions return a suitable default so that
+ * even data types that are not directly supported can be
+ * used (it is handled as char data).
+ *
+ * Classes: n/a
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *--------
+ */
+
+#include "pgtypes.h"
+
+#include "dlg_specific.h"
+#include "statement.h"
+#include "connection.h"
+#include "qresult.h"
+
+
+
+Int4 getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as);
+
+/*
+ * these are the types we support. all of the pgtype_ functions should
+ * return values for each one of these.
+ * Even types not directly supported are handled as character types
+ * so all types should work (points, etc.)
+ */
+
+/*
+ * ALL THESE TYPES ARE NO LONGER REPORTED in SQLGetTypeInfo. Instead, all
+ * the SQL TYPES are reported and mapped to a corresponding Postgres Type
+ */
+
+/*
+Int4 pgtypes_defined[] = {
+ PG_TYPE_CHAR,
+ PG_TYPE_CHAR2,
+ PG_TYPE_CHAR4,
+ PG_TYPE_CHAR8,
+ PG_TYPE_CHAR16,
+ PG_TYPE_NAME,
+ PG_TYPE_VARCHAR,
+ PG_TYPE_BPCHAR,
+ PG_TYPE_DATE,
+ PG_TYPE_TIME,
+ PG_TYPE_DATETIME,
+ PG_TYPE_ABSTIME,
+ PG_TYPE_TIMESTAMP,
+ PG_TYPE_TEXT,
+ PG_TYPE_INT2,
+ PG_TYPE_INT4,
+ PG_TYPE_FLOAT4,
+ PG_TYPE_FLOAT8,
+ PG_TYPE_OID,
+ PG_TYPE_MONEY,
+ PG_TYPE_BOOL,
+ PG_TYPE_BYTEA,
+ PG_TYPE_LO,
+ 0 };
+*/
+
+
+/* These are NOW the SQL Types reported in SQLGetTypeInfo. */
+Int2 sqlTypes[] = {
+ SQL_BIGINT,
+ /* SQL_BINARY, -- Commented out because VarBinary is more correct. */
+ SQL_BIT,
+ SQL_CHAR,
+ SQL_DATE,
+ SQL_DECIMAL,
+ SQL_DOUBLE,
+ SQL_FLOAT,
+ SQL_INTEGER,
+ SQL_LONGVARBINARY,
+ SQL_LONGVARCHAR,
+ SQL_NUMERIC,
+ SQL_REAL,
+ SQL_SMALLINT,
+ SQL_TIME,
+ SQL_TIMESTAMP,
+ SQL_TINYINT,
+ SQL_VARBINARY,
+ SQL_VARCHAR,
+ 0
+};
+
+
+Int4
+sqltype_to_pgtype(StatementClass *stmt, SWORD fSqlType)
+{
+ Int4 pgType;
+ ConnInfo *ci = &(SC_get_conn(stmt)->connInfo);
+
+ switch (fSqlType)
+ {
+ case SQL_BINARY:
+ pgType = PG_TYPE_BYTEA;
+ break;
+
+ case SQL_CHAR:
+ pgType = PG_TYPE_BPCHAR;
+ break;
+
+ case SQL_BIT:
+ pgType = ci->drivers.bools_as_char ? PG_TYPE_CHAR : PG_TYPE_BOOL;
+ break;
+
+ case SQL_DATE:
+ pgType = PG_TYPE_DATE;
+ break;
+
+ case SQL_DOUBLE:
+ case SQL_FLOAT:
+ pgType = PG_TYPE_FLOAT8;
+ break;
+
+ case SQL_DECIMAL:
+ case SQL_NUMERIC:
+ pgType = PG_TYPE_NUMERIC;
+ break;
+
+ case SQL_BIGINT:
+ pgType = PG_TYPE_INT8;
+ break;
+
+ case SQL_INTEGER:
+ pgType = PG_TYPE_INT4;
+ break;
+
+ case SQL_LONGVARBINARY:
+ pgType = PG_TYPE_LO;
+ break;
+
+ case SQL_LONGVARCHAR:
+ pgType = ci->drivers.text_as_longvarchar ? PG_TYPE_TEXT : PG_TYPE_VARCHAR;
+ break;
+
+ case SQL_REAL:
+ pgType = PG_TYPE_FLOAT4;
+ break;
+
+ case SQL_SMALLINT:
+ case SQL_TINYINT:
+ pgType = PG_TYPE_INT2;
+ break;
+
+ case SQL_TIME:
+ pgType = PG_TYPE_TIME;
+ break;
+
+ case SQL_TIMESTAMP:
+ pgType = PG_TYPE_DATETIME;
+ break;
+
+ case SQL_VARBINARY:
+ pgType = PG_TYPE_BYTEA;
+ break;
+
+ case SQL_VARCHAR:
+ pgType = PG_TYPE_VARCHAR;
+ break;
+
+ default:
+ pgType = 0; /* ??? */
+ break;
+ }
+
+ return pgType;
+}
+
+
+/*
+ * There are two ways of calling this function:
+ *
+ * 1. When going through the supported PG types (SQLGetTypeInfo)
+ *
+ * 2. When taking any type id (SQLColumns, SQLGetData)
+ *
+ * The first type will always work because all the types defined are returned here.
+ * The second type will return a default based on global parameter when it does not
+ * know. This allows for supporting
+ * types that are unknown. All other pg routines in here return a suitable default.
+ */
+Int2
+pgtype_to_sqltype(StatementClass *stmt, Int4 type)
+{
+ ConnInfo *ci = &(SC_get_conn(stmt)->connInfo);
+
+ switch (type)
+ {
+ case PG_TYPE_CHAR:
+ case PG_TYPE_CHAR2:
+ case PG_TYPE_CHAR4:
+ case PG_TYPE_CHAR8:
+ case PG_TYPE_NAME:
+ return SQL_CHAR;
+
+ case PG_TYPE_BPCHAR:
+ return SQL_CHAR;
+
+ case PG_TYPE_VARCHAR:
+ return SQL_VARCHAR;
+
+ case PG_TYPE_TEXT:
+ return ci->drivers.text_as_longvarchar ? SQL_LONGVARCHAR : SQL_VARCHAR;
+
+ case PG_TYPE_BYTEA:
+ return SQL_VARBINARY;
+ case PG_TYPE_LO:
+ return SQL_LONGVARBINARY;
+
+ case PG_TYPE_INT2:
+ return SQL_SMALLINT;
+
+ case PG_TYPE_OID:
+ case PG_TYPE_XID:
+ case PG_TYPE_INT4:
+ return SQL_INTEGER;
+
+ /* Change this to SQL_BIGINT for ODBC v3 bjm 2001-01-23 */
+ case PG_TYPE_INT8:
+ return SQL_CHAR;
+
+ case PG_TYPE_NUMERIC:
+ return SQL_NUMERIC;
+
+ case PG_TYPE_FLOAT4:
+ return SQL_REAL;
+ case PG_TYPE_FLOAT8:
+ return SQL_FLOAT;
+ case PG_TYPE_DATE:
+ return SQL_DATE;
+ case PG_TYPE_TIME:
+ return SQL_TIME;
+ case PG_TYPE_ABSTIME:
+ case PG_TYPE_DATETIME:
+ case PG_TYPE_TIMESTAMP:
+ return SQL_TIMESTAMP;
+ case PG_TYPE_MONEY:
+ return SQL_FLOAT;
+ case PG_TYPE_BOOL:
+ return ci->drivers.bools_as_char ? SQL_CHAR : SQL_BIT;
+
+ default:
+
+ /*
+ * first, check to see if 'type' is in list. If not, look up
+ * with query. Add oid, name to list. If it's already in
+ * list, just return.
+ */
+ /* hack until permanent type is available */
+ if (type == stmt->hdbc->lobj_type)
+ return SQL_LONGVARBINARY;
+
+ return ci->drivers.unknowns_as_longvarchar ? SQL_LONGVARCHAR : SQL_VARCHAR;
+ }
+}
+
+
+Int2
+pgtype_to_ctype(StatementClass *stmt, Int4 type)
+{
+ ConnInfo *ci = &(SC_get_conn(stmt)->connInfo);
+
+ switch (type)
+ {
+ case PG_TYPE_INT8:
+ return SQL_C_CHAR;
+ case PG_TYPE_NUMERIC:
+ return SQL_C_CHAR;
+ case PG_TYPE_INT2:
+ return SQL_C_SSHORT;
+ case PG_TYPE_OID:
+ case PG_TYPE_XID:
+ case PG_TYPE_INT4:
+ return SQL_C_SLONG;
+ case PG_TYPE_FLOAT4:
+ return SQL_C_FLOAT;
+ case PG_TYPE_FLOAT8:
+ return SQL_C_DOUBLE;
+ case PG_TYPE_DATE:
+ return SQL_C_DATE;
+ case PG_TYPE_TIME:
+ return SQL_C_TIME;
+ case PG_TYPE_ABSTIME:
+ case PG_TYPE_DATETIME:
+ case PG_TYPE_TIMESTAMP:
+ return SQL_C_TIMESTAMP;
+ case PG_TYPE_MONEY:
+ return SQL_C_FLOAT;
+ case PG_TYPE_BOOL:
+ return ci->drivers.bools_as_char ? SQL_C_CHAR : SQL_C_BIT;
+
+ case PG_TYPE_BYTEA:
+ return SQL_C_BINARY;
+ case PG_TYPE_LO:
+ return SQL_C_BINARY;
+
+ default:
+ /* hack until permanent type is available */
+ if (type == stmt->hdbc->lobj_type)
+ return SQL_C_BINARY;
+
+ return SQL_C_CHAR;
+ }
+}
+
+
+char *
+pgtype_to_name(StatementClass *stmt, Int4 type)
+{
+ switch (type)
+ {
+ case PG_TYPE_CHAR:
+ return "char";
+ case PG_TYPE_CHAR2:
+ return "char2";
+ case PG_TYPE_CHAR4:
+ return "char4";
+ case PG_TYPE_CHAR8:
+ return "char8";
+ case PG_TYPE_INT8:
+ return "int8";
+ case PG_TYPE_NUMERIC:
+ return "numeric";
+ case PG_TYPE_VARCHAR:
+ return "varchar";
+ case PG_TYPE_BPCHAR:
+ return "char";
+ case PG_TYPE_TEXT:
+ return "text";
+ case PG_TYPE_NAME:
+ return "name";
+ case PG_TYPE_INT2:
+ return "int2";
+ case PG_TYPE_OID:
+ return "oid";
+ case PG_TYPE_INT4:
+ return "int4";
+ case PG_TYPE_FLOAT4:
+ return "float4";
+ case PG_TYPE_FLOAT8:
+ return "float8";
+ case PG_TYPE_DATE:
+ return "date";
+ case PG_TYPE_TIME:
+ return "time";
+ case PG_TYPE_ABSTIME:
+ return "abstime";
+ case PG_TYPE_DATETIME:
+ return "datetime";
+ case PG_TYPE_TIMESTAMP:
+ return "timestamp";
+ case PG_TYPE_MONEY:
+ return "money";
+ case PG_TYPE_BOOL:
+ return "bool";
+ case PG_TYPE_BYTEA:
+ return "bytea";
+
+ case PG_TYPE_LO:
+ return PG_TYPE_LO_NAME;
+
+ default:
+ /* hack until permanent type is available */
+ if (type == stmt->hdbc->lobj_type)
+ return PG_TYPE_LO_NAME;
+
+ /*
+ * "unknown" can actually be used in alter table because it is
+ * a real PG type!
+ */
+ return "unknown";
+ }
+}
+
+
+static Int2
+getNumericScale(StatementClass *stmt, Int4 type, int col)
+{
+ Int4 atttypmod;
+ QResultClass *result;
+ ColumnInfoClass *flds;
+
+ mylog("getNumericScale: type=%d, col=%d\n", type, col);
+
+ if (col < 0)
+ return PG_NUMERIC_MAX_SCALE;
+
+ result = SC_get_Result(stmt);
+
+ /*
+ * Manual Result Sets -- use assigned column width (i.e., from
+ * set_tuplefield_string)
+ */
+ if (stmt->manual_result)
+ {
+ flds = result->fields;
+ if (flds)
+ return flds->adtsize[col];
+ else
+ return PG_NUMERIC_MAX_SCALE;
+ }
+
+ atttypmod = QR_get_atttypmod(result, col);
+ if (atttypmod > -1)
+ return (atttypmod & 0xffff);
+ else
+ return (QR_get_display_size(result, col) ?
+ QR_get_display_size(result, col) :
+ PG_NUMERIC_MAX_SCALE);
+}
+
+
+static Int4
+getNumericPrecision(StatementClass *stmt, Int4 type, int col)
+{
+ Int4 atttypmod;
+ QResultClass *result;
+ ColumnInfoClass *flds;
+
+ mylog("getNumericPrecision: type=%d, col=%d\n", type, col);
+
+ if (col < 0)
+ return PG_NUMERIC_MAX_PRECISION;
+
+ result = SC_get_Result(stmt);
+
+ /*
+ * Manual Result Sets -- use assigned column width (i.e., from
+ * set_tuplefield_string)
+ */
+ if (stmt->manual_result)
+ {
+ flds = result->fields;
+ if (flds)
+ return flds->adtsize[col];
+ else
+ return PG_NUMERIC_MAX_PRECISION;
+ }
+
+ atttypmod = QR_get_atttypmod(result, col);
+ if (atttypmod > -1)
+ return (atttypmod >> 16) & 0xffff;
+ else
+ return (QR_get_display_size(result, col) >= 0 ?
+ QR_get_display_size(result, col) :
+ PG_NUMERIC_MAX_PRECISION);
+}
+
+
+Int4
+getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
+{
+ int p = -1,
+ maxsize;
+ QResultClass *result;
+ ColumnInfoClass *flds;
+ ConnInfo *ci = &(SC_get_conn(stmt)->connInfo);
+
+ mylog("getCharPrecision: type=%d, col=%d, unknown = %d\n", type, col, handle_unknown_size_as);
+
+ /* Assign Maximum size based on parameters */
+ switch (type)
+ {
+ case PG_TYPE_TEXT:
+ if (ci->drivers.text_as_longvarchar)
+ maxsize = ci->drivers.max_longvarchar_size;
+ else
+ maxsize = ci->drivers.max_varchar_size;
+ break;
+
+ case PG_TYPE_VARCHAR:
+ case PG_TYPE_BPCHAR:
+ maxsize = ci->drivers.max_varchar_size;
+ break;
+
+ default:
+ if (ci->drivers.unknowns_as_longvarchar)
+ maxsize = ci->drivers.max_longvarchar_size;
+ else
+ maxsize = ci->drivers.max_varchar_size;
+ break;
+ }
+
+ /*
+ * Static Precision (i.e., the Maximum Precision of the datatype) This
+ * has nothing to do with a result set.
+ */
+ if (maxsize == TEXT_FIELD_SIZE + 1) /* magic length for testing */
+ {
+ if (PG_VERSION_GE(SC_get_conn(stmt), 7.1))
+ maxsize = 0;
+ else
+ maxsize = TEXT_FIELD_SIZE;
+ }
+ if (col < 0)
+ return maxsize;
+
+ result = SC_get_Result(stmt);
+
+ /*
+ * Manual Result Sets -- use assigned column width (i.e., from
+ * set_tuplefield_string)
+ */
+ if (stmt->manual_result)
+ {
+ flds = result->fields;
+ if (flds)
+ return flds->adtsize[col];
+ else
+ return maxsize;
+ }
+
+ /* Size is unknown -- handle according to parameter */
+ if (QR_get_atttypmod(result, col) > -1)
+ return QR_get_atttypmod(result, col);
+
+ if (type == PG_TYPE_BPCHAR || handle_unknown_size_as == UNKNOWNS_AS_LONGEST)
+ {
+ p = QR_get_display_size(result, col);
+ mylog("getCharPrecision: LONGEST: p = %d\n", p);
+ }
+
+ if (p < 0 && handle_unknown_size_as == UNKNOWNS_AS_MAX)
+ return maxsize;
+ else
+ return p;
+}
+
+static Int2
+getTimestampScale(StatementClass *stmt, Int4 type, int col)
+{
+ ConnectionClass *conn = SC_get_conn(stmt);
+ Int4 atttypmod;
+ QResultClass *result;
+ ColumnInfoClass *flds;
+
+ mylog("getTimestampScale: type=%d, col=%d\n", type, col);
+
+ if (col < 0)
+ return 0;
+ if (PG_VERSION_LT(conn, 7.2))
+ return 0;
+
+ result = SC_get_Result(stmt);
+
+ /*
+ * Manual Result Sets -- use assigned column width (i.e., from
+ * set_tuplefield_string)
+ */
+ atttypmod = 0;
+ if (stmt->manual_result)
+ {
+ flds = result->fields;
+ if (flds)
+ atttypmod = flds->atttypmod[col];
+ mylog("atttypmod1=%d\n", atttypmod);
+ }
+ else
+ atttypmod = QR_get_atttypmod(result, col);
+ mylog("atttypmod2=%d\n", atttypmod);
+ return (atttypmod > -1 ? atttypmod : 0);
+}
+
+
+static Int4
+getTimestampPrecision(StatementClass *stmt, Int4 type, int col)
+{
+ Int4 fixed,
+ scale;
+
+ mylog("getTimestampPrecision: type=%d, col=%d\n", type, col);
+
+ switch (type)
+ {
+ case PG_TYPE_TIME:
+ fixed = 8;
+ break;
+ case PG_TYPE_TIME_WITH_TMZONE:
+ fixed = 11;
+ break;
+ case PG_TYPE_TIMESTAMP_NO_TMZONE:
+ fixed = 19;
+ break;
+ default:
+ fixed = 22;
+ break;
+ }
+ scale = getTimestampScale(stmt, type, col);
+ return (scale > 0) ? fixed + 1 + scale : fixed;
+}
+
+/*
+ * For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, PG_TYPE_NUMERIC, SQLColumns will
+ * override this length with the atttypmod length from pg_attribute .
+ *
+ * If col >= 0, then will attempt to get the info from the result set.
+ * This is used for functions SQLDescribeCol and SQLColAttributes.
+ */
+Int4
+pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
+{
+ switch (type)
+ {
+ case PG_TYPE_CHAR:
+ return 1;
+ case PG_TYPE_CHAR2:
+ return 2;
+ case PG_TYPE_CHAR4:
+ return 4;
+ case PG_TYPE_CHAR8:
+ return 8;
+
+ case PG_TYPE_NAME:
+ return NAME_FIELD_SIZE;
+
+ case PG_TYPE_INT2:
+ return 5;
+
+ case PG_TYPE_OID:
+ case PG_TYPE_XID:
+ case PG_TYPE_INT4:
+ return 10;
+
+ case PG_TYPE_INT8:
+ return 19; /* signed */
+
+ case PG_TYPE_NUMERIC:
+ return getNumericPrecision(stmt, type, col);
+
+ case PG_TYPE_FLOAT4:
+ case PG_TYPE_MONEY:
+ return 7;
+
+ case PG_TYPE_FLOAT8:
+ return 15;
+
+ case PG_TYPE_DATE:
+ return 10;
+ case PG_TYPE_TIME:
+ return 8;
+
+ case PG_TYPE_ABSTIME:
+ case PG_TYPE_TIMESTAMP:
+ return 22;
+ case PG_TYPE_DATETIME:
+ /* return 22; */
+ return getTimestampPrecision(stmt, type, col);
+
+ case PG_TYPE_BOOL:
+ return 1;
+
+ case PG_TYPE_LO:
+ return SQL_NO_TOTAL;
+
+ default:
+
+ if (type == stmt->hdbc->lobj_type) /* hack until permanent
+ * type is available */
+ return SQL_NO_TOTAL;
+
+ /* Handle Character types and unknown types */
+ return getCharPrecision(stmt, type, col, handle_unknown_size_as);
+ }
+}
+
+
+Int4
+pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
+{
+ switch (type)
+ {
+ case PG_TYPE_INT2:
+ return 6;
+
+ case PG_TYPE_OID:
+ case PG_TYPE_XID:
+ return 10;
+
+ case PG_TYPE_INT4:
+ return 11;
+
+ case PG_TYPE_INT8:
+ return 20; /* signed: 19 digits + sign */
+
+ case PG_TYPE_NUMERIC:
+ return getNumericPrecision(stmt, type, col) + 2;
+
+ case PG_TYPE_MONEY:
+ return 15; /* ($9,999,999.99) */
+
+ case PG_TYPE_FLOAT4:
+ return 13;
+
+ case PG_TYPE_FLOAT8:
+ return 22;
+
+ /* Character types use regular precision */
+ default:
+ return pgtype_precision(stmt, type, col, handle_unknown_size_as);
+ }
+}
+
+
+/*
+ * For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, SQLColumns will
+ * override this length with the atttypmod length from pg_attribute
+ */
+Int4
+pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as)
+{
+ switch (type)
+ {
+ case PG_TYPE_INT2:
+ return 2;
+
+ case PG_TYPE_OID:
+ case PG_TYPE_XID:
+ case PG_TYPE_INT4:
+ return 4;
+
+ case PG_TYPE_INT8:
+ return 20; /* signed: 19 digits + sign */
+
+ case PG_TYPE_NUMERIC:
+ return getNumericPrecision(stmt, type, col) + 2;
+
+ case PG_TYPE_FLOAT4:
+ case PG_TYPE_MONEY:
+ return 4;
+
+ case PG_TYPE_FLOAT8:
+ return 8;
+
+ case PG_TYPE_DATE:
+ case PG_TYPE_TIME:
+ return 6; /* sizeof(DATE(TIME)_STRUCT) */
+
+ case PG_TYPE_ABSTIME:
+ case PG_TYPE_DATETIME:
+ case PG_TYPE_TIMESTAMP:
+ return 16; /* sizeof(TIMESTAMP_STRUCT) */
+
+ /* Character types (and NUMERIC) use the default precision */
+ case PG_TYPE_VARCHAR:
+ case PG_TYPE_BPCHAR:
+#ifdef MULTIBYTE
+ /* after 7.2 */
+ if (PG_VERSION_GE(SC_get_conn(stmt), 7.2))
+ return 3 * pgtype_precision(stmt, type, col, handle_unknown_size_as);
+ else
+#else
+ /* CR -> CR/LF */
+ return 2 * pgtype_precision(stmt, type, col, handle_unknown_size_as);
+#endif /* MULTIBYTE */
+ default:
+ return pgtype_precision(stmt, type, col, handle_unknown_size_as);
+ }
+}
+
+
+Int2
+pgtype_scale(StatementClass *stmt, Int4 type, int col)
+{
+ switch (type)
+ {
+ case PG_TYPE_INT2:
+ case PG_TYPE_OID:
+ case PG_TYPE_XID:
+ case PG_TYPE_INT4:
+ case PG_TYPE_INT8:
+ case PG_TYPE_FLOAT4:
+ case PG_TYPE_FLOAT8:
+ case PG_TYPE_MONEY:
+ case PG_TYPE_BOOL:
+
+ /*
+ * Number of digits to the right of the decimal point in
+ * "yyyy-mm=dd hh:mm:ss[.f...]"
+ */
+ case PG_TYPE_ABSTIME:
+ case PG_TYPE_TIMESTAMP:
+ return 0;
+ case PG_TYPE_DATETIME:
+ /* return 0; */
+ return getTimestampScale(stmt, type, col);
+
+ case PG_TYPE_NUMERIC:
+ return getNumericScale(stmt, type, col);
+
+ default:
+ return -1;
+ }
+}
+
+
+Int2
+pgtype_radix(StatementClass *stmt, Int4 type)
+{
+ switch (type)
+ {
+ case PG_TYPE_INT2:
+ case PG_TYPE_OID:
+ case PG_TYPE_INT4:
+ case PG_TYPE_INT8:
+ case PG_TYPE_NUMERIC:
+ case PG_TYPE_FLOAT4:
+ case PG_TYPE_MONEY:
+ case PG_TYPE_FLOAT8:
+ return 10;
+ default:
+ return -1;
+ }
+}
+
+
+Int2
+pgtype_nullable(StatementClass *stmt, Int4 type)
+{
+ return SQL_NULLABLE; /* everything should be nullable */
+}
+
+
+Int2
+pgtype_auto_increment(StatementClass *stmt, Int4 type)
+{
+ switch (type)
+ {
+ case PG_TYPE_INT2:
+ case PG_TYPE_OID:
+ case PG_TYPE_XID:
+ case PG_TYPE_INT4:
+ case PG_TYPE_FLOAT4:
+ case PG_TYPE_MONEY:
+ case PG_TYPE_BOOL:
+ case PG_TYPE_FLOAT8:
+ case PG_TYPE_INT8:
+ case PG_TYPE_NUMERIC:
+
+ case PG_TYPE_DATE:
+ case PG_TYPE_TIME:
+ case PG_TYPE_ABSTIME:
+ case PG_TYPE_DATETIME:
+ case PG_TYPE_TIMESTAMP:
+ return FALSE;
+
+ default:
+ return -1;
+ }
+}
+
+
+Int2
+pgtype_case_sensitive(StatementClass *stmt, Int4 type)
+{
+ switch (type)
+ {
+ case PG_TYPE_CHAR:
+
+ case PG_TYPE_CHAR2:
+ case PG_TYPE_CHAR4:
+ case PG_TYPE_CHAR8:
+
+ case PG_TYPE_VARCHAR:
+ case PG_TYPE_BPCHAR:
+ case PG_TYPE_TEXT:
+ case PG_TYPE_NAME:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+
+Int2
+pgtype_money(StatementClass *stmt, Int4 type)
+{
+ switch (type)
+ {
+ case PG_TYPE_MONEY:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+Int2
+pgtype_searchable(StatementClass *stmt, Int4 type)
+{
+ switch (type)
+ {
+ case PG_TYPE_CHAR:
+ case PG_TYPE_CHAR2:
+ case PG_TYPE_CHAR4:
+ case PG_TYPE_CHAR8:
+
+ case PG_TYPE_VARCHAR:
+ case PG_TYPE_BPCHAR:
+ case PG_TYPE_TEXT:
+ case PG_TYPE_NAME:
+ return SQL_SEARCHABLE;
+
+ default:
+ return SQL_ALL_EXCEPT_LIKE;
+ }
+}
+
+
+Int2
+pgtype_unsigned(StatementClass *stmt, Int4 type)
+{
+ switch (type)
+ {
+ case PG_TYPE_OID:
+ case PG_TYPE_XID:
+ return TRUE;
+
+ case PG_TYPE_INT2:
+ case PG_TYPE_INT4:
+ case PG_TYPE_INT8:
+ case PG_TYPE_NUMERIC:
+ case PG_TYPE_FLOAT4:
+ case PG_TYPE_FLOAT8:
+ case PG_TYPE_MONEY:
+ return FALSE;
+
+ default:
+ return -1;
+ }
+}
+
+
+char *
+pgtype_literal_prefix(StatementClass *stmt, Int4 type)
+{
+ switch (type)
+ {
+ case PG_TYPE_INT2:
+ case PG_TYPE_OID:
+ case PG_TYPE_XID:
+ case PG_TYPE_INT4:
+ case PG_TYPE_INT8:
+ case PG_TYPE_NUMERIC:
+ case PG_TYPE_FLOAT4:
+ case PG_TYPE_FLOAT8:
+ case PG_TYPE_MONEY:
+ return NULL;
+
+ default:
+ return "'";
+ }
+}
+
+
+char *
+pgtype_literal_suffix(StatementClass *stmt, Int4 type)
+{
+ switch (type)
+ {
+ case PG_TYPE_INT2:
+ case PG_TYPE_OID:
+ case PG_TYPE_XID:
+ case PG_TYPE_INT4:
+ case PG_TYPE_INT8:
+ case PG_TYPE_NUMERIC:
+ case PG_TYPE_FLOAT4:
+ case PG_TYPE_FLOAT8:
+ case PG_TYPE_MONEY:
+ return NULL;
+
+ default:
+ return "'";
+ }
+}
+
+
+char *
+pgtype_create_params(StatementClass *stmt, Int4 type)
+{
+ switch (type)
+ {
+ case PG_TYPE_CHAR:
+ case PG_TYPE_VARCHAR:
+ return "max. length";
+ default:
+ return NULL;
+ }
+}
+
+
+Int2
+sqltype_to_default_ctype(Int2 sqltype)
+{
+ /*
+ * from the table on page 623 of ODBC 2.0 Programmer's Reference
+ * (Appendix D)
+ */
+ switch (sqltype)
+ {
+ case SQL_CHAR:
+ case SQL_VARCHAR:
+ case SQL_LONGVARCHAR:
+ case SQL_DECIMAL:
+ case SQL_NUMERIC:
+ case SQL_BIGINT:
+ return SQL_C_CHAR;
+
+ case SQL_BIT:
+ return SQL_C_BIT;
+
+ case SQL_TINYINT:
+ return SQL_C_STINYINT;
+
+ case SQL_SMALLINT:
+ return SQL_C_SSHORT;
+
+ case SQL_INTEGER:
+ return SQL_C_SLONG;
+
+ case SQL_REAL:
+ return SQL_C_FLOAT;
+
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ return SQL_C_DOUBLE;
+
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ case SQL_LONGVARBINARY:
+ return SQL_C_BINARY;
+
+ case SQL_DATE:
+ return SQL_C_DATE;
+
+ case SQL_TIME:
+ return SQL_C_TIME;
+
+ case SQL_TIMESTAMP:
+ return SQL_C_TIMESTAMP;
+
+ default:
+ /* should never happen */
+ return SQL_C_CHAR;
+ }
+}
+
+Int4
+ctype_length(Int2 ctype)
+{
+ switch (ctype)
+ {
+ case SQL_C_SSHORT:
+ case SQL_C_SHORT:
+ return sizeof(SWORD);
+
+ case SQL_C_USHORT:
+ return sizeof(UWORD);
+
+ case SQL_C_SLONG:
+ case SQL_C_LONG:
+ return sizeof(SDWORD);
+
+ case SQL_C_ULONG:
+ return sizeof(UDWORD);
+
+ case SQL_C_FLOAT:
+ return sizeof(SFLOAT);
+
+ case SQL_C_DOUBLE:
+ return sizeof(SDOUBLE);
+
+ case SQL_C_BIT:
+ return sizeof(UCHAR);
+
+ case SQL_C_STINYINT:
+ case SQL_C_TINYINT:
+ return sizeof(SCHAR);
+
+ case SQL_C_UTINYINT:
+ return sizeof(UCHAR);
+
+ case SQL_C_DATE:
+ return sizeof(DATE_STRUCT);
+
+ case SQL_C_TIME:
+ return sizeof(TIME_STRUCT);
+
+ case SQL_C_TIMESTAMP:
+ return sizeof(TIMESTAMP_STRUCT);
+
+ case SQL_C_BINARY:
+ case SQL_C_CHAR:
+ return 0;
+
+ default: /* should never happen */
+ return 0;
+ }
+}
--- /dev/null
+/* File: pgtypes.h
+ *
+ * Description: See "pgtypes.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __PGTYPES_H__
+#define __PGTYPES_H__
+
+#include "psqlodbc.h"
+
+/* the type numbers are defined by the OID's of the types' rows */
+/* in table pg_type */
+
+
+#if 0
+#define PG_TYPE_LO ???? /* waiting for permanent type */
+#endif
+
+#define PG_TYPE_BOOL 16
+#define PG_TYPE_BYTEA 17
+#define PG_TYPE_CHAR 18
+#define PG_TYPE_NAME 19
+#define PG_TYPE_INT8 20
+#define PG_TYPE_INT2 21
+#define PG_TYPE_INT2VECTOR 22
+#define PG_TYPE_INT4 23
+#define PG_TYPE_REGPROC 24
+#define PG_TYPE_TEXT 25
+#define PG_TYPE_OID 26
+#define PG_TYPE_TID 27
+#define PG_TYPE_XID 28
+#define PG_TYPE_CID 29
+#define PG_TYPE_OIDVECTOR 30
+#define PG_TYPE_SET 32
+#define PG_TYPE_CHAR2 409
+#define PG_TYPE_CHAR4 410
+#define PG_TYPE_CHAR8 411
+#define PG_TYPE_POINT 600
+#define PG_TYPE_LSEG 601
+#define PG_TYPE_PATH 602
+#define PG_TYPE_BOX 603
+#define PG_TYPE_POLYGON 604
+#define PG_TYPE_FILENAME 605
+#define PG_TYPE_FLOAT4 700
+#define PG_TYPE_FLOAT8 701
+#define PG_TYPE_ABSTIME 702
+#define PG_TYPE_RELTIME 703
+#define PG_TYPE_TINTERVAL 704
+#define PG_TYPE_UNKNOWN 705
+#define PG_TYPE_MONEY 790
+#define PG_TYPE_OIDINT2 810
+#define PG_TYPE_OIDINT4 910
+#define PG_TYPE_OIDNAME 911
+#define PG_TYPE_BPCHAR 1042
+#define PG_TYPE_VARCHAR 1043
+#define PG_TYPE_DATE 1082
+#define PG_TYPE_TIME 1083
+#define PG_TYPE_TIMESTAMP_NO_TMZONE 1114 /* since 7.2 */
+#define PG_TYPE_DATETIME 1184
+#define PG_TYPE_TIME_WITH_TMZONE 1266 /* since 7.1 */
+#define PG_TYPE_TIMESTAMP 1296 /* deprecated since 7.0 */
+#define PG_TYPE_NUMERIC 1700
+
+/* extern Int4 pgtypes_defined[]; */
+extern Int2 sqlTypes[];
+
+/* Defines for pgtype_precision */
+#define PG_STATIC (-1)
+
+Int4 sqltype_to_pgtype(StatementClass *stmt, Int2 fSqlType);
+
+Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type);
+Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type);
+char *pgtype_to_name(StatementClass *stmt, Int4 type);
+
+/* These functions can use static numbers or result sets(col parameter) */
+Int4 pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as);
+Int4 pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as);
+Int4 pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as);
+
+Int2 pgtype_scale(StatementClass *stmt, Int4 type, int col);
+Int2 pgtype_radix(StatementClass *stmt, Int4 type);
+Int2 pgtype_nullable(StatementClass *stmt, Int4 type);
+Int2 pgtype_auto_increment(StatementClass *stmt, Int4 type);
+Int2 pgtype_case_sensitive(StatementClass *stmt, Int4 type);
+Int2 pgtype_money(StatementClass *stmt, Int4 type);
+Int2 pgtype_searchable(StatementClass *stmt, Int4 type);
+Int2 pgtype_unsigned(StatementClass *stmt, Int4 type);
+char *pgtype_literal_prefix(StatementClass *stmt, Int4 type);
+char *pgtype_literal_suffix(StatementClass *stmt, Int4 type);
+char *pgtype_create_params(StatementClass *stmt, Int4 type);
+
+Int2 sqltype_to_default_ctype(Int2 sqltype);
+Int4 ctype_length(Int2 ctype);
+
+#endif
--- /dev/null
+/*--------
+ * Module: psqlodbc.c
+ *
+ * Description: This module contains the main entry point (DllMain)
+ * for the library. It also contains functions to get
+ * and set global variables for the driver in the registry.
+ *
+ * Classes: n/a
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *--------
+ */
+
+#include "psqlodbc.h"
+#include "dlg_specific.h"
+
+#ifdef WIN32
+#include <winsock.h>
+#endif
+
+GLOBAL_VALUES globals;
+
+RETCODE SQL_API SQLDummyOrdinal(void);
+
+#ifdef WIN32
+HINSTANCE NEAR s_hModule; /* Saved module handle. */
+
+/* This is where the Driver Manager attaches to this Driver */
+BOOL WINAPI
+DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
+{
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ s_hModule = hInst; /* Save for dialog boxes */
+
+ /* Load the WinSock Library */
+ wVersionRequested = MAKEWORD(1, 1);
+
+ if (WSAStartup(wVersionRequested, &wsaData))
+ return FALSE;
+
+ /* Verify that this is the minimum version of WinSock */
+ if (LOBYTE(wsaData.wVersion) != 1 ||
+ HIBYTE(wsaData.wVersion) != 1)
+ {
+ WSACleanup();
+ return FALSE;
+ }
+
+ getCommonDefaults(DBMS_NAME, ODBCINST_INI, NULL);
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_PROCESS_DETACH:
+ WSACleanup();
+ return TRUE;
+
+ case DLL_THREAD_DETACH:
+ break;
+
+ default:
+ break;
+ }
+
+ return TRUE;
+
+ UNREFERENCED_PARAMETER(lpReserved);
+}
+
+#else /* not WIN32 */
+
+#ifndef TRUE
+#define TRUE (BOOL)1
+#endif
+#ifndef FALSE
+#define FALSE (BOOL)0
+#endif
+
+#ifdef __GNUC__
+
+/* This function is called at library initialization time. */
+
+static BOOL
+__attribute__((constructor))
+init(void)
+{
+ getCommonDefaults(DBMS_NAME, ODBCINST_INI, NULL);
+ return TRUE;
+}
+
+#else /* not __GNUC__ */
+
+/*
+ * These two functions do shared library initialziation on UNIX, well at least
+ * on Linux. I don't know about other systems.
+ */
+BOOL
+_init(void)
+{
+ getCommonDefaults(DBMS_NAME, ODBCINST_INI, NULL);
+ return TRUE;
+}
+
+BOOL
+_fini(void)
+{
+ return TRUE;
+}
+#endif /* not __GNUC__ */
+#endif /* not WIN32 */
+
+
+/*
+ * This function is used to cause the Driver Manager to
+ * call functions by number rather than name, which is faster.
+ * The ordinal value of this function must be 199 to have the
+ * Driver Manager do this. Also, the ordinal values of the
+ * functions must match the value of fFunction in SQLGetFunctions()
+ */
+RETCODE SQL_API
+SQLDummyOrdinal(void)
+{
+ return SQL_SUCCESS;
+}
--- /dev/null
+/* File: psqlodbc.h
+ *
+ * Description: This file contains defines and declarations that are related to
+ * the entire driver.
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ * $Id: psqlodbc.h,v 1.1 2002/01/11 02:50:01 inoue Exp $
+ *
+ */
+
+#ifndef __PSQLODBC_H__
+#define __PSQLODBC_H__
+
+#ifndef WIN32
+#include "pg_config.h"
+#else
+#include <windows.h>
+#endif
+
+#include <stdio.h> /* for FILE* pointers: see GLOBAL_VALUES */
+
+/* Must come before sql.h */
+#ifndef ODBCVER
+#define ODBCVER 0x0250
+#endif /* ODBCVER_REP */
+
+
+#if defined(WIN32) || defined(WITH_UNIXODBC) || defined(WITH_IODBC)
+#include <sql.h>
+#include <sqlext.h>
+#else
+#include "iodbc.h"
+#include "isql.h"
+#include "isqlext.h"
+#endif
+
+#if defined(WIN32)
+#include <odbcinst.h>
+#elif defined(WITH_UNIXODBC)
+#include <odbcinst.h>
+#elif defined(WITH_IODBC)
+#include <iodbcinst.h>
+#else
+#include "gpps.h"
+#endif
+
+#ifndef WIN32
+#define Int4 long int
+#define UInt4 unsigned int
+#define Int2 short
+#define UInt2 unsigned short
+
+#if !defined(WITH_UNIXODBC) && !defined(WITH_IODBC)
+typedef float SFLOAT;
+typedef double SDOUBLE;
+#endif
+
+#ifndef CALLBACK
+#define CALLBACK
+#endif
+
+#else
+#define Int4 int
+#define UInt4 unsigned int
+#define Int2 short
+#define UInt2 unsigned short
+#endif
+
+typedef UInt4 Oid;
+
+#ifndef WIN32
+#define stricmp strcasecmp
+#define strnicmp strncasecmp
+#endif
+
+/* Driver stuff */
+
+#define DRIVERNAME "PostgreSQL ODBC"
+#if (ODBCVER >= 0x0300)
+#define DRIVER_ODBC_VER "03.00"
+#define DBMS_NAME "PostgreSQL30"
+#else
+#define DRIVER_ODBC_VER "02.50"
+#define DBMS_NAME "PostgreSQL"
+#endif /* ODBCVER */
+
+#define POSTGRESDRIVERVERSION "07.01.0009"
+
+#ifdef WIN32
+#if (ODBCVER >= 0x0300)
+#define DRIVER_FILE_NAME "PSQLODBC30.DLL"
+#else
+#define DRIVER_FILE_NAME "PSQLODBC.DLL"
+#endif /* ODBCVER */
+#else
+#define DRIVER_FILE_NAME "libpsqlodbc.so"
+#endif /* WIN32 */
+
+/* Limits */
+#ifdef WIN32
+#define BLCKSZ 4096
+#endif
+
+#define MAX_MESSAGE_LEN 65536 /* This puts a limit on
+ * query size but I don't */
+ /* see an easy way round this - DJP 24-1-2001 */
+#define MAX_CONNECT_STRING 4096
+#define ERROR_MSG_LENGTH 4096
+#define FETCH_MAX 100 /* default number of rows to cache
+ * for declare/fetch */
+#define TUPLE_MALLOC_INC 100
+#define SOCK_BUFFER_SIZE 4096 /* default socket buffer
+ * size */
+#define MAX_CONNECTIONS 128 /* conns per environment
+ * (arbitrary) */
+#define MAX_FIELDS 512
+#define BYTELEN 8
+#define VARHDRSZ sizeof(Int4)
+
+#define MAX_TABLE_LEN 32
+#define MAX_COLUMN_LEN 32
+#define MAX_CURSOR_LEN 32
+
+/* Registry length limits */
+#define LARGE_REGISTRY_LEN 4096 /* used for special cases */
+#define MEDIUM_REGISTRY_LEN 256 /* normal size for
+ * user,database,etc. */
+#define SMALL_REGISTRY_LEN 10 /* for 1/0 settings */
+
+
+/* These prefixes denote system tables */
+#define POSTGRES_SYS_PREFIX "pg_"
+#define KEYS_TABLE "dd_fkey"
+
+/* Info limits */
+#define MAX_INFO_STRING 128
+#define MAX_KEYPARTS 20
+#define MAX_KEYLEN 512 /* max key of the form
+ * "date+outlet+invoice" */
+#define MAX_ROW_SIZE 0 /* Unlimited rowsize with the
+ * Tuple Toaster */
+#define MAX_STATEMENT_LEN 0 /* Unlimited statement size with
+ * 7.0 */
+
+/* Previously, numerous query strings were defined of length MAX_STATEMENT_LEN */
+/* Now that's 0, lets use this instead. DJP 24-1-2001 */
+#define STD_STATEMENT_LEN MAX_MESSAGE_LEN
+
+#define PG62 "6.2" /* "Protocol" key setting
+ * to force Postgres 6.2 */
+#define PG63 "6.3" /* "Protocol" key setting
+ * to force postgres 6.3 */
+#define PG64 "6.4"
+
+typedef struct ConnectionClass_ ConnectionClass;
+typedef struct StatementClass_ StatementClass;
+typedef struct QResultClass_ QResultClass;
+typedef struct SocketClass_ SocketClass;
+typedef struct BindInfoClass_ BindInfoClass;
+typedef struct ParameterInfoClass_ ParameterInfoClass;
+typedef struct ColumnInfoClass_ ColumnInfoClass;
+typedef struct TupleListClass_ TupleListClass;
+typedef struct EnvironmentClass_ EnvironmentClass;
+typedef struct TupleNode_ TupleNode;
+typedef struct TupleField_ TupleField;
+
+typedef struct col_info COL_INFO;
+typedef struct lo_arg LO_ARG;
+
+typedef struct GlobalValues_
+{
+ int fetch_max;
+ int socket_buffersize;
+ int unknown_sizes;
+ int max_varchar_size;
+ int max_longvarchar_size;
+ char debug;
+ char commlog;
+ char disable_optimizer;
+ char ksqo;
+ char unique_index;
+ char onlyread; /* readonly is reserved on Digital C++
+ * compiler */
+ char use_declarefetch;
+ char text_as_longvarchar;
+ char unknowns_as_longvarchar;
+ char bools_as_char;
+ char lie;
+ char parse;
+ char cancel_as_freestmt;
+ char extra_systable_prefixes[MEDIUM_REGISTRY_LEN];
+ char conn_settings[LARGE_REGISTRY_LEN];
+ char protocol[SMALL_REGISTRY_LEN];
+} GLOBAL_VALUES;
+
+typedef struct StatementOptions_
+{
+ int maxRows;
+ int maxLength;
+ int rowset_size;
+ int keyset_size;
+ int cursor_type;
+ int scroll_concurrency;
+ int retrieve_data;
+ int bind_size; /* size of each structure if using Row
+ * Binding */
+ int use_bookmarks;
+ UInt4 *rowsFetched;
+ UInt2 *rowStatusArray;
+ void *bookmark_ptr;
+ UInt2 *row_operation_ptr;
+ UInt4 *row_offset_ptr;
+ UInt4 paramset_size;
+ UInt4 param_bind_type;
+ UInt4 *param_processed_ptr;
+ UInt2 *param_status_ptr;
+ UInt2 *param_operation_ptr;
+ UInt4 *param_offset_ptr;
+} StatementOptions;
+
+/* Used to pass extra query info to send_query */
+typedef struct QueryInfo_
+{
+ int row_size;
+ QResultClass *result_in;
+ char *cursor;
+} QueryInfo;
+
+void logs_on_off(int cnopen, int, int);
+
+#define PG_TYPE_LO (-999) /* hack until permanent
+ * type available */
+#define PG_TYPE_LO_NAME "lo"
+#define OID_ATTNUM (-2) /* the attnum in pg_index
+ * of the oid */
+
+/* sizes */
+#define TEXT_FIELD_SIZE 8190 /* size of text fields
+ * (not including null
+ * term) */
+#define NAME_FIELD_SIZE 32 /* size of name fields */
+#define MAX_VARCHAR_SIZE 254 /* maximum size of a varchar (not
+ * including null term) */
+
+#define PG_NUMERIC_MAX_PRECISION 1000
+#define PG_NUMERIC_MAX_SCALE 1000
+
+#define INFO_INQUIRY_LEN 8192 /* this seems sufficiently big for
+ * queries used in info.c inoue
+ * 2001/05/17 */
+
+#include "misc.h"
+
+#ifdef _MEMORY_DEBUG_
+void *debug_alloc(size_t);
+void *debug_realloc(void *, size_t);
+char *debug_strdup(const char *);
+void debug_free(void *);
+void debug_memory_check(void);
+
+#define malloc debug_alloc
+#define realloc debug_realloc
+#define strdup debug_strdup
+#define free debug_free
+#endif /* _MEMORY_DEBUG_ */
+
+#endif
--- /dev/null
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+#ifdef MULTIBYTE
+DLG_CONFIG DIALOG DISCARDABLE 65, 43, 299, 113
+STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "PostgreSQL Driver Setup"
+FONT 10, "Terminal"
+BEGIN
+ RTEXT "&Data Source:",IDC_DSNAMETEXT,3,9,49,8,NOT WS_GROUP
+ EDITTEXT IDC_DSNAME,59,9,72,12,ES_AUTOHSCROLL | WS_GROUP
+ RTEXT "Des&cription:",IDC_DESCTEXT,135,10,49,8,NOT WS_GROUP
+ EDITTEXT IDC_DESC,185,10,110,25,ES_AUTOHSCROLL
+ RTEXT "Data&base:",IDC_STATIC,15,24,37,8,NOT WS_GROUP
+ EDITTEXT IDC_DATABASE,59,24,72,12,ES_AUTOHSCROLL
+ RTEXT "&Server:",IDC_STATIC,23,38,29,8,NOT WS_GROUP
+ EDITTEXT IDC_SERVER,59,38,72,12,ES_AUTOHSCROLL
+ RTEXT "&Port:",IDC_STATIC,161,38,21,8
+ EDITTEXT IDC_PORT,185,38,37,12,ES_AUTOHSCROLL
+ RTEXT "&User Name:",IDC_STATIC,11,53,41,8
+ EDITTEXT IDC_USER,59,53,72,12,ES_AUTOHSCROLL
+ RTEXT "Pass&word:",IDC_STATIC,145,53,37,8
+ EDITTEXT IDC_PASSWORD,185,53,72,12,ES_PASSWORD | ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,27,88,40,14,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,81,88,40,14
+ GROUPBOX "Options (Advanced):",IDC_OPTIONS,141,72,140,35,
+ BS_CENTER
+ PUSHBUTTON "Driver",IDC_DRIVER,149,89,50,14
+ PUSHBUTTON "DataSource",IDC_DATASOURCE,221,88,50,14
+ CTEXT "Please supply any missing information needed to connect.",
+ DRV_MSG_LABEL,25,4,238,10
+END
+
+DLG_OPTIONS_DRV DIALOG DISCARDABLE 0, 0, 306, 226
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Advanced Options (Driver)"
+FONT 10, "Terminal"
+BEGIN
+ CONTROL "Disable Genetic &Optimizer",DRV_OPTIMIZER,"Button",
+ BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,13,11,116,10
+ CONTROL "Comm&Log (C:\\psqlodbc.log)",DRV_COMMLOG,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,164,11,120,10
+ CONTROL "&KSQO (Keyset Query Optimization)",DRV_KSQO,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,23,144,10
+ CONTROL "&ReadOnly (Default)",DRV_READONLY,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,164,24,88,10
+ CONTROL "Recognize Unique &Indexes",DRV_UNIQUEINDEX,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,35,112,10
+ CONTROL "P&arse Statements",DRV_PARSE,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,164,37,80,10
+ CONTROL "&Use Declare/Fetch",DRV_USEDECLAREFETCH,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,47,84,10
+ CONTROL "Cancel as FreeStmt (Exp)",DRV_CANCELASFREESTMT,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,164,50,112,10
+ CONTROL "Mylog(Debug ouput)",DRV_DEBUG,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,164,63,112,10
+ GROUPBOX "Unknown Sizes",IDC_STATIC,13,76,175,24
+ CONTROL "Maximum",DRV_UNKNOWN_MAX,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP | WS_TABSTOP,21,84,44,10
+ CONTROL "Don't Know",DRV_UNKNOWN_DONTKNOW,"Button",
+ BS_AUTORADIOBUTTON | WS_TABSTOP,72,84,56,10
+ CONTROL "Longest",DRV_UNKNOWN_LONGEST,"Button",
+ BS_AUTORADIOBUTTON | WS_TABSTOP,135,84,44,10
+ GROUPBOX "Data Type Options",IDC_STATIC,13,104,282,23
+ CONTROL "Text as LongVarChar",DRV_TEXT_LONGVARCHAR,"Button",
+ BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,15,115,92,10
+ CONTROL "Unknowns as LongVarChar",DRV_UNKNOWNS_LONGVARCHAR,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,115,108,10
+ CONTROL "Bools as Char",DRV_BOOLS_CHAR,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,225,115,68,10
+ LTEXT "&Cache Size:",IDC_STATIC,15,133,45,8
+ EDITTEXT DRV_CACHE_SIZE,61,129,35,12,ES_AUTOHSCROLL
+ LTEXT "Max &Varchar:",IDC_STATIC,99,133,49,8
+ EDITTEXT DRV_VARCHAR_SIZE,149,129,35,12,ES_AUTOHSCROLL
+ LTEXT "Max Lon&gVarChar:",IDC_STATIC,192,133,65,8
+ EDITTEXT DRV_LONGVARCHAR_SIZE,259,129,35,12,ES_AUTOHSCROLL
+ LTEXT "SysTable &Prefixes:",IDC_STATIC,23,144,36,20
+ EDITTEXT DRV_EXTRASYSTABLEPREFIXES,61,153,75,12,ES_AUTOHSCROLL
+ LTEXT "Connect &Settings:",IDC_STATIC,22,165,35,20
+ EDITTEXT DRV_CONNSETTINGS,61,165,225,25,ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+ DEFPUSHBUTTON "OK",IDOK,59,201,50,14,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,124,201,50,14
+ PUSHBUTTON "Defaults",IDDEFAULTS,189,201,50,15
+ CONTROL "Default",DRV_OR_DSN,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT |
+ BS_NOTIFY | WS_TABSTOP,247,205,40,10
+END
+
+DLG_OPTIONS_DS DIALOG DISCARDABLE 0, 0, 267, 161
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Advanced Options (DataSource)"
+FONT 10, "Terminal"
+BEGIN
+ CONTROL "&ReadOnly",DS_READONLY,"Button",BS_AUTOCHECKBOX |
+ WS_GROUP | WS_TABSTOP,45,13,48,10
+ CONTROL "Row &Versioning",DS_ROWVERSIONING,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,149,13,72,10
+ CONTROL "Show System &Tables",DS_SHOWSYSTEMTABLES,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,45,28,88,10
+ CONTROL "Disallow &Premature",DS_DISALLOWPREMATURE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,149,28,86,10
+ GROUPBOX "Protocol",IDC_STATIC,43,44,180,25
+ CONTROL "7.X,6.4+",DS_PG64,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP,53,54,47,10
+ CONTROL "6.3",DS_PG63,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,
+ 131,54,26,10
+ CONTROL "6.2",DS_PG62,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,
+ 187,54,26,10
+ GROUPBOX "OID Options",IDC_STATIC,43,74,180,25
+ CONTROL "Show &Column",DS_SHOWOIDCOLUMN,"Button",BS_AUTOCHECKBOX |
+ WS_GROUP | WS_TABSTOP,53,85,59,10
+ CONTROL "Fake &Index",DS_FAKEOIDINDEX,"Button",BS_AUTOCHECKBOX |
+ WS_GROUP | WS_TABSTOP,161,85,55,10
+ LTEXT "Connect &Settings:",IDC_STATIC,10,105,35,25
+ EDITTEXT DS_CONNSETTINGS,50,105,200,20,ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+ DEFPUSHBUTTON "OK",IDOK,71,135,50,14,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,146,135,50,14
+END
+#else
+DLG_CONFIG DIALOG DISCARDABLE 65, 43, 292, 116
+STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "PostgreSQL Driver Setup"
+FONT 8, "MS Sans Serif"
+BEGIN
+ RTEXT "&Data Source:",IDC_DSNAMETEXT,5,10,50,12,NOT WS_GROUP
+ EDITTEXT IDC_DSNAME,57,10,72,12,ES_AUTOHSCROLL | WS_GROUP
+ RTEXT "Des&cription:",IDC_DESCTEXT,135,10,39,12,NOT WS_GROUP
+ EDITTEXT IDC_DESC,175,10,108,12,ES_AUTOHSCROLL
+ RTEXT "Data&base:",IDC_STATIC,17,25,38,12,NOT WS_GROUP
+ EDITTEXT IDC_DATABASE,57,25,72,12,ES_AUTOHSCROLL
+ RTEXT "&Server:",IDC_STATIC,27,40,29,12,NOT WS_GROUP
+ EDITTEXT IDC_SERVER,57,40,72,12,ES_AUTOHSCROLL
+ RTEXT "&Port:",IDC_STATIC,153,40,22,12
+ EDITTEXT IDC_PORT,175,40,37,12,ES_AUTOHSCROLL
+ RTEXT "&User Name:",IDC_STATIC,17,55,39,12
+ EDITTEXT IDC_USER,57,55,72,12,ES_AUTOHSCROLL
+ RTEXT "Pass&word:",IDC_STATIC,141,55,34,12
+ EDITTEXT IDC_PASSWORD,175,55,72,12,ES_PASSWORD | ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,25,90,40,14,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,80,90,40,14
+ GROUPBOX "Options (Advanced):",IDC_OPTIONS,140,74,140,35,
+ BS_CENTER
+ PUSHBUTTON "Driver",IDC_DRIVER,160,90,50,14
+ PUSHBUTTON "DataSource",IDC_DATASOURCE,220,90,50,14
+ CTEXT "Please supply any missing information needed to connect.",
+ DRV_MSG_LABEL,36,5,220,15
+END
+
+DLG_OPTIONS_DRV DIALOG DISCARDABLE 0, 0, 287, 241
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Advanced Options (Driver)"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "Disable Genetic &Optimizer",DRV_OPTIMIZER,"Button",
+ BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,15,5,97,10
+ CONTROL "Comm&Log (C:\\psqlodbc.log)",DRV_COMMLOG,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,140,5,113,10
+ CONTROL "&KSQO (Keyset Query Optimization)",DRV_KSQO,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,15,20,124,10
+ CONTROL "&ReadOnly (Default)",DRV_READONLY,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,140,20,80,10
+ CONTROL "Recognize Unique &Indexes",DRV_UNIQUEINDEX,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,15,35,101,10
+ CONTROL "P&arse Statements",DRV_PARSE,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,140,35,80,10
+ CONTROL "&Use Declare/Fetch",DRV_USEDECLAREFETCH,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,15,50,80,10
+ CONTROL "Cancel as FreeStmt (Exp)",DRV_CANCELASFREESTMT,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,140,50,105,10
+ CONTROL "Mylog(Debug ouput)",DRV_DEBUG,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,140,65,112,10
+ GROUPBOX "Unknown Sizes",IDC_STATIC,10,80,175,25
+ CONTROL "Maximum",DRV_UNKNOWN_MAX,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP | WS_TABSTOP,15,91,45,10
+ CONTROL "Don't Know",DRV_UNKNOWN_DONTKNOW,"Button",
+ BS_AUTORADIOBUTTON | WS_TABSTOP,70,91,53,10
+ CONTROL "Longest",DRV_UNKNOWN_LONGEST,"Button",
+ BS_AUTORADIOBUTTON | WS_TABSTOP,130,91,50,10
+ GROUPBOX "Data Type Options",IDC_STATIC,10,110,270,25
+ CONTROL "Text as LongVarChar",DRV_TEXT_LONGVARCHAR,"Button",
+ BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,15,120,80,10
+ CONTROL "Unknowns as LongVarChar",DRV_UNKNOWNS_LONGVARCHAR,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,105,120,100,10
+ CONTROL "Bools as Char",DRV_BOOLS_CHAR,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,215,120,60,10
+ LTEXT "&Cache Size:",IDC_STATIC,10,145,40,10
+ EDITTEXT DRV_CACHE_SIZE,50,145,35,12,ES_AUTOHSCROLL
+ LTEXT "Max &Varchar:",IDC_STATIC,90,145,45,10
+ EDITTEXT DRV_VARCHAR_SIZE,135,145,35,12,ES_AUTOHSCROLL
+ LTEXT "Max Lon&gVarChar:",IDC_STATIC,180,145,60,10
+ EDITTEXT DRV_LONGVARCHAR_SIZE,240,145,35,12,ES_AUTOHSCROLL
+ LTEXT "SysTable &Prefixes:",IDC_STATIC,15,160,35,20
+ EDITTEXT DRV_EXTRASYSTABLEPREFIXES,50,166,75,12,ES_AUTOHSCROLL
+ RTEXT "Connect &Settings:",IDC_STATIC,10,185,35,20
+ EDITTEXT DRV_CONNSETTINGS,50,185,225,25,ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+ DEFPUSHBUTTON "OK",IDOK,45,220,50,14,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,110,220,50,14
+ PUSHBUTTON "Defaults",IDDEFAULTS,175,220,50,15
+ CONTROL "Default",DRV_OR_DSN,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT |
+ BS_NOTIFY | WS_TABSTOP,233,224,40,10
+END
+
+DLG_OPTIONS_DS DIALOG DISCARDABLE 0, 0, 267, 161
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Advanced Options (DataSource)"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "&ReadOnly",DS_READONLY,"Button",BS_AUTOCHECKBOX |
+ WS_GROUP | WS_TABSTOP,25,10,53,10
+ CONTROL "Row &Versioning",DS_ROWVERSIONING,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,130,10,85,10
+ CONTROL "Show System &Tables",DS_SHOWSYSTEMTABLES,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,25,25,85,10
+ CONTROL "Disallow &Premature",DS_DISALLOWPREMATURE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,130,25,85,10
+ GROUPBOX "Protocol",IDC_STATIC,15,40,180,25
+ CONTROL "7.X,6.4+",DS_PG64,"Button",BS_AUTORADIOBUTTON | WS_GROUP,25,
+ 50,35,10
+ CONTROL "6.3",DS_PG63,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,
+ 75,50,26,10
+ CONTROL "6.2",DS_PG62,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,
+ 130,50,26,10
+ GROUPBOX "OID Options",IDC_STATIC,15,70,180,25
+ CONTROL "Show &Column",DS_SHOWOIDCOLUMN,"Button",BS_AUTOCHECKBOX |
+ WS_GROUP | WS_TABSTOP,25,81,59,10
+ CONTROL "Fake &Index",DS_FAKEOIDINDEX,"Button",BS_AUTOCHECKBOX |
+ WS_GROUP | WS_TABSTOP,115,81,51,10
+ RTEXT "Connect &Settings:",IDC_STATIC,10,105,35,25
+ EDITTEXT DS_CONNSETTINGS,50,105,200,20,ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+ DEFPUSHBUTTON "OK",IDOK,71,135,50,14,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,146,135,50,14
+END
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+#ifdef MULTIBYTE
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ DLG_CONFIG, DIALOG
+ BEGIN
+ BOTTOMMARGIN, 112
+ END
+
+ DLG_OPTIONS_DRV, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 301
+ TOPMARGIN, 5
+ BOTTOMMARGIN, 206
+ END
+
+ DLG_OPTIONS_DS, DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ RIGHTMARGIN, 260
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 154
+ END
+END
+#else
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ DLG_CONFIG, DIALOG
+ BEGIN
+ BOTTOMMARGIN, 115
+ END
+
+ DLG_OPTIONS_DRV, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 280
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 219
+ END
+
+ DLG_OPTIONS_DS, DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ RIGHTMARGIN, 260
+ VERTGUIDE, 55
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 154
+ END
+END
+#endif // MULTIBYTE
+#endif // APSTUDIO_INVOKED
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 7,1,0,9
+ PRODUCTVERSION 7,1,0,9
+ FILEFLAGSMASK 0x3L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "Comments", "PostgreSQL ODBC driver\0"
+#ifdef MULTIBYTE
+ VALUE "CompanyName", "Insight Distribution Systems & Sankyo Unyu Service (MULTIBYTE support)\0"
+#else
+ VALUE "CompanyName", "Insight Distribution Systems\0"
+#endif
+ VALUE "FileDescription", "PostgreSQL Driver\0"
+ VALUE "FileVersion", " 07.01.0009\0"
+ VALUE "InternalName", "psqlodbc\0"
+ VALUE "LegalCopyright", "\0"
+ VALUE "LegalTrademarks", "ODBC(TM) is a trademark of Microsoft Corporation. Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation.\0"
+ VALUE "OriginalFilename", "psqlodbc.dll\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "Microsoft Open Database Connectivity\0"
+ VALUE "ProductVersion", " 07.01.0009\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_BADDSN "Invalid DSN entry, please recheck."
+ IDS_MSGTITLE "Invalid DSN"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
--- /dev/null
+REGEDIT4\r
+\r
+[HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI]\r
+\r
+[HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI\ODBC Drivers]\r
+"PostgreSQL"="Installed"\r
+\r
+[HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI\PostgreSQL]\r
+"APILevel"="1"\r
+"ConnectFunctions"="YYN"\r
+"Driver"="PSQLODBC.DLL"\r
+"DriverODBCVer"="02.50"\r
+"FileUsage"="0"\r
+"Setup"="PSQLODBC.DLL"\r
+"SQLLevel"="1"\r
+"UsageCount"=dword:00000001\r
+\r
--- /dev/null
+LIBRARY psqlodbc
+EXPORTS
+SQLAllocConnect @1
+SQLAllocEnv @2
+SQLAllocStmt @3
+SQLBindCol @4
+SQLCancel @5
+SQLColAttributes @6
+SQLConnect @7
+SQLDescribeCol @8
+SQLDisconnect @9
+SQLError @10
+SQLExecDirect @11
+SQLExecute @12
+SQLFetch @13
+SQLFreeConnect @14
+SQLFreeEnv @15
+SQLFreeStmt @16
+SQLGetCursorName @17
+SQLNumResultCols @18
+SQLPrepare @19
+SQLRowCount @20
+SQLSetCursorName @21
+SQLTransact @23
+SQLColumns @40
+SQLDriverConnect @41
+SQLGetConnectOption @42
+SQLGetData @43
+SQLGetFunctions @44
+SQLGetInfo @45
+SQLGetStmtOption @46
+SQLGetTypeInfo @47
+SQLParamData @48
+SQLPutData @49
+SQLSetConnectOption @50
+SQLSetStmtOption @51
+SQLSpecialColumns @52
+SQLStatistics @53
+SQLTables @54
+SQLBrowseConnect @55
+SQLColumnPrivileges @56
+SQLDescribeParam @58
+SQLExtendedFetch @59
+SQLForeignKeys @60
+SQLMoreResults @61
+SQLNativeSql @62
+SQLNumParams @63
+SQLParamOptions @64
+SQLPrimaryKeys @65
+SQLProcedureColumns @66
+SQLProcedures @67
+SQLSetPos @68
+SQLSetScrollOptions @69
+SQLTablePrivileges @70
+SQLBindParameter @72
+SQLDummyOrdinal @199
+dconn_FDriverConnectProc @200
+DllMain @201
+ConfigDSN @202
+
--- /dev/null
+/*---------
+ * Module: qresult.c
+ *
+ * Description: This module contains functions related to
+ * managing result information (i.e, fetching rows
+ * from the backend, managing the tuple cache, etc.)
+ * and retrieving it. Depending on the situation, a
+ * QResultClass will hold either data from the backend
+ * or a manually built result (see "qresult.h" to
+ * see which functions/macros are for manual or backend
+ * results. For manually built results, the
+ * QResultClass simply points to TupleList and
+ * ColumnInfo structures, which actually hold the data.
+ *
+ * Classes: QResultClass (Functions prefix: "QR_")
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *---------
+ */
+
+#include "qresult.h"
+
+#include "misc.h"
+#include <stdio.h>
+#include <string.h>
+
+#ifndef TRUE
+#define TRUE (BOOL)1
+#endif
+#ifndef FALSE
+#define FALSE (BOOL)0
+#endif
+
+
+/*
+ * Used for building a Manual Result only
+ * All info functions call this function to create the manual result set.
+ */
+void
+QR_set_num_fields(QResultClass *self, int new_num_fields)
+{
+ mylog("in QR_set_num_fields\n");
+
+ CI_set_num_fields(self->fields, new_num_fields);
+ if (self->manual_tuples)
+ TL_Destructor(self->manual_tuples);
+
+ self->manual_tuples = TL_Constructor(new_num_fields);
+
+ mylog("exit QR_set_num_fields\n");
+}
+
+
+void
+QR_set_position(QResultClass *self, int pos)
+{
+ self->tupleField = self->backend_tuples + ((self->base + pos) * self->num_fields);
+}
+
+
+void
+QR_set_cache_size(QResultClass *self, int cache_size)
+{
+ self->cache_size = cache_size;
+}
+
+
+void
+QR_set_rowset_size(QResultClass *self, int rowset_size)
+{
+ self->rowset_size = rowset_size;
+}
+
+
+void
+QR_inc_base(QResultClass *self, int base_inc)
+{
+ self->base += base_inc;
+}
+
+
+/*
+ * CLASS QResult
+ */
+QResultClass *
+QR_Constructor()
+{
+ QResultClass *rv;
+
+ mylog("in QR_Constructor\n");
+ rv = (QResultClass *) malloc(sizeof(QResultClass));
+
+ if (rv != NULL)
+ {
+ rv->status = PGRES_EMPTY_QUERY;
+
+ /* construct the column info */
+ if (!(rv->fields = CI_Constructor()))
+ {
+ free(rv);
+ return NULL;
+ }
+ rv->manual_tuples = NULL;
+ rv->backend_tuples = NULL;
+ rv->message = NULL;
+ rv->command = NULL;
+ rv->notice = NULL;
+ rv->conn = NULL;
+ rv->inTuples = FALSE;
+ rv->fcount = 0;
+ rv->fetch_count = 0;
+ rv->base = 0;
+ rv->currTuple = -1;
+ rv->num_fields = 0;
+ rv->tupleField = NULL;
+ rv->cursor = NULL;
+ rv->aborted = FALSE;
+
+ rv->cache_size = 0;
+ rv->rowset_size = 1;
+ }
+
+ mylog("exit QR_Constructor\n");
+ return rv;
+}
+
+
+void
+QR_Destructor(QResultClass *self)
+{
+ mylog("QResult: in DESTRUCTOR\n");
+
+ /* manual result set tuples */
+ if (self->manual_tuples)
+ TL_Destructor(self->manual_tuples);
+
+ /*
+ * If conn is defined, then we may have used "backend_tuples", so in
+ * case we need to, free it up. Also, close the cursor.
+ */
+ if (self->conn && self->conn->sock && CC_is_in_trans(self->conn))
+ QR_close(self); /* close the cursor if there is one */
+
+ QR_free_memory(self); /* safe to call anyway */
+
+ /* Should have been freed in the close() but just in case... */
+ if (self->cursor)
+ free(self->cursor);
+
+ /* Free up column info */
+ if (self->fields)
+ CI_Destructor(self->fields);
+
+ /* Free command info (this is from strdup()) */
+ if (self->command)
+ free(self->command);
+
+ /* Free notice info (this is from strdup()) */
+ if (self->notice)
+ free(self->notice);
+
+ free(self);
+
+ mylog("QResult: exit DESTRUCTOR\n");
+}
+
+
+void
+QR_set_command(QResultClass *self, char *msg)
+{
+ if (self->command)
+ free(self->command);
+
+ self->command = msg ? strdup(msg) : NULL;
+}
+
+
+void
+QR_set_notice(QResultClass *self, char *msg)
+{
+ if (self->notice)
+ free(self->notice);
+
+ self->notice = msg ? strdup(msg) : NULL;
+}
+
+
+void
+QR_free_memory(QResultClass *self)
+{
+ register int lf,
+ row;
+ register TupleField *tuple = self->backend_tuples;
+ int fcount = self->fcount;
+ int num_fields = self->num_fields;
+
+ mylog("QResult: free memory in, fcount=%d\n", fcount);
+
+ if (self->backend_tuples)
+ {
+ for (row = 0; row < fcount; row++)
+ {
+ mylog("row = %d, num_fields = %d\n", row, num_fields);
+ for (lf = 0; lf < num_fields; lf++)
+ {
+ if (tuple[lf].value != NULL)
+ {
+ mylog("free [lf=%d] %u\n", lf, tuple[lf].value);
+ free(tuple[lf].value);
+ }
+ }
+ tuple += num_fields; /* next row */
+ }
+
+ free(self->backend_tuples);
+ self->backend_tuples = NULL;
+ }
+
+ self->fcount = 0;
+
+ mylog("QResult: free memory out\n");
+}
+
+
+/* This function is called by send_query() */
+char
+QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
+{
+ int tuple_size;
+
+ /*
+ * If called from send_query the first time (conn != NULL), then set
+ * the inTuples state, and read the tuples. If conn is NULL, it
+ * implies that we are being called from next_tuple(), like to get
+ * more rows so don't call next_tuple again!
+ */
+ if (conn != NULL)
+ {
+ ConnInfo *ci = &(conn->connInfo);
+ BOOL fetch_cursor = (ci->drivers.use_declarefetch && cursor && cursor[0]);
+
+ self->conn = conn;
+
+ mylog("QR_fetch_tuples: cursor = '%s', self->cursor=%u\n", (cursor == NULL) ? "" : cursor, self->cursor);
+
+ if (self->cursor)
+ free(self->cursor);
+
+ if (fetch_cursor)
+ {
+ if (!cursor || cursor[0] == '\0')
+ {
+ self->status = PGRES_INTERNAL_ERROR;
+ QR_set_message(self, "Internal Error -- no cursor for fetch");
+ return FALSE;
+ }
+ self->cursor = strdup(cursor);
+ }
+
+ /*
+ * Read the field attributes.
+ *
+ * $$$$ Should do some error control HERE! $$$$
+ */
+ if (CI_read_fields(self->fields, self->conn))
+ {
+ self->status = PGRES_FIELDS_OK;
+ self->num_fields = CI_get_num_fields(self->fields);
+ }
+ else
+ {
+ self->status = PGRES_BAD_RESPONSE;
+ QR_set_message(self, "Error reading field information");
+ return FALSE;
+ }
+
+ mylog("QR_fetch_tuples: past CI_read_fields: num_fields = %d\n", self->num_fields);
+
+ if (fetch_cursor)
+ tuple_size = self->cache_size;
+ else
+ tuple_size = TUPLE_MALLOC_INC;
+
+ /* allocate memory for the tuple cache */
+ mylog("MALLOC: tuple_size = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size);
+ self->count_allocated = 0;
+ self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * tuple_size);
+ if (!self->backend_tuples)
+ {
+ self->status = PGRES_FATAL_ERROR;
+ QR_set_message(self, "Could not get memory for tuple cache.");
+ return FALSE;
+ }
+ self->count_allocated = tuple_size;
+
+ self->inTuples = TRUE;
+
+ /* Force a read to occur in next_tuple */
+ self->fcount = tuple_size + 1;
+ self->fetch_count = tuple_size + 1;
+ self->base = 0;
+
+ return QR_next_tuple(self);
+ }
+ else
+ {
+ /*
+ * Always have to read the field attributes. But we dont have to
+ * reallocate memory for them!
+ */
+
+ if (!CI_read_fields(NULL, self->conn))
+ {
+ self->status = PGRES_BAD_RESPONSE;
+ QR_set_message(self, "Error reading field information");
+ return FALSE;
+ }
+ return TRUE;
+ }
+}
+
+
+/*
+ * Close the cursor and end the transaction (if no cursors left)
+ * We only close cursor/end the transaction if a cursor was used.
+ */
+int
+QR_close(QResultClass *self)
+{
+ QResultClass *res;
+
+ if (self->conn && self->cursor && self->conn->connInfo.drivers.use_declarefetch)
+ {
+ char buf[64];
+
+ sprintf(buf, "close %s", self->cursor);
+ mylog("QResult: closing cursor: '%s'\n", buf);
+
+ res = CC_send_query(self->conn, buf, NULL);
+
+ self->inTuples = FALSE;
+ self->currTuple = -1;
+
+ free(self->cursor);
+ self->cursor = NULL;
+
+ if (res == NULL)
+ {
+ self->status = PGRES_FATAL_ERROR;
+ QR_set_message(self, "Error closing cursor.");
+ return FALSE;
+ }
+ QR_Destructor(res);
+
+ /* End the transaction if there are no cursors left on this conn */
+ if (CC_is_in_autocommit(self->conn) && CC_cursor_count(self->conn) == 0)
+ {
+ mylog("QResult: END transaction on conn=%u\n", self->conn);
+
+ res = CC_send_query(self->conn, "END", NULL);
+
+ CC_set_no_trans(self->conn);
+
+ if (res == NULL)
+ {
+ self->status = PGRES_FATAL_ERROR;
+ QR_set_message(self, "Error ending transaction.");
+ return FALSE;
+ }
+ QR_Destructor(res);
+ }
+ }
+
+ return TRUE;
+}
+
+
+/* This function is called by fetch_tuples() AND SQLFetch() */
+int
+QR_next_tuple(QResultClass *self)
+{
+ int id;
+ QResultClass *res;
+ SocketClass *sock;
+
+ /* Speed up access */
+ int fetch_count = self->fetch_count;
+ int fcount = self->fcount;
+ int fetch_size,
+ offset = 0;
+ int end_tuple = self->rowset_size + self->base;
+ char corrected = FALSE;
+ TupleField *the_tuples = self->backend_tuples;
+
+ /* ERROR_MSG_LENGTH is sufficient */
+ static char msgbuffer[ERROR_MSG_LENGTH + 1];
+
+ /* QR_set_command() dups this string so doesn't need static */
+ char cmdbuffer[ERROR_MSG_LENGTH + 1];
+ char fetch[128];
+ QueryInfo qi;
+ ConnInfo *ci = NULL;
+
+ if (fetch_count < fcount)
+ {
+ /* return a row from cache */
+ mylog("next_tuple: fetch_count < fcount: returning tuple %d, fcount = %d\n", fetch_count, fcount);
+ self->tupleField = the_tuples + (fetch_count * self->num_fields); /* next row */
+ self->fetch_count++;
+ return TRUE;
+ }
+ else if (self->fcount < self->cache_size)
+ {
+ /* last row from cache */
+ /* We are done because we didn't even get CACHE_SIZE tuples */
+ mylog("next_tuple: fcount < CACHE_SIZE: fcount = %d, fetch_count = %d\n", fcount, fetch_count);
+ self->tupleField = NULL;
+ self->status = PGRES_END_TUPLES;
+ /* end of tuples */
+ return -1;
+ }
+ else
+ {
+ /*
+ * See if we need to fetch another group of rows. We may be being
+ * called from send_query(), and if so, don't send another fetch,
+ * just fall through and read the tuples.
+ */
+ self->tupleField = NULL;
+
+ if (!self->inTuples)
+ {
+ ci = &(self->conn->connInfo);
+ if (!self->cursor || !ci->drivers.use_declarefetch)
+ {
+ mylog("next_tuple: ALL_ROWS: done, fcount = %d, fetch_count = %d\n", fcount, fetch_count);
+ self->tupleField = NULL;
+ self->status = PGRES_END_TUPLES;
+ return -1; /* end of tuples */
+ }
+
+ if (self->base == fcount)
+ {
+ /* not a correction */
+ /* Determine the optimum cache size. */
+ if (ci->drivers.fetch_max % self->rowset_size == 0)
+ fetch_size = ci->drivers.fetch_max;
+ else if (self->rowset_size < ci->drivers.fetch_max)
+ fetch_size = (ci->drivers.fetch_max / self->rowset_size) * self->rowset_size;
+ else
+ fetch_size = self->rowset_size;
+
+ self->cache_size = fetch_size;
+ self->fetch_count = 1;
+ }
+ else
+ {
+ /* need to correct */
+ corrected = TRUE;
+
+ fetch_size = end_tuple - fcount;
+
+ self->cache_size += fetch_size;
+
+ offset = self->fetch_count;
+ self->fetch_count++;
+ }
+
+ if (!self->backend_tuples || self->cache_size > self->count_allocated)
+ {
+ self->count_allocated = 0;
+ self->backend_tuples = (TupleField *) realloc(self->backend_tuples, self->num_fields * sizeof(TupleField) * self->cache_size);
+ if (!self->backend_tuples)
+ {
+ self->status = PGRES_FATAL_ERROR;
+ QR_set_message(self, "Out of memory while reading tuples.");
+ return FALSE;
+ }
+ self->count_allocated = self->cache_size;
+ }
+ sprintf(fetch, "fetch %d in %s", fetch_size, self->cursor);
+
+ mylog("next_tuple: sending actual fetch (%d) query '%s'\n", fetch_size, fetch);
+
+ /* don't read ahead for the next tuple (self) ! */
+ qi.row_size = self->cache_size;
+ qi.result_in = self;
+ qi.cursor = NULL;
+ res = CC_send_query(self->conn, fetch, &qi);
+ if (res == NULL || QR_get_aborted(res))
+ {
+ self->status = PGRES_FATAL_ERROR;
+ QR_set_message(self, "Error fetching next group.");
+ if (res)
+ QR_Destructor(res);
+ return FALSE;
+ }
+ self->inTuples = TRUE;
+ }
+ else
+ {
+ mylog("next_tuple: inTuples = true, falling through: fcount = %d, fetch_count = %d\n", self->fcount, self->fetch_count);
+
+ /*
+ * This is a pre-fetch (fetching rows right after query but
+ * before any real SQLFetch() calls. This is done so the
+ * field attributes are available.
+ */
+ self->fetch_count = 0;
+ }
+ }
+
+ if (!corrected)
+ {
+ self->base = 0;
+ self->fcount = 0;
+ }
+
+ sock = CC_get_socket(self->conn);
+ self->tupleField = NULL;
+ ci = &(self->conn->connInfo);
+
+ for (;;)
+ {
+ id = SOCK_get_char(sock);
+
+ switch (id)
+ {
+
+ case 'T': /* Tuples within tuples cannot be handled */
+ self->status = PGRES_BAD_RESPONSE;
+ QR_set_message(self, "Tuples within tuples cannot be handled");
+ return FALSE;
+ case 'B': /* Tuples in binary format */
+ case 'D': /* Tuples in ASCII format */
+
+ if ((!self->cursor || !ci->drivers.use_declarefetch) && self->fcount >= self->count_allocated)
+ {
+ int tuple_size = self->count_allocated;
+
+ mylog("REALLOC: old_count = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size);
+ tuple_size *= 2;
+ self->backend_tuples = (TupleField *) realloc(self->backend_tuples,
+ tuple_size * self->num_fields * sizeof(TupleField));
+ if (!self->backend_tuples)
+ {
+ self->status = PGRES_FATAL_ERROR;
+ QR_set_message(self, "Out of memory while reading tuples.");
+ return FALSE;
+ }
+ self->count_allocated = tuple_size;
+ }
+
+ if (!QR_read_tuple(self, (char) (id == 0)))
+ {
+ self->status = PGRES_BAD_RESPONSE;
+ QR_set_message(self, "Error reading the tuple");
+ return FALSE;
+ }
+ self->fcount++;
+ break; /* continue reading */
+
+ case 'C': /* End of tuple list */
+ SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH);
+ QR_set_command(self, cmdbuffer);
+
+ mylog("end of tuple list -- setting inUse to false: this = %u\n", self);
+
+ self->inTuples = FALSE;
+ if (self->fcount > 0)
+ {
+ qlog(" [ fetched %d rows ]\n", self->fcount);
+ mylog("_next_tuple: 'C' fetch_max && fcount = %d\n", self->fcount);
+
+ /* set to first row */
+ self->tupleField = self->backend_tuples + (offset * self->num_fields);
+ return TRUE;
+ }
+ else
+ {
+ /* We are surely done here (we read 0 tuples) */
+ qlog(" [ fetched 0 rows ]\n");
+ mylog("_next_tuple: 'C': DONE (fcount == 0)\n");
+ return -1; /* end of tuples */
+ }
+
+ case 'E': /* Error */
+ SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
+ QR_set_message(self, msgbuffer);
+ self->status = PGRES_FATAL_ERROR;
+
+ if (!strncmp(msgbuffer, "FATAL", 5))
+ CC_set_no_trans(self->conn);
+
+ qlog("ERROR from backend in next_tuple: '%s'\n", msgbuffer);
+
+ return FALSE;
+
+ case 'N': /* Notice */
+ SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
+ QR_set_message(self, msgbuffer);
+ self->status = PGRES_NONFATAL_ERROR;
+ qlog("NOTICE from backend in next_tuple: '%s'\n", msgbuffer);
+ continue;
+
+ default: /* this should only happen if the backend
+ * dumped core */
+ mylog("QR_next_tuple: Unexpected result from backend: id = '%c' (%d)\n", id, id);
+ qlog("QR_next_tuple: Unexpected result from backend: id = '%c' (%d)\n", id, id);
+ QR_set_message(self, "Unexpected result from backend. It probably crashed");
+ self->status = PGRES_FATAL_ERROR;
+ CC_set_no_trans(self->conn);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+char
+QR_read_tuple(QResultClass *self, char binary)
+{
+ Int2 field_lf;
+ TupleField *this_tuplefield;
+ char bmp,
+ bitmap[MAX_FIELDS]; /* Max. len of the bitmap */
+ Int2 bitmaplen; /* len of the bitmap in bytes */
+ Int2 bitmap_pos;
+ Int2 bitcnt;
+ Int4 len;
+ char *buffer;
+ int num_fields = self->num_fields; /* speed up access */
+ SocketClass *sock = CC_get_socket(self->conn);
+ ColumnInfoClass *flds;
+
+ /* set the current row to read the fields into */
+ this_tuplefield = self->backend_tuples + (self->fcount * num_fields);
+
+ bitmaplen = (Int2) num_fields / BYTELEN;
+ if ((num_fields % BYTELEN) > 0)
+ bitmaplen++;
+
+ /*
+ * At first the server sends a bitmap that indicates which database
+ * fields are null
+ */
+ SOCK_get_n_char(sock, bitmap, bitmaplen);
+
+ bitmap_pos = 0;
+ bitcnt = 0;
+ bmp = bitmap[bitmap_pos];
+
+ for (field_lf = 0; field_lf < num_fields; field_lf++)
+ {
+ /* Check if the current field is NULL */
+ if (!(bmp & 0200))
+ {
+ /* YES, it is NULL ! */
+ this_tuplefield[field_lf].len = 0;
+ this_tuplefield[field_lf].value = 0;
+ }
+ else
+ {
+ /*
+ * NO, the field is not null. so get at first the length of
+ * the field (four bytes)
+ */
+ len = SOCK_get_int(sock, VARHDRSZ);
+ if (!binary)
+ len -= VARHDRSZ;
+
+ buffer = (char *) malloc(len + 1);
+ SOCK_get_n_char(sock, buffer, len);
+ buffer[len] = '\0';
+
+ mylog("qresult: len=%d, buffer='%s'\n", len, buffer);
+
+ this_tuplefield[field_lf].len = len;
+ this_tuplefield[field_lf].value = buffer;
+
+ /*
+ * This can be used to set the longest length of the column
+ * for any row in the tuple cache. It would not be accurate
+ * for varchar and text fields to use this since a tuple cache
+ * is only 100 rows. Bpchar can be handled since the strlen of
+ * all rows is fixed, assuming there are not 100 nulls in a
+ * row!
+ */
+
+ flds = self->fields;
+ if (flds->display_size[field_lf] < len)
+ flds->display_size[field_lf] = len;
+ }
+
+ /*
+ * Now adjust for the next bit to be scanned in the next loop.
+ */
+ bitcnt++;
+ if (BYTELEN == bitcnt)
+ {
+ bitmap_pos++;
+ bmp = bitmap[bitmap_pos];
+ bitcnt = 0;
+ }
+ else
+ bmp <<= 1;
+ }
+ self->currTuple++;
+ return TRUE;
+}
--- /dev/null
+/* File: qresult.h
+ *
+ * Description: See "qresult.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __QRESULT_H__
+#define __QRESULT_H__
+
+#include "psqlodbc.h"
+
+#include "connection.h"
+#include "socket.h"
+#include "columninfo.h"
+#include "tuplelist.h"
+#include "tuple.h"
+
+enum QueryResultCode_
+{
+ PGRES_EMPTY_QUERY = 0,
+ PGRES_COMMAND_OK, /* a query command that doesn't return */
+ /* anything was executed properly by the backend */
+ PGRES_TUPLES_OK, /* a query command that returns tuples */
+ /* was executed properly by the backend, PGresult */
+ /* contains the resulttuples */
+ PGRES_COPY_OUT,
+ PGRES_COPY_IN,
+ PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from
+ * the backend */
+ PGRES_NONFATAL_ERROR,
+ PGRES_FATAL_ERROR,
+ PGRES_FIELDS_OK, /* field information from a query was
+ * successful */
+ PGRES_END_TUPLES,
+ PGRES_INTERNAL_ERROR
+};
+typedef enum QueryResultCode_ QueryResultCode;
+
+
+struct QResultClass_
+{
+ ColumnInfoClass *fields; /* the Column information */
+ TupleListClass *manual_tuples; /* manual result tuple list */
+ ConnectionClass *conn; /* the connection this result is using
+ * (backend) */
+
+ /* Stuff for declare/fetch tuples */
+ int count_allocated; /* m(re)alloced count */
+ int fetch_count; /* logical rows read so far */
+ int fcount; /* actual rows read in the fetch */
+ int currTuple;
+ int base;
+
+ int num_fields; /* number of fields in the result */
+ int cache_size;
+ int rowset_size;
+
+ QueryResultCode status;
+
+ char *message;
+ char *cursor; /* The name of the cursor for select
+ * statements */
+ char *command;
+ char *notice;
+
+ TupleField *backend_tuples; /* data from the backend (the tuple cache) */
+ TupleField *tupleField; /* current backend tuple being retrieved */
+
+ char inTuples; /* is a fetch of rows from the backend in
+ * progress? */
+ char aborted; /* was aborted? */
+};
+
+#define QR_get_fields(self) (self->fields)
+
+
+/* These functions are for retrieving data from the qresult */
+#define QR_get_value_manual(self, tupleno, fieldno) (TL_get_fieldval(self->manual_tuples, tupleno, fieldno))
+#define QR_get_value_backend(self, fieldno) (self->tupleField[fieldno].value)
+#define QR_get_value_backend_row(self, tupleno, fieldno) ((self->backend_tuples + (tupleno * self->num_fields))[fieldno].value)
+
+/* These functions are used by both manual and backend results */
+#define QR_NumResultCols(self) (CI_get_num_fields(self->fields))
+#define QR_get_fieldname(self, fieldno_) (CI_get_fieldname(self->fields, fieldno_))
+#define QR_get_fieldsize(self, fieldno_) (CI_get_fieldsize(self->fields, fieldno_))
+#define QR_get_display_size(self, fieldno_) (CI_get_display_size(self->fields, fieldno_))
+#define QR_get_atttypmod(self, fieldno_) (CI_get_atttypmod(self->fields, fieldno_))
+#define QR_get_field_type(self, fieldno_) (CI_get_oid(self->fields, fieldno_))
+
+/* These functions are used only for manual result sets */
+#define QR_get_num_tuples(self) (self->manual_tuples ? TL_get_num_tuples(self->manual_tuples) : self->fcount)
+#define QR_add_tuple(self, new_tuple) (TL_add_tuple(self->manual_tuples, new_tuple))
+#define QR_set_field_info(self, field_num, name, adtid, adtsize) (CI_set_field_info(self->fields, field_num, name, adtid, adtsize, -1))
+
+/* status macros */
+#define QR_command_successful(self) ( !(self->status == PGRES_BAD_RESPONSE || self->status == PGRES_NONFATAL_ERROR || self->status == PGRES_FATAL_ERROR))
+#define QR_command_nonfatal(self) ( self->status == PGRES_NONFATAL_ERROR)
+#define QR_end_tuples(self) ( self->status == PGRES_END_TUPLES)
+#define QR_set_status(self, condition) ( self->status = condition )
+#define QR_set_message(self, message_) ( self->message = message_)
+#define QR_set_aborted(self, aborted_) ( self->aborted = aborted_)
+
+#define QR_get_message(self) (self->message)
+#define QR_get_command(self) (self->command)
+#define QR_get_notice(self) (self->notice)
+#define QR_get_status(self) (self->status)
+#define QR_get_aborted(self) (self->aborted)
+
+#define QR_aborted(self) (!self || self->aborted)
+
+/* Core Functions */
+QResultClass *QR_Constructor(void);
+void QR_Destructor(QResultClass *self);
+char QR_read_tuple(QResultClass *self, char binary);
+int QR_next_tuple(QResultClass *self);
+int QR_close(QResultClass *self);
+char QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor);
+void QR_free_memory(QResultClass *self);
+void QR_set_command(QResultClass *self, char *msg);
+void QR_set_notice(QResultClass *self, char *msg);
+
+void QR_set_num_fields(QResultClass *self, int new_num_fields); /* manual result only */
+
+void QR_inc_base(QResultClass *self, int base_inc);
+void QR_set_cache_size(QResultClass *self, int cache_size);
+void QR_set_rowset_size(QResultClass *self, int rowset_size);
+void QR_set_position(QResultClass *self, int pos);
+
+#endif
--- /dev/null
+/* {{NO_DEPENDENCIES}}\r */
+/* Microsoft Developer Studio generated include file.\r */
+/* Used by psqlodbc.rc\r */
+/*\r */
+#define IDS_BADDSN 1\r
+#define IDS_MSGTITLE 2\r
+#define DLG_OPTIONS_DRV 102\r
+#define DLG_OPTIONS_DS 103\r
+#define IDC_DSNAME 400\r
+#define IDC_DSNAMETEXT 401\r
+#define IDC_DESC 404\r
+#define IDC_SERVER 407\r
+#define IDC_DATABASE 408\r
+#define DLG_CONFIG 1001\r
+#define IDC_PORT 1002\r
+#define IDC_USER 1006\r
+#define IDC_PASSWORD 1009\r
+#define DS_READONLY 1011\r
+#define DS_SHOWOIDCOLUMN 1012\r
+#define DS_FAKEOIDINDEX 1013\r
+#define DRV_COMMLOG 1014\r
+#define DS_PG62 1016\r
+#define IDC_DATASOURCE 1018\r
+#define DRV_OPTIMIZER 1019\r
+#define DS_CONNSETTINGS 1020\r
+#define IDC_DRIVER 1021\r
+#define DRV_CONNSETTINGS 1031\r
+#define DRV_UNIQUEINDEX 1032\r
+#define DRV_UNKNOWN_MAX 1035\r
+#define DRV_UNKNOWN_DONTKNOW 1036\r
+#define DRV_READONLY 1037\r
+#define IDC_DESCTEXT 1039\r
+#define DRV_MSG_LABEL 1040\r
+#define DRV_UNKNOWN_LONGEST 1041\r
+#define DRV_TEXT_LONGVARCHAR 1043\r
+#define DRV_UNKNOWNS_LONGVARCHAR 1044\r
+#define DRV_CACHE_SIZE 1045\r
+#define DRV_VARCHAR_SIZE 1046\r
+#define DRV_LONGVARCHAR_SIZE 1047\r
+#define IDDEFAULTS 1048\r
+#define DRV_USEDECLAREFETCH 1049\r
+#define DRV_BOOLS_CHAR 1050\r
+#define DS_SHOWSYSTEMTABLES 1051\r
+#define DRV_EXTRASYSTABLEPREFIXES 1051\r
+#define DS_ROWVERSIONING 1052\r
+#define DRV_PARSE 1052\r
+#define DRV_CANCELASFREESTMT 1053\r
+#define IDC_OPTIONS 1054\r
+#define DRV_KSQO 1055\r
+#define DS_PG64 1057\r
+#define DS_PG63 1058\r
+#define DRV_OR_DSN 1059\r
+#define DRV_DEBUG 1060\r
+#define DS_DISALLOWPREMATURE 1061\r
+\r
+/* Next default values for new objects\r */
+/*\r */
+#ifdef APSTUDIO_INVOKED\r
+#ifndef APSTUDIO_READONLY_SYMBOLS\r
+#define _APS_NEXT_RESOURCE_VALUE 105\r
+#define _APS_NEXT_COMMAND_VALUE 40001\r
+#define _APS_NEXT_CONTROL_VALUE 1062\r
+#define _APS_NEXT_SYMED_VALUE 101\r
+#endif /*\r */
+
+#endif /*\r */
--- /dev/null
+/*-------
+ * Module: results.c
+ *
+ * Description: This module contains functions related to
+ * retrieving result information through the ODBC API.
+ *
+ * Classes: n/a
+ *
+ * API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol,
+ * SQLColAttributes, SQLGetData, SQLFetch, SQLExtendedFetch,
+ * SQLMoreResults(NI), SQLSetPos, SQLSetScrollOptions(NI),
+ * SQLSetCursorName, SQLGetCursorName
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "psqlodbc.h"
+
+#include <string.h>
+#include "dlg_specific.h"
+#include "environ.h"
+#include "connection.h"
+#include "statement.h"
+#include "bind.h"
+#include "qresult.h"
+#include "convert.h"
+#include "pgtypes.h"
+
+#include <stdio.h>
+
+#include "pgapifunc.h"
+
+
+
+RETCODE SQL_API
+PGAPI_RowCount(
+ HSTMT hstmt,
+ SDWORD FAR * pcrow)
+{
+ static char *func = "PGAPI_RowCount";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ QResultClass *res;
+ char *msg,
+ *ptr;
+ ConnInfo *ci;
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ ci = &(SC_get_conn(stmt)->connInfo);
+ if (stmt->manual_result)
+ {
+ if (pcrow)
+ *pcrow = -1;
+ return SQL_SUCCESS;
+ }
+
+ if (stmt->statement_type == STMT_TYPE_SELECT)
+ {
+ if (stmt->status == STMT_FINISHED)
+ {
+ res = SC_get_Result(stmt);
+
+ if (res && pcrow)
+ {
+ *pcrow = SC_is_fetchcursor(stmt) ? -1 : QR_get_num_tuples(res);
+ return SQL_SUCCESS;
+ }
+ }
+ }
+ else
+ {
+ res = SC_get_Result(stmt);
+ if (res && pcrow)
+ {
+ msg = QR_get_command(res);
+ mylog("*** msg = '%s'\n", msg);
+ trim(msg); /* get rid of trailing spaces */
+ ptr = strrchr(msg, ' ');
+ if (ptr)
+ {
+ *pcrow = atoi(ptr + 1);
+ mylog("**** PGAPI_RowCount(): THE ROWS: *pcrow = %d\n", *pcrow);
+ }
+ else
+ {
+ *pcrow = -1;
+ mylog("**** PGAPI_RowCount(): NO ROWS: *pcrow = %d\n", *pcrow);
+ }
+
+ return SQL_SUCCESS;
+ }
+ }
+
+ SC_log_error(func, "Bad return value", stmt);
+ return SQL_ERROR;
+}
+
+
+/*
+ * This returns the number of columns associated with the database
+ * attached to "hstmt".
+ */
+RETCODE SQL_API
+PGAPI_NumResultCols(
+ HSTMT hstmt,
+ SWORD FAR * pccol)
+{
+ static char *func = "PGAPI_NumResultCols";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ QResultClass *result;
+ char parse_ok;
+ ConnInfo *ci;
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ ci = &(SC_get_conn(stmt)->connInfo);
+
+ SC_clear_error(stmt);
+
+ parse_ok = FALSE;
+ if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
+ {
+ if (stmt->parse_status == STMT_PARSE_NONE)
+ {
+ mylog("PGAPI_NumResultCols: calling parse_statement on stmt=%u\n", stmt);
+ parse_statement(stmt);
+ }
+
+ if (stmt->parse_status != STMT_PARSE_FATAL)
+ {
+ parse_ok = TRUE;
+ *pccol = stmt->nfld;
+ mylog("PARSE: PGAPI_NumResultCols: *pccol = %d\n", *pccol);
+ }
+ }
+
+ if (!parse_ok)
+ {
+ SC_pre_execute(stmt);
+ result = SC_get_Result(stmt);
+
+ mylog("PGAPI_NumResultCols: result = %u, status = %d, numcols = %d\n", result, stmt->status, result != NULL ? QR_NumResultCols(result) : -1);
+ if ((!result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
+ {
+ /* no query has been executed on this statement */
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ stmt->errormsg = "No query has been executed with that handle";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ *pccol = QR_NumResultCols(result);
+ /* updatable cursors */
+ if (ci->updatable_cursors &&
+ stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
+ *pccol -= 2;
+ }
+
+ return SQL_SUCCESS;
+}
+
+
+/*
+ * Return information about the database column the user wants
+ * information about.
+ */
+RETCODE SQL_API
+PGAPI_DescribeCol(
+ HSTMT hstmt,
+ UWORD icol,
+ UCHAR FAR * szColName,
+ SWORD cbColNameMax,
+ SWORD FAR * pcbColName,
+ SWORD FAR * pfSqlType,
+ UDWORD FAR * pcbColDef,
+ SWORD FAR * pibScale,
+ SWORD FAR * pfNullable)
+{
+ static char *func = "PGAPI_DescribeCol";
+
+ /* gets all the information about a specific column */
+ StatementClass *stmt = (StatementClass *) hstmt;
+ QResultClass *res;
+ char *col_name = NULL;
+ Int4 fieldtype = 0;
+ int precision = 0,
+ scale = 0;
+ ConnInfo *ci;
+ char parse_ok;
+ char buf[255];
+ int len = 0;
+ RETCODE result;
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ ci = &(SC_get_conn(stmt)->connInfo);
+
+ SC_clear_error(stmt);
+
+ /*
+ * Dont check for bookmark column. This is the responsibility of the
+ * driver manager.
+ */
+
+ icol--; /* use zero based column numbers */
+
+ parse_ok = FALSE;
+ if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
+ {
+ if (stmt->parse_status == STMT_PARSE_NONE)
+ {
+ mylog("PGAPI_DescribeCol: calling parse_statement on stmt=%u\n", stmt);
+ parse_statement(stmt);
+ }
+
+ mylog("PARSE: DescribeCol: icol=%d, stmt=%u, stmt->nfld=%d, stmt->fi=%u\n", icol, stmt, stmt->nfld, stmt->fi);
+
+ if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[icol])
+ {
+ if (icol >= stmt->nfld)
+ {
+ stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
+ stmt->errormsg = "Invalid column number in DescribeCol.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ mylog("DescribeCol: getting info for icol=%d\n", icol);
+
+ fieldtype = stmt->fi[icol]->type;
+ if (stmt->fi[icol]->alias[0])
+ col_name = stmt->fi[icol]->alias;
+ else
+ col_name = stmt->fi[icol]->name;
+ precision = stmt->fi[icol]->precision;
+ scale = stmt->fi[icol]->scale;
+
+ mylog("PARSE: fieldtype=%d, col_name='%s', precision=%d\n", fieldtype, col_name, precision);
+ if (fieldtype > 0)
+ parse_ok = TRUE;
+ }
+ }
+
+ /*
+ * If couldn't parse it OR the field being described was not parsed
+ * (i.e., because it was a function or expression, etc, then do it the
+ * old fashioned way.
+ */
+ if (!parse_ok)
+ {
+ SC_pre_execute(stmt);
+
+ res = SC_get_Result(stmt);
+
+ mylog("**** PGAPI_DescribeCol: res = %u, stmt->status = %d, !finished=%d, !premature=%d\n", res, stmt->status, stmt->status != STMT_FINISHED, stmt->status != STMT_PREMATURE);
+ if ((NULL == res) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
+ {
+ /* no query has been executed on this statement */
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ stmt->errormsg = "No query has been assigned to this statement.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (icol >= QR_NumResultCols(res))
+ {
+ stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
+ stmt->errormsg = "Invalid column number in DescribeCol.";
+ sprintf(buf, "Col#=%d, #Cols=%d", icol, QR_NumResultCols(res));
+ SC_log_error(func, buf, stmt);
+ return SQL_ERROR;
+ }
+
+ col_name = QR_get_fieldname(res, icol);
+ fieldtype = QR_get_field_type(res, icol);
+
+ /* atoi(ci->unknown_sizes) */
+ precision = pgtype_precision(stmt, fieldtype, icol, ci->drivers.unknown_sizes);
+ scale = pgtype_scale(stmt, fieldtype, icol);
+ }
+
+ mylog("describeCol: col %d fieldname = '%s'\n", icol, col_name);
+ mylog("describeCol: col %d fieldtype = %d\n", icol, fieldtype);
+ mylog("describeCol: col %d precision = %d\n", icol, precision);
+
+ result = SQL_SUCCESS;
+
+ /*
+ * COLUMN NAME
+ */
+ len = strlen(col_name);
+
+ if (pcbColName)
+ *pcbColName = len;
+
+ if (szColName)
+ {
+ strncpy_null(szColName, col_name, cbColNameMax);
+
+ if (len >= cbColNameMax)
+ {
+ result = SQL_SUCCESS_WITH_INFO;
+ stmt->errornumber = STMT_TRUNCATED;
+ stmt->errormsg = "The buffer was too small for the colName.";
+ }
+ }
+
+ /*
+ * SQL TYPE
+ */
+ if (pfSqlType)
+ {
+ *pfSqlType = pgtype_to_sqltype(stmt, fieldtype);
+
+ mylog("describeCol: col %d *pfSqlType = %d\n", icol, *pfSqlType);
+ }
+
+ /*
+ * PRECISION
+ */
+ if (pcbColDef)
+ {
+ if (precision < 0)
+ precision = 0; /* "I dont know" */
+
+ *pcbColDef = precision;
+
+ mylog("describeCol: col %d *pcbColDef = %d\n", icol, *pcbColDef);
+ }
+
+ /*
+ * SCALE
+ */
+ if (pibScale)
+ {
+ if (scale < 0)
+ scale = 0;
+
+ *pibScale = scale;
+ mylog("describeCol: col %d *pibScale = %d\n", icol, *pibScale);
+ }
+
+ /*
+ * NULLABILITY
+ */
+ if (pfNullable)
+ {
+ *pfNullable = (parse_ok) ? stmt->fi[icol]->nullable : pgtype_nullable(stmt, fieldtype);
+
+ mylog("describeCol: col %d *pfNullable = %d\n", icol, *pfNullable);
+ }
+
+ return result;
+}
+
+
+/* Returns result column descriptor information for a result set. */
+RETCODE SQL_API
+PGAPI_ColAttributes(
+ HSTMT hstmt,
+ UWORD icol,
+ UWORD fDescType,
+ PTR rgbDesc,
+ SWORD cbDescMax,
+ SWORD FAR * pcbDesc,
+ SDWORD FAR * pfDesc)
+{
+ static char *func = "PGAPI_ColAttributes";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ Int4 field_type = 0;
+ ConnInfo *ci;
+ int unknown_sizes;
+ int cols = 0;
+ char parse_ok;
+ RETCODE result;
+ char *p = NULL;
+ int len = 0,
+ value = 0;
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ ci = &(SC_get_conn(stmt)->connInfo);
+
+ /*
+ * Dont check for bookmark column. This is the responsibility of the
+ * driver manager. For certain types of arguments, the column number
+ * is ignored anyway, so it may be 0.
+ */
+
+ icol--;
+
+ /* atoi(ci->unknown_sizes); */
+ unknown_sizes = ci->drivers.unknown_sizes;
+
+ /* not appropriate for SQLColAttributes() */
+ if (unknown_sizes == UNKNOWNS_AS_DONTKNOW)
+ unknown_sizes = UNKNOWNS_AS_MAX;
+
+ parse_ok = FALSE;
+ if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
+ {
+ if (stmt->parse_status == STMT_PARSE_NONE)
+ {
+ mylog("PGAPI_ColAttributes: calling parse_statement\n");
+ parse_statement(stmt);
+ }
+
+ cols = stmt->nfld;
+
+ /*
+ * Column Count is a special case. The Column number is ignored
+ * in this case.
+ */
+ if (fDescType == SQL_COLUMN_COUNT)
+ {
+ if (pfDesc)
+ *pfDesc = cols;
+
+ return SQL_SUCCESS;
+ }
+
+ if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[icol])
+ {
+ if (icol >= cols)
+ {
+ stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
+ stmt->errormsg = "Invalid column number in ColAttributes.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ field_type = stmt->fi[icol]->type;
+ if (field_type > 0)
+ parse_ok = TRUE;
+ }
+ }
+
+ if (!parse_ok)
+ {
+ SC_pre_execute(stmt);
+
+ mylog("**** PGAPI_ColAtt: result = %u, status = %d, numcols = %d\n", stmt->result, stmt->status, stmt->result != NULL ? QR_NumResultCols(stmt->result) : -1);
+
+ if ((NULL == stmt->result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
+ {
+ stmt->errormsg = "Can't get column attributes: no result found.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ cols = QR_NumResultCols(stmt->result);
+
+ /*
+ * Column Count is a special case. The Column number is ignored
+ * in this case.
+ */
+ if (fDescType == SQL_COLUMN_COUNT)
+ {
+ if (pfDesc)
+ *pfDesc = cols;
+
+ return SQL_SUCCESS;
+ }
+
+ if (icol >= cols)
+ {
+ stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
+ stmt->errormsg = "Invalid column number in ColAttributes.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ field_type = QR_get_field_type(stmt->result, icol);
+ }
+
+ mylog("colAttr: col %d field_type = %d\n", icol, field_type);
+
+ switch (fDescType)
+ {
+ case SQL_COLUMN_AUTO_INCREMENT:
+ value = pgtype_auto_increment(stmt, field_type);
+ if (value == -1) /* non-numeric becomes FALSE (ODBC Doc) */
+ value = FALSE;
+
+ break;
+
+ case SQL_COLUMN_CASE_SENSITIVE:
+ value = pgtype_case_sensitive(stmt, field_type);
+ break;
+
+ /*
+ * This special case is handled above.
+ *
+ * case SQL_COLUMN_COUNT:
+ */
+ case SQL_COLUMN_DISPLAY_SIZE:
+ value = (parse_ok) ? stmt->fi[icol]->display_size : pgtype_display_size(stmt, field_type, icol, unknown_sizes);
+
+ mylog("PGAPI_ColAttributes: col %d, display_size= %d\n", icol, value);
+
+ break;
+
+ case SQL_COLUMN_LABEL:
+ if (parse_ok && stmt->fi[icol]->alias[0] != '\0')
+ {
+ p = stmt->fi[icol]->alias;
+
+ mylog("PGAPI_ColAttr: COLUMN_LABEL = '%s'\n", p);
+ break;
+
+ }
+ /* otherwise same as column name -- FALL THROUGH!!! */
+
+ case SQL_COLUMN_NAME:
+ p = (parse_ok) ? stmt->fi[icol]->name : QR_get_fieldname(stmt->result, icol);
+
+ mylog("PGAPI_ColAttr: COLUMN_NAME = '%s'\n", p);
+ break;
+
+ case SQL_COLUMN_LENGTH:
+ value = (parse_ok) ? stmt->fi[icol]->length : pgtype_length(stmt, field_type, icol, unknown_sizes);
+
+ mylog("PGAPI_ColAttributes: col %d, length = %d\n", icol, value);
+ break;
+
+ case SQL_COLUMN_MONEY:
+ value = pgtype_money(stmt, field_type);
+ break;
+
+ case SQL_COLUMN_NULLABLE:
+ value = (parse_ok) ? stmt->fi[icol]->nullable : pgtype_nullable(stmt, field_type);
+ break;
+
+ case SQL_COLUMN_OWNER_NAME:
+ p = "";
+ break;
+
+ case SQL_COLUMN_PRECISION:
+ value = (parse_ok) ? stmt->fi[icol]->precision : pgtype_precision(stmt, field_type, icol, unknown_sizes);
+
+ mylog("PGAPI_ColAttributes: col %d, precision = %d\n", icol, value);
+ break;
+
+ case SQL_COLUMN_QUALIFIER_NAME:
+ p = "";
+ break;
+
+ case SQL_COLUMN_SCALE:
+ value = pgtype_scale(stmt, field_type, icol);
+ break;
+
+ case SQL_COLUMN_SEARCHABLE:
+ value = pgtype_searchable(stmt, field_type);
+ break;
+
+ case SQL_COLUMN_TABLE_NAME:
+ p = (parse_ok && stmt->fi[icol]->ti) ? stmt->fi[icol]->ti->name : "";
+
+ mylog("PGAPI_ColAttr: TABLE_NAME = '%s'\n", p);
+ break;
+
+ case SQL_COLUMN_TYPE:
+ value = pgtype_to_sqltype(stmt, field_type);
+ break;
+
+ case SQL_COLUMN_TYPE_NAME:
+ p = pgtype_to_name(stmt, field_type);
+ break;
+
+ case SQL_COLUMN_UNSIGNED:
+ value = pgtype_unsigned(stmt, field_type);
+ if (value == -1) /* non-numeric becomes TRUE (ODBC Doc) */
+ value = TRUE;
+
+ break;
+
+ case SQL_COLUMN_UPDATABLE:
+
+ /*
+ * Neither Access or Borland care about this.
+ *
+ * if (field_type == PG_TYPE_OID) pfDesc = SQL_ATTR_READONLY;
+ * else
+ */
+ value = SQL_ATTR_WRITE;
+
+ mylog("PGAPI_ColAttr: UPDATEABLE = %d\n", value);
+ break;
+ }
+
+ result = SQL_SUCCESS;
+
+ if (p)
+ { /* char/binary data */
+ len = strlen(p);
+
+ if (rgbDesc)
+ {
+ strncpy_null((char *) rgbDesc, p, (size_t) cbDescMax);
+
+ if (len >= cbDescMax)
+ {
+ result = SQL_SUCCESS_WITH_INFO;
+ stmt->errornumber = STMT_TRUNCATED;
+ stmt->errormsg = "The buffer was too small for the rgbDesc.";
+ }
+ }
+
+ if (pcbDesc)
+ *pcbDesc = len;
+ }
+ else
+ {
+ /* numeric data */
+ if (pfDesc)
+ *pfDesc = value;
+ }
+
+ return result;
+}
+
+
+/* Returns result data for a single column in the current row. */
+RETCODE SQL_API
+PGAPI_GetData(
+ HSTMT hstmt,
+ UWORD icol,
+ SWORD fCType,
+ PTR rgbValue,
+ SDWORD cbValueMax,
+ SDWORD FAR * pcbValue)
+{
+ static char *func = "PGAPI_GetData";
+ QResultClass *res;
+ StatementClass *stmt = (StatementClass *) hstmt;
+ int num_cols,
+ num_rows;
+ Int4 field_type;
+ void *value = NULL;
+ int result;
+ char get_bookmark = FALSE;
+ ConnInfo *ci;
+
+ mylog("PGAPI_GetData: enter, stmt=%u\n", stmt);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ ci = &(SC_get_conn(stmt)->connInfo);
+ res = stmt->result;
+
+ if (STMT_EXECUTING == stmt->status)
+ {
+ stmt->errormsg = "Can't get data while statement is still executing.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (stmt->status != STMT_FINISHED)
+ {
+ stmt->errornumber = STMT_STATUS_ERROR;
+ stmt->errormsg = "GetData can only be called after the successful execution on a SQL statement";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (icol == 0)
+ {
+ if (stmt->options.use_bookmarks == SQL_UB_OFF)
+ {
+ stmt->errornumber = STMT_COLNUM_ERROR;
+ stmt->errormsg = "Attempt to retrieve bookmark with bookmark usage disabled";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* Make sure it is the bookmark data type */
+ if (fCType != SQL_C_BOOKMARK)
+ {
+ stmt->errormsg = "Column 0 is not of type SQL_C_BOOKMARK";
+ stmt->errornumber = STMT_PROGRAM_TYPE_OUT_OF_RANGE;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ get_bookmark = TRUE;
+ }
+ else
+ {
+ /* use zero-based column numbers */
+ icol--;
+
+ /* make sure the column number is valid */
+ num_cols = QR_NumResultCols(res);
+ if (icol >= num_cols)
+ {
+ stmt->errormsg = "Invalid column number.";
+ stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+
+ if (stmt->manual_result || !SC_is_fetchcursor(stmt))
+ {
+ /* make sure we're positioned on a valid row */
+ num_rows = QR_get_num_tuples(res);
+ if ((stmt->currTuple < 0) ||
+ (stmt->currTuple >= num_rows))
+ {
+ stmt->errormsg = "Not positioned on a valid row for GetData.";
+ stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ mylog(" num_rows = %d\n", num_rows);
+
+ if (!get_bookmark)
+ {
+ if (stmt->manual_result)
+ value = QR_get_value_manual(res, stmt->currTuple, icol);
+ else
+ value = QR_get_value_backend_row(res, stmt->currTuple, icol);
+ mylog(" value = '%s'\n", value);
+ }
+ }
+ else
+ {
+ /* it's a SOCKET result (backend data) */
+ if (stmt->currTuple == -1 || !res || !res->tupleField)
+ {
+ stmt->errormsg = "Not positioned on a valid row for GetData.";
+ stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (!get_bookmark)
+ value = QR_get_value_backend(res, icol);
+
+ mylog(" socket: value = '%s'\n", value);
+ }
+
+ if (get_bookmark)
+ {
+ *((UDWORD *) rgbValue) = SC_get_bookmark(stmt);
+
+ if (pcbValue)
+ *pcbValue = 4;
+
+ return SQL_SUCCESS;
+ }
+
+ field_type = QR_get_field_type(res, icol);
+
+ mylog("**** PGAPI_GetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value);
+
+ stmt->current_col = icol;
+
+ result = copy_and_convert_field(stmt, field_type, value,
+ fCType, rgbValue, cbValueMax, pcbValue);
+
+ stmt->current_col = -1;
+
+ switch (result)
+ {
+ case COPY_OK:
+ return SQL_SUCCESS;
+
+ case COPY_UNSUPPORTED_TYPE:
+ stmt->errormsg = "Received an unsupported type from Postgres.";
+ stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+
+ case COPY_UNSUPPORTED_CONVERSION:
+ stmt->errormsg = "Couldn't handle the necessary data type conversion.";
+ stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+
+ case COPY_RESULT_TRUNCATED:
+ stmt->errornumber = STMT_TRUNCATED;
+ stmt->errormsg = "The buffer was too small for the GetData.";
+ return SQL_SUCCESS_WITH_INFO;
+
+ case COPY_GENERAL_ERROR: /* error msg already filled in */
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+
+ case COPY_NO_DATA_FOUND:
+ /* SC_log_error(func, "no data found", stmt); */
+ return SQL_NO_DATA_FOUND;
+
+ default:
+ stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
+ stmt->errornumber = STMT_INTERNAL_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+}
+
+
+/*
+ * Returns data for bound columns in the current row ("hstmt->iCursor"),
+ * advances the cursor.
+ */
+RETCODE SQL_API
+PGAPI_Fetch(
+ HSTMT hstmt)
+{
+ static char *func = "PGAPI_Fetch";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ QResultClass *res;
+
+ mylog("PGAPI_Fetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ SC_clear_error(stmt);
+
+ if (!(res = stmt->result))
+ {
+ stmt->errormsg = "Null statement result in PGAPI_Fetch.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* Not allowed to bind a bookmark column when using SQLFetch. */
+ if (stmt->bookmark.buffer)
+ {
+ stmt->errornumber = STMT_COLNUM_ERROR;
+ stmt->errormsg = "Not allowed to bind a bookmark column when using PGAPI_Fetch";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (stmt->status == STMT_EXECUTING)
+ {
+ stmt->errormsg = "Can't fetch while statement is still executing.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (stmt->status != STMT_FINISHED)
+ {
+ stmt->errornumber = STMT_STATUS_ERROR;
+ stmt->errormsg = "Fetch can only be called after the successful execution on a SQL statement";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (stmt->bindings == NULL)
+ {
+ /* just to avoid a crash if the user insists on calling this */
+ /* function even if SQL_ExecDirect has reported an Error */
+ stmt->errormsg = "Bindings were not allocated properly.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ QR_set_rowset_size(res, 1);
+ QR_inc_base(res, stmt->last_fetch_count);
+
+ return SC_fetch(stmt);
+}
+
+
+/* This fetchs a block of data (rowset). */
+RETCODE SQL_API
+PGAPI_ExtendedFetch(
+ HSTMT hstmt,
+ UWORD fFetchType,
+ SDWORD irow,
+ UDWORD FAR * pcrow,
+ UWORD FAR * rgfRowStatus)
+{
+ static char *func = "PGAPI_ExtendedFetch";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ QResultClass *res;
+ int num_tuples,
+ i,
+ save_rowset_size;
+ RETCODE result;
+ char truncated,
+ error;
+ ConnInfo *ci;
+
+ mylog("PGAPI_ExtendedFetch: stmt=%u\n", stmt);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ ci = &(SC_get_conn(stmt)->connInfo);
+
+ if (SC_is_fetchcursor(stmt) && !stmt->manual_result)
+ {
+ if (fFetchType != SQL_FETCH_NEXT)
+ {
+ stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+ stmt->errormsg = "Unsupported fetch type for PGAPI_ExtendedFetch with UseDeclareFetch option.";
+ return SQL_ERROR;
+ }
+ }
+
+ SC_clear_error(stmt);
+
+ if (!(res = stmt->result))
+ {
+ stmt->errormsg = "Null statement result in PGAPI_ExtendedFetch.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /*
+ * If a bookmark colunmn is bound but bookmark usage is off, then
+ * error
+ */
+ if (stmt->bookmark.buffer && stmt->options.use_bookmarks == SQL_UB_OFF)
+ {
+ stmt->errornumber = STMT_COLNUM_ERROR;
+ stmt->errormsg = "Attempt to retrieve bookmark with bookmark usage disabled";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (stmt->status == STMT_EXECUTING)
+ {
+ stmt->errormsg = "Can't fetch while statement is still executing.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (stmt->status != STMT_FINISHED)
+ {
+ stmt->errornumber = STMT_STATUS_ERROR;
+ stmt->errormsg = "ExtendedFetch can only be called after the successful execution on a SQL statement";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (stmt->bindings == NULL)
+ {
+ /* just to avoid a crash if the user insists on calling this */
+ /* function even if SQL_ExecDirect has reported an Error */
+ stmt->errormsg = "Bindings were not allocated properly.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* Initialize to no rows fetched */
+ if (rgfRowStatus)
+ for (i = 0; i < stmt->options.rowset_size; i++)
+ *(rgfRowStatus + i) = SQL_ROW_NOROW;
+
+ if (pcrow)
+ *pcrow = 0;
+
+ num_tuples = QR_get_num_tuples(res);
+
+ /* Save and discard the saved rowset size */
+ save_rowset_size = stmt->save_rowset_size;
+ stmt->save_rowset_size = -1;
+
+ switch (fFetchType)
+ {
+ case SQL_FETCH_NEXT:
+
+ /*
+ * From the odbc spec... If positioned before the start of the
+ * RESULT SET, then this should be equivalent to
+ * SQL_FETCH_FIRST.
+ */
+
+ if (stmt->rowset_start < 0)
+ stmt->rowset_start = 0;
+
+ else
+ stmt->rowset_start += (save_rowset_size > 0 ? save_rowset_size : stmt->options.rowset_size);
+
+ mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
+ break;
+
+ case SQL_FETCH_PRIOR:
+ mylog("SQL_FETCH_PRIOR: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
+
+ /*
+ * From the odbc spec... If positioned after the end of the
+ * RESULT SET, then this should be equivalent to
+ * SQL_FETCH_LAST.
+ */
+ if (stmt->rowset_start >= num_tuples)
+ {
+ if (stmt->options.rowset_size > num_tuples)
+ {
+ stmt->errornumber = STMT_POS_BEFORE_RECORDSET;
+ stmt->errormsg = "fetch prior from eof and before the beggining";
+ }
+ stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size);
+
+ }
+ else
+ {
+ if (stmt->rowset_start < stmt->options.rowset_size)
+ {
+ stmt->errormsg = "fetch prior and before the beggining";
+ stmt->errornumber = STMT_POS_BEFORE_RECORDSET;
+ }
+ stmt->rowset_start -= stmt->options.rowset_size;
+ }
+ break;
+
+ case SQL_FETCH_FIRST:
+ mylog("SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
+
+ stmt->rowset_start = 0;
+ break;
+
+ case SQL_FETCH_LAST:
+ mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
+
+ stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size);
+ break;
+
+ case SQL_FETCH_ABSOLUTE:
+ mylog("SQL_FETCH_ABSOLUTE: num_tuples=%d, currtuple=%d, irow=%d\n", num_tuples, stmt->currTuple, irow);
+
+ /* Position before result set, but dont fetch anything */
+ if (irow == 0)
+ {
+ stmt->rowset_start = -1;
+ stmt->currTuple = -1;
+ return SQL_NO_DATA_FOUND;
+ }
+ /* Position before the desired row */
+ else if (irow > 0)
+ stmt->rowset_start = irow - 1;
+ /* Position with respect to the end of the result set */
+ else
+ stmt->rowset_start = num_tuples + irow;
+ break;
+
+ case SQL_FETCH_RELATIVE:
+
+ /*
+ * Refresh the current rowset -- not currently implemented,
+ * but lie anyway
+ */
+ if (irow == 0)
+ break;
+
+ stmt->rowset_start += irow;
+ break;
+
+ case SQL_FETCH_BOOKMARK:
+ stmt->rowset_start = irow - 1;
+ break;
+
+ default:
+ SC_log_error(func, "Unsupported PGAPI_ExtendedFetch Direction", stmt);
+ return SQL_ERROR;
+ }
+
+ /*
+ * CHECK FOR PROPER CURSOR STATE
+ */
+
+ /*
+ * Handle Declare Fetch style specially because the end is not really
+ * the end...
+ */
+ if (SC_is_fetchcursor(stmt) && !stmt->manual_result)
+ {
+ if (QR_end_tuples(res))
+ return SQL_NO_DATA_FOUND;
+ }
+ else
+ {
+ /* If *new* rowset is after the result_set, return no data found */
+ if (stmt->rowset_start >= num_tuples)
+ {
+ stmt->rowset_start = num_tuples;
+ return SQL_NO_DATA_FOUND;
+ }
+ }
+
+ /* If *new* rowset is prior to result_set, return no data found */
+ if (stmt->rowset_start < 0)
+ {
+ if (stmt->rowset_start + stmt->options.rowset_size <= 0)
+ {
+ stmt->rowset_start = -1;
+ return SQL_NO_DATA_FOUND;
+ }
+ else
+ { /* overlap with beginning of result set,
+ * so get first rowset */
+ stmt->rowset_start = 0;
+ }
+ }
+
+ /* currTuple is always 1 row prior to the rowset */
+ stmt->currTuple = stmt->rowset_start - 1;
+
+ /* increment the base row in the tuple cache */
+ QR_set_rowset_size(res, stmt->options.rowset_size);
+ /* QR_inc_base(res, stmt->last_fetch_count); */
+ /* Is inc_base right ? */
+ res->base = stmt->rowset_start;
+
+ /* Physical Row advancement occurs for each row fetched below */
+
+ mylog("PGAPI_ExtendedFetch: new currTuple = %d\n", stmt->currTuple);
+
+ truncated = error = FALSE;
+ for (i = 0; i < stmt->options.rowset_size; i++)
+ {
+ stmt->bind_row = i; /* set the binding location */
+ result = SC_fetch(stmt);
+
+ /* Determine Function status */
+ if (result == SQL_NO_DATA_FOUND)
+ break;
+ else if (result == SQL_SUCCESS_WITH_INFO)
+ truncated = TRUE;
+ else if (result == SQL_ERROR)
+ error = TRUE;
+
+ /* Determine Row Status */
+ if (rgfRowStatus)
+ {
+ if (result == SQL_ERROR)
+ *(rgfRowStatus + i) = SQL_ROW_ERROR;
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ /* this should be refined */
+ else if (result > 10 && result < 20)
+ *(rgfRowStatus + i) = result - 10;
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ else
+ *(rgfRowStatus + i) = SQL_ROW_SUCCESS;
+ }
+ }
+
+ /* Save the fetch count for SQLSetPos */
+ stmt->last_fetch_count = i;
+
+ /* Reset next binding row */
+ stmt->bind_row = 0;
+
+ /* Move the cursor position to the first row in the result set. */
+ stmt->currTuple = stmt->rowset_start;
+
+ /* For declare/fetch, need to reset cursor to beginning of rowset */
+ if (SC_is_fetchcursor(stmt) && !stmt->manual_result)
+ QR_set_position(res, 0);
+
+ /* Set the number of rows retrieved */
+ if (pcrow)
+ *pcrow = i;
+
+ if (i == 0)
+ /* Only DeclareFetch should wind up here */
+ return SQL_NO_DATA_FOUND;
+ else if (error)
+ return SQL_ERROR;
+ else if (truncated)
+ return SQL_SUCCESS_WITH_INFO;
+ else if (stmt->errornumber == STMT_POS_BEFORE_RECORDSET)
+ return SQL_SUCCESS_WITH_INFO;
+ else
+ return SQL_SUCCESS;
+}
+
+
+/*
+ * This determines whether there are more results sets available for
+ * the "hstmt".
+ */
+/* CC: return SQL_NO_DATA_FOUND since we do not support multiple result sets */
+RETCODE SQL_API
+PGAPI_MoreResults(
+ HSTMT hstmt)
+{
+ return SQL_NO_DATA_FOUND;
+}
+
+
+#ifdef DRIVER_CURSOR_IMPLEMENT
+/*
+ * Stuff for updatable cursors.
+ */
+static QResultClass *
+positioned_load(StatementClass *stmt, BOOL latest, int res_cols, UInt4 oid, const char *tidval)
+{
+ int i;
+ QResultClass *qres;
+ char selstr[4096];
+
+ sprintf(selstr, "select");
+ for (i = 0; i < res_cols; i++)
+ sprintf(selstr, "%s \"%s\",", selstr, stmt->fi[i]->name);
+ sprintf(selstr, "%s CTID, OID from \"%s\" where", selstr, stmt->ti[0]->name);
+ if (tidval)
+ {
+ if (latest)
+ sprintf(selstr, "%s ctid = currtid2('%s', '%s') and",
+ selstr, stmt->ti[0]->name, tidval);
+ else
+ sprintf(selstr, "%s ctid = '%s' and", selstr, tidval);
+ }
+ sprintf(selstr, "%s oid = %u", selstr, oid),
+ mylog("selstr=%s\n", selstr);
+ qres = CC_send_query(SC_get_conn(stmt), selstr, NULL);
+ if (qres && QR_aborted(qres))
+ {
+ QR_Destructor(qres);
+ qres = (QResultClass *) 0;
+ }
+ return qres;
+}
+
+RETCODE SQL_API
+SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count)
+{
+ int i,
+ res_cols;
+ UWORD rcnt,
+ global_ridx;
+ UInt4 oid;
+ QResultClass *res,
+ *qres;
+ RETCODE ret = SQL_ERROR;
+ char *tidval,
+ *oidval;
+
+ mylog("positioned load fi=%x ti=%x\n", stmt->fi, stmt->ti);
+ rcnt = 0;
+ if (count)
+ *count = 0;
+ if (!(res = stmt->result))
+ return SQL_ERROR;
+ if (!stmt->ti)
+ parse_statement(stmt); /* not preferable */
+ if (!stmt->ti || stmt->ntab != 1)
+ {
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ return SQL_ERROR;
+ }
+ global_ridx = irow + stmt->rowset_start;
+ res_cols = QR_NumResultCols(res);
+ if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
+ return SQL_SUCCESS_WITH_INFO;
+ sscanf(oidval, "%u", &oid);
+ tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2);
+ res_cols -= 2;
+ if (qres = positioned_load(stmt, TRUE, res_cols, oid, tidval), qres)
+ {
+ TupleField *tupleo,
+ *tuplen;
+
+ rcnt = QR_get_num_tuples(qres);
+ tupleo = res->backend_tuples + res->num_fields * global_ridx;
+ if (rcnt == 1)
+ {
+ QR_set_position(qres, 0);
+ tuplen = res->tupleField;
+ for (i = 0; i < res->num_fields; i++)
+ {
+ if (tupleo[i].value)
+ free(tupleo[i].value);
+ tupleo[i].len = tuplen[i].len;
+ tuplen[i].len = 0;
+ tupleo[i].value = tuplen[i].value;
+ tuplen[i].value = NULL;
+ }
+ ret = SQL_SUCCESS;
+ }
+ else
+ {
+ stmt->errornumber = STMT_ROW_VERSION_CHANGED;
+ stmt->errormsg = "the content was deleted after last fetch";
+ ret = SQL_SUCCESS_WITH_INFO;
+ if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
+ {
+ if (tupleo[res_cols + 1].value)
+ free(tupleo[res_cols + 1].value);
+ tupleo[res_cols + 1].value = NULL;
+ tupleo[res_cols + 1].len = 0;
+ }
+ }
+ QR_Destructor(qres);
+ }
+ else if (stmt->errornumber == 0)
+ stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
+ if (count)
+ *count = rcnt;
+ return ret;
+}
+
+RETCODE SQL_API
+SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval)
+{
+ int i;
+ QResultClass *res,
+ *qres;
+ RETCODE ret = SQL_ERROR;
+
+ mylog("positioned new fi=%x ti=%x\n", stmt->fi, stmt->ti);
+ if (!(res = stmt->result))
+ return SQL_ERROR;
+ if (!stmt->ti)
+ parse_statement(stmt); /* not preferable */
+ if (!stmt->ti || stmt->ntab != 1)
+ {
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ return SQL_ERROR;
+ }
+ if (qres = positioned_load(stmt, TRUE, QR_NumResultCols(res) - 2, oid, tidval), qres)
+ {
+ TupleField *tupleo,
+ *tuplen;
+ int count = QR_get_num_tuples(qres);
+
+ QR_set_position(qres, 0);
+ if (count == 1)
+ {
+ tuplen = qres->tupleField;
+ if (res->fcount >= res->count_allocated)
+ {
+ int tuple_size;
+
+ if (!res->count_allocated)
+ tuple_size = TUPLE_MALLOC_INC;
+ else
+ tuple_size = res->count_allocated * 2;
+ res->backend_tuples = (TupleField *) realloc(
+ res->backend_tuples,
+ res->num_fields * sizeof(TupleField) * tuple_size);
+ if (!res->backend_tuples)
+ {
+ stmt->errornumber = res->status = PGRES_FATAL_ERROR;
+ stmt->errormsg = "Out of memory while reading tuples.";
+ QR_Destructor(qres);
+ return SQL_ERROR;
+ }
+ res->count_allocated = tuple_size;
+ }
+ tupleo = res->backend_tuples + res->num_fields * res->fcount;
+ for (i = 0; i < res->num_fields; i++)
+ {
+ tupleo[i].len = tuplen[i].len;
+ tuplen[i].len = 0;
+ tupleo[i].value = tuplen[i].value;
+ tuplen[i].value = NULL;
+ }
+ res->fcount++;
+ ret = SQL_SUCCESS;
+ }
+ else
+ {
+ stmt->errornumber = STMT_ROW_VERSION_CHANGED;
+ stmt->errormsg = "the content was changed before updation";
+ ret = SQL_SUCCESS_WITH_INFO;
+ }
+ QR_Destructor(qres);
+ /* stmt->currTuple = stmt->rowset_start + irow; */
+ }
+ return ret;
+}
+
+RETCODE SQL_API
+SC_pos_update(StatementClass *stmt,
+ UWORD irow)
+{
+ int i,
+ res_cols,
+ num_cols,
+ upd_cols;
+ UWORD global_ridx;
+ QResultClass *res;
+ BindInfoClass *bindings = stmt->bindings;
+ char updstr[4096];
+ RETCODE ret;
+ char *tidval,
+ *oidval;
+
+ mylog("POS UPDATE %d+%d fi=%x ti=%x\n", irow, stmt->result->base, stmt->fi, stmt->ti);
+ if (!(res = stmt->result))
+ return SQL_ERROR;
+ if (!stmt->ti)
+ parse_statement(stmt); /* not preferable */
+ if (!stmt->ti || stmt->ntab != 1)
+ {
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ return SQL_ERROR;
+ }
+ global_ridx = irow + stmt->rowset_start;
+ res_cols = QR_NumResultCols(res);
+ if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
+ {
+ stmt->errormsg = "The row is already deleted";
+ return SQL_ERROR;
+ }
+ tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2);
+
+ sprintf(updstr, "update \"%s\" set", stmt->ti[0]->name);
+ num_cols = stmt->nfld;
+ for (i = upd_cols = 0; i < num_cols; i++)
+ {
+ if (bindings[i].used)
+ {
+ mylog("%d used=%d\n", i, *bindings[i].used);
+ if (*bindings[i].used != SQL_IGNORE)
+ {
+ if (upd_cols)
+ sprintf(updstr, "%s, \"%s\" = ?", updstr, stmt->fi[i]->name);
+ else
+ sprintf(updstr, "%s \"%s\" = ?", updstr, stmt->fi[i]->name);
+ upd_cols++;
+ }
+ }
+ else
+ mylog("%d null bind\n", i);
+ }
+ if (upd_cols > 0)
+ {
+ HSTMT hstmt;
+ int j;
+ int res_cols = QR_NumResultCols(res);
+ StatementClass *qstmt;
+
+ sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr,
+ tidval, oidval);
+ mylog("updstr=%s\n", updstr);
+ if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS)
+ return SQL_ERROR;
+ qstmt = (StatementClass *) hstmt;
+ for (i = j = 0; i < num_cols; i++)
+ {
+ if (bindings[i].used)
+ {
+ mylog("%d used=%d\n", i, *bindings[i].used);
+ if (*bindings[i].used != SQL_IGNORE)
+ {
+ PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++j,
+ SQL_PARAM_INPUT, bindings[i].returntype,
+ pgtype_to_sqltype(stmt, QR_get_field_type(res, i)),
+ QR_get_fieldsize(res, i),
+ (SQLSMALLINT) stmt->fi[i]->precision,
+ bindings[i].buffer,
+ bindings[i].buflen,
+ bindings[i].used);
+ }
+ }
+ }
+ ret = PGAPI_ExecDirect(hstmt, updstr, strlen(updstr));
+ if (ret == SQL_ERROR)
+ {
+ stmt->errornumber = qstmt->errornumber;
+ stmt->errormsg = qstmt->errormsg;
+ }
+ else if (ret == SQL_NEED_DATA) /* must be fixed */
+ {
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
+ stmt->errormsg = "SetPos with data_at_exec not yet supported";
+ ret = SQL_ERROR;
+ }
+ if (ret != SQL_ERROR)
+ {
+ int updcnt;
+ const char *cmdstr = QR_get_command(qstmt->result);
+
+ if (cmdstr &&
+ sscanf(cmdstr, "UPDATE %d", &updcnt) == 1)
+ {
+ if (updcnt == 1)
+ SC_pos_reload(stmt, irow, (UWORD *) 0);
+ else if (updcnt == 0)
+ {
+ stmt->errornumber = STMT_ROW_VERSION_CHANGED;
+ stmt->errormsg = "the content was changed before updation";
+ ret = SQL_ERROR;
+ if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
+ SC_pos_reload(stmt, irow, (UWORD *) 0);
+ }
+ else
+ ret = SQL_ERROR;
+ stmt->currTuple = stmt->rowset_start + irow;
+ }
+ else
+ ret = SQL_ERROR;
+ if (ret == SQL_ERROR && stmt->errornumber == 0)
+ {
+ stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
+ stmt->errormsg = "SetPos update return error";
+ }
+ }
+ PGAPI_FreeStmt(hstmt, SQL_DROP);
+ }
+ else
+ ret = SQL_SUCCESS_WITH_INFO;
+ return ret;
+}
+RETCODE SQL_API
+SC_pos_delete(StatementClass *stmt,
+ UWORD irow)
+{
+ int res_cols;
+ UWORD global_ridx;
+ QResultClass *res,
+ *qres;
+ BindInfoClass *bindings = stmt->bindings;
+ char dltstr[4096];
+ RETCODE ret;
+ char *oidval;
+
+ mylog("POS DELETE fi=%x ti=%x\n", stmt->fi, stmt->ti);
+ if (!(res = stmt->result))
+ return SQL_ERROR;
+ if (!stmt->ti)
+ parse_statement(stmt); /* not preferable */
+ if (!stmt->ti || stmt->ntab != 1)
+ {
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ return SQL_ERROR;
+ }
+ res_cols = QR_NumResultCols(res);
+ global_ridx = irow + stmt->rowset_start;
+ if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
+ {
+ stmt->errormsg = "The row is already deleted";
+ return SQL_ERROR;
+ }
+ sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s",
+ stmt->ti[0]->name,
+ QR_get_value_backend_row(stmt->result, global_ridx, res_cols - 2),
+ oidval);
+
+ mylog("dltstr=%s\n", dltstr);
+ qres = CC_send_query(SC_get_conn(stmt), dltstr, NULL);
+ if (qres && QR_command_successful(qres))
+ {
+ int dltcnt;
+ const char *cmdstr = QR_get_command(qres);
+
+ if (cmdstr &&
+ sscanf(cmdstr, "DELETE %d", &dltcnt) == 1)
+ {
+ if (dltcnt == 1)
+ SC_pos_reload(stmt, irow, (UWORD *) 0);
+ else if (dltcnt == 0)
+ {
+ stmt->errornumber = STMT_ROW_VERSION_CHANGED;
+ stmt->errormsg = "the content was changed before deletion";
+ ret = SQL_ERROR;
+ if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
+ SC_pos_reload(stmt, irow, (UWORD *) 0);
+ }
+ else
+ ret = SQL_ERROR;
+ stmt->currTuple = stmt->rowset_start + irow;
+ }
+ else
+ ret = SQL_ERROR;
+ }
+ else
+ ret = SQL_ERROR;
+ if (ret == SQL_ERROR && stmt->errornumber == 0)
+ {
+ stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
+ stmt->errormsg = "SetPos delete return error";
+ }
+ if (qres)
+ QR_Destructor(qres);
+ return ret;
+}
+RETCODE SQL_API
+SC_pos_add(StatementClass *stmt,
+ UWORD irow)
+{
+ int num_cols,
+ add_cols,
+ i;
+ HSTMT hstmt;
+ QResultClass *res;
+ BindInfoClass *bindings = stmt->bindings;
+ char addstr[4096];
+ RETCODE ret;
+
+ mylog("POS ADD fi=%x ti=%x\n", stmt->fi, stmt->ti);
+ if (!(res = stmt->result))
+ return SQL_ERROR;
+ if (!stmt->ti)
+ parse_statement(stmt); /* not preferable */
+ if (!stmt->ti || stmt->ntab != 1)
+ {
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ return SQL_ERROR;
+ }
+ num_cols = stmt->nfld;
+ sprintf(addstr, "insert into \"%s\" (", stmt->ti[0]->name);
+ if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS)
+ return SQL_ERROR;
+ for (i = add_cols = 0; i < num_cols; i++)
+ {
+ if (bindings[i].used)
+ {
+ mylog("%d used=%d\n", i, *bindings[i].used);
+ if (*bindings[i].used != SQL_IGNORE)
+ {
+ if (add_cols)
+ sprintf(addstr, "%s, \"%s\"", addstr, stmt->fi[i]->name);
+ else
+ sprintf(addstr, "%s\"%s\"", addstr, stmt->fi[i]->name);
+ PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++add_cols,
+ SQL_PARAM_INPUT, bindings[i].returntype,
+ pgtype_to_sqltype(stmt, QR_get_field_type(res, i)),
+ QR_get_fieldsize(res, i),
+ (SQLSMALLINT) stmt->fi[i]->precision,
+ bindings[i].buffer,
+ bindings[i].buflen,
+ bindings[i].used);
+ }
+ }
+ else
+ mylog("%d null bind\n", i);
+ }
+ if (add_cols > 0)
+ {
+ StatementClass *qstmt = (StatementClass *) hstmt;
+
+ sprintf(addstr, "%s) values (", addstr);
+ for (i = 0; i < add_cols; i++)
+ {
+ if (i)
+ strcat(addstr, ", ?");
+ else
+ strcat(addstr, "?");
+ }
+ strcat(addstr, ")");
+ mylog("addstr=%s\n", addstr);
+ ret = PGAPI_ExecDirect(hstmt, addstr, strlen(addstr));
+ if (ret == SQL_NEED_DATA) /* must be fixed */
+ {
+ stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
+ stmt->errormsg = "SetPos with data_at_exec not yet supported";
+ ret = SQL_ERROR;
+ }
+ if (ret == SQL_ERROR)
+ {
+ stmt->errornumber = qstmt->errornumber;
+ stmt->errormsg = qstmt->errormsg;
+ }
+ else
+ {
+ int addcnt;
+ UInt4 oid;
+ const char *cmdstr = QR_get_command(qstmt->result);
+
+ if (cmdstr &&
+ sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 &&
+ addcnt == 1)
+ {
+ SC_pos_newload(stmt, oid, NULL);
+ if (stmt->bookmark.buffer)
+ {
+ char buf[32];
+
+ sprintf(buf, "%ld", res->fcount);
+ copy_and_convert_field(stmt, 0, buf,
+ SQL_C_ULONG, stmt->bookmark.buffer,
+ 0, stmt->bookmark.used);
+ }
+ }
+ else
+ {
+ stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
+ stmt->errormsg = "SetPos insert return error";
+ ret = SQL_ERROR;
+ }
+ }
+ }
+ else
+ ret = SQL_SUCCESS_WITH_INFO;
+ PGAPI_FreeStmt(hstmt, SQL_DROP);
+ return ret;
+}
+
+/*
+ * Stuff for updatable cursors end.
+ */
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+
+/*
+ * This positions the cursor within a rowset, that was positioned using SQLExtendedFetch.
+ * This will be useful (so far) only when using SQLGetData after SQLExtendedFetch.
+ */
+RETCODE SQL_API
+PGAPI_SetPos(
+ HSTMT hstmt,
+ UWORD irow,
+ UWORD fOption,
+ UWORD fLock)
+{
+ static char *func = "PGAPI_SetPos";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ QResultClass *res;
+ int num_cols,
+ i;
+ BindInfoClass *bindings = stmt->bindings;
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ mylog("SetPos fOption=%d irow=%d lock=%d currt=%d\n", fOption, irow, fLock, stmt->currTuple);
+ if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
+ ;
+ else
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ if (fOption != SQL_POSITION && fOption != SQL_REFRESH)
+ {
+ stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+ stmt->errormsg = "Only SQL_POSITION/REFRESH is supported for PGAPI_SetPos";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (!(res = stmt->result))
+ {
+ stmt->errormsg = "Null statement result in PGAPI_SetPos.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ num_cols = QR_NumResultCols(res);
+
+ if (irow == 0)
+ {
+ stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
+ stmt->errormsg = "Driver does not support Bulk operations.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (irow > stmt->last_fetch_count)
+ {
+ stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
+ stmt->errormsg = "Row value out of range";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ irow--;
+
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ switch (fOption)
+ {
+ case SQL_UPDATE:
+ return SC_pos_update(stmt, irow);
+ case SQL_DELETE:
+ return SC_pos_delete(stmt, irow);
+ case SQL_ADD:
+ return SC_pos_add(stmt, irow);
+ }
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ /* Reset for SQLGetData */
+ for (i = 0; i < num_cols; i++)
+ bindings[i].data_left = -1;
+
+ if (fOption == SQL_REFRESH)
+ {
+ /* save the last_fetch_count */
+ int last_fetch = stmt->last_fetch_count;
+ int bind_save = stmt->bind_row;
+
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
+ SC_pos_reload(stmt, irow, (UWORD *) 0);
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ stmt->currTuple = stmt->rowset_start + irow - 1;
+ stmt->bind_row = irow;
+ SC_fetch(stmt);
+ /* restore the last_fetch_count */
+ stmt->last_fetch_count = last_fetch;
+ stmt->bind_row = bind_save;
+ }
+ else
+ stmt->currTuple = stmt->rowset_start + irow;
+ QR_set_position(res, irow);
+
+ return SQL_SUCCESS;
+}
+
+
+/* Sets options that control the behavior of cursors. */
+RETCODE SQL_API
+PGAPI_SetScrollOptions(
+ HSTMT hstmt,
+ UWORD fConcurrency,
+ SDWORD crowKeyset,
+ UWORD crowRowset)
+{
+ static char *func = "PGAPI_SetScrollOptions";
+ StatementClass *stmt = (StatementClass *) hstmt;
+
+ mylog("PGAPI_SetScrollOptions fConcurrency=%d crowKeyset=%d crowRowset=%d\n",
+ fConcurrency, crowKeyset, crowRowset);
+ stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+ stmt->errormsg = "SetScroll option not implemeted";
+
+ SC_log_error(func, "Function not implemented", hstmt);
+ return SQL_ERROR;
+}
+
+
+/* Set the cursor name on a statement handle */
+RETCODE SQL_API
+PGAPI_SetCursorName(
+ HSTMT hstmt,
+ UCHAR FAR * szCursor,
+ SWORD cbCursor)
+{
+ static char *func = "PGAPI_SetCursorName";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ int len;
+
+ mylog("PGAPI_SetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d\n", hstmt, szCursor, cbCursor);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ len = (cbCursor == SQL_NTS) ? strlen(szCursor) : cbCursor;
+
+ if (len <= 0 || len > sizeof(stmt->cursor_name) - 1)
+ {
+ stmt->errornumber = STMT_INVALID_CURSOR_NAME;
+ stmt->errormsg = "Invalid Cursor Name";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ strncpy_null(stmt->cursor_name, szCursor, len + 1);
+ return SQL_SUCCESS;
+}
+
+
+/* Return the cursor name for a statement handle */
+RETCODE SQL_API
+PGAPI_GetCursorName(
+ HSTMT hstmt,
+ UCHAR FAR * szCursor,
+ SWORD cbCursorMax,
+ SWORD FAR * pcbCursor)
+{
+ static char *func = "PGAPI_GetCursorName";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ int len = 0;
+ RETCODE result;
+
+ mylog("PGAPI_GetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d, pcbCursor=%u\n", hstmt, szCursor, cbCursorMax, pcbCursor);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ if (stmt->cursor_name[0] == '\0')
+ {
+ stmt->errornumber = STMT_NO_CURSOR_NAME;
+ stmt->errormsg = "No Cursor name available";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ result = SQL_SUCCESS;
+ len = strlen(stmt->cursor_name);
+
+ if (szCursor)
+ {
+ strncpy_null(szCursor, stmt->cursor_name, cbCursorMax);
+
+ if (len >= cbCursorMax)
+ {
+ result = SQL_SUCCESS_WITH_INFO;
+ stmt->errornumber = STMT_TRUNCATED;
+ stmt->errormsg = "The buffer was too small for the GetCursorName.";
+ }
+ }
+
+ if (pcbCursor)
+ *pcbCursor = len;
+
+ return result;
+}
--- /dev/null
+/*-------
+ * Module: setup.c
+ *
+ * Description: This module contains the setup functions for
+ * adding/modifying a Data Source in the ODBC.INI portion
+ * of the registry.
+ *
+ * Classes: n/a
+ *
+ * API functions: ConfigDSN
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "psqlodbc.h"
+
+#include "connection.h"
+#include <windowsx.h>
+#include <string.h>
+#include <stdlib.h>
+#include "resource.h"
+#include "dlg_specific.h"
+
+
+#define INTFUNC __stdcall
+
+extern HINSTANCE NEAR s_hModule; /* Saved module handle. */
+
+/* Constants */
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
+#ifdef WIN32
+#define MAXPGPATH (255+1)
+#endif
+
+#define MAXKEYLEN (15+1) /* Max keyword length */
+#define MAXDESC (255+1) /* Max description length */
+#define MAXDSNAME (32+1) /* Max data source name length */
+
+
+/* Globals */
+/* NOTE: All these are used by the dialog procedures */
+typedef struct tagSETUPDLG
+{
+ HWND hwndParent; /* Parent window handle */
+ LPCSTR lpszDrvr; /* Driver description */
+ ConnInfo ci;
+ char szDSN[MAXDSNAME]; /* Original data source name */
+ BOOL fNewDSN; /* New data source flag */
+ BOOL fDefault; /* Default data source flag */
+
+} SETUPDLG, FAR * LPSETUPDLG;
+
+
+
+/* Prototypes */
+void INTFUNC CenterDialog(HWND hdlg);
+int CALLBACK ConfigDlgProc(HWND hdlg, WORD wMsg, WPARAM wParam, LPARAM lParam);
+void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg);
+BOOL INTFUNC SetDSNAttributes(HWND hwnd, LPSETUPDLG lpsetupdlg);
+
+
+/*--------
+ * ConfigDSN
+ *
+ * Description: ODBC Setup entry point
+ * This entry point is called by the ODBC Installer
+ * (see file header for more details)
+ * Input : hwnd ----------- Parent window handle
+ * fRequest ------- Request type (i.e., add, config, or remove)
+ * lpszDriver ----- Driver name
+ * lpszAttributes - data source attribute string
+ * Output : TRUE success, FALSE otherwise
+ *--------
+ */
+BOOL CALLBACK
+ConfigDSN(HWND hwnd,
+ WORD fRequest,
+ LPCSTR lpszDriver,
+ LPCSTR lpszAttributes)
+{
+ BOOL fSuccess; /* Success/fail flag */
+ GLOBALHANDLE hglbAttr;
+ LPSETUPDLG lpsetupdlg;
+
+
+ /* Allocate attribute array */
+ hglbAttr = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(SETUPDLG));
+ if (!hglbAttr)
+ return FALSE;
+ lpsetupdlg = (LPSETUPDLG) GlobalLock(hglbAttr);
+
+ /* Parse attribute string */
+ if (lpszAttributes)
+ ParseAttributes(lpszAttributes, lpsetupdlg);
+
+ /* Save original data source name */
+ if (lpsetupdlg->ci.dsn[0])
+ lstrcpy(lpsetupdlg->szDSN, lpsetupdlg->ci.dsn);
+ else
+ lpsetupdlg->szDSN[0] = '\0';
+
+ /* Remove data source */
+ if (ODBC_REMOVE_DSN == fRequest)
+ {
+ /* Fail if no data source name was supplied */
+ if (!lpsetupdlg->ci.dsn[0])
+ fSuccess = FALSE;
+
+ /* Otherwise remove data source from ODBC.INI */
+ else
+ fSuccess = SQLRemoveDSNFromIni(lpsetupdlg->ci.dsn);
+ }
+ /* Add or Configure data source */
+ else
+ {
+ /* Save passed variables for global access (e.g., dialog access) */
+ lpsetupdlg->hwndParent = hwnd;
+ lpsetupdlg->lpszDrvr = lpszDriver;
+ lpsetupdlg->fNewDSN = (ODBC_ADD_DSN == fRequest);
+ lpsetupdlg->fDefault = !lstrcmpi(lpsetupdlg->ci.dsn, INI_DSN);
+
+ /*
+ * Display the appropriate dialog (if parent window handle
+ * supplied)
+ */
+ if (hwnd)
+ {
+ /* Display dialog(s) */
+ fSuccess = (IDOK == DialogBoxParam(s_hModule,
+ MAKEINTRESOURCE(DLG_CONFIG),
+ hwnd,
+ ConfigDlgProc,
+ (LONG) (LPSTR) lpsetupdlg));
+ }
+ else if (lpsetupdlg->ci.dsn[0])
+ fSuccess = SetDSNAttributes(hwnd, lpsetupdlg);
+ else
+ fSuccess = FALSE;
+ }
+
+ GlobalUnlock(hglbAttr);
+ GlobalFree(hglbAttr);
+
+ return fSuccess;
+}
+
+
+/*-------
+ * CenterDialog
+ *
+ * Description: Center the dialog over the frame window
+ * Input : hdlg -- Dialog window handle
+ * Output : None
+ *-------
+ */
+void INTFUNC
+CenterDialog(HWND hdlg)
+{
+ HWND hwndFrame;
+ RECT rcDlg,
+ rcScr,
+ rcFrame;
+ int cx,
+ cy;
+
+ hwndFrame = GetParent(hdlg);
+
+ GetWindowRect(hdlg, &rcDlg);
+ cx = rcDlg.right - rcDlg.left;
+ cy = rcDlg.bottom - rcDlg.top;
+
+ GetClientRect(hwndFrame, &rcFrame);
+ ClientToScreen(hwndFrame, (LPPOINT) (&rcFrame.left));
+ ClientToScreen(hwndFrame, (LPPOINT) (&rcFrame.right));
+ rcDlg.top = rcFrame.top + (((rcFrame.bottom - rcFrame.top) - cy) >> 1);
+ rcDlg.left = rcFrame.left + (((rcFrame.right - rcFrame.left) - cx) >> 1);
+ rcDlg.bottom = rcDlg.top + cy;
+ rcDlg.right = rcDlg.left + cx;
+
+ GetWindowRect(GetDesktopWindow(), &rcScr);
+ if (rcDlg.bottom > rcScr.bottom)
+ {
+ rcDlg.bottom = rcScr.bottom;
+ rcDlg.top = rcDlg.bottom - cy;
+ }
+ if (rcDlg.right > rcScr.right)
+ {
+ rcDlg.right = rcScr.right;
+ rcDlg.left = rcDlg.right - cx;
+ }
+
+ if (rcDlg.left < 0)
+ rcDlg.left = 0;
+ if (rcDlg.top < 0)
+ rcDlg.top = 0;
+
+ MoveWindow(hdlg, rcDlg.left, rcDlg.top, cx, cy, TRUE);
+ return;
+}
+
+/*-------
+ * ConfigDlgProc
+ * Description: Manage add data source name dialog
+ * Input : hdlg --- Dialog window handle
+ * wMsg --- Message
+ * wParam - Message parameter
+ * lParam - Message parameter
+ * Output : TRUE if message processed, FALSE otherwise
+ *-------
+ */
+int CALLBACK
+ConfigDlgProc(HWND hdlg,
+ WORD wMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ LPSETUPDLG lpsetupdlg;
+ ConnInfo *ci;
+
+ switch (wMsg)
+ {
+ /* Initialize the dialog */
+ case WM_INITDIALOG:
+ lpsetupdlg = (LPSETUPDLG) lParam;
+ ci = &lpsetupdlg->ci;
+
+ /* Hide the driver connect message */
+ ShowWindow(GetDlgItem(hdlg, DRV_MSG_LABEL), SW_HIDE);
+
+ SetWindowLong(hdlg, DWL_USER, lParam);
+ CenterDialog(hdlg); /* Center dialog */
+
+ /*
+ * NOTE: Values supplied in the attribute string will always
+ */
+ /* override settings in ODBC.INI */
+
+ /* Get the rest of the common attributes */
+ getDSNinfo(ci, CONN_DONT_OVERWRITE);
+
+ /* Fill in any defaults */
+ getDSNdefaults(ci);
+
+ /* Initialize dialog fields */
+ SetDlgStuff(hdlg, ci);
+
+ if (lpsetupdlg->fDefault)
+ {
+ EnableWindow(GetDlgItem(hdlg, IDC_DSNAME), FALSE);
+ EnableWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), FALSE);
+ }
+ else
+ SendDlgItemMessage(hdlg, IDC_DSNAME,
+ EM_LIMITTEXT, (WPARAM) (MAXDSNAME - 1), 0L);
+
+ SendDlgItemMessage(hdlg, IDC_DESC,
+ EM_LIMITTEXT, (WPARAM) (MAXDESC - 1), 0L);
+ return TRUE; /* Focus was not set */
+
+ /* Process buttons */
+ case WM_COMMAND:
+ switch (GET_WM_COMMAND_ID(wParam, lParam))
+ {
+ /*
+ * Ensure the OK button is enabled only when a data
+ * source name
+ */
+ /* is entered */
+ case IDC_DSNAME:
+ if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
+ {
+ char szItem[MAXDSNAME]; /* Edit control text */
+
+ /* Enable/disable the OK button */
+ EnableWindow(GetDlgItem(hdlg, IDOK),
+ GetDlgItemText(hdlg, IDC_DSNAME,
+ szItem, sizeof(szItem)));
+ return TRUE;
+ }
+ break;
+
+ /* Accept results */
+ case IDOK:
+ lpsetupdlg = (LPSETUPDLG) GetWindowLong(hdlg, DWL_USER);
+ /* Retrieve dialog values */
+ if (!lpsetupdlg->fDefault)
+ GetDlgItemText(hdlg, IDC_DSNAME,
+ lpsetupdlg->ci.dsn,
+ sizeof(lpsetupdlg->ci.dsn));
+ /* Get Dialog Values */
+ GetDlgStuff(hdlg, &lpsetupdlg->ci);
+
+ /* Update ODBC.INI */
+ SetDSNAttributes(hdlg, lpsetupdlg);
+
+ /* Return to caller */
+ case IDCANCEL:
+ EndDialog(hdlg, wParam);
+ return TRUE;
+
+ case IDC_DRIVER:
+ lpsetupdlg = (LPSETUPDLG) GetWindowLong(hdlg, DWL_USER);
+ DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DRV),
+ hdlg, driver_optionsProc, (LPARAM) &lpsetupdlg->ci);
+ return TRUE;
+
+ case IDC_DATASOURCE:
+ lpsetupdlg = (LPSETUPDLG) GetWindowLong(hdlg, DWL_USER);
+
+ DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DS),
+ hdlg, ds_optionsProc, (LPARAM) &lpsetupdlg->ci);
+
+ return TRUE;
+ }
+ break;
+ }
+
+ /* Message not processed */
+ return FALSE;
+}
+
+
+/*-------
+ * ParseAttributes
+ *
+ * Description: Parse attribute string moving values into the aAttr array
+ * Input : lpszAttributes - Pointer to attribute string
+ * Output : None (global aAttr normally updated)
+ *-------
+ */
+void INTFUNC
+ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
+{
+ LPCSTR lpsz;
+ LPCSTR lpszStart;
+ char aszKey[MAXKEYLEN];
+ int cbKey;
+ char value[MAXPGPATH];
+
+ memset(&lpsetupdlg->ci, 0, sizeof(ConnInfo));
+
+ for (lpsz = lpszAttributes; *lpsz; lpsz++)
+ {
+ /*
+ * Extract key name (e.g., DSN), it must be terminated by an
+ * equals
+ */
+ lpszStart = lpsz;
+ for (;; lpsz++)
+ {
+ if (!*lpsz)
+ return; /* No key was found */
+ else if (*lpsz == '=')
+ break; /* Valid key found */
+ }
+ /* Determine the key's index in the key table (-1 if not found) */
+ cbKey = lpsz - lpszStart;
+ if (cbKey < sizeof(aszKey))
+ {
+ _fmemcpy(aszKey, lpszStart, cbKey);
+ aszKey[cbKey] = '\0';
+ }
+
+ /* Locate end of key value */
+ lpszStart = ++lpsz;
+ for (; *lpsz; lpsz++)
+ ;
+
+ /* lpsetupdlg->aAttr[iElement].fSupplied = TRUE; */
+ _fmemcpy(value, lpszStart, MIN(lpsz - lpszStart + 1, MAXPGPATH));
+
+ mylog("aszKey='%s', value='%s'\n", aszKey, value);
+
+ /* Copy the appropriate value to the conninfo */
+ copyAttributes(&lpsetupdlg->ci, aszKey, value);
+ }
+ return;
+}
+
+
+/*--------
+ * SetDSNAttributes
+ *
+ * Description: Write data source attributes to ODBC.INI
+ * Input : hwnd - Parent window handle (plus globals)
+ * Output : TRUE if successful, FALSE otherwise
+ *--------
+ */
+BOOL INTFUNC
+SetDSNAttributes(HWND hwndParent, LPSETUPDLG lpsetupdlg)
+{
+ LPCSTR lpszDSN; /* Pointer to data source name */
+
+ lpszDSN = lpsetupdlg->ci.dsn;
+
+ /* Validate arguments */
+ if (lpsetupdlg->fNewDSN && !*lpsetupdlg->ci.dsn)
+ return FALSE;
+
+ /* Write the data source name */
+ if (!SQLWriteDSNToIni(lpszDSN, lpsetupdlg->lpszDrvr))
+ {
+ if (hwndParent)
+ {
+ char szBuf[MAXPGPATH];
+ char szMsg[MAXPGPATH];
+
+ LoadString(s_hModule, IDS_BADDSN, szBuf, sizeof(szBuf));
+ wsprintf(szMsg, szBuf, lpszDSN);
+ LoadString(s_hModule, IDS_MSGTITLE, szBuf, sizeof(szBuf));
+ MessageBox(hwndParent, szMsg, szBuf, MB_ICONEXCLAMATION | MB_OK);
+ }
+ return FALSE;
+ }
+
+ /* Update ODBC.INI */
+ writeDSNinfo(&lpsetupdlg->ci);
+
+ /* If the data source name has changed, remove the old name */
+ if (lstrcmpi(lpsetupdlg->szDSN, lpsetupdlg->ci.dsn))
+ SQLRemoveDSNFromIni(lpsetupdlg->szDSN);
+ return TRUE;
+}
--- /dev/null
+/*
+# Insight Distribution Systems - System V - Apr 1998
+#ident "@(#)setup.rul 1.13 :/sccs/sql/odbc/s.setup.rul 1/6/99 14:47:48"
+*/
+
+/*----------------------------------------------------------------------------*\
+ *
+ * PostgreSQL ODBC Driver Installation Script for InstallShield
+ *
+\*----------------------------------------------------------------------------*/
+
+
+#define APP_NAME "PostgreSQL ODBC Driver"
+#define DRIVER_NAME "PostgreSQL"
+#define DRIVER_FILE "PSQLODBC.DLL"
+#define OLD_DRIVER_FILE "PODBC32.DLL"
+#define OLD_DRIVER_FILE_RENAMED "podbc32_sav.dll"
+
+#define COMPANY_NAME "Insight"
+#define PRODUCT_NAME "PostgreSQL ODBC Driver"
+#define PRODUCT_VERSION "6.3"
+#define PRODUCT_KEY "PSQLODBC.DLL"
+#define UNINSTALL_KEY "PSQLODBCv6.3"
+
+#define ODBC_DM_KEY "\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs"
+#define ODBC_COMP_KEY "\\SOFTWARE\\ODBC\\ODBCINST.INI"
+#define ODBC_CORE_KEY "\\SOFTWARE\\ODBC\\ODBCINST.INI\\ODBC Core"
+#define ODBC_DRIVERS_KEY "\\SOFTWARE\\ODBC\\ODBCINST.INI\\ODBC Drivers"
+
+
+declare
+ // functions
+ prototype SetupScreen();
+ prototype FileCompare(STRING, STRING, STRING, STRING);
+
+ // variables
+ STRING svMainDirectory [_MAX_STRING], svGrp, svUninstLogFile, svPath;
+ STRING svValue, szName, szKey, szMessage;
+ STRING szMsg, szTmp, szTmp2, szFileSet, szProgram;
+ NUMBER nResult, pos, nvType, nvSize, nStartup, ComponentUsageCount;
+
+ NUMBER nvDoNot, nvVersion, nvInstall, nCore, nDM;
+ STRING dm, core, szFileName, svFileName;
+ NUMBER options, nvInfo, nvResult;
+ LONG lResult;
+ STRING svCompVersion, svFileVersion, svCompDate, svCompTime, svFileDate, svFileTime;
+
+program
+
+StartHere:
+ Disable( BACKGROUND );
+
+ // Set up the installation screen.
+ SetupScreen();
+ InstallationInfo(COMPANY_NAME, PRODUCT_NAME, PRODUCT_VERSION, PRODUCT_KEY);
+ RegDBSetAppInfo("Location", REGDB_STRING, WINSYSDIR ^ DRIVER_FILE, -1);
+
+// Create a Welcome dialog.
+WelcomeDlg:
+ Disable( BACKBUTTON );
+ Welcome( "Welcome to the PostgreSQL Odbc Driver Installation", 0 );
+ Enable( BACKBUTTON );
+ Enable( NEXTBUTTON );
+
+GetTargetDirectory:
+ svMainDirectory = WINSYSDIR;
+
+OptionsDlg:
+ RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
+ szKey = ODBC_DM_KEY;
+ nCore = RegDBKeyExist(szKey);
+
+ szName = WINSYSDIR ^ "ODBC32.DLL";
+ nDM = RegDBGetKeyValueEx(szKey, szName, nvType, svValue, nvSize);
+
+ szMessage = "Select options for installing the ODBC Driver Manager.\n" +
+ "Analysis of your system suggests that the ODBC Driver Manager\n";
+
+ nvDoNot = FALSE;
+ nvInstall = FALSE;
+ nvVersion = FALSE;
+ if (nCore >= 0 && nDM >= 0) then
+ nvDoNot = TRUE;
+ szMessage = szMessage + "is already installed. Therefore, you may choose not to install it.";
+ else
+ nvInstall = TRUE;
+ szMessage = szMessage + "is not installed. Therefore, you should install it now.";
+ endif;
+
+ Enable(FINISHBUTTON);
+ nResult = AskOptions(EXCLUSIVE, szMessage,
+ "Do not install Driver Manager", nvDoNot,
+ "Install Driver Manager ", nvInstall,
+ "Install Driver Manager (with version checking)", nvVersion);
+
+ if (nResult = BACK) then
+ Disable(FINISHBUTTON);
+ goto WelcomeDlg;
+ endif;
+
+Version:
+ CompressInfo("driver.z", DRIVER_FILE, COMP_INFO_VERSIONMS|COMP_INFO_VERSIONLS, nvInfo, svCompVersion);
+
+ szFileName = WINSYSDIR ^ DRIVER_FILE;
+ nResult = VerGetFileVersion(szFileName, svFileVersion);
+
+ // MessageBox("System file PSQLODBC.dll version is " + svFileVersion, INFORMATION);
+
+ lResult = VerCompare(svCompVersion, svFileVersion, VERSION);
+
+ if (lResult = EQUALS) then
+ //date
+ CompressInfo("driver.z", DRIVER_FILE, COMP_INFO_DATE, nvInfo, svCompDate);
+ GetFileInfo(szFileName, FILE_DATE, nvResult, svFileDate);
+
+ //time
+ CompressInfo("driver.z", DRIVER_FILE, COMP_INFO_TIME, nvInfo, svCompTime);
+ GetFileInfo(szFileName, FILE_TIME, nvResult, svFileTime);
+
+ // If compressed file date/time is earlier than system file date/time
+ // then
+ nResult = FileCompare(svCompDate, svCompTime, svFileDate, svFileTime);
+ if (nResult < 0) then
+ lResult = LESS_THAN;
+ endif;
+
+ NumToStr(szTmp, nResult);
+ // MessageBox("File Compare = " + szTmp, INFORMATION);
+ endif;
+
+ if (lResult = LESS_THAN) then
+ MessageBeep(0);
+ nResult = AskYesNo("The " + PRODUCT_NAME + " is already installed on your system \nand is a newer version than the one that is about to be installed.\n\n" +
+ "Would you like to continue the installation anyway (not recommended)?", NO);
+ if (nResult = NO) then
+ MessageBeep(0);
+ MessageBox("Installation has been aborted.\nNo changes have been made to your system.", WARNING);
+ exit;
+ endif;
+ else
+ /*
+ nResult = AskYesNo("Ready to install " + PRODUCT_NAME + ".\n\nPress Yes to proceed with the installation.\nPress No to abort the installation.", YES);
+ if (nResult = NO) then
+ MessageBeep(0);
+ MessageBox("Installation has been aborted.\nNo changes have been made to your system.", WARNING);
+ exit;
+ endif;
+ */
+ endif;
+
+CheckRegistry:
+ Enable(STATUSDLG);
+
+ SetStatusWindow(5, "Checking registry entries...");
+ Delay(1);
+
+ RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
+ szKey = ODBC_DM_KEY;
+ nResult = RegDBKeyExist(szKey);
+ if (nResult < 0 && nvDoNot = TRUE) then
+ MessageBeep(0);
+ MessageBox("ODBC Core Components are not installed!", SEVERE);
+ Disable(STATUSDLG);
+ MessageBeep(0);
+ MessageBox("Please install the ODBC Core Components\nand rerun this setup program.", INFORMATION);
+ exit;
+ endif;
+
+ szName = WINSYSDIR ^ "ODBC32.DLL";
+ nResult = RegDBGetKeyValueEx(szKey, szName, nvType, svValue, nvSize);
+ if (nResult < 0 && nvDoNot = TRUE) then
+ MessageBeep(0);
+ MessageBox("ODBC Driver Manager (ODBC32.DLL) is not installed!", SEVERE);
+ Disable(STATUSDLG);
+ MessageBeep(0);
+ MessageBox("Please install the ODBC Driver Manager\nand rerun this setup program.", INFORMATION);
+ exit;
+ endif;
+
+
+FileSetup:
+
+ SetStatusWindow( 10, "Copying program files...");
+ StatusUpdate(ON, 90);
+
+ DeinstallStart(svMainDirectory, svUninstLogFile, UNINSTALL_KEY, 0);
+
+ // Show the uninstall under Add/Remove Programs in Control Panel
+ RegDBSetItem(REGDB_UNINSTALL_NAME, PRODUCT_NAME);
+
+ szFileSet = "psqlodbc";
+
+ TARGETDIR = svMainDirectory; // winsys
+
+ FileSetBeginDefine(szFileSet);
+
+
+ nResult = CompressGet("driver.z", "*.*", COMP_NORMAL);
+ if (nResult < 0) then
+ NumToStr(szTmp, nResult);
+ MessageBox("Compress Get Error on driver.z files.\n\nError # " + szTmp, SEVERE);
+ exit;
+ endif;
+
+ TARGETDIR = svMainDirectory; // winsys
+
+ // Driver Manager stuff
+ if (! nvDoNot) then
+ if (nvVersion) then
+ options = COMP_UPDATE_VERSION;
+ else
+ options = COMP_NORMAL;
+ endif;
+
+ // The File usage count increments are handled by CompressGet
+ // with the SHAREDFILE option.
+
+ nResult = CompressGet("redist.z", "*.*", options|SHAREDFILE);
+ if (nResult < 0) then
+ NumToStr(szTmp, nResult);
+ MessageBox("Compress Get Error on redist.z files.\n\nError # " + szTmp, SEVERE);
+ exit;
+ endif;
+ endif;
+
+
+ FileSetEndDefine(szFileSet);
+
+FileTransfer:
+ nResult = FileSetPerformEz(szFileSet, 0);
+
+ switch(nResult)
+ case FS_DONE:
+ case FS_CREATEDIR:
+ MessageBeep(0);
+ MessageBox("Unable to create a required subdirectory under " + TARGETDIR + "."
+ + "\nPlease check write access to this directory.", SEVERE);
+
+ abort;
+ default:
+ NumToStr(szTmp, nResult);
+ MessageBeep(0);
+ MessageBox("Error copying files to " + TARGETDIR + "."
+ + "\nPlease check this location and try again."
+ + "\n\nError Number:"+szTmp, SEVERE);
+
+ abort;
+
+ endswitch;
+
+
+UpdateRegistry:
+ SetStatusWindow(95, "Creating registry entries...");
+ Delay(2);
+
+ RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
+
+ Disable(LOGGING);
+
+ // Create ODBC Core Subkey (if it doesn't exist)
+ // (But don't create uninstall information for it)
+ szKey = ODBC_CORE_KEY;
+ nResult = RegDBCreateKeyEx(szKey, "");
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create ODBC Core subkey.", SEVERE);
+ exit;
+ endif;
+
+ // Create Installed Driver Key (if it doesn't exist)
+ // (But don't create uninstall information for it)
+ szKey = ODBC_DRIVERS_KEY;
+ nResult = RegDBCreateKeyEx(szKey, "");
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create ODBC Drivers subkey.", SEVERE);
+ exit;
+ endif;
+
+
+ // Increment Driver Manager Component UsageCount
+ szKey = ODBC_CORE_KEY;
+ szName = "UsageCount";
+ if (RegDBGetKeyValueEx(szKey, szName, nvType, svValue, nvSize) < 0) then
+ ComponentUsageCount = 0;
+ endif;
+
+ // MessageBox("Current Driver Manager Component Usage Count = " + svValue, INFORMATION);
+
+ StrToNum(ComponentUsageCount, svValue);
+ ComponentUsageCount = ComponentUsageCount + 1;
+ NumToStr(szTmp, ComponentUsageCount);
+ // MessageBox("New Driver Manager Component Usage Count = " + szTmp, INFORMATION);
+
+ nResult = RegDBSetKeyValueEx(szKey, szName, REGDB_NUMBER, szTmp, -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to increment Driver Manager component usage count.", SEVERE);
+ exit;
+ endif;
+
+ // Re-enable logging now
+ Enable(LOGGING);
+
+ // set ODBC Drivers Subkey (installed)
+ szKey = ODBC_DRIVERS_KEY;
+ nResult = RegDBSetKeyValueEx(szKey, DRIVER_NAME, REGDB_STRING, "Installed", -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create 'Installed' key value.", SEVERE);
+ exit;
+ endif;
+
+
+ // Driver Specification Subkey (PostgreSQL)
+ szKey = ODBC_COMP_KEY + "\\" + DRIVER_NAME;
+ nResult = RegDBCreateKeyEx(szKey, "");
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create ODBC Driver Key.", SEVERE);
+ exit;
+ endif;
+
+ nResult = RegDBSetKeyValueEx(szKey, "APILevel", REGDB_STRING, "1", -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create 'APILevel' key value.", SEVERE);
+ exit;
+ endif;
+
+ nResult = RegDBSetKeyValueEx(szKey, "ConnectFunctions", REGDB_STRING, "YYN", -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create 'ConnectFunctions' key value.", SEVERE);
+ exit;
+ endif;
+
+ nResult = RegDBSetKeyValueEx(szKey, "Driver", REGDB_STRING, WINSYSDIR ^ DRIVER_FILE, -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create 'Driver' key value.", SEVERE);
+ exit;
+ endif;
+
+ nResult = RegDBSetKeyValueEx(szKey, "DriverODBCVer", REGDB_STRING, "02.00", -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create 'DriverODBCVer' key value.", SEVERE);
+ exit;
+ endif;
+
+ nResult = RegDBSetKeyValueEx(szKey, "FileUsage", REGDB_STRING, "0", -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create 'FileUsage' key value.", SEVERE);
+ exit;
+ endif;
+
+ nResult = RegDBSetKeyValueEx(szKey, "Setup", REGDB_STRING, WINSYSDIR ^ DRIVER_FILE, -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create 'Setup' key value.", SEVERE);
+ exit;
+ endif;
+
+ nResult = RegDBSetKeyValueEx(szKey, "SQLLevel", REGDB_STRING, "1", -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create 'SQLLevel' key value.", SEVERE);
+ exit;
+ endif;
+
+ nResult = RegDBSetKeyValueEx(szKey, "UsageCount", REGDB_NUMBER, "1", -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create 'UsageCount' key value.", SEVERE);
+ exit;
+ endif;
+
+ pos = StrFind(CMDLINE, "UseDeclareFetch=");
+ if (pos >= 0) then
+ StrSub(svValue, CMDLINE, pos + 16, 1);
+ nResult = RegDBSetKeyValueEx(szKey, "UseDeclareFetch", REGDB_STRING, svValue, -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create 'UseDeclareFetch' key value.", SEVERE);
+ exit;
+ endif;
+ endif;
+
+ pos = StrFind(CMDLINE, "Protocol=");
+ if (pos >= 0) then
+ StrSub(svValue, CMDLINE, pos + 9, 3);
+ nResult = RegDBSetKeyValueEx(szKey, "Protocol", REGDB_STRING, svValue, -1);
+ if (nResult < 0) then
+ MessageBeep(0);
+ MessageBox("Unable to create 'Protocol' key value.", SEVERE);
+ exit;
+ endif;
+ endif;
+
+RenameOld:
+ if (FindFile(WINSYSDIR, OLD_DRIVER_FILE, svFileName) = 0) then
+ szMessage = "Renaming old driver to " + OLD_DRIVER_FILE_RENAMED + " ...";
+ SetStatusWindow(98, szMessage);
+ Delay(1);
+
+ Disable(LOGGING);
+
+ SRCDIR= WINSYSDIR;
+ TARGETDIR = WINSYSDIR;
+
+ RenameFile(OLD_DRIVER_FILE, OLD_DRIVER_FILE_RENAMED);
+
+ Enable(LOGGING);
+ endif;
+
+Done:
+ Delay(1);
+ SetStatusWindow(100, "Installation complete");
+
+ Delay(1);
+ Disable(STATUSDLG);
+
+ if (BATCH_INSTALL = TRUE) then
+ szMsg = "Some files could not be updated because they are " +
+ "currently in use by other programs on the system. " +
+ "Files in use will be updated the next time you restart " +
+ "your system.";
+ RebootDialog("Restart Windows", szMsg, SYS_BOOTMACHINE);
+ CommitSharedFiles(0);
+ szMsg = "Driver setup complete.\n\nReboot your system to complete the installation.";
+ MessageBeep(0);
+ MessageBox(szMsg, INFORMATION);
+ else
+
+ szMsg = "Driver installation completed successfully.";
+ MessageBeep(0);
+ MessageBox(szMsg, INFORMATION);
+ endif;
+
+ exit;
+
+/*---------------------------------------------------------------------------*\
+ *
+ * Function: SetupScreen
+ *
+ * Purpose: This function will set up the screen look. This includes
+ * colors, fonts, text to be displayed, etc.
+ *
+ *
+ * Input:
+ *
+ * Returns:
+ *
+ * Comments:
+\*---------------------------------------------------------------------------*/
+
+function SetupScreen()
+ begin
+
+ Enable( INDVFILESTATUS );
+
+ SetTitle( APP_NAME + " Setup", 28, WHITE );
+
+ SetTitle( "Setup", 0, BACKGROUNDCAPTION ); // Caption bar text.
+
+ Enable( BACKGROUND );
+
+ end;
+
+function FileCompare(szCompInfoDate, szCompInfoTime, szFileInfoDate, szFileInfoTime)
+ STRING year, month, day, file_date, file_time;
+ NUMBER nResult;
+ begin
+ StrSub(year, szFileInfoDate, 2, 2);
+ StrSub(month, szFileInfoDate, 5, 2);
+ StrSub(day, szFileInfoDate, 8, 2);
+ file_date = month + "-" + day + "-" + year;
+
+ nResult = StrCompare(szCompInfoDate, file_date);
+ if (nResult != 0) then
+ return nResult;
+ endif;
+
+ StrSub(file_time, szFileInfoTime, 0, 5);
+
+ // MessageBox("Comp = " + szCompInfoDate + " " + szCompInfoTime + ", File = " + file_date + " " + file_time, INFORMATION);
+ nResult = StrCompare(szCompInfoTime, file_time);
+
+ return nResult;
+ end;
+
+
+
--- /dev/null
+/*-------
+ * Module: socket.c
+ *
+ * Description: This module contains functions for low level socket
+ * operations (connecting/reading/writing to the backend)
+ *
+ * Classes: SocketClass (Functions prefix: "SOCK_")
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "socket.h"
+
+#include "connection.h"
+
+#ifndef WIN32
+#include <stdlib.h>
+#include <string.h> /* for memset */
+#endif
+
+extern GLOBAL_VALUES globals;
+
+#ifndef BOOL
+#define BOOL int
+#endif
+#ifndef TRUE
+#define TRUE (BOOL)1
+#endif
+#ifndef FALSE
+#define FALSE (BOOL)0
+#endif
+
+
+void
+SOCK_clear_error(SocketClass *self)
+{
+ self->errornumber = 0;
+ self->errormsg = NULL;
+}
+
+
+SocketClass *
+SOCK_Constructor(const ConnectionClass *conn)
+{
+ SocketClass *rv;
+
+ rv = (SocketClass *) malloc(sizeof(SocketClass));
+
+ if (rv != NULL)
+ {
+ rv->socket = (SOCKETFD) - 1;
+ rv->buffer_filled_in = 0;
+ rv->buffer_filled_out = 0;
+ rv->buffer_read_in = 0;
+
+ if (rv)
+ rv->buffer_size = conn->connInfo.drivers.socket_buffersize;
+ else
+ rv->buffer_size = globals.socket_buffersize;
+ rv->buffer_in = (unsigned char *) malloc(rv->buffer_size);
+ if (!rv->buffer_in)
+ {
+ free(rv);
+ return NULL;
+ }
+
+ rv->buffer_out = (unsigned char *) malloc(rv->buffer_size);
+ if (!rv->buffer_out)
+ {
+ free(rv->buffer_in);
+ free(rv);
+ return NULL;
+ }
+ rv->errormsg = NULL;
+ rv->errornumber = 0;
+ rv->reverse = FALSE;
+ }
+ return rv;
+}
+
+
+void
+SOCK_Destructor(SocketClass *self)
+{
+ mylog("SOCK_Destructor\n");
+ if (self->socket != -1)
+ {
+ SOCK_put_char(self, 'X');
+ SOCK_flush_output(self);
+ closesocket(self->socket);
+ }
+
+ if (self->buffer_in)
+ free(self->buffer_in);
+
+ if (self->buffer_out)
+ free(self->buffer_out);
+
+ free(self);
+}
+
+
+char
+SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname)
+{
+ struct hostent *host;
+ struct sockaddr_in sadr;
+ unsigned long iaddr;
+
+ if (self->socket != -1)
+ {
+ self->errornumber = SOCKET_ALREADY_CONNECTED;
+ self->errormsg = "Socket is already connected";
+ return 0;
+ }
+
+ memset((char *) &sadr, 0, sizeof(sadr));
+
+ /*
+ * If it is a valid IP address, use it. Otherwise use hostname lookup.
+ */
+ iaddr = inet_addr(hostname);
+ if (iaddr == INADDR_NONE)
+ {
+ host = gethostbyname(hostname);
+ if (host == NULL)
+ {
+ self->errornumber = SOCKET_HOST_NOT_FOUND;
+ self->errormsg = "Could not resolve hostname.";
+ return 0;
+ }
+ memcpy(&(sadr.sin_addr), host->h_addr, host->h_length);
+ }
+ else
+ memcpy(&(sadr.sin_addr), (struct in_addr *) & iaddr, sizeof(iaddr));
+
+ sadr.sin_family = AF_INET;
+ sadr.sin_port = htons(port);
+
+ self->socket = socket(AF_INET, SOCK_STREAM, 0);
+ if (self->socket == -1)
+ {
+ self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET;
+ self->errormsg = "Could not create Socket.";
+ return 0;
+ }
+
+ if (connect(self->socket, (struct sockaddr *) & (sadr),
+ sizeof(sadr)) < 0)
+ {
+ self->errornumber = SOCKET_COULD_NOT_CONNECT;
+ self->errormsg = "Could not connect to remote socket.";
+ closesocket(self->socket);
+ self->socket = (SOCKETFD) - 1;
+ return 0;
+ }
+ return 1;
+}
+
+
+void
+SOCK_get_n_char(SocketClass *self, char *buffer, int len)
+{
+ int lf;
+
+ if (!buffer)
+ {
+ self->errornumber = SOCKET_NULLPOINTER_PARAMETER;
+ self->errormsg = "get_n_char was called with NULL-Pointer";
+ return;
+ }
+
+ for (lf = 0; lf < len; lf++)
+ buffer[lf] = SOCK_get_next_byte(self);
+}
+
+
+void
+SOCK_put_n_char(SocketClass *self, char *buffer, int len)
+{
+ int lf;
+
+ if (!buffer)
+ {
+ self->errornumber = SOCKET_NULLPOINTER_PARAMETER;
+ self->errormsg = "put_n_char was called with NULL-Pointer";
+ return;
+ }
+
+ for (lf = 0; lf < len; lf++)
+ SOCK_put_next_byte(self, (unsigned char) buffer[lf]);
+}
+
+
+/*
+ * bufsize must include room for the null terminator
+ * will read at most bufsize-1 characters + null.
+ * returns TRUE if truncation occurs.
+ */
+BOOL
+SOCK_get_string(SocketClass *self, char *buffer, int bufsize)
+{
+ register int lf = 0;
+
+ for (lf = 0; lf < bufsize - 1; lf++)
+ if (!(buffer[lf] = SOCK_get_next_byte(self)))
+ return FALSE;
+
+ buffer[bufsize - 1] = '\0';
+ return TRUE;
+}
+
+
+void
+SOCK_put_string(SocketClass *self, char *string)
+{
+ register int lf;
+ int len;
+
+ len = strlen(string) + 1;
+
+ for (lf = 0; lf < len; lf++)
+ SOCK_put_next_byte(self, (unsigned char) string[lf]);
+}
+
+
+int
+SOCK_get_int(SocketClass *self, short len)
+{
+ switch (len)
+ {
+ case 2:
+ {
+ unsigned short buf;
+
+ SOCK_get_n_char(self, (char *) &buf, len);
+ if (self->reverse)
+ return buf;
+ else
+ return ntohs(buf);
+ }
+
+ case 4:
+ {
+ unsigned int buf;
+
+ SOCK_get_n_char(self, (char *) &buf, len);
+ if (self->reverse)
+ return buf;
+ else
+ return ntohl(buf);
+ }
+
+ default:
+ self->errornumber = SOCKET_GET_INT_WRONG_LENGTH;
+ self->errormsg = "Cannot read ints of that length";
+ return 0;
+ }
+}
+
+
+void
+SOCK_put_int(SocketClass *self, int value, short len)
+{
+ unsigned int rv;
+
+ switch (len)
+ {
+ case 2:
+ rv = self->reverse ? value : htons((unsigned short) value);
+ SOCK_put_n_char(self, (char *) &rv, 2);
+ return;
+
+ case 4:
+ rv = self->reverse ? value : htonl((unsigned int) value);
+ SOCK_put_n_char(self, (char *) &rv, 4);
+ return;
+
+ default:
+ self->errornumber = SOCKET_PUT_INT_WRONG_LENGTH;
+ self->errormsg = "Cannot write ints of that length";
+ return;
+ }
+}
+
+
+void
+SOCK_flush_output(SocketClass *self)
+{
+ int written;
+
+ written = send(self->socket, (char *) self->buffer_out, self->buffer_filled_out, 0);
+ if (written != self->buffer_filled_out)
+ {
+ self->errornumber = SOCKET_WRITE_ERROR;
+ self->errormsg = "Could not flush socket buffer.";
+ }
+ self->buffer_filled_out = 0;
+}
+
+
+unsigned char
+SOCK_get_next_byte(SocketClass *self)
+{
+ if (self->buffer_read_in >= self->buffer_filled_in)
+ {
+ /*
+ * there are no more bytes left in the buffer so reload the buffer
+ */
+ self->buffer_read_in = 0;
+ self->buffer_filled_in = recv(self->socket, (char *) self->buffer_in, self->buffer_size, 0);
+
+ mylog("read %d, global_socket_buffersize=%d\n", self->buffer_filled_in, self->buffer_size);
+
+ if (self->buffer_filled_in < 0)
+ {
+ self->errornumber = SOCKET_READ_ERROR;
+ self->errormsg = "Error while reading from the socket.";
+ self->buffer_filled_in = 0;
+ return 0;
+ }
+ if (self->buffer_filled_in == 0)
+ {
+ self->errornumber = SOCKET_CLOSED;
+ self->errormsg = "Socket has been closed.";
+ self->buffer_filled_in = 0;
+ return 0;
+ }
+ }
+ return self->buffer_in[self->buffer_read_in++];
+}
+
+
+void
+SOCK_put_next_byte(SocketClass *self, unsigned char next_byte)
+{
+ int bytes_sent;
+
+ self->buffer_out[self->buffer_filled_out++] = next_byte;
+
+ if (self->buffer_filled_out == self->buffer_size)
+ {
+ /* buffer is full, so write it out */
+ bytes_sent = send(self->socket, (char *) self->buffer_out, self->buffer_size, 0);
+ if (bytes_sent != self->buffer_size)
+ {
+ self->errornumber = SOCKET_WRITE_ERROR;
+ self->errormsg = "Error while writing to the socket.";
+ }
+ self->buffer_filled_out = 0;
+ }
+}
--- /dev/null
+/* File: socket.h
+ *
+ * Description: See "socket.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __SOCKET_H__
+#define __SOCKET_H__
+
+#include "psqlodbc.h"
+
+#ifndef WIN32
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define closesocket(xxx) close(xxx)
+#define SOCKETFD int
+
+#ifndef INADDR_NONE
+#ifndef _IN_ADDR_T
+#define _IN_ADDR_T
+typedef unsigned int in_addr_t;
+#endif
+#define INADDR_NONE ((in_addr_t)-1)
+#endif
+
+#else
+#include <winsock.h>
+#define SOCKETFD SOCKET
+#endif
+
+#define SOCKET_ALREADY_CONNECTED 1
+#define SOCKET_HOST_NOT_FOUND 2
+#define SOCKET_COULD_NOT_CREATE_SOCKET 3
+#define SOCKET_COULD_NOT_CONNECT 4
+#define SOCKET_READ_ERROR 5
+#define SOCKET_WRITE_ERROR 6
+#define SOCKET_NULLPOINTER_PARAMETER 7
+#define SOCKET_PUT_INT_WRONG_LENGTH 8
+#define SOCKET_GET_INT_WRONG_LENGTH 9
+#define SOCKET_CLOSED 10
+
+
+struct SocketClass_
+{
+
+ int buffer_size;
+ int buffer_filled_in;
+ int buffer_filled_out;
+ int buffer_read_in;
+ unsigned char *buffer_in;
+ unsigned char *buffer_out;
+
+ SOCKETFD socket;
+
+ char *errormsg;
+ int errornumber;
+
+ char reverse; /* used to handle Postgres 6.2 protocol
+ * (reverse byte order) */
+
+};
+
+#define SOCK_get_char(self) (SOCK_get_next_byte(self))
+#define SOCK_put_char(self, c) (SOCK_put_next_byte(self, c))
+
+
+/* error functions */
+#define SOCK_get_errcode(self) (self->errornumber)
+#define SOCK_get_errmsg(self) (self->errormsg)
+
+
+/* Socket prototypes */
+SocketClass *SOCK_Constructor(const ConnectionClass *conn);
+void SOCK_Destructor(SocketClass *self);
+char SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname);
+void SOCK_get_n_char(SocketClass *self, char *buffer, int len);
+void SOCK_put_n_char(SocketClass *self, char *buffer, int len);
+BOOL SOCK_get_string(SocketClass *self, char *buffer, int bufsize);
+void SOCK_put_string(SocketClass *self, char *string);
+int SOCK_get_int(SocketClass *self, short len);
+void SOCK_put_int(SocketClass *self, int value, short len);
+void SOCK_flush_output(SocketClass *self);
+unsigned char SOCK_get_next_byte(SocketClass *self);
+void SOCK_put_next_byte(SocketClass *self, unsigned char next_byte);
+void SOCK_clear_error(SocketClass *self);
+
+#endif
--- /dev/null
+/*-------
+ * Module: statement.c
+ *
+ * Description: This module contains functions related to creating
+ * and manipulating a statement.
+ *
+ * Classes: StatementClass (Functions prefix: "SC_")
+ *
+ * API functions: SQLAllocStmt, SQLFreeStmt
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "statement.h"
+
+#include "bind.h"
+#include "connection.h"
+#include "qresult.h"
+#include "convert.h"
+#include "environ.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "pgapifunc.h"
+
+
+#define PRN_NULLCHECK
+
+
+/* Map sql commands to statement types */
+static struct
+{
+ int type;
+ char *s;
+} Statement_Type[] =
+
+{
+ {
+ STMT_TYPE_SELECT, "SELECT"
+ },
+ {
+ STMT_TYPE_INSERT, "INSERT"
+ },
+ {
+ STMT_TYPE_UPDATE, "UPDATE"
+ },
+ {
+ STMT_TYPE_DELETE, "DELETE"
+ },
+ {
+ STMT_TYPE_CREATE, "CREATE"
+ },
+ {
+ STMT_TYPE_ALTER, "ALTER"
+ },
+ {
+ STMT_TYPE_DROP, "DROP"
+ },
+ {
+ STMT_TYPE_GRANT, "GRANT"
+ },
+ {
+ STMT_TYPE_REVOKE, "REVOKE"
+ },
+ {
+ STMT_TYPE_PROCCALL, "{"
+ },
+ {
+ 0, NULL
+ }
+};
+
+
+RETCODE SQL_API
+PGAPI_AllocStmt(HDBC hdbc,
+ HSTMT FAR * phstmt)
+{
+ static char *func = "PGAPI_AllocStmt";
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ StatementClass *stmt;
+
+ mylog("%s: entering...\n", func);
+
+ if (!conn)
+ {
+ CC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ stmt = SC_Constructor();
+
+ mylog("**** PGAPI_AllocStmt: hdbc = %u, stmt = %u\n", hdbc, stmt);
+
+ if (!stmt)
+ {
+ conn->errornumber = CONN_STMT_ALLOC_ERROR;
+ conn->errormsg = "No more memory to allocate a further SQL-statement";
+ *phstmt = SQL_NULL_HSTMT;
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+
+ if (!CC_add_statement(conn, stmt))
+ {
+ conn->errormsg = "Maximum number of connections exceeded.";
+ conn->errornumber = CONN_STMT_ALLOC_ERROR;
+ CC_log_error(func, "", conn);
+ SC_Destructor(stmt);
+ *phstmt = SQL_NULL_HSTMT;
+ return SQL_ERROR;
+ }
+
+ *phstmt = (HSTMT) stmt;
+
+ /* Copy default statement options based from Connection options */
+ stmt->options = conn->stmtOptions;
+
+ stmt->stmt_size_limit = CC_get_max_query_len(conn);
+ /* Save the handle for later */
+ stmt->phstmt = phstmt;
+
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_FreeStmt(HSTMT hstmt,
+ UWORD fOption)
+{
+ static char *func = "PGAPI_FreeStmt";
+ StatementClass *stmt = (StatementClass *) hstmt;
+
+ mylog("%s: entering...hstmt=%u, fOption=%d\n", func, hstmt, fOption);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ SC_clear_error(stmt);
+
+ if (fOption == SQL_DROP)
+ {
+ ConnectionClass *conn = stmt->hdbc;
+
+ /* Remove the statement from the connection's statement list */
+ if (conn)
+ {
+ if (!CC_remove_statement(conn, stmt))
+ {
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ stmt->errormsg = "Statement is currently executing a transaction.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR; /* stmt may be executing a
+ * transaction */
+ }
+
+ /* Free any cursors and discard any result info */
+ if (stmt->result)
+ {
+ QR_Destructor(stmt->result);
+ stmt->result = NULL;
+ }
+ }
+
+ /* Destroy the statement and free any results, cursors, etc. */
+ SC_Destructor(stmt);
+ }
+ else if (fOption == SQL_UNBIND)
+ SC_unbind_cols(stmt);
+ else if (fOption == SQL_CLOSE)
+ {
+ /*
+ * this should discard all the results, but leave the statement
+ * itself in place (it can be executed again)
+ */
+ if (!SC_recycle_statement(stmt))
+ {
+ /* errormsg passed in above */
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+ else if (fOption == SQL_RESET_PARAMS)
+ SC_free_params(stmt, STMT_FREE_PARAMS_ALL);
+ else
+ {
+ stmt->errormsg = "Invalid option passed to PGAPI_FreeStmt.";
+ stmt->errornumber = STMT_OPTION_OUT_OF_RANGE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ return SQL_SUCCESS;
+}
+
+
+/*
+ * StatementClass implementation
+ */
+void
+InitializeStatementOptions(StatementOptions *opt)
+{
+ memset(opt, 0, sizeof(StatementOptions));
+ opt->maxRows = 0; /* driver returns all rows */
+ opt->maxLength = 0; /* driver returns all data for char/binary */
+ opt->rowset_size = 1;
+ opt->keyset_size = 0; /* fully keyset driven is the default */
+ opt->scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ opt->cursor_type = SQL_CURSOR_FORWARD_ONLY;
+ opt->bind_size = 0; /* default is to bind by column */
+ opt->retrieve_data = SQL_RD_ON;
+ opt->use_bookmarks = SQL_UB_OFF;
+ opt->paramset_size = 1;
+ opt->param_bind_type = 0; /* default is column-wise binding */
+}
+
+
+StatementClass *
+SC_Constructor(void)
+{
+ StatementClass *rv;
+
+ rv = (StatementClass *) malloc(sizeof(StatementClass));
+ if (rv)
+ {
+ rv->hdbc = NULL; /* no connection associated yet */
+ rv->phstmt = NULL;
+ rv->result = NULL;
+ rv->manual_result = FALSE;
+ rv->prepare = FALSE;
+ rv->status = STMT_ALLOCATED;
+ rv->internal = FALSE;
+
+ rv->errormsg = NULL;
+ rv->errornumber = 0;
+ rv->errormsg_created = FALSE;
+ rv->errormsg_malloced = FALSE;
+
+ rv->statement = NULL;
+ rv->stmt_with_params = NULL;
+ rv->stmt_size_limit = -1;
+ rv->statement_type = STMT_TYPE_UNKNOWN;
+
+ rv->bindings = NULL;
+ rv->bindings_allocated = 0;
+
+ rv->bookmark.buffer = NULL;
+ rv->bookmark.used = NULL;
+
+ rv->parameters_allocated = 0;
+ rv->parameters = 0;
+
+ rv->currTuple = -1;
+ rv->rowset_start = -1;
+ rv->current_col = -1;
+ rv->bind_row = 0;
+ rv->last_fetch_count = 0;
+ rv->save_rowset_size = -1;
+
+ rv->data_at_exec = -1;
+ rv->current_exec_param = -1;
+ rv->put_data = FALSE;
+
+ rv->lobj_fd = -1;
+ rv->cursor_name[0] = '\0';
+
+ /* Parse Stuff */
+ rv->ti = NULL;
+ rv->fi = NULL;
+ rv->ntab = 0;
+ rv->nfld = 0;
+ rv->parse_status = STMT_PARSE_NONE;
+
+ /* Clear Statement Options -- defaults will be set in AllocStmt */
+ memset(&rv->options, 0, sizeof(StatementOptions));
+
+ rv->pre_executing = FALSE;
+ rv->inaccurate_result = FALSE;
+ rv->miscinfo = 0;
+ }
+ return rv;
+}
+
+
+char
+SC_Destructor(StatementClass *self)
+{
+ mylog("SC_Destructor: self=%u, self->result=%u, self->hdbc=%u\n", self, self->result, self->hdbc);
+ SC_clear_error(self);
+ if (STMT_EXECUTING == self->status)
+ {
+ self->errornumber = STMT_SEQUENCE_ERROR;
+ self->errormsg = "Statement is currently executing a transaction.";
+ return FALSE;
+ }
+
+ if (self->result)
+ {
+ if (!self->hdbc)
+ self->result->conn = NULL; /* prevent any dbase activity */
+
+ QR_Destructor(self->result);
+ }
+
+ if (self->statement)
+ free(self->statement);
+ if (self->stmt_with_params)
+ {
+ free(self->stmt_with_params);
+ self->stmt_with_params = NULL;
+ }
+
+ SC_free_params(self, STMT_FREE_PARAMS_ALL);
+
+ /*
+ * the memory pointed to by the bindings is not deallocated by the
+ * driver but by the application that uses that driver, so we don't
+ * have to care
+ */
+ /* about that here. */
+ if (self->bindings)
+ {
+ int lf;
+
+ for (lf = 0; lf < self->bindings_allocated; lf++)
+ {
+ if (self->bindings[lf].ttlbuf != NULL)
+ free(self->bindings[lf].ttlbuf);
+ }
+ free(self->bindings);
+ }
+
+ /* Free the parsed table information */
+ if (self->ti)
+ {
+ int i;
+
+ for (i = 0; i < self->ntab; i++)
+ free(self->ti[i]);
+
+ free(self->ti);
+ }
+
+ /* Free the parsed field information */
+ if (self->fi)
+ {
+ int i;
+
+ for (i = 0; i < self->nfld; i++)
+ free(self->fi[i]);
+ free(self->fi);
+ }
+
+ free(self);
+
+ mylog("SC_Destructor: EXIT\n");
+
+ return TRUE;
+}
+
+
+/*
+ * Free parameters and free the memory from the
+ * data-at-execution parameters that was allocated in SQLPutData.
+ */
+void
+SC_free_params(StatementClass *self, char option)
+{
+ int i;
+
+ mylog("SC_free_params: ENTER, self=%d\n", self);
+
+ if (!self->parameters)
+ return;
+
+ for (i = 0; i < self->parameters_allocated; i++)
+ {
+ if (self->parameters[i].data_at_exec == TRUE)
+ {
+ if (self->parameters[i].EXEC_used)
+ {
+ free(self->parameters[i].EXEC_used);
+ self->parameters[i].EXEC_used = NULL;
+ }
+
+ if (self->parameters[i].EXEC_buffer)
+ {
+ if (self->parameters[i].SQLType != SQL_LONGVARBINARY)
+ free(self->parameters[i].EXEC_buffer);
+ self->parameters[i].EXEC_buffer = NULL;
+ }
+ }
+ }
+ self->data_at_exec = -1;
+ self->current_exec_param = -1;
+ self->put_data = FALSE;
+
+ if (option == STMT_FREE_PARAMS_ALL)
+ {
+ free(self->parameters);
+ self->parameters = NULL;
+ self->parameters_allocated = 0;
+ }
+
+ mylog("SC_free_params: EXIT\n");
+}
+
+
+int
+statement_type(char *statement)
+{
+ int i;
+
+ /* ignore leading whitespace in query string */
+ while (*statement && isspace((unsigned char) *statement))
+ statement++;
+
+ for (i = 0; Statement_Type[i].s; i++)
+ if (!strnicmp(statement, Statement_Type[i].s, strlen(Statement_Type[i].s)))
+ return Statement_Type[i].type;
+
+ return STMT_TYPE_OTHER;
+}
+
+
+/*
+ * Called from SQLPrepare if STMT_PREMATURE, or
+ * from SQLExecute if STMT_FINISHED, or
+ * from SQLFreeStmt(SQL_CLOSE)
+ */
+char
+SC_recycle_statement(StatementClass *self)
+{
+ ConnectionClass *conn;
+
+ mylog("recycle statement: self= %u\n", self);
+
+ SC_clear_error(self);
+ /* This would not happen */
+ if (self->status == STMT_EXECUTING)
+ {
+ self->errornumber = STMT_SEQUENCE_ERROR;
+ self->errormsg = "Statement is currently executing a transaction.";
+ return FALSE;
+ }
+
+ switch (self->status)
+ {
+ case STMT_ALLOCATED:
+ /* this statement does not need to be recycled */
+ return TRUE;
+
+ case STMT_READY:
+ break;
+
+ case STMT_PREMATURE:
+
+ /*
+ * Premature execution of the statement might have caused the
+ * start of a transaction. If so, we have to rollback that
+ * transaction.
+ */
+ conn = SC_get_conn(self);
+ if (!CC_is_in_autocommit(conn) && CC_is_in_trans(conn))
+ {
+ if (SC_is_pre_executable(self) && !conn->connInfo.disallow_premature)
+ CC_abort(conn);
+ }
+ break;
+
+ case STMT_FINISHED:
+ break;
+
+ default:
+ self->errormsg = "An internal error occured while recycling statements";
+ self->errornumber = STMT_INTERNAL_ERROR;
+ return FALSE;
+ }
+
+ /* Free the parsed table information */
+ if (self->ti)
+ {
+ int i;
+
+ for (i = 0; i < self->ntab; i++)
+ free(self->ti[i]);
+
+ free(self->ti);
+ self->ti = NULL;
+ self->ntab = 0;
+ }
+
+ /* Free the parsed field information */
+ if (self->fi)
+ {
+ int i;
+
+ for (i = 0; i < self->nfld; i++)
+ free(self->fi[i]);
+ free(self->fi);
+ self->fi = NULL;
+ self->nfld = 0;
+ }
+ self->parse_status = STMT_PARSE_NONE;
+
+ /* Free any cursors */
+ if (self->result)
+ {
+ QR_Destructor(self->result);
+ self->result = NULL;
+ }
+ self->inaccurate_result = FALSE;
+
+ /*
+ * Reset only parameters that have anything to do with results
+ */
+ self->status = STMT_READY;
+ self->manual_result = FALSE; /* very important */
+
+ self->currTuple = -1;
+ self->rowset_start = -1;
+ self->current_col = -1;
+ self->bind_row = 0;
+ self->last_fetch_count = 0;
+
+ if (self->errormsg_malloced && self->errormsg)
+ free(self->errormsg);
+ self->errormsg = NULL;
+ self->errornumber = 0;
+ self->errormsg_created = FALSE;
+ self->errormsg_malloced = FALSE;
+
+ self->lobj_fd = -1;
+
+ /*
+ * Free any data at exec params before the statement is executed
+ * again. If not, then there will be a memory leak when the next
+ * SQLParamData/SQLPutData is called.
+ */
+ SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
+
+ return TRUE;
+}
+
+
+/* Pre-execute a statement (SQLPrepare/SQLDescribeCol) */
+void
+SC_pre_execute(StatementClass *self)
+{
+ mylog("SC_pre_execute: status = %d\n", self->status);
+
+ if (self->status == STMT_READY)
+ {
+ mylog(" preprocess: status = READY\n");
+
+ self->miscinfo = 0;
+ if (self->statement_type == STMT_TYPE_SELECT)
+ {
+ char old_pre_executing = self->pre_executing;
+
+ self->pre_executing = TRUE;
+ self->inaccurate_result = FALSE;
+
+ PGAPI_Execute(self);
+
+ self->pre_executing = old_pre_executing;
+
+ if (self->status == STMT_FINISHED)
+ {
+ mylog(" preprocess: after status = FINISHED, so set PREMATURE\n");
+ self->status = STMT_PREMATURE;
+ }
+ }
+ if (!SC_is_pre_executable(self))
+ {
+ self->result = QR_Constructor();
+ QR_set_status(self->result, PGRES_TUPLES_OK);
+ self->inaccurate_result = TRUE;
+ self->status = STMT_PREMATURE;
+ }
+ }
+}
+
+
+/* This is only called from SQLFreeStmt(SQL_UNBIND) */
+char
+SC_unbind_cols(StatementClass *self)
+{
+ Int2 lf;
+
+ for (lf = 0; lf < self->bindings_allocated; lf++)
+ {
+ self->bindings[lf].data_left = -1;
+ self->bindings[lf].buflen = 0;
+ self->bindings[lf].buffer = NULL;
+ self->bindings[lf].used = NULL;
+ self->bindings[lf].returntype = SQL_C_CHAR;
+ }
+
+ self->bookmark.buffer = NULL;
+ self->bookmark.used = NULL;
+
+ return 1;
+}
+
+
+void
+SC_clear_error(StatementClass *self)
+{
+ if (self->errormsg_malloced && self->errormsg)
+ free(self->errormsg);
+ self->errornumber = 0;
+ self->errormsg = NULL;
+ self->errormsg_created = FALSE;
+ self->errormsg_malloced = FALSE;
+}
+
+
+/*
+ * This function creates an error msg which is the concatenation
+ * of the result, statement, connection, and socket messages.
+ */
+char *
+SC_create_errormsg(StatementClass *self)
+{
+ QResultClass *res = self->result;
+ ConnectionClass *conn = self->hdbc;
+ int pos;
+ static char msg[4096];
+
+ msg[0] = '\0';
+
+ if (res && res->message)
+ strcpy(msg, res->message);
+
+ else if (self->errormsg)
+ strcpy(msg, self->errormsg);
+
+ if (conn)
+ {
+ SocketClass *sock = conn->sock;
+
+ if (conn->errormsg && conn->errormsg[0] != '\0')
+ {
+ pos = strlen(msg);
+ sprintf(&msg[pos], ";\n%s", conn->errormsg);
+ }
+
+ if (sock && sock->errormsg && sock->errormsg[0] != '\0')
+ {
+ pos = strlen(msg);
+ sprintf(&msg[pos], ";\n%s", sock->errormsg);
+ }
+ }
+ if (!msg[0] && res && QR_get_notice(res))
+ return QR_get_notice(res);
+
+ return msg;
+}
+
+
+char
+SC_get_error(StatementClass *self, int *number, char **message)
+{
+ char rv;
+
+ /* Create a very informative errormsg if it hasn't been done yet. */
+ if (!self->errormsg_created)
+ {
+ self->errormsg = SC_create_errormsg(self);
+ self->errormsg_created = TRUE;
+ }
+
+ if (self->errornumber)
+ {
+ *number = self->errornumber;
+ *message = self->errormsg;
+ if (!self->errormsg_malloced)
+ self->errormsg = NULL;
+ }
+
+ rv = (self->errornumber != 0);
+ self->errornumber = 0;
+
+ return rv;
+}
+
+
+/*
+ * Currently, the driver offers very simple bookmark support -- it is
+ * just the current row number. But it could be more sophisticated
+ * someday, such as mapping a key to a 32 bit value
+ */
+unsigned long
+SC_get_bookmark(StatementClass *self)
+{
+ return (self->currTuple + 1);
+}
+
+
+RETCODE
+SC_fetch(StatementClass *self)
+{
+ static char *func = "SC_fetch";
+ QResultClass *res = self->result;
+ int retval,
+ result;
+
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ int updret;
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ Int2 num_cols,
+ lf;
+ Oid type;
+ char *value;
+ ColumnInfoClass *coli;
+
+ /* TupleField *tupleField; */
+ ConnInfo *ci = &(SC_get_conn(self)->connInfo);
+
+ self->last_fetch_count = 0;
+ coli = QR_get_fields(res); /* the column info */
+
+ mylog("manual_result = %d, use_declarefetch = %d\n", self->manual_result, ci->drivers.use_declarefetch);
+
+ if (self->manual_result || !SC_is_fetchcursor(self))
+ {
+ if (self->currTuple >= QR_get_num_tuples(res) - 1 ||
+ (self->options.maxRows > 0 && self->currTuple == self->options.maxRows - 1))
+ {
+ /*
+ * if at the end of the tuples, return "no data found" and set
+ * the cursor past the end of the result set
+ */
+ self->currTuple = QR_get_num_tuples(res);
+ return SQL_NO_DATA_FOUND;
+ }
+
+ mylog("**** SC_fetch: manual_result\n");
+ (self->currTuple)++;
+ }
+ else
+ {
+ /* read from the cache or the physical next tuple */
+ retval = QR_next_tuple(res);
+ if (retval < 0)
+ {
+ mylog("**** SC_fetch: end_tuples\n");
+ return SQL_NO_DATA_FOUND;
+ }
+ else if (retval > 0)
+ (self->currTuple)++; /* all is well */
+ else
+ {
+ mylog("SC_fetch: error\n");
+ self->errornumber = STMT_EXEC_ERROR;
+ self->errormsg = "Error fetching next row";
+ SC_log_error(func, "", self);
+ return SQL_ERROR;
+ }
+ }
+
+ num_cols = QR_NumResultCols(res);
+
+ result = SQL_SUCCESS;
+ self->last_fetch_count = 1;
+
+ /*
+ * If the bookmark column was bound then return a bookmark. Since this
+ * is used with SQLExtendedFetch, and the rowset size may be greater
+ * than 1, and an application can use row or column wise binding, use
+ * the code in copy_and_convert_field() to handle that.
+ */
+ if (self->bookmark.buffer)
+ {
+ char buf[32];
+
+ sprintf(buf, "%ld", SC_get_bookmark(self));
+ result = copy_and_convert_field(self, 0, buf,
+ SQL_C_ULONG, self->bookmark.buffer, 0, self->bookmark.used);
+ }
+
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ updret = 0;
+ if (self->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
+ {
+ if (!QR_get_value_backend_row(res, self->currTuple, num_cols - 1))
+ updret = SQL_ROW_DELETED;
+ num_cols -= 2;
+ }
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ if (self->options.retrieve_data == SQL_RD_OFF) /* data isn't required */
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ return updret ? updret + 10 : SQL_SUCCESS;
+#else
+ return SQL_SUCCESS;
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ for (lf = 0; lf < num_cols; lf++)
+ {
+ mylog("fetch: cols=%d, lf=%d, self = %u, self->bindings = %u, buffer[] = %u\n", num_cols, lf, self, self->bindings, self->bindings[lf].buffer);
+
+ /* reset for SQLGetData */
+ self->bindings[lf].data_left = -1;
+
+ if (self->bindings[lf].buffer != NULL)
+ {
+ /* this column has a binding */
+
+ /* type = QR_get_field_type(res, lf); */
+ type = CI_get_oid(coli, lf); /* speed things up */
+
+ mylog("type = %d\n", type);
+
+ if (self->manual_result)
+ {
+ value = QR_get_value_manual(res, self->currTuple, lf);
+ mylog("manual_result\n");
+ }
+ else if (SC_is_fetchcursor(self))
+ value = QR_get_value_backend(res, lf);
+ else
+ value = QR_get_value_backend_row(res, self->currTuple, lf);
+
+ mylog("value = '%s'\n", (value == NULL) ? "<NULL>" : value);
+
+ retval = copy_and_convert_field_bindinfo(self, type, value, lf);
+
+ mylog("copy_and_convert: retval = %d\n", retval);
+
+ switch (retval)
+ {
+ case COPY_OK:
+ break; /* OK, do next bound column */
+
+ case COPY_UNSUPPORTED_TYPE:
+ self->errormsg = "Received an unsupported type from Postgres.";
+ self->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
+ SC_log_error(func, "", self);
+ result = SQL_ERROR;
+ break;
+
+ case COPY_UNSUPPORTED_CONVERSION:
+ self->errormsg = "Couldn't handle the necessary data type conversion.";
+ self->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
+ SC_log_error(func, "", self);
+ result = SQL_ERROR;
+ break;
+
+ case COPY_RESULT_TRUNCATED:
+ self->errornumber = STMT_TRUNCATED;
+ self->errormsg = "Fetched item was truncated.";
+ qlog("The %dth item was truncated\n", lf + 1);
+ qlog("The buffer size = %d", self->bindings[lf].buflen);
+ qlog(" and the value is '%s'\n", value);
+ result = SQL_SUCCESS_WITH_INFO;
+ break;
+
+ /* error msg already filled in */
+ case COPY_GENERAL_ERROR:
+ SC_log_error(func, "", self);
+ result = SQL_ERROR;
+ break;
+
+ /* This would not be meaningful in SQLFetch. */
+ case COPY_NO_DATA_FOUND:
+ break;
+
+ default:
+ self->errormsg = "Unrecognized return value from copy_and_convert_field.";
+ self->errornumber = STMT_INTERNAL_ERROR;
+ SC_log_error(func, "", self);
+ result = SQL_ERROR;
+ break;
+ }
+ }
+ }
+
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ if (updret)
+ result = updret + 10;
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ return result;
+}
+
+
+RETCODE
+SC_execute(StatementClass *self)
+{
+ static char *func = "SC_execute";
+ ConnectionClass *conn;
+ QResultClass *res;
+ char ok,
+ was_ok,
+ was_nonfatal;
+ Int2 oldstatus,
+ numcols;
+ QueryInfo qi;
+ ConnInfo *ci;
+
+
+ conn = SC_get_conn(self);
+ ci = &(conn->connInfo);
+
+ /* Begin a transaction if one is not already in progress */
+
+ /*
+ * Basically we don't have to begin a transaction in autocommit mode
+ * because Postgres backend runs in autocomit mode. We issue "BEGIN"
+ * in the following cases. 1) we use declare/fetch and the statement
+ * is SELECT (because declare/fetch must be called in a transaction).
+ * 2) we are in autocommit off state and the statement isn't of type
+ * OTHER.
+ */
+ if (!self->internal && !CC_is_in_trans(conn) &&
+ (SC_is_fetchcursor(self) ||
+ (!CC_is_in_autocommit(conn) && self->statement_type != STMT_TYPE_OTHER)))
+ {
+ mylog(" about to begin a transaction on statement = %u\n", self);
+ res = CC_send_query(conn, "BEGIN", NULL);
+ if (QR_aborted(res))
+ {
+ self->errormsg = "Could not begin a transaction";
+ self->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", self);
+ return SQL_ERROR;
+ }
+
+ ok = QR_command_successful(res);
+
+ mylog("SC_exec: begin ok = %d, status = %d\n", ok, QR_get_status(res));
+
+ QR_Destructor(res);
+
+ if (!ok)
+ {
+ self->errormsg = "Could not begin a transaction";
+ self->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", self);
+ return SQL_ERROR;
+ }
+ else
+ CC_set_in_trans(conn);
+ }
+
+ oldstatus = conn->status;
+ conn->status = CONN_EXECUTING;
+ self->status = STMT_EXECUTING;
+
+ /* If it's a SELECT statement, use a cursor. */
+
+ /*
+ * Note that the declare cursor has already been prepended to the
+ * statement
+ */
+ /* in copy_statement... */
+ if (self->statement_type == STMT_TYPE_SELECT)
+ {
+ char fetch[128];
+
+ mylog(" Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name);
+
+ /* send the declare/select */
+ self->result = CC_send_query(conn, self->stmt_with_params, NULL);
+
+ if (SC_is_fetchcursor(self) && self->result != NULL &&
+ QR_command_successful(self->result))
+ {
+ QR_Destructor(self->result);
+
+ /*
+ * That worked, so now send the fetch to start getting data
+ * back
+ */
+ qi.result_in = NULL;
+ qi.cursor = self->cursor_name;
+ qi.row_size = ci->drivers.fetch_max;
+
+ /*
+ * Most likely the rowset size will not be set by the
+ * application until after the statement is executed, so might
+ * as well use the cache size. The qr_next_tuple() function
+ * will correct for any discrepancies in sizes and adjust the
+ * cache accordingly.
+ */
+ sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name);
+
+ self->result = CC_send_query(conn, fetch, &qi);
+ }
+ mylog(" done sending the query:\n");
+ }
+ else
+ {
+ /* not a SELECT statement so don't use a cursor */
+ mylog(" it's NOT a select statement: stmt=%u\n", self);
+ self->result = CC_send_query(conn, self->stmt_with_params, NULL);
+
+ /*
+ * We shouldn't send COMMIT. Postgres backend does the autocommit
+ * if neccessary. (Zoltan, 04/26/2000)
+ */
+
+ /*
+ * Above seems wrong. Even in case of autocommit, started
+ * transactions must be committed. (Hiroshi, 02/11/2001)
+ */
+ if (!self->internal && CC_is_in_autocommit(conn) && CC_is_in_trans(conn))
+ {
+ res = CC_send_query(conn, "COMMIT", NULL);
+ QR_Destructor(res);
+ CC_set_no_trans(conn);
+ }
+ }
+
+ conn->status = oldstatus;
+ self->status = STMT_FINISHED;
+
+ /* Check the status of the result */
+ if (self->result)
+ {
+ was_ok = QR_command_successful(self->result);
+ was_nonfatal = QR_command_nonfatal(self->result);
+
+ if (was_ok)
+ self->errornumber = STMT_OK;
+ else
+ self->errornumber = was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND;
+
+ /* set cursor before the first tuple in the list */
+ self->currTuple = -1;
+ self->current_col = -1;
+ self->rowset_start = -1;
+
+ /* see if the query did return any result columns */
+ numcols = QR_NumResultCols(self->result);
+
+ /* now allocate the array to hold the binding info */
+ if (numcols > 0)
+ {
+ extend_bindings(self, numcols);
+ if (self->bindings == NULL)
+ {
+ self->errornumber = STMT_NO_MEMORY_ERROR;
+ self->errormsg = "Could not get enough free memory to store the binding information";
+ SC_log_error(func, "", self);
+ return SQL_ERROR;
+ }
+ }
+ /* issue "ABORT" when query aborted */
+ if (QR_get_aborted(self->result) && !self->internal)
+ CC_abort(conn);
+ }
+ else
+ {
+ /* Bad Error -- The error message will be in the Connection */
+ if (self->statement_type == STMT_TYPE_CREATE)
+ {
+ self->errornumber = STMT_CREATE_TABLE_ERROR;
+ self->errormsg = "Error creating the table";
+
+ /*
+ * This would allow the table to already exists, thus
+ * appending rows to it. BUT, if the table didn't have the
+ * same attributes, it would fail. return
+ * SQL_SUCCESS_WITH_INFO;
+ */
+ }
+ else
+ {
+ self->errornumber = STMT_EXEC_ERROR;
+ self->errormsg = "Error while executing the query";
+ }
+
+ if (!self->internal)
+ CC_abort(conn);
+ }
+
+ if (self->statement_type == STMT_TYPE_PROCCALL &&
+ (self->errornumber == STMT_OK ||
+ self->errornumber == STMT_INFO_ONLY) &&
+ self->parameters &&
+ self->parameters[0].buffer &&
+ self->parameters[0].paramType == SQL_PARAM_OUTPUT)
+ { /* get the return value of the procedure
+ * call */
+ RETCODE ret;
+ HSTMT hstmt = (HSTMT) self;
+
+ ret = SC_fetch(hstmt);
+ if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
+ {
+ ret = PGAPI_GetData(hstmt, 1, self->parameters[0].CType, self->parameters[0].buffer, self->parameters[0].buflen, self->parameters[0].used);
+ if (ret != SQL_SUCCESS)
+ {
+ self->errornumber = STMT_EXEC_ERROR;
+ self->errormsg = "GetData to Procedure return failed.";
+ }
+ }
+ else
+ {
+ self->errornumber = STMT_EXEC_ERROR;
+ self->errormsg = "SC_fetch to get a Procedure return failed.";
+ }
+ }
+ if (self->errornumber == STMT_OK)
+ return SQL_SUCCESS;
+ else if (self->errornumber == STMT_INFO_ONLY)
+ return SQL_SUCCESS_WITH_INFO;
+ else
+ {
+ self->errormsg = "Error while executing the query";
+ SC_log_error(func, "", self);
+ return SQL_ERROR;
+ }
+}
+
+
+void
+SC_log_error(char *func, char *desc, StatementClass *self)
+{
+#ifdef PRN_NULLCHECK
+#define nullcheck(a) (a ? a : "(NULL)")
+#endif
+ if (self)
+ {
+ qlog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, nullcheck(self->errormsg));
+ mylog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, nullcheck(self->errormsg));
+ qlog(" ------------------------------------------------------------\n");
+ qlog(" hdbc=%u, stmt=%u, result=%u\n", self->hdbc, self, self->result);
+ qlog(" manual_result=%d, prepare=%d, internal=%d\n", self->manual_result, self->prepare, self->internal);
+ qlog(" bindings=%u, bindings_allocated=%d\n", self->bindings, self->bindings_allocated);
+ qlog(" parameters=%u, parameters_allocated=%d\n", self->parameters, self->parameters_allocated);
+ qlog(" statement_type=%d, statement='%s'\n", self->statement_type, nullcheck(self->statement));
+ qlog(" stmt_with_params='%s'\n", nullcheck(self->stmt_with_params));
+ qlog(" data_at_exec=%d, current_exec_param=%d, put_data=%d\n", self->data_at_exec, self->current_exec_param, self->put_data);
+ qlog(" currTuple=%d, current_col=%d, lobj_fd=%d\n", self->currTuple, self->current_col, self->lobj_fd);
+ qlog(" maxRows=%d, rowset_size=%d, keyset_size=%d, cursor_type=%d, scroll_concurrency=%d\n", self->options.maxRows, self->options.rowset_size, self->options.keyset_size, self->options.cursor_type, self->options.scroll_concurrency);
+ qlog(" cursor_name='%s'\n", nullcheck(self->cursor_name));
+
+ qlog(" ----------------QResult Info -------------------------------\n");
+
+ if (self->result)
+ {
+ QResultClass *res = self->result;
+
+ qlog(" fields=%u, manual_tuples=%u, backend_tuples=%u, tupleField=%d, conn=%u\n", res->fields, res->manual_tuples, res->backend_tuples, res->tupleField, res->conn);
+ qlog(" fetch_count=%d, fcount=%d, num_fields=%d, cursor='%s'\n", res->fetch_count, res->fcount, res->num_fields, nullcheck(res->cursor));
+ qlog(" message='%s', command='%s', notice='%s'\n", nullcheck(res->message), nullcheck(res->command), nullcheck(res->notice));
+ qlog(" status=%d, inTuples=%d\n", res->status, res->inTuples);
+ }
+
+ /* Log the connection error if there is one */
+ CC_log_error(func, desc, self->hdbc);
+ }
+ else
+ qlog("INVALID STATEMENT HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
+#undef PRN_NULLCHECK
+}
--- /dev/null
+/* File: statement.h
+ *
+ * Description: See "statement.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __STATEMENT_H__
+#define __STATEMENT_H__
+
+#include "psqlodbc.h"
+
+#include "bind.h"
+
+
+#ifndef FALSE
+#define FALSE (BOOL)0
+#endif
+#ifndef TRUE
+#define TRUE (BOOL)1
+#endif
+
+typedef enum
+{
+ STMT_ALLOCATED, /* The statement handle is allocated, but
+ * not used so far */
+ STMT_READY, /* the statement is waiting to be executed */
+ STMT_PREMATURE, /* ODBC states that it is legal to call
+ * e.g. SQLDescribeCol before a call to
+ * SQLExecute, but after SQLPrepare. To
+ * get all the necessary information in
+ * such a case, we simply execute the
+ * query _before_ the actual call to
+ * SQLExecute, so that statement is
+ * considered to be "premature". */
+ STMT_FINISHED, /* statement execution has finished */
+ STMT_EXECUTING /* statement execution is still going on */
+} STMT_Status;
+
+#define STMT_ROW_VERSION_CHANGED (-4)
+#define STMT_POS_BEFORE_RECORDSET (-3)
+#define STMT_TRUNCATED (-2)
+#define STMT_INFO_ONLY (-1) /* not an error message,
+ * just a notification
+ * to be returned by
+ * SQLError */
+#define STMT_OK 0 /* will be interpreted
+ * as "no error pending" */
+#define STMT_EXEC_ERROR 1
+#define STMT_STATUS_ERROR 2
+#define STMT_SEQUENCE_ERROR 3
+#define STMT_NO_MEMORY_ERROR 4
+#define STMT_COLNUM_ERROR 5
+#define STMT_NO_STMTSTRING 6
+#define STMT_ERROR_TAKEN_FROM_BACKEND 7
+#define STMT_INTERNAL_ERROR 8
+#define STMT_STILL_EXECUTING 9
+#define STMT_NOT_IMPLEMENTED_ERROR 10
+#define STMT_BAD_PARAMETER_NUMBER_ERROR 11
+#define STMT_OPTION_OUT_OF_RANGE_ERROR 12
+#define STMT_INVALID_COLUMN_NUMBER_ERROR 13
+#define STMT_RESTRICTED_DATA_TYPE_ERROR 14
+#define STMT_INVALID_CURSOR_STATE_ERROR 15
+#define STMT_OPTION_VALUE_CHANGED 16
+#define STMT_CREATE_TABLE_ERROR 17
+#define STMT_NO_CURSOR_NAME 18
+#define STMT_INVALID_CURSOR_NAME 19
+#define STMT_INVALID_ARGUMENT_NO 20
+#define STMT_ROW_OUT_OF_RANGE 21
+#define STMT_OPERATION_CANCELLED 22
+#define STMT_INVALID_CURSOR_POSITION 23
+#define STMT_VALUE_OUT_OF_RANGE 24
+#define STMT_OPERATION_INVALID 25
+#define STMT_PROGRAM_TYPE_OUT_OF_RANGE 26
+#define STMT_BAD_ERROR 27
+#define STMT_INVALID_OPTION_IDENTIFIER 28
+
+/* statement types */
+enum
+{
+ STMT_TYPE_UNKNOWN = -2,
+ STMT_TYPE_OTHER = -1,
+ STMT_TYPE_SELECT = 0,
+ STMT_TYPE_INSERT,
+ STMT_TYPE_UPDATE,
+ STMT_TYPE_DELETE,
+ STMT_TYPE_CREATE,
+ STMT_TYPE_ALTER,
+ STMT_TYPE_DROP,
+ STMT_TYPE_GRANT,
+ STMT_TYPE_REVOKE,
+ STMT_TYPE_PROCCALL
+};
+
+#define STMT_UPDATE(stmt) (stmt->statement_type > STMT_TYPE_SELECT)
+
+
+/* Parsing status */
+enum
+{
+ STMT_PARSE_NONE = 0,
+ STMT_PARSE_COMPLETE,
+ STMT_PARSE_INCOMPLETE,
+ STMT_PARSE_FATAL,
+};
+
+/* Result style */
+enum
+{
+ STMT_FETCH_NONE = 0,
+ STMT_FETCH_NORMAL,
+ STMT_FETCH_EXTENDED,
+};
+
+typedef struct
+{
+ COL_INFO *col_info; /* cached SQLColumns info for this table */
+ char name[MAX_TABLE_LEN + 1];
+ char alias[MAX_TABLE_LEN + 1];
+} TABLE_INFO;
+
+typedef struct
+{
+ TABLE_INFO *ti; /* resolve to explicit table names */
+ int precision;
+ int scale;
+ int display_size;
+ int length;
+ int type;
+ char nullable;
+ char func;
+ char expr;
+ char quote;
+ char dquote;
+ char numeric;
+ char dot[MAX_TABLE_LEN + 1];
+ char name[MAX_COLUMN_LEN + 1];
+ char alias[MAX_COLUMN_LEN + 1];
+} FIELD_INFO;
+
+
+/******** Statement Handle ***********/
+struct StatementClass_
+{
+ ConnectionClass *hdbc; /* pointer to ConnectionClass this
+ * statement belongs to */
+ QResultClass *result; /* result of the current statement */
+ HSTMT FAR *phstmt;
+ StatementOptions options;
+
+ STMT_Status status;
+ char *errormsg;
+ int errornumber;
+
+ /* information on bindings */
+ BindInfoClass *bindings; /* array to store the binding information */
+ BindInfoClass bookmark;
+ int bindings_allocated;
+
+ /* information on statement parameters */
+ int parameters_allocated;
+ ParameterInfoClass *parameters;
+
+ Int4 currTuple; /* current absolute row number (GetData,
+ * SetPos, SQLFetch) */
+ int save_rowset_size; /* saved rowset size in case of
+ * change/FETCH_NEXT */
+ int rowset_start; /* start of rowset (an absolute row
+ * number) */
+ int bind_row; /* current offset for Multiple row/column
+ * binding */
+ int last_fetch_count; /* number of rows retrieved in
+ * last fetch/extended fetch */
+ int current_col; /* current column for GetData -- used to
+ * handle multiple calls */
+ int lobj_fd; /* fd of the current large object */
+
+ char *statement; /* if non--null pointer to the SQL
+ * statement that has been executed */
+
+ TABLE_INFO **ti;
+ FIELD_INFO **fi;
+ int nfld;
+ int ntab;
+
+ int parse_status;
+
+ int statement_type; /* According to the defines above */
+ int data_at_exec; /* Number of params needing SQLPutData */
+ int current_exec_param; /* The current parameter for
+ * SQLPutData */
+
+ char put_data; /* Has SQLPutData been called yet? */
+
+ char errormsg_created; /* has an informative error msg
+ * been created? */
+ char manual_result; /* Is the statement result manually built? */
+ char prepare; /* is this statement a prepared statement
+ * or direct */
+
+ char internal; /* Is this statement being called
+ * internally? */
+
+ char cursor_name[MAX_CURSOR_LEN + 1];
+
+ char *stmt_with_params; /* statement after parameter
+ * substitution */
+ int stmt_size_limit;
+
+ char pre_executing; /* This statement is prematurely executing */
+ char inaccurate_result; /* Current status is PREMATURE but
+ * result is inaccurate */
+ char errormsg_malloced; /* Current error message is
+ * malloed (not in a static
+ * variable) ? */
+ char miscinfo;
+};
+
+#define SC_get_conn(a) (a->hdbc)
+#define SC_get_Result(a) (a->result);
+
+/* options for SC_free_params() */
+#define STMT_FREE_PARAMS_ALL 0
+#define STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY 1
+
+/* misc info */
+#define SC_set_pre_executable(a) (a->miscinfo |= 1L)
+#define SC_no_pre_executable(a) (a->miscinfo &= ~1L)
+#define SC_is_pre_executable(a) ((a->miscinfo & 1L) != 0)
+#define SC_set_fetchcursor(a) (a->miscinfo |= 2L)
+#define SC_no_fetchcursor(a) (a->miscinfo &= ~2L)
+#define SC_is_fetchcursor(a) ((a->miscinfo & 2L) != 0)
+
+/* Statement prototypes */
+StatementClass *SC_Constructor(void);
+void InitializeStatementOptions(StatementOptions *opt);
+char SC_Destructor(StatementClass *self);
+int statement_type(char *statement);
+char parse_statement(StatementClass *stmt);
+void SC_pre_execute(StatementClass *self);
+char SC_unbind_cols(StatementClass *self);
+char SC_recycle_statement(StatementClass *self);
+
+void SC_clear_error(StatementClass *self);
+char SC_get_error(StatementClass *self, int *number, char **message);
+char *SC_create_errormsg(StatementClass *self);
+RETCODE SC_execute(StatementClass *self);
+RETCODE SC_fetch(StatementClass *self);
+void SC_free_params(StatementClass *self, char option);
+void SC_log_error(char *func, char *desc, StatementClass *self);
+unsigned long SC_get_bookmark(StatementClass *self);
+
+#endif
--- /dev/null
+/*-------
+ * Module: tuple.c
+ *
+ * Description: This module contains functions for setting the data
+ * for individual fields (TupleField structure) of a
+ * manual result set.
+ *
+ * Important Note: These functions are ONLY used in building manual
+ * result sets for info functions (SQLTables,
+ * SQLColumns, etc.)
+ *
+ * Classes: n/a
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "tuple.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+
+void
+set_tuplefield_null(TupleField *tuple_field)
+{
+ tuple_field->len = 0;
+ tuple_field->value = NULL; /* strdup(""); */
+}
+
+
+void
+set_tuplefield_string(TupleField *tuple_field, char *string)
+{
+ tuple_field->len = strlen(string);
+ tuple_field->value = malloc(strlen(string) + 1);
+ strcpy(tuple_field->value, string);
+}
+
+
+void
+set_tuplefield_int2(TupleField *tuple_field, Int2 value)
+{
+ char buffer[10];
+
+ sprintf(buffer, "%d", value);
+
+ tuple_field->len = strlen(buffer) + 1;
+ /* +1 ... is this correct (better be on the save side-...) */
+ tuple_field->value = strdup(buffer);
+}
+
+
+void
+set_tuplefield_int4(TupleField *tuple_field, Int4 value)
+{
+ char buffer[15];
+
+ sprintf(buffer, "%ld", value);
+
+ tuple_field->len = strlen(buffer) + 1;
+ /* +1 ... is this correct (better be on the save side-...) */
+ tuple_field->value = strdup(buffer);
+}
--- /dev/null
+/* File: tuple.h
+ *
+ * Description: See "tuple.c"
+ *
+ * Important NOTE: The TupleField structure is used both to hold backend data and
+ * manual result set data. The "set_" functions and the TupleNode
+ * structure are only used for manual result sets by info routines.
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __TUPLE_H__
+#define __TUPLE_H__
+
+#include "psqlodbc.h"
+
+/* Used by backend data AND manual result sets */
+struct TupleField_
+{
+ Int4 len; /* length of the current Tuple */
+ void *value; /* an array representing the value */
+};
+
+/* Used ONLY for manual result sets */
+struct TupleNode_
+{
+ struct TupleNode_ *prev,
+ *next;
+ TupleField tuple[1];
+};
+
+/* These macros are wrappers for the corresponding set_tuplefield functions
+ but these handle automatic NULL determination and call set_tuplefield_null()
+ if appropriate for the datatype (used by SQLGetTypeInfo).
+*/
+#define set_nullfield_string(FLD, VAL) ((VAL) ? set_tuplefield_string(FLD, (VAL)) : set_tuplefield_null(FLD))
+#define set_nullfield_int2(FLD, VAL) ((VAL) != -1 ? set_tuplefield_int2(FLD, (VAL)) : set_tuplefield_null(FLD))
+#define set_nullfield_int4(FLD, VAL) ((VAL) != -1 ? set_tuplefield_int4(FLD, (VAL)) : set_tuplefield_null(FLD))
+
+void set_tuplefield_null(TupleField *tuple_field);
+void set_tuplefield_string(TupleField *tuple_field, char *string);
+void set_tuplefield_int2(TupleField *tuple_field, Int2 value);
+void set_tuplefield_int4(TupleField *tuple_field, Int4 value);
+
+#endif
--- /dev/null
+/*--------
+ * Module: tuplelist.c
+ *
+ * Description: This module contains functions for creating a manual
+ * result set (the TupleList) and retrieving data from
+ * it for a specific row/column.
+ *
+ * Classes: TupleListClass (Functions prefix: "TL_")
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *--------
+ */
+
+#include "tuplelist.h"
+
+#include <stdlib.h>
+#include "tuple.h"
+
+
+TupleListClass *
+TL_Constructor(UInt4 fieldcnt)
+{
+ TupleListClass *rv;
+
+ mylog("in TL_Constructor\n");
+
+ rv = (TupleListClass *) malloc(sizeof(TupleListClass));
+ if (rv)
+ {
+ rv->num_fields = fieldcnt;
+ rv->num_tuples = 0;
+ rv->list_start = NULL;
+ rv->list_end = NULL;
+ rv->lastref = NULL;
+ rv->last_indexed = -1;
+ }
+
+ mylog("exit TL_Constructor\n");
+
+ return rv;
+}
+
+
+void
+TL_Destructor(TupleListClass *self)
+{
+ int lf;
+ TupleNode *node,
+ *tp;
+
+ mylog("TupleList: in DESTRUCTOR\n");
+
+ node = self->list_start;
+ while (node != NULL)
+ {
+ for (lf = 0; lf < self->num_fields; lf++)
+ if (node->tuple[lf].value != NULL)
+ free(node->tuple[lf].value);
+ tp = node->next;
+ free(node);
+ node = tp;
+ }
+
+ free(self);
+
+ mylog("TupleList: exit DESTRUCTOR\n");
+}
+
+
+void *
+TL_get_fieldval(TupleListClass *self, Int4 tupleno, Int2 fieldno)
+{
+ Int4 lf;
+ Int4 delta,
+ from_end;
+ char end_is_closer,
+ start_is_closer;
+ TupleNode *rv;
+
+ if (self->last_indexed == -1)
+ /* we have an empty tuple list */
+ return NULL;
+
+ /* some more sanity checks */
+ if ((tupleno >= self->num_tuples) || (tupleno < 0))
+ /* illegal tuple number range */
+ return NULL;
+
+ if ((fieldno >= self->num_fields) || (fieldno < 0))
+ /* illegel field number range */
+ return NULL;
+
+ /*
+ * check if we are accessing the same tuple that was used in the last
+ * fetch (e.g: for fetching all the fields one after another. Do this
+ * to speed things up
+ */
+ if (tupleno == self->last_indexed)
+ return self->lastref->tuple[fieldno].value;
+
+ /* now for the tricky part... */
+
+ /*
+ * Since random access is quite inefficient for linked lists we use
+ * the lastref pointer that points to the last element referenced by a
+ * get_fieldval() call in conjunction with the its index number that
+ * is stored in last_indexed. (So we use some locality of reference
+ * principle to speed things up)
+ */
+
+ delta = tupleno - self->last_indexed;
+ /* if delta is positive, we have to go forward */
+
+ /*
+ * now check if we are closer to the start or the end of the list than
+ * to our last_indexed pointer
+ */
+ from_end = (self->num_tuples - 1) - tupleno;
+
+ start_is_closer = labs(delta) > tupleno;
+
+ /*
+ * true if we are closer to the start of the list than to the
+ * last_indexed pointer
+ */
+
+ end_is_closer = labs(delta) > from_end;
+ /* true if we are closer at the end of the list */
+
+ if (end_is_closer)
+ {
+ /* scanning from the end is the shortest way. so we do that... */
+ rv = self->list_end;
+ for (lf = 0; lf < from_end; lf++)
+ rv = rv->prev;
+ }
+ else if (start_is_closer)
+ {
+ /*
+ * the shortest way is to start the search from the head of the
+ * list
+ */
+ rv = self->list_start;
+ for (lf = 0; lf < tupleno; lf++)
+ rv = rv->next;
+ }
+ else
+ {
+ /* the closest way is starting from our lastref - pointer */
+ rv = self->lastref;
+
+ /*
+ * at first determine whether we have to search forward or
+ * backwards
+ */
+ if (delta < 0)
+ {
+ /* we have to search backwards */
+ for (lf = 0; lf < (-1) * delta; lf++)
+ rv = rv->prev;
+ }
+ else
+ {
+ /* ok, we have to search forward... */
+ for (lf = 0; lf < delta; lf++)
+ rv = rv->next;
+ }
+ }
+
+ /*
+ * now we have got our return pointer, so update the lastref and the
+ * last_indexed values
+ */
+ self->lastref = rv;
+ self->last_indexed = tupleno;
+
+ return rv->tuple[fieldno].value;
+}
+
+
+char
+TL_add_tuple(TupleListClass *self, TupleNode *new_field)
+{
+ /*
+ * we append the tuple at the end of the doubly linked list of the
+ * tuples we have already read in
+ */
+
+ new_field->prev = NULL;
+ new_field->next = NULL;
+
+ if (self->list_start == NULL)
+ {
+ /* the list is empty, we have to add the first tuple */
+ self->list_start = new_field;
+ self->list_end = new_field;
+ self->lastref = new_field;
+ self->last_indexed = 0;
+ }
+ else
+ {
+ /*
+ * there is already an element in the list, so add the new one at
+ * the end of the list
+ */
+ self->list_end->next = new_field;
+ new_field->prev = self->list_end;
+ self->list_end = new_field;
+ }
+ self->num_tuples++;
+
+ /* this method of building a list cannot fail, so we return 1 */
+ return 1;
+}
--- /dev/null
+/* File: tuplelist.h
+ *
+ * Description: See "tuplelist.c"
+ *
+ * Important Note: This structure and its functions are ONLY used in building manual result
+ * sets for info functions (SQLTables, SQLColumns, etc.)
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __TUPLELIST_H__
+#define __TUPLELIST_H__
+
+#include "psqlodbc.h"
+
+struct TupleListClass_
+{
+ Int4 num_fields;
+ Int4 num_tuples;
+ TupleNode *list_start,
+ *list_end,
+ *lastref;
+ Int4 last_indexed;
+};
+
+#define TL_get_num_tuples(x) (x->num_tuples)
+
+/* Create a TupleList. Each tuple consits of fieldcnt columns */
+TupleListClass *TL_Constructor(UInt4 fieldcnt);
+void TL_Destructor(TupleListClass *self);
+void *TL_get_fieldval(TupleListClass *self, Int4 tupleno, Int2 fieldno);
+char TL_add_tuple(TupleListClass *self, TupleNode *new_field);
+
+#endif
--- /dev/null
+
+#
+# File: win32.mak
+#
+# Description: psqlodbc Makefile for Win32.
+#
+# Configurations: Debug, Release, MultibyteDebug, MultibyteRelease
+# Build Types: ALL, CLEAN
+# Usage: NMAKE /f win32.mak CFG=[Release | Debug | MultibyteRelease | MultiByteDebug] [ALL | CLEAN]
+#
+# Comments: Created by Dave Page, 2001-02-12
+#
+
+!MESSAGE Building the PostgreSQL ODBC Driver for Win32...
+!MESSAGE
+!IF "$(CFG)" == ""
+CFG=Release
+!MESSAGE No configuration specified. Defaulting to Release.
+!MESSAGE
+!ENDIF
+
+!IF "$(CFG)" != "Release" && "$(CFG)" != "Debug" && "$(CFG)" != "MultibyteRelease" && "$(CFG)" != "MultibyteDebug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f win32.mak CFG=[Release | Debug | MultibyteRelease | MultiByteDebug] [ALL | CLEAN]
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Release" (Win32 Release DLL)
+!MESSAGE "Debug" (Win32 Debug DLL)
+!MESSAGE "MultibyteRelease" (Win32 Release DLL with Multibyte support)
+!MESSAGE "MultibyteDebug" (Win32 Release DLL with Multibyte support)
+!MESSAGE
+!ERROR An invalid configuration was specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "Release" || "$(CFG)" == "MultibyteRelease"
+
+!IF "$(CFG)" == "MultibyteRelease"
+OUTDIR=.\MultibyteRelease
+INTDIR=.\MultibyteRelease
+!ELSE
+OUTDIR=.\Release
+INTDIR=.\Release
+!ENDIF
+
+ALL : "$(OUTDIR)\psqlodbc.dll"
+
+
+CLEAN :
+ -@erase "$(INTDIR)\bind.obj"
+ -@erase "$(INTDIR)\columninfo.obj"
+ -@erase "$(INTDIR)\connection.obj"
+ -@erase "$(INTDIR)\convert.obj"
+ -@erase "$(INTDIR)\dlg_specific.obj"
+ -@erase "$(INTDIR)\drvconn.obj"
+ -@erase "$(INTDIR)\environ.obj"
+ -@erase "$(INTDIR)\execute.obj"
+ -@erase "$(INTDIR)\gpps.obj"
+ -@erase "$(INTDIR)\info.obj"
+ -@erase "$(INTDIR)\lobj.obj"
+ -@erase "$(INTDIR)\win_md5.obj"
+ -@erase "$(INTDIR)\misc.obj"
+!IF "$(CFG)" == "MultibyteRelease"
+ -@erase "$(INTDIR)\multibyte.obj"
+!ENDIF
+ -@erase "$(INTDIR)\options.obj"
+ -@erase "$(INTDIR)\parse.obj"
+ -@erase "$(INTDIR)\pgtypes.obj"
+ -@erase "$(INTDIR)\psqlodbc.obj"
+ -@erase "$(INTDIR)\psqlodbc.res"
+ -@erase "$(INTDIR)\qresult.obj"
+ -@erase "$(INTDIR)\results.obj"
+ -@erase "$(INTDIR)\setup.obj"
+ -@erase "$(INTDIR)\socket.obj"
+ -@erase "$(INTDIR)\statement.obj"
+ -@erase "$(INTDIR)\tuple.obj"
+ -@erase "$(INTDIR)\tuplelist.obj"
+ -@erase "$(INTDIR)\odbcapi.obj"
+ -@erase "$(INTDIR)\vc60.idb"
+ -@erase "$(OUTDIR)\psqlodbc.dll"
+ -@erase "$(OUTDIR)\psqlodbc.exp"
+ -@erase "$(OUTDIR)\psqlodbc.lib"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+!IF "$(CFG)" == "MultibyteRelease"
+CPP_PROJ=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "MULTIBYTE" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+!ELSE
+CPP_PROJ=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+!ENDIF
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "NDEBUG"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\psqlodbc.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /incremental:no /pdb:"$(OUTDIR)\psqlodbc.pdb" /machine:I386 /def:"psqlodbc_win32.def" /out:"$(OUTDIR)\psqlodbc.dll" /implib:"$(OUTDIR)\psqlodbc.lib"
+DEF_FILE= "psqlodbc_win32.def"
+LINK32_OBJS= \
+ "$(INTDIR)\bind.obj" \
+ "$(INTDIR)\columninfo.obj" \
+ "$(INTDIR)\connection.obj" \
+ "$(INTDIR)\convert.obj" \
+ "$(INTDIR)\dlg_specific.obj" \
+ "$(INTDIR)\drvconn.obj" \
+ "$(INTDIR)\environ.obj" \
+ "$(INTDIR)\execute.obj" \
+ "$(INTDIR)\gpps.obj" \
+ "$(INTDIR)\info.obj" \
+ "$(INTDIR)\lobj.obj" \
+ "$(INTDIR)\win_md5.obj" \
+ "$(INTDIR)\misc.obj" \
+!IF "$(CFG)" == "MultibyteRelease"
+ "$(INTDIR)\multibyte.obj" \
+!ENDIF
+ "$(INTDIR)\options.obj" \
+ "$(INTDIR)\parse.obj" \
+ "$(INTDIR)\pgtypes.obj" \
+ "$(INTDIR)\psqlodbc.obj" \
+ "$(INTDIR)\qresult.obj" \
+ "$(INTDIR)\results.obj" \
+ "$(INTDIR)\setup.obj" \
+ "$(INTDIR)\socket.obj" \
+ "$(INTDIR)\statement.obj" \
+ "$(INTDIR)\tuple.obj" \
+ "$(INTDIR)\tuplelist.obj" \
+ "$(INTDIR)\odbcapi.obj" \
+ "$(INTDIR)\psqlodbc.res"
+
+"$(OUTDIR)\psqlodbc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "Debug" || "$(CFG)" == "MultibyteDebug"
+
+!IF "$(CFG)" == "MultibyteDebug"
+OUTDIR=.\MultibyteDebug
+INTDIR=.\MultibyteDebug
+!ELSE
+OUTDIR=.\Debug
+INTDIR=.\Debug
+!ENDIF
+
+ALL : "$(OUTDIR)\psqlodbc.dll"
+
+
+CLEAN :
+ -@erase "$(INTDIR)\bind.obj"
+ -@erase "$(INTDIR)\columninfo.obj"
+ -@erase "$(INTDIR)\connection.obj"
+ -@erase "$(INTDIR)\convert.obj"
+ -@erase "$(INTDIR)\dlg_specific.obj"
+ -@erase "$(INTDIR)\drvconn.obj"
+ -@erase "$(INTDIR)\environ.obj"
+ -@erase "$(INTDIR)\execute.obj"
+ -@erase "$(INTDIR)\gpps.obj"
+ -@erase "$(INTDIR)\info.obj"
+ -@erase "$(INTDIR)\lobj.obj"
+ -@erase "$(INTDIR)\win_md5.obj"
+ -@erase "$(INTDIR)\misc.obj"
+!IF "$(CFG)" == "MultibyteDebug"
+ -@erase "$(INTDIR)\multibyte.obj"
+!ENDIF
+ -@erase "$(INTDIR)\options.obj"
+ -@erase "$(INTDIR)\parse.obj"
+ -@erase "$(INTDIR)\pgtypes.obj"
+ -@erase "$(INTDIR)\psqlodbc.obj"
+ -@erase "$(INTDIR)\psqlodbc.res"
+ -@erase "$(INTDIR)\qresult.obj"
+ -@erase "$(INTDIR)\results.obj"
+ -@erase "$(INTDIR)\setup.obj"
+ -@erase "$(INTDIR)\socket.obj"
+ -@erase "$(INTDIR)\statement.obj"
+ -@erase "$(INTDIR)\tuple.obj"
+ -@erase "$(INTDIR)\tuplelist.obj"
+ -@erase "$(INTDIR)\odbcapi.obj"
+ -@erase "$(INTDIR)\vc60.idb"
+ -@erase "$(INTDIR)\vc60.pdb"
+ -@erase "$(OUTDIR)\psqlodbc.dll"
+ -@erase "$(OUTDIR)\psqlodbc.exp"
+ -@erase "$(OUTDIR)\psqlodbc.ilk"
+ -@erase "$(OUTDIR)\psqlodbc.lib"
+ -@erase "$(OUTDIR)\psqlodbc.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+!IF "$(CFG)" == "MultibyteDebug"
+CPP_PROJ=/nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "MULTIBYTE" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+!ELSE
+CPP_PROJ=/nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+!ENDIF
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "_DEBUG"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\psqlodbc.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\psqlodbc.pdb" /debug /machine:I386 /def:"psqlodbc_win32.def" /out:"$(OUTDIR)\psqlodbc.dll" /implib:"$(OUTDIR)\psqlodbc.lib" /pdbtype:sept
+DEF_FILE= "psqlodbc_win32.def"
+LINK32_OBJS= \
+ "$(INTDIR)\bind.obj" \
+ "$(INTDIR)\columninfo.obj" \
+ "$(INTDIR)\connection.obj" \
+ "$(INTDIR)\convert.obj" \
+ "$(INTDIR)\dlg_specific.obj" \
+ "$(INTDIR)\drvconn.obj" \
+ "$(INTDIR)\environ.obj" \
+ "$(INTDIR)\execute.obj" \
+ "$(INTDIR)\gpps.obj" \
+ "$(INTDIR)\info.obj" \
+ "$(INTDIR)\lobj.obj" \
+ "$(INTDIR)\win_md5.obj"
+ "$(INTDIR)\misc.obj" \
+!IF "$(CFG)" == "MultibyteDebug"
+ "$(INTDIR)\multibyte.obj" \
+!ENDIF
+ "$(INTDIR)\options.obj" \
+ "$(INTDIR)\parse.obj" \
+ "$(INTDIR)\pgtypes.obj" \
+ "$(INTDIR)\psqlodbc.obj" \
+ "$(INTDIR)\qresult.obj" \
+ "$(INTDIR)\results.obj" \
+ "$(INTDIR)\setup.obj" \
+ "$(INTDIR)\socket.obj" \
+ "$(INTDIR)\statement.obj" \
+ "$(INTDIR)\tuple.obj" \
+ "$(INTDIR)\tuplelist.obj" \
+ "$(INTDIR)\odbcapi.obj" \
+ "$(INTDIR)\psqlodbc.res"
+
+"$(OUTDIR)\psqlodbc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+!IF "$(CFG)" == "Release" || "$(CFG)" == "Debug" || "$(CFG)" == "MultibyteRelease" || "$(CFG)" == "MultibyteDebug"
+
+SOURCE=bind.c
+
+"$(INTDIR)\bind.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=columninfo.c
+
+"$(INTDIR)\columninfo.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=connection.c
+
+"$(INTDIR)\connection.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=convert.c
+
+"$(INTDIR)\convert.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=dlg_specific.c
+
+"$(INTDIR)\dlg_specific.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=drvconn.c
+
+"$(INTDIR)\drvconn.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=environ.c
+
+"$(INTDIR)\environ.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=execute.c
+
+"$(INTDIR)\execute.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=gpps.c
+
+"$(INTDIR)\gpps.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=info.c
+
+"$(INTDIR)\info.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=lobj.c
+
+"$(INTDIR)\lobj.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=misc.c
+
+"$(INTDIR)\misc.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!IF "$(CFG)" == "MultibyteRelease" || "$(CFG)" == "MultibyteDebug"
+
+SOURCE=multibyte.c
+
+"$(INTDIR)\multibyte.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+!ENDIF
+
+
+SOURCE=options.c
+
+"$(INTDIR)\options.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=parse.c
+
+"$(INTDIR)\parse.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=pgtypes.c
+
+"$(INTDIR)\pgtypes.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=psqlodbc.c
+
+"$(INTDIR)\psqlodbc.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=psqlodbc.rc
+
+!IF "$(CFG)" == "Release"
+"$(INTDIR)\psqlodbc.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "NDEBUG" $(SOURCE)
+!ENDIF
+
+!IF "$(CFG)" == "MultibyteRelease"
+"$(INTDIR)\psqlodbc.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "NDEBUG" /d "MULTIBYTE" $(SOURCE)
+!ENDIF
+
+!IF "$(CFG)" == "Debug"
+"$(INTDIR)\psqlodbc.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "_DEBUG" $(SOURCE)
+!ENDIF
+
+!IF "$(CFG)" == "MultibyteDebug"
+"$(INTDIR)\psqlodbc.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "_DEBUG" /d "MULTIBYTE" $(SOURCE)
+!ENDIF
+
+
+SOURCE=qresult.c
+
+"$(INTDIR)\qresult.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=results.c
+
+"$(INTDIR)\results.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=setup.c
+
+"$(INTDIR)\setup.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=socket.c
+
+"$(INTDIR)\socket.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=statement.c
+
+"$(INTDIR)\statement.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=tuple.c
+
+"$(INTDIR)\tuple.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=tuplelist.c
+
+"$(INTDIR)\tuplelist.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=win_md5.c
+
+"$(INTDIR)\win_md5.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=odbcapi.c
+
+"$(INTDIR)\odbcapi.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+
+
+!ENDIF
--- /dev/null
+/*
+ * win_md5.c
+ * Under Windows I don't love the following /D in makefiles. - inoue
+ */
+#define MD5_ODBC
+#define FRONTEND
+
+/*
+ * md5.c is the exact copy of the src/backend/libpq/md5.c.
+ *
+ * psqlodbc driver stuff never refer(link) to other
+ * stuff directly.
+ *
+ */
+#include "md5.c"