+++ /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.
-
+++ /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;
- &nb