1998-06-03 22:33:45 +02:00
|
|
|
|
|
|
|
/* Module: statement.c
|
|
|
|
*
|
|
|
|
* Description: This module contains functions related to creating
|
|
|
|
* and manipulating a statement.
|
|
|
|
*
|
|
|
|
* Classes: StatementClass (Functions prefix: "SC_")
|
|
|
|
*
|
|
|
|
* API functions: SQLAllocStmt, SQLFreeStmt
|
|
|
|
*
|
|
|
|
* Comments: See "notice.txt" for copyright and license information.
|
|
|
|
*
|
|
|
|
*/
|
1998-04-13 17:02:05 +02:00
|
|
|
|
1998-07-23 01:47:48 +02:00
|
|
|
#ifdef HAVE_CONFIG_H
|
1998-10-06 07:58:41 +02:00
|
|
|
#include "config.h"
|
1998-07-23 01:47:48 +02:00
|
|
|
#endif
|
|
|
|
|
1998-04-13 17:02:05 +02:00
|
|
|
#include "statement.h"
|
|
|
|
#include "bind.h"
|
|
|
|
#include "connection.h"
|
|
|
|
#include "qresult.h"
|
|
|
|
#include "convert.h"
|
|
|
|
#include "environ.h"
|
|
|
|
#include <stdio.h>
|
1998-07-23 01:47:48 +02:00
|
|
|
#include <string.h>
|
1998-04-13 17:02:05 +02:00
|
|
|
|
1998-10-06 07:58:41 +02:00
|
|
|
#ifndef WIN32
|
1998-07-23 01:47:48 +02:00
|
|
|
#include "iodbc.h"
|
|
|
|
#include "isql.h"
|
|
|
|
#else
|
1998-04-13 17:02:05 +02:00
|
|
|
#include <windows.h>
|
|
|
|
#include <sql.h>
|
1998-07-23 01:47:48 +02:00
|
|
|
#endif
|
1998-04-13 17:02:05 +02:00
|
|
|
|
1998-06-03 22:33:45 +02:00
|
|
|
extern GLOBAL_VALUES globals;
|
|
|
|
|
1998-10-06 07:58:41 +02:00
|
|
|
#ifndef WIN32
|
|
|
|
#ifndef HAVE_STRICMP
|
1998-07-23 01:47:48 +02:00
|
|
|
#define stricmp(s1,s2) strcasecmp(s1,s2)
|
|
|
|
#define strnicmp(s1,s2,n) strncasecmp(s1,s2,n)
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
1998-06-03 22:33:45 +02:00
|
|
|
/* Map sql commands to statement types */
|
|
|
|
static struct {
|
|
|
|
int type;
|
|
|
|
char *s;
|
|
|
|
} Statement_Type[] = {
|
|
|
|
{ STMT_TYPE_SELECT, "SELECT" },
|
|
|
|
{ STMT_TYPE_INSERT, "INSERT" },
|
|
|
|
{ STMT_TYPE_UPDATE, "UPDATE" },
|
|
|
|
{ STMT_TYPE_DELETE, "DELETE" },
|
|
|
|
{ STMT_TYPE_CREATE, "CREATE" },
|
|
|
|
{ STMT_TYPE_ALTER, "ALTER" },
|
|
|
|
{ STMT_TYPE_DROP, "DROP" },
|
|
|
|
{ STMT_TYPE_GRANT, "GRANT" },
|
|
|
|
{ STMT_TYPE_REVOKE, "REVOKE" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
1998-04-13 17:02:05 +02:00
|
|
|
|
|
|
|
RETCODE SQL_API SQLAllocStmt(HDBC hdbc,
|
|
|
|
HSTMT FAR *phstmt)
|
1998-07-23 01:47:48 +02:00
|
|
|
{
|
1998-10-06 07:58:41 +02:00
|
|
|
static char *func="SQLAllocStmt";
|
1998-04-13 17:02:05 +02:00
|
|
|
ConnectionClass *conn = (ConnectionClass *) hdbc;
|
|
|
|
StatementClass *stmt;
|
|
|
|
|
1998-10-06 07:58:41 +02:00
|
|
|
mylog("%s: entering...\n", func);
|
|
|
|
|
1998-06-16 23:29:19 +02:00
|
|
|
if( ! conn) {
|
|
|
|
CC_log_error(func, "", NULL);
|
1998-04-13 17:02:05 +02:00
|
|
|
return SQL_INVALID_HANDLE;
|
1998-06-16 23:29:19 +02:00
|
|
|
}
|
1998-04-13 17:02:05 +02:00
|
|
|
|
|
|
|
stmt = SC_Constructor();
|
|
|
|
|
|
|
|
mylog("**** SQLAllocStmt: hdbc = %u, stmt = %u\n", hdbc, stmt);
|
|
|
|
|
|
|
|
if ( ! stmt) {
|
|
|
|
conn->errornumber = CONN_STMT_ALLOC_ERROR;
|
|
|
|
conn->errormsg = "No more memory to allocate a further SQL-statement";
|
|
|
|
*phstmt = SQL_NULL_HSTMT;
|
1998-06-16 23:29:19 +02:00
|
|
|
CC_log_error(func, "", conn);
|
1998-04-13 17:02:05 +02:00
|
|
|
return SQL_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! CC_add_statement(conn, stmt)) {
|
|
|
|
conn->errormsg = "Maximum number of connections exceeded.";
|
|
|
|
conn->errornumber = CONN_STMT_ALLOC_ERROR;
|
1998-06-16 23:29:19 +02:00
|
|
|
CC_log_error(func, "", conn);
|
1998-04-13 17:02:05 +02:00
|
|
|
SC_Destructor(stmt);
|
|
|
|
*phstmt = SQL_NULL_HSTMT;
|
|
|
|
return SQL_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
*phstmt = (HSTMT) stmt;
|
|
|
|
|
|
|
|
return SQL_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-10-06 07:58:41 +02:00
|
|
|
RETCODE SQL_API SQLFreeStmt(HSTMT hstmt,
|
1998-04-13 17:02:05 +02:00
|
|
|
UWORD fOption)
|
|
|
|
{
|
1998-10-06 07:58:41 +02:00
|
|
|
static char *func="SQLFreeStmt";
|
1998-04-13 17:02:05 +02:00
|
|
|
StatementClass *stmt = (StatementClass *) hstmt;
|
|
|
|
|
1998-10-06 07:58:41 +02:00
|
|
|
mylog("%s: entering...hstmt=%u, fOption=%d\n", func, hstmt, fOption);
|
1998-04-13 17:02:05 +02:00
|
|
|
|
1998-06-16 23:29:19 +02:00
|
|
|
if ( ! stmt) {
|
|
|
|
SC_log_error(func, "", NULL);
|
1998-04-13 17:02:05 +02:00
|
|
|
return SQL_INVALID_HANDLE;
|
1998-06-16 23:29:19 +02:00
|
|
|
}
|
1998-04-13 17:02:05 +02:00
|
|
|
|
|
|
|
if (fOption == SQL_DROP) {
|
|
|
|
ConnectionClass *conn = stmt->hdbc;
|
|
|
|
|
|
|
|
/* Remove the statement from the connection's statement list */
|
|
|
|
if ( conn) {
|
|
|
|
if ( ! CC_remove_statement(conn, stmt)) {
|
|
|
|
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
|
|
|
stmt->errormsg = "Statement is currently executing a transaction.";
|
1998-06-16 23:29:19 +02:00
|
|
|
SC_log_error(func, "", stmt);
|
1998-04-13 17:02:05 +02:00
|
|
|
return SQL_ERROR; /* stmt may be executing a transaction */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free any cursors and discard any result info */
|
|
|
|
if (stmt->result) {
|
|
|
|
QR_Destructor(stmt->result);
|
|
|
|
stmt->result = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Destroy the statement and free any results, cursors, etc. */
|
|
|
|
SC_Destructor(stmt);
|
|
|
|
|
|
|
|
} else if (fOption == SQL_UNBIND) {
|
|
|
|
SC_unbind_cols(stmt);
|
|
|
|
|
|
|
|
} else if (fOption == SQL_CLOSE) {
|
|
|
|
/* this should discard all the results, but leave the statement */
|
|
|
|
/* itself in place (it can be executed again) */
|
1998-06-16 23:29:19 +02:00
|
|
|
if (!SC_recycle_statement(stmt)) {
|
1998-04-13 17:02:05 +02:00
|
|
|
// errormsg passed in above
|
1998-06-16 23:29:19 +02:00
|
|
|
SC_log_error(func, "", stmt);
|
1998-04-13 17:02:05 +02:00
|
|
|
return SQL_ERROR;
|
1998-06-16 23:29:19 +02:00
|
|
|
}
|
1998-04-13 17:02:05 +02:00
|
|
|
|
1998-06-03 22:33:45 +02:00
|
|
|
} else if(fOption == SQL_RESET_PARAMS) {
|
1998-04-13 17:02:05 +02:00
|
|
|
SC_free_params(stmt, STMT_FREE_PARAMS_ALL);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
stmt->errormsg = "Invalid option passed to SQLFreeStmt.";
|
|
|
|
stmt->errornumber = STMT_OPTION_OUT_OF_RANGE_ERROR;
|
1998-06-16 23:29:19 +02:00
|
|
|
SC_log_error(func, "", stmt);
|
1998-04-13 17:02:05 +02:00
|
|
|
return SQL_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SQL_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-07-23 01:47:48 +02:00
|
|
|
|
1998-04-13 17:02:05 +02:00
|
|
|
/**********************************************************************
|
|
|
|
* StatementClass implementation
|
|
|
|
*/
|
|
|
|
|
|
|
|
StatementClass *
|
1998-10-06 07:58:41 +02:00
|
|
|
SC_Constructor(void)
|
1998-04-13 17:02:05 +02:00
|
|
|
{
|
|
|
|
StatementClass *rv;
|
|
|
|
|
|
|
|
rv = (StatementClass *) malloc(sizeof(StatementClass));
|
|
|
|
if (rv) {
|
|
|
|
rv->hdbc = NULL; /* no connection associated yet */
|
|
|
|
rv->result = NULL;
|
|
|
|
rv->manual_result = FALSE;
|
|
|
|
rv->prepare = FALSE;
|
|
|
|
rv->status = STMT_ALLOCATED;
|
|
|
|
rv->maxRows = 0; // driver returns all rows
|
1998-06-03 22:33:45 +02:00
|
|
|
rv->rowset_size = 1;
|
1998-06-08 18:15:12 +02:00
|
|
|
rv->keyset_size = 0; // fully keyset driven is the default
|
1998-06-03 22:33:45 +02:00
|
|
|
rv->scroll_concurrency = SQL_CONCUR_READ_ONLY;
|
|
|
|
rv->cursor_type = SQL_CURSOR_FORWARD_ONLY;
|
1998-04-13 17:02:05 +02:00
|
|
|
rv->errormsg = NULL;
|
|
|
|
rv->errornumber = 0;
|
|
|
|
rv->errormsg_created = FALSE;
|
1998-06-03 22:33:45 +02:00
|
|
|
rv->statement = NULL;
|
|
|
|
rv->stmt_with_params[0] = '\0';
|
1998-04-13 17:02:05 +02:00
|
|
|
rv->statement_type = STMT_TYPE_UNKNOWN;
|
|
|
|
rv->bindings = NULL;
|
|
|
|
rv->bindings_allocated = 0;
|
|
|
|
rv->parameters_allocated = 0;
|
|
|
|
rv->parameters = 0;
|
|
|
|
rv->currTuple = -1;
|
1998-06-03 22:33:45 +02:00
|
|
|
rv->current_col = -1;
|
1998-04-13 17:02:05 +02:00
|
|
|
rv->result = 0;
|
1998-06-03 22:33:45 +02:00
|
|
|
rv->data_at_exec = -1;
|
|
|
|
rv->current_exec_param = -1;
|
|
|
|
rv->put_data = FALSE;
|
|
|
|
rv->lobj_fd = -1;
|
|
|
|
rv->internal = FALSE;
|
|
|
|
rv->cursor_name[0] = '\0';
|
1998-07-23 01:47:48 +02:00
|
|
|
|
|
|
|
rv->ti = NULL;
|
|
|
|
rv->fi = NULL;
|
|
|
|
rv->ntab = 0;
|
|
|
|
rv->nfld = 0;
|
|
|
|
rv->parse_status = STMT_PARSE_NONE;
|
1998-04-13 17:02:05 +02:00
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
char
|
|
|
|
SC_Destructor(StatementClass *self)
|
|
|
|
{
|
|
|
|
|
|
|
|
mylog("SC_Destructor: self=%u, self->result=%u, self->hdbc=%u\n", self, self->result, self->hdbc);
|
|
|
|
if (STMT_EXECUTING == self->status) {
|
|
|
|
self->errornumber = STMT_SEQUENCE_ERROR;
|
|
|
|
self->errormsg = "Statement is currently executing a transaction.";
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->result) {
|
|
|
|
if ( ! self->hdbc)
|
|
|
|
self->result->conn = NULL; /* prevent any dbase activity */
|
|
|
|
|
|
|
|
QR_Destructor(self->result);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->statement)
|
|
|
|
free(self->statement);
|
1998-06-03 22:33:45 +02:00
|
|
|
|
|
|
|
SC_free_params(self, STMT_FREE_PARAMS_ALL);
|
|
|
|
|
1998-04-13 17:02:05 +02:00
|
|
|
/* the memory pointed to by the bindings is not deallocated by the driver */
|
|
|
|
/* by by the application that uses that driver, so we don't have to care */
|
|
|
|
/* about that here. */
|
|
|
|
if (self->bindings)
|
|
|
|
free(self->bindings);
|
|
|
|
|
1998-07-23 01:47:48 +02:00
|
|
|
|
|
|
|
/* Free the parsed table information */
|
|
|
|
if (self->ti) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < self->ntab; i++) {
|
|
|
|
free(self->ti[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(self->ti);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free the parsed field information */
|
|
|
|
if (self->fi) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < self->nfld; i++) {
|
|
|
|
free(self->fi[i]);
|
|
|
|
}
|
|
|
|
free(self->fi);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-04-13 17:02:05 +02:00
|
|
|
free(self);
|
|
|
|
|
1998-06-03 22:33:45 +02:00
|
|
|
mylog("SC_Destructor: EXIT\n");
|
|
|
|
|
1998-04-13 17:02:05 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
1998-06-03 22:33:45 +02:00
|
|
|
/* Free parameters and free the memory from the
|
|
|
|
data-at-execution parameters that was allocated in SQLPutData.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SC_free_params(StatementClass *self, char option)
|
1998-04-13 17:02:05 +02:00
|
|
|
{
|
1998-06-03 22:33:45 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
mylog("SC_free_params: ENTER, self=%d\n", self);
|
|
|
|
|
|
|
|
if( ! self->parameters)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < self->parameters_allocated; i++) {
|
|
|
|
if (self->parameters[i].data_at_exec == TRUE) {
|
|
|
|
|
|
|
|
if (self->parameters[i].EXEC_used) {
|
|
|
|
free(self->parameters[i].EXEC_used);
|
|
|
|
self->parameters[i].EXEC_used = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->parameters[i].EXEC_buffer) {
|
|
|
|
free(self->parameters[i].EXEC_buffer);
|
|
|
|
self->parameters[i].EXEC_buffer = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self->data_at_exec = -1;
|
|
|
|
self->current_exec_param = -1;
|
|
|
|
self->put_data = FALSE;
|
|
|
|
|
|
|
|
if (option == STMT_FREE_PARAMS_ALL) {
|
|
|
|
free(self->parameters);
|
|
|
|
self->parameters = NULL;
|
|
|
|
self->parameters_allocated = 0;
|
|
|
|
}
|
1998-04-13 17:02:05 +02:00
|
|
|
|
1998-06-03 22:33:45 +02:00
|
|
|
mylog("SC_free_params: EXIT\n");
|
|
|
|
}
|
1998-04-13 17:02:05 +02:00
|
|
|
|
|
|
|
|
1998-06-03 22:33:45 +02:00
|
|
|
int
|
|
|
|
statement_type(char *statement)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; Statement_Type[i].s; i++)
|
|
|
|
if ( ! strnicmp(statement, Statement_Type[i].s, strlen(Statement_Type[i].s)))
|
|
|
|
return Statement_Type[i].type;
|
|
|
|
|
|
|
|
return STMT_TYPE_OTHER;
|
1998-04-13 17:02:05 +02:00
|
|
|
}
|
|
|
|
|
1998-07-23 01:47:48 +02:00
|
|
|
|
1998-04-13 17:02:05 +02:00
|
|
|
/* Called from SQLPrepare if STMT_PREMATURE, or
|
|
|
|
from SQLExecute if STMT_FINISHED, or
|
|
|
|
from SQLFreeStmt(SQL_CLOSE)
|
|
|
|
*/
|
|
|
|
char
|
|
|
|
SC_recycle_statement(StatementClass *self)
|
|
|
|
{
|
|
|
|
ConnectionClass *conn;
|
|
|
|
|
1998-10-06 07:58:41 +02:00
|
|
|
mylog("recycle statement: self= %u\n", self);
|
|
|
|
|
1998-04-13 17:02:05 +02:00
|
|
|
/* This would not happen */
|
|
|
|
if (self->status == STMT_EXECUTING) {
|
|
|
|
self->errornumber = STMT_SEQUENCE_ERROR;
|
|
|
|
self->errormsg = "Statement is currently executing a transaction.";
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
self->errormsg = NULL;
|
|
|
|
self->errornumber = 0;
|
|
|
|
self->errormsg_created = FALSE;
|
|
|
|
|
|
|
|
switch (self->status) {
|
|
|
|
case STMT_ALLOCATED:
|
|
|
|
/* this statement does not need to be recycled */
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
case STMT_READY:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STMT_PREMATURE:
|
|
|
|
/* Premature execution of the statement might have caused the start of a transaction.
|
|
|
|
If so, we have to rollback that transaction.
|
|
|
|
*/
|
|
|
|
conn = SC_get_conn(self);
|
|
|
|
if ( ! CC_is_in_autocommit(conn) && CC_is_in_trans(conn)) {
|
|
|
|
|
|
|
|
CC_send_query(conn, "ABORT", NULL, NULL);
|
|
|
|
CC_set_no_trans(conn);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STMT_FINISHED:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
self->errormsg = "An internal error occured while recycling statements";
|
|
|
|
self->errornumber = STMT_INTERNAL_ERROR;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
1998-07-23 01:47:48 +02:00
|
|
|
/* Free the parsed table information */
|
|
|
|
if (self->ti) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < self->ntab; i++) {
|
|
|
|
free(self->ti[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(self->ti);
|
|
|
|
self->ti = NULL;
|
|
|
|
self->ntab = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free the parsed field information */
|
|
|
|
if (self->fi) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < self->nfld; i++) {
|
|
|
|
free(self->fi[i]);
|
|
|
|
}
|
|
|
|
free(self->fi);
|
|
|
|
self->fi = NULL;
|
|
|
|
self->nfld = 0;
|
|
|
|
}
|
|
|
|
self->parse_status = STMT_PARSE_NONE;
|
1998-04-13 17:02:05 +02:00
|
|
|
|
|
|
|
/* Free any cursors */
|
|
|
|
if (self->result) {
|
|
|
|
QR_Destructor(self->result);
|
|
|
|
self->result = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
self->status = STMT_READY;
|
1998-06-03 22:33:45 +02:00
|
|
|
self->manual_result = FALSE; // very important
|
|
|
|
|
1998-04-13 17:02:05 +02:00
|
|
|
self->currTuple = -1;
|
1998-06-03 22:33:45 +02:00
|
|
|
self->current_col = -1;
|
1998-04-13 17:02:05 +02:00
|
|
|
|
|
|
|
self->errormsg = NULL;
|
|
|
|
self->errornumber = 0;
|
|
|
|
self->errormsg_created = FALSE;
|
1998-06-03 22:33:45 +02:00
|
|
|
|
|
|
|
self->lobj_fd = -1;
|
|
|
|
|
|
|
|
// Free any data at exec params before the statement is executed
|
|
|
|
// again. If not, then there will be a memory leak when
|
|
|
|
// the next SQLParamData/SQLPutData is called.
|
|
|
|
SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
|
1998-04-13 17:02:05 +02:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pre-execute a statement (SQLPrepare/SQLDescribeCol) */
|
|
|
|
void
|
|
|
|
SC_pre_execute(StatementClass *self)
|
|
|
|
{
|
1998-07-23 01:47:48 +02:00
|
|
|
|
1998-04-13 17:02:05 +02:00
|
|
|
mylog("SC_pre_execute: status = %d\n", self->status);
|
|
|
|
|
|
|
|
if (self->status == STMT_READY) {
|
|
|
|
mylog(" preprocess: status = READY\n");
|
|
|
|
|
|
|
|
SQLExecute(self);
|
|
|
|
|
|
|
|
if (self->status == STMT_FINISHED) {
|
|
|
|
mylog(" preprocess: after status = FINISHED, so set PREMATURE\n");
|
|
|
|
self->status = STMT_PREMATURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is only called from SQLFreeStmt(SQL_UNBIND) */
|
|
|
|
char
|
|
|
|
SC_unbind_cols(StatementClass *self)
|
|
|
|
{
|
|
|
|
Int2 lf;
|
|
|
|
|
|
|
|
for(lf = 0; lf < self->bindings_allocated; lf++) {
|
|
|
|
self->bindings[lf].buflen = 0;
|
|
|
|
self->bindings[lf].buffer = NULL;
|
|
|
|
self->bindings[lf].used = NULL;
|
|
|
|
self->bindings[lf].returntype = SQL_C_CHAR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SC_clear_error(StatementClass *self)
|
|
|
|
{
|
|
|
|
self->errornumber = 0;
|
|
|
|
self->errormsg = NULL;
|
|
|
|
self->errormsg_created = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This function creates an error msg which is the concatenation
|
|
|
|
// of the result, statement, connection, and socket messages.
|
|
|
|
char *
|
|
|
|
SC_create_errormsg(StatementClass *self)
|
|
|
|
{
|
|
|
|
QResultClass *res = self->result;
|
|
|
|
ConnectionClass *conn = self->hdbc;
|
|
|
|
int pos;
|
|
|
|
static char msg[4096];
|
|
|
|
|
|
|
|
msg[0] = '\0';
|
|
|
|
|
|
|
|
if (res && res->message)
|
|
|
|
strcpy(msg, res->message);
|
|
|
|
|
|
|
|
else if (self->errormsg)
|
|
|
|
strcpy(msg, self->errormsg);
|
|
|
|
|
|
|
|
if (conn) {
|
|
|
|
SocketClass *sock = conn->sock;
|
|
|
|
|
|
|
|
if (conn->errormsg && conn->errormsg[0] != '\0') {
|
|
|
|
pos = strlen(msg);
|
|
|
|
sprintf(&msg[pos], ";\n%s", conn->errormsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sock && sock->errormsg && sock->errormsg[0] != '\0') {
|
|
|
|
pos = strlen(msg);
|
|
|
|
sprintf(&msg[pos], ";\n%s", sock->errormsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
char
|
|
|
|
SC_get_error(StatementClass *self, int *number, char **message)
|
|
|
|
{
|
|
|
|
char rv;
|
|
|
|
|
|
|
|
// Create a very informative errormsg if it hasn't been done yet.
|
|
|
|
if ( ! self->errormsg_created) {
|
|
|
|
self->errormsg = SC_create_errormsg(self);
|
|
|
|
self->errormsg_created = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( self->errornumber) {
|
|
|
|
*number = self->errornumber;
|
|
|
|
*message = self->errormsg;
|
|
|
|
self->errormsg = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = (self->errornumber != 0);
|
|
|
|
self->errornumber = 0;
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
1998-06-03 22:33:45 +02:00
|
|
|
RETCODE SC_execute(StatementClass *self)
|
|
|
|
{
|
1998-10-06 07:58:41 +02:00
|
|
|
static char *func="SC_execute";
|
1998-06-03 22:33:45 +02:00
|
|
|
ConnectionClass *conn;
|
|
|
|
QResultClass *res;
|
|
|
|
char ok, was_ok, was_nonfatal;
|
|
|
|
Int2 oldstatus, numcols;
|
|
|
|
|
|
|
|
|
|
|
|
conn = SC_get_conn(self);
|
|
|
|
|
|
|
|
/* Begin a transaction if one is not already in progress */
|
|
|
|
/* The reason is because we can't use declare/fetch cursors without
|
|
|
|
starting a transaction first.
|
|
|
|
*/
|
1998-10-06 07:58:41 +02:00
|
|
|
if ( ! self->internal && ! CC_is_in_trans(conn) && (globals.use_declarefetch || STMT_UPDATE(self))) {
|
1998-06-03 22:33:45 +02:00
|
|
|
|
|
|
|
mylog(" about to begin a transaction on statement = %u\n", self);
|
|
|
|
res = CC_send_query(conn, "BEGIN", NULL, NULL);
|
|
|
|
if ( ! res) {
|
|
|
|
self->errormsg = "Could not begin a transaction";
|
|
|
|
self->errornumber = STMT_EXEC_ERROR;
|
1998-06-16 23:29:19 +02:00
|
|
|
SC_log_error(func, "", self);
|
1998-06-03 22:33:45 +02:00
|
|
|
return SQL_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
ok = QR_command_successful(res);
|
|
|
|
|
|
|
|
mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res));
|
|
|
|
|
|
|
|
QR_Destructor(res);
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
self->errormsg = "Could not begin a transaction";
|
|
|
|
self->errornumber = STMT_EXEC_ERROR;
|
1998-06-16 23:29:19 +02:00
|
|
|
SC_log_error(func, "", self);
|
1998-06-03 22:33:45 +02:00
|
|
|
return SQL_ERROR;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
CC_set_in_trans(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
oldstatus = conn->status;
|
|
|
|
conn->status = CONN_EXECUTING;
|
|
|
|
self->status = STMT_EXECUTING;
|
|
|
|
|
|
|
|
|
|
|
|
// If its a SELECT statement, use a cursor.
|
|
|
|
// Note that the declare cursor has already been prepended to the statement
|
|
|
|
// in copy_statement...
|
|
|
|
if (self->statement_type == STMT_TYPE_SELECT) {
|
|
|
|
|
|
|
|
char fetch[128];
|
|
|
|
|
|
|
|
mylog(" Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name);
|
|
|
|
|
1998-07-23 01:47:48 +02:00
|
|
|
|
1998-06-03 22:33:45 +02:00
|
|
|
/* send the declare/select */
|
|
|
|
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
|
|
|
|
|
|
|
|
if (globals.use_declarefetch && self->result != NULL) {
|
|
|
|
/* That worked, so now send the fetch to start getting data back */
|
|
|
|
sprintf(fetch, "fetch %d in %s", globals.fetch_max, self->cursor_name);
|
|
|
|
|
|
|
|
// Save the cursor in the result for later use
|
|
|
|
self->result = CC_send_query( conn, fetch, NULL, self->cursor_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
mylog(" done sending the query:\n");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
else { // not a SELECT statement so don't use a cursor
|
|
|
|
mylog(" its NOT a select statement: stmt=%u\n", self);
|
|
|
|
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
|
|
|
|
|
|
|
|
// If we are in autocommit, we must send the commit.
|
1998-10-06 07:58:41 +02:00
|
|
|
if ( ! self->internal && CC_is_in_autocommit(conn) && STMT_UPDATE(self)) {
|
1998-06-03 22:33:45 +02:00
|
|
|
CC_send_query(conn, "COMMIT", NULL, NULL);
|
|
|
|
CC_set_no_trans(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
conn->status = oldstatus;
|
|
|
|
self->status = STMT_FINISHED;
|
|
|
|
|
|
|
|
/* Check the status of the result */
|
|
|
|
if (self->result) {
|
|
|
|
|
|
|
|
was_ok = QR_command_successful(self->result);
|
|
|
|
was_nonfatal = QR_command_nonfatal(self->result);
|
|
|
|
|
|
|
|
if ( was_ok)
|
|
|
|
self->errornumber = STMT_OK;
|
|
|
|
else
|
|
|
|
self->errornumber = was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND;
|
|
|
|
|
|
|
|
self->currTuple = -1; /* set cursor before the first tuple in the list */
|
|
|
|
self->current_col = -1;
|
|
|
|
|
|
|
|
/* see if the query did return any result columns */
|
|
|
|
numcols = QR_NumResultCols(self->result);
|
|
|
|
|
|
|
|
/* now allocate the array to hold the binding info */
|
|
|
|
if (numcols > 0) {
|
|
|
|
extend_bindings(self, numcols);
|
|
|
|
if (self->bindings == NULL) {
|
|
|
|
self->errornumber = STMT_NO_MEMORY_ERROR;
|
|
|
|
self->errormsg = "Could not get enough free memory to store the binding information";
|
1998-06-16 23:29:19 +02:00
|
|
|
SC_log_error(func, "", self);
|
1998-06-03 22:33:45 +02:00
|
|
|
return SQL_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else { /* Bad Error -- The error message will be in the Connection */
|
|
|
|
|
|
|
|
if (self->statement_type == STMT_TYPE_CREATE) {
|
|
|
|
self->errornumber = STMT_CREATE_TABLE_ERROR;
|
|
|
|
self->errormsg = "Error creating the table";
|
|
|
|
/* This would allow the table to already exists, thus appending
|
|
|
|
rows to it. BUT, if the table didn't have the same attributes,
|
|
|
|
it would fail.
|
|
|
|
return SQL_SUCCESS_WITH_INFO;
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
self->errornumber = STMT_EXEC_ERROR;
|
|
|
|
self->errormsg = "Error while executing the query";
|
|
|
|
}
|
1998-10-06 07:58:41 +02:00
|
|
|
|
|
|
|
if ( ! self->internal)
|
|
|
|
CC_abort(conn);
|
1998-06-03 22:33:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (self->errornumber == STMT_OK)
|
|
|
|
return SQL_SUCCESS;
|
|
|
|
|
|
|
|
else if (self->errornumber == STMT_INFO_ONLY)
|
|
|
|
return SQL_SUCCESS_WITH_INFO;
|
|
|
|
|
1998-06-16 23:29:19 +02:00
|
|
|
else {
|
|
|
|
SC_log_error(func, "", self);
|
1998-06-03 22:33:45 +02:00
|
|
|
return SQL_ERROR;
|
1998-06-16 23:29:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SC_log_error(char *func, char *desc, StatementClass *self)
|
|
|
|
{
|
|
|
|
if (self) {
|
|
|
|
qlog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, self->errormsg);
|
1998-10-06 07:58:41 +02:00
|
|
|
mylog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, self->errormsg);
|
1998-06-16 23:29:19 +02:00
|
|
|
qlog(" ------------------------------------------------------------\n");
|
|
|
|
qlog(" hdbc=%u, stmt=%u, result=%u\n", self->hdbc, self, self->result);
|
|
|
|
qlog(" manual_result=%d, prepare=%d, internal=%d\n", self->manual_result, self->prepare, self->internal);
|
|
|
|
qlog(" bindings=%u, bindings_allocated=%d\n", self->bindings, self->bindings_allocated);
|
|
|
|
qlog(" parameters=%u, parameters_allocated=%d\n", self->parameters, self->parameters_allocated);
|
|
|
|
qlog(" statement_type=%d, statement='%s'\n", self->statement_type, self->statement);
|
|
|
|
qlog(" stmt_with_params='%s'\n", self->stmt_with_params);
|
|
|
|
qlog(" data_at_exec=%d, current_exec_param=%d, put_data=%d\n", self->data_at_exec, self->current_exec_param, self->put_data);
|
|
|
|
qlog(" currTuple=%d, current_col=%d, lobj_fd=%d\n", self->currTuple, self->current_col, self->lobj_fd);
|
|
|
|
qlog(" maxRows=%d, rowset_size=%d, keyset_size=%d, cursor_type=%d, scroll_concurrency=%d\n", self->maxRows, self->rowset_size, self->keyset_size, self->cursor_type, self->scroll_concurrency);
|
|
|
|
qlog(" cursor_name='%s'\n", self->cursor_name);
|
|
|
|
|
|
|
|
qlog(" ----------------QResult Info -------------------------------\n");
|
|
|
|
|
|
|
|
if (self->result) {
|
|
|
|
QResultClass *res = self->result;
|
|
|
|
qlog(" fields=%u, manual_tuples=%u, backend_tuples=%u, tupleField=%d, conn=%u\n", res->fields, res->manual_tuples, res->backend_tuples, res->tupleField, res->conn);
|
|
|
|
qlog(" fetch_count=%d, fcount=%d, num_fields=%d, cursor='%s'\n", res->fetch_count, res->fcount, res->num_fields, res->cursor);
|
|
|
|
qlog(" message='%s', command='%s', notice='%s'\n", res->message, res->command, res->notice);
|
|
|
|
qlog(" status=%d, inTuples=%d\n", res->status, res->inTuples);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Log the connection error if there is one
|
|
|
|
CC_log_error(func, desc, self->hdbc);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
qlog("INVALID STATEMENT HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
|
1998-06-03 22:33:45 +02:00
|
|
|
}
|
1998-06-16 23:29:19 +02:00
|
|
|
|