libdatabase/lib/libdatabase.c
2006-02-28 23:28:21 +00:00

972 lines
42 KiB
C
Raw Blame History

/*---------------------------------------------------------------------------------*/
/* $RCSfile: libdatabase.c,v $ */
/*---------------------------------------------------------------------------------*/
/* $Revision: 1.1 $ */
/* $Name: $ */
/* $Date: 2006/02/28 23:28:21 $ */
/* $Author: agibert $ */
/*---------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------*/
/* This file is part of LibDataBase */
/* */
/* LibDataBase is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU Lesser General Public Licence as published by */
/* the Free Software Foundation; either version 2.1 of the License, or */
/* (at your option) any later version. */
/* */
/* LibDataBase is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Lesser General Public License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with LibDataBase; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*---------------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
extern char *strdup (const char *);
#include <bdm.h>
#include "database.h"
BDM_VERSION_EXPORT(libdatabase, "$Revision: 1.1 $", "$Name: $", __FILE__, "$Author: agibert $")
/* fonctions locales */
static DBT_Status DB__Disconnect(DBT_Connection *connection, int reportErrors);
static void DB__Error(OCIError *errhp, char *buffer, int length, dword *errorCode, sword status);
static void DB__Error_Connection(DBT_Connection *connection, sword status);
static void DB__Error_Statement(DBT_Statement *statement, sword status);
static DBT_Status DB__CheckFetch(DBT_Statement *statement, sword OCIrc, DBT_Result *result);
static int initialized = 0;
/*---------------------------------------------------------------------------------*/
/* Initializes library and OCI environment. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Library_Open(int flags) {
/* flags non utilis<69> */
if (flags) BDM_Trace(1, "libdatabase", "DB_Library_Open", "Le param<61>tre flags (%d) est ignor<6F> car il est obsol<6F>te", flags);
if (!initialized) {
sword OCIrc = OCIInitialize(OCI_OBJECT | OCI_THREADED, NULL, NULL, NULL, NULL);
if (OCIrc != OCI_SUCCESS) {
BDM_Trace(0, "libdatabase", "DB_Library_Open", "Echec d'initialisation du process OCI");
return DBS_ERROCI;
}
initialized = 1;
}
initialized++;
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Closes library. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Library_Close(void) {
initialized--;
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Connects to an Oracle database. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Connect(DBT_Connection *connection, const char *Login, const char *Password, const char *Database) {
sword OCIrc;
char *buf, *ptr;
memset(connection, 0, sizeof(DBT_Connection));
ptr = buf = strdup(Password);
while (*ptr) *(ptr++) = '*';
BDM_Trace(3, "libdatabase", "DB_Connect", "Connexion <20> la base %s/%s%s%s...\n", Login, buf, Database ? "@" : "", Database ? Database : "");
free(buf);
if (!initialized) {
BDM_Trace(0, "libdatabase", "DB_Connect", "La librairie n'a pas <20>t<EFBFBD> initialis<69>e");
return DBS_ERRAPI;
}
/* Initialisation de l'environnement OCI */
OCIrc = OCIEnvInit(&connection->envhp, OCI_DEFAULT, 0, NULL);
if (OCIrc != OCI_SUCCESS) {
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec de l'initialisation du process OCI");
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
/* Allocation d'un handle de contexte */
OCIrc = OCIHandleAlloc(connection->envhp, (void **)&connection->svchp, OCI_HTYPE_SVCCTX, 0, NULL);
if (OCIrc != OCI_SUCCESS) {
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec de l'allocation du handle de contexte");
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
/* Allocation d'un handle d'erreur */
OCIrc = OCIHandleAlloc(connection->envhp, (void **)&connection->errhp, OCI_HTYPE_ERROR, 0, NULL);
if (OCIrc != OCI_SUCCESS) {
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec de l'allocation du handle d'erreur");
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
/* Allocation d'un handle de serveur */
OCIrc = OCIHandleAlloc(connection->envhp, (void **)&connection->srvhp, OCI_HTYPE_SERVER, 0, NULL);
if (OCIrc != OCI_SUCCESS) {
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec de l'allocation du handle de serveur");
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
/* Allocation d'un handle de session */
OCIrc = OCIHandleAlloc(connection->envhp, (void **)&connection->authp, OCI_HTYPE_SESSION, 0, NULL);
if (OCIrc != OCI_SUCCESS) {
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec de l'allocation du handle de session");
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
/* Allocation d'un handle de transaction */
OCIrc = OCIHandleAlloc(connection->envhp, (void **)&connection->transp, OCI_HTYPE_TRANS, 0, NULL);
if (OCIrc != OCI_SUCCESS) {
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec de l'allocation du handle de transaction");
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
connection->handlesAreValid = 1;
/* log in */
OCIrc = OCIAttrSet(connection->authp, OCI_HTYPE_SESSION, (char *)(int)Login, strlen(Login), OCI_ATTR_USERNAME, connection->errhp);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(connection, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec de d<>finition de l'attribut USERNAME : %s", connection->error);
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
OCIrc = OCIAttrSet(connection->authp, OCI_HTYPE_SESSION, (char *)(int)Password, strlen(Password), OCI_ATTR_PASSWORD, connection->errhp);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(connection, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec de d<>finition de l'attribut PASSWORD : %s", connection->error);
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
/* Allocation et initialisation du handle de serveur */
OCIrc = OCIServerAttach(connection->srvhp, connection->errhp, (text *)(int)Database, (signed)(Database ? strlen(Database) : 0), OCI_DEFAULT);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(connection, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec de l'attachement de la base %s%sau serveur : %s", Database ? Database : "", Database ? " " : "", connection->error);
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
/* Ajoute le handle du serveur au handle de contexte */
OCIrc = OCIAttrSet(connection->svchp, OCI_HTYPE_SVCCTX, connection->srvhp, 0, OCI_ATTR_SERVER, connection->errhp);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(connection, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec d'attachement du handle de serveur au handle de service : %s", connection->error);
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
/* Ouverture d'une session */
OCIrc = OCISessionBegin(connection->svchp, connection->errhp, connection->authp, OCI_CRED_RDBMS, OCI_DEFAULT);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(connection, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec du d<>marrage de la session : %s", connection->error);
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
/* R<>f<EFBFBD>rencement de la session ouverte dans le handle de contexte */
OCIrc = OCIAttrSet(connection->svchp, OCI_HTYPE_SVCCTX, connection->authp, 0, OCI_ATTR_SESSION, connection->errhp);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(connection, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec du r<>f<EFBFBD>rencement de la session dans le handle de contexte : %s", connection->error);
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
/* R<>f<EFBFBD>rencement de la transaction */
OCIrc = OCIAttrSet(connection->svchp, OCI_HTYPE_SVCCTX, connection->transp, 0, OCI_ATTR_TRANS, connection->errhp);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(connection, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Connect", "Echec du r<>f<EFBFBD>rencement de la transaction");
DB__Disconnect(connection, 0);
return DBS_ERROCI;
}
BDM_Trace(2, "libdatabase", "DB_Connect", "Connect<EFBFBD> <20> Oracle %s%s%s", Login, Database ? "@" : "", Database ? Database : "");
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Disconnects from an Oracle database. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Disconnect(DBT_Connection *connection) {
return DB__Disconnect(connection, 1);
}
/*---------------------------------------------------------------------------------*/
/* Starts a new transaction. */
/* */
/* User can then execute one or more statements before committing or rolling back. */
/* If multiple transactions are necessary, each DB_Transaction_Start() must be */
/* called on a different DBT_Connection, i.e. DB_Connect() should be called as */
/* many times as there will be transactions. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Transaction_Start(DBT_Connection *conn) {
sword OCIrc;
OCIrc = OCITransStart(conn->svchp, conn->errhp, 1, OCI_TRANS_NEW);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(conn, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Transaction_Start", "Echec du d<>marrage de la transaction : %s", conn->error);
return DBS_ERROCI;
}
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Commits a transaction. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Transaction_Commit(DBT_Connection *conn) {
sword OCIrc;
OCIrc = OCITransCommit(conn->svchp, conn->errhp, OCI_DEFAULT);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(conn, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Transaction_Commit", "Echec du commit de la transaction : %s", conn->error);
return DBS_ERROCI;
}
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Rolls back a transaction. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Transaction_Rollback(DBT_Connection *conn) {
sword OCIrc;
OCIrc = OCITransRollback(conn->svchp, conn->errhp, OCI_DEFAULT);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(conn, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Transaction_Rollback", "Echec du rollback de la transaction : %s", conn->error);
return DBS_ERROCI;
}
return DBS_OK;
}
/*------------------------------------------------------------------------------*/
/* Initializes an 'hostvar', variable that will be later associated to an */
/* input or output value. */
/*------------------------------------------------------------------------------*/
/* (I/O) HostVar: Address of an existing DBT_HostVar that will be filled in. */
/* (I) type : Type of data this hostvar refers to. */
/* (I) size : Size in bytes of the data type. Ex: DBD_INTEGER: sizeof(int); */
/* DBD_STRING: size of largest string + 1. */
/* (I) value : Pointer to input or output value (or array of values). */
/* (I) indicator: DBT_Indicator or array of DBT_Indicator. */
/* */
/* Type can be DBD_{INTEGER,STRING,FLOAT,DOUBLE,DATE}. */
/* */
/* Value points to an array of data (int[] for DBD_INTEGER, double[] for */
/* DBD_DOUBLE etc. Note: for strings, value points to a char[][] and not */
/* a char*[]) */
/* For DBD_DATE fields, the array must be allocated via DB_DateArray_Alloc. */
/* */
/* The value of indicator, for outgoing data, must be -1 for a null value */
/* and 0 otherwise. For incoming data, it is -1 if the value is null, 0 */
/* if the value was retrieved correctly, and >0 if the value is truncated */
/* (in the latter case, it is the real length of the data value). */
/*------------------------------------------------------------------------------*/
DBT_Status DB_HostVar_Setup(DBT_HostVar *HostVar, int type, int size, void *value, DBT_Indicator *indicator) {
if (type != DBD_STRING && size)
BDM_Trace(1, "libdatabase", "DB_HostVar_Setup", "Le param<61>tre size n'est utilis<69> que pour le type DBD_STRING");
switch (type) {
case DBD_STRING:
if (size <= 0) {
BDM_Trace(0, "libdatabase", "DB_HostVar_Setup", "Le param<61>tre size doit <20>tre > 0");
return DBS_ERRAPI;
}
HostVar->size = size;
HostVar->type = SQLT_STR;
break;
case DBD_INTEGER:
HostVar->size = sizeof(int);
HostVar->type = SQLT_INT;
break;
case DBD_FLOAT:
HostVar->size = sizeof(float);
HostVar->type = SQLT_FLT;
break;
case DBD_DOUBLE:
HostVar->size = sizeof(double);
HostVar->type = SQLT_FLT;
break;
case DBD_DATE:
HostVar->size = sizeof(OCIDate);
HostVar->type = SQLT_ODT;
break;
case DBD_ROWID:
HostVar->size = 16;/*sizeof(OCIRowid);*/
HostVar->type = SQLT_RDD;
break;
default:
BDM_Trace(0, "libdatabase", "DB_HostVar_Setup", "Param<EFBFBD>tre type (%d) non reconnu ; les valeurs possibles sont DBD_STRING, DBD_INTEGER, DBD_FLOAT, DBD_DOUBLE, DBD_DATE");
return DBS_ERRAPI;
}
HostVar->value = value;
HostVar->indicator = indicator;
return DBS_OK;
}
/*------------------------------------------------------------------------------*/
/* The following functions are used to manipulate Oracle dates in their */
/* native format. */
/* Although less compact, it is often more simple to consider dates in a table */
/* as strings and to define hostvars in consequence. */
/* Ex: if col1 is a date, "SELECT col1 from TABLEX WHERE ..." or */
/* "SELECT TO_CHAR(col1, 'DD/MM/YYYY HH24:MI:SS') from TABLEX WHERE ..." */
/* will do the job with a hostvar's type of DBD_STRING with size 20. */
/*------------------------------------------------------------------------------*/
DBT_Status DB_DateArray_Alloc(DBT_Date_Ptr *array, int size) {
*array = (OCIDate *)malloc(size * sizeof(OCIDate));
if (!array) {
BDM_Trace(0, "libdatabase", "DB_DateArray_Alloc", "Impossible d'allouer de la m<>moire pour un tableau de %d dates", size);
return DBS_ERRMEM;
}
return DBS_OK;
}
/*------------------------------------------------------------------------------*/
/* Free memory allocated by DB_DateArray_Alloc */
/*------------------------------------------------------------------------------*/
DBT_Status DB_DateArray_Free(DBT_Date_Ptr array) {
free(array);
return DBS_OK;
}
/*------------------------------------------------------------------------------*/
/* Convert a string into the native type Date using a given format */
/*------------------------------------------------------------------------------*/
DBT_Status DB_String_To_Date(DBT_Date_Ptr array, int idx, char *value, char *format) {
sword OCIrc;
OCIDate *Date = ((OCIDate *)array) + idx;
OCIrc = OCIDateFromText(NULL, (text *)value, strlen(value), (text *)format, strlen(format), NULL, 0, Date);
if (OCIrc != OCI_SUCCESS) {
BDM_Trace(0, "libdatabase", "DB_String_To_Date", "Erreur lors de la conversion de \"%s\" en date avec le format \"%s\"", value, format);
return DBS_ERRAPI;
}
return DBS_OK;
}
/*------------------------------------------------------------------------------*/
/* Convert a date into a string using a given format */
/*------------------------------------------------------------------------------*/
DBT_Status DB_Date_To_String(DBT_Date_Ptr array, int idx, char *Value, char *format) {
sword OCIrc;
unsigned long Value_Size;
OCIDate *Date = ((OCIDate*)array) + idx;
Value_Size = strlen(format) + 1;
OCIrc = OCIDateToText(NULL, Date, (text *)format, strlen(format), NULL, 0, &Value_Size, (text *)Value);
if (OCIrc != OCI_SUCCESS) {
BDM_Trace(0, "libdatabase", "DB_Date_To_String", "Erreur lors de la conversion d'une date avec le format \"%s\"", format);
return DBS_ERRAPI;
}
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Initializes a statement. */
/* */
/* A statement is initialized only once, but can be prepared as many times as */
/* wanted (cf. DB_Statement_Prepare()). */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_Init(DBT_Connection *connection, DBT_Statement *statement) {
sword OCIrc;
memset(statement, 0, sizeof(DBT_Statement));
statement->connection = connection;
/* Allocation d'un handle de statement */
OCIrc = OCIHandleAlloc(connection->envhp, (dvoid **)&statement->handle, OCI_HTYPE_STMT, 0, NULL);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(connection, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Statement_Init", "Echec de l'allocation d'un handle de statement : %s", connection->error);
return DBS_ERROCI;
}
/* Allocation d'un handle d'erreur */
OCIrc = OCIHandleAlloc(connection->envhp, (dvoid **)&statement->errhp, OCI_HTYPE_ERROR, 0, NULL);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Connection(connection, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Statement_Init", "Echec de l'allocation d'un handle d'erreur : %s", connection->error);
DB_Statement_Close(statement);
return DBS_ERROCI;
}
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Frees all resources used by a previously initialized statement. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_Close(DBT_Statement *statement) {
if (statement->errhp)
OCIHandleFree(statement->errhp, OCI_HTYPE_ERROR);
if (statement->handle)
OCIHandleFree(statement->handle, OCI_HTYPE_STMT);
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Prepares a statement, i.e. associates SQL request and statement structure. */
/* A statement is initialized only once, but can be prepared as many times as */
/* wanted. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_Prepare(DBT_Statement *statement, const char *query) {
sword OCIrc;
ub2 statementType;
OCIrc = OCIStmtPrepare(statement->handle, statement->errhp, (text *)(int)query, strlen(query), OCI_NTV_SYNTAX, OCI_DEFAULT);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Statement(statement, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Statement_Prepare", "Echec de la pr<70>paration du statement \"%s\" : %s", query, statement->error);
return DBS_ERROCI;
}
/* It will be useful to know if the query is a SELECT: */
OCIrc = OCIAttrGet(statement->handle, OCI_HTYPE_STMT, &statementType, 0, OCI_ATTR_STMT_TYPE, statement->errhp);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Statement(statement, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Statement_Prepare", "Erreur lors de la r<>cup<75>ration du type pour le statement \"%s\" : %s", query, statement->error);
return DBS_ERROCI;
}
statement->isSelect = (statementType == OCI_STMT_SELECT);
statement->isPrepared = 1;
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Defines output variables associations. */
/* Should be called to associate hostvar variables to output results (one hostvar */
/* for one column). Not needed if SQL request doesn't produce any result. */
/*---------------------------------------------------------------------------------*/
/* (I) statement: An initialized statement. */
/* (I) rows : Size of the array of values each HostVar uses (1 if not an array)*/
/* (I) ... : One or more pointers to initialized HostVar, ending with NULL. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_DefineVars(DBT_Statement *statement, int rows, ...) {
va_list args;
DBT_HostVar *hostVar = NULL;
int column = 1;
DBT_Status rc = DBS_OK;
va_start(args, rows);
/* Associate all hostvars to each columns that will be potentially output */
while (rc == DBS_OK && (hostVar = va_arg(args, DBT_HostVar *))) {
rc = DB_Statement_DefineVar(statement, rows, column, hostVar);
if (rc != DBS_OK) break;
++column;
}
va_end(args);
return rc;
}
/*---------------------------------------------------------------------------------*/
/* Defines output variable association. */
/* Called to associate one hostvar variable to one output result. See */
/* DB_Statement_DefineVars(). */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_DefineVar(DBT_Statement *statement, int rows, int column, DBT_HostVar *hv) {
OCIDefine *def = NULL;
sword OCIrc;
if (!statement->isPrepared) {
BDM_Trace(0, "libdatabase", "DB_Statement_DefineVar", "Le statement n'a pas <20>t<EFBFBD> pr<70>par<61>");
return DBS_ERRAPI;
}
/* Associate hostvar to the current column: */
OCIrc = OCIDefineByPos(statement->handle, &def, statement->errhp, (unsigned)column, hv->value, hv->size, hv->type, hv->indicator, NULL, NULL, OCI_DEFAULT);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Statement(statement, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Statement_DefineVar", "Echec de la d<>finition de la variable n<> %d : %s", column, statement->error);
return DBS_ERROCI;
}
statement->varsDefined = 1;
statement->rowsToFetch = rows;
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Binds input variables to placeholders in the SQL request. */
/* NOTE: The statement must have been prepared using the SQL request */
/* */
/* Should be called to associate hostvar variables to input placeholders */
/* e.g., :1 and :2 in "INSERT INTO TableX (col1, col2) VALUES (:1, :2)". */
/* Placeholders can be named whatever you want since binding is done by position, */
/* not by name. */
/*---------------------------------------------------------------------------------*/
/* (I) statement: An initialized statement. */
/* (I) rowsBound: Size of the array of values each HostVar uses (1 if not an array)*/
/* (I) ... : One or more pointers to initialized HostVar, ending with NULL. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_BindVars(DBT_Statement *statement, int rowsBound, ...) {
va_list args;
DBT_HostVar *hostVar;
int placeHolderIndex = 1;
DBT_Status rc = DBS_OK;
va_start(args, rowsBound);
/* Bind each hostvar to each placeholder, starting form 1: */
while (rc == DBS_OK && (hostVar = va_arg(args, DBT_HostVar *))) {
rc = DB_Statement_BindVar(statement, rowsBound, placeHolderIndex, hostVar);
if (rc != DBS_OK) break;
++placeHolderIndex;
}
va_end(args);
return rc;
}
/*---------------------------------------------------------------------------------*/
/* Binds one input variable to one placeholders in the SQL request. */
/* Cf. DB_Statement_BindVars(). */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_BindVar(DBT_Statement *statement, int rowsBound, int placeHolderIndex, DBT_HostVar *hostVar) {
OCIBind *bind = NULL;
sword OCIrc;
if (!statement->isPrepared) {
BDM_Trace(0, "libdatabase", "DB_Statement_BindVar", "Le statement n'a pas <20>t<EFBFBD> pr<70>par<61>");
return DBS_ERRAPI;
}
OCIrc = OCIBindByPos(statement->handle, &bind, statement->errhp, (unsigned)placeHolderIndex, hostVar->value, hostVar->size, hostVar->type, hostVar->indicator,NULL, NULL, 0, NULL, OCI_DEFAULT);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Statement(statement, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Statement_BindVar", "Echec du binding de la variable n<> %d : %s", placeHolderIndex, statement->error);
return DBS_ERROCI;
}
statement->varsBound = 1;
statement->rowsBound = rowsBound;
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Shorthand to execute a prepared statement using a SELECT query. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_ExecuteSelect(DBT_Statement *statement, DBT_Result *result) {
return DB_Statement_Execute(statement, 0, 1, result);
}
/*---------------------------------------------------------------------------------*/
/* Executes a prepared statement. */
/* */
/* If statement is SELECT: */
/* _ if DB_Statement_DefineVars() has already been called, as many rows as */
/* specified by the size of defined hostvars will be directly fetched here. */
/* Ex: "SELECT col1, col2 FROM table WHERE col3 == 12" */
/* _ If DB_Statement_DefineVars() has not been called yet, user will need to */
/* perform one or more DB_Statement_Fetch() to retrieve the results. */
/* */
/* If statement is not SELECT: */
/* _ If no variables were bound, execute statement only once. */
/* Ex: "INSERT INTO table VALUES (123, 'ABC')" */
/* _ If some variables are bound, execute statement request 'count' times. */
/* Ex: "INSERT INTO table VALUES (:1, :2)" */
/*---------------------------------------------------------------------------------*/
/* */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_Execute(DBT_Statement *statement, int startingRow, int count, DBT_Result *result) {
sword OCIrc;
sword OCIrc2;
unsigned int iters;
char err[512];
int errLen = sizeof(err);
dword errCode;
DBT_Status rc;
if (!statement->isPrepared) {
BDM_Trace(0, "libdatabase", "DB_Statement_Execute", "Le statement n'a pas <20>t<EFBFBD> pr<70>par<61>");
return DBS_ERRAPI;
}
/* Set 'iters', number of times the request will be executed */
if (statement->isSelect) {
iters = (statement->varsDefined) ? statement->rowsToFetch : 0;
if (statement->varsBound) {
if (startingRow != 0 || count != 1) {
BDM_Trace(0, "libdatabase", "DB_Statement_Execute", "Un statement SELECT avec des variables en sortie ne peut travailler avec des tableaux en entr<74>e");
return DBS_ERRAPI;
}
}
} else {
if (!statement->varsBound) {
iters = 1;
} else {
if (startingRow < 0 || startingRow >= statement->rowsBound) {
BDM_Trace(0, "libdatabase", "DB_Statement_Execute", "Le param<61>tre startingRow est invalide (0 <20> %d au lieu de %d)", statement->rowsBound, startingRow);
return DBS_ERRAPI;
}
if (count <= 0 || startingRow + count > statement->rowsBound) {
BDM_Trace(0, "libdatabase", "DB_Statement_Execute", "Le param<61>tre count est invalide (1 <20> %d au lieu de %d)", statement->rowsBound - startingRow, count);
return DBS_ERRAPI;
}
iters = startingRow + count;
}
}
statement->rowsFetchedSoFar = 0;
statement->isEOF = 0;
statement->isExecuted = 0;
BDM_Trace(3, "libdatabase", "DB_Statement_Execute", "Ex<EFBFBD>cution du statement, iter = %d, <20> partir de %d", iters, startingRow);
OCIrc = OCIStmtExecute(statement->connection->svchp, statement->handle, statement->errhp, iters, (unsigned)startingRow, NULL, NULL, OCI_DEFAULT);
if (statement->isSelect) {
result->errorIteration = 0; /* only one iteration for SELECTs */
rc = DB__CheckFetch(statement, OCIrc, result);
if (!DB_ERROR(rc)) statement->isExecuted = 1;
return rc;
}
/* This is not a SELECT statement */
result->rowsProcessed = -1;
result->errorIteration = -1;
/* Get number of rows processed */
OCIrc2 = OCIAttrGet(statement->handle, OCI_HTYPE_STMT, &result->rowsProcessed, NULL, OCI_ATTR_ROW_COUNT, statement->errhp);
if (OCIrc2 != OCI_SUCCESS) {
/* Don't report any errors, since the statement succeeded.
Leave rowsProcessed to -1 if impossible to get it. */
DB__Error(statement->errhp, err, errLen, &errCode, OCIrc2);
BDM_Trace(0, "libdatabase", "DB_Statement_Execute", "Erreur lors de la r<>cup<75>ration du nombre de lignes trait<69>es : %s", err);
return DBS_ERROCI;
}
if (OCIrc == OCI_SUCCESS) return DBS_OK;
/* An error happened */
DB__Error_Statement(statement, OCIrc);
BDM_Trace(0, "libdatabase", "DB_Statement_Execute", "Erreur lors de l'ex<65>cution du statement : %s", statement->error);
if (iters == 1) result->errorIteration = 0;
return DBS_ERROCI;
}
/*---------------------------------------------------------------------------------*/
/* Fetches next rows. */
/* Called by user after a DB_Statement_Execute() using a SELECT query. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_Fetch(DBT_Statement *statement, DBT_Result *result) {
sword OCIrc;
if (!statement->isPrepared) {
BDM_Trace(0, "libdatabase", "DB_Statement_Fetch", "Le statement n'a pas <20>t<EFBFBD> pr<70>par<61>");
return DBS_ERRAPI;
}
if (!statement->isSelect) {
BDM_Trace(0, "libdatabase", "DB_Statement_Fetch", "Le statement n'est pas un SELECT");
return DBS_ERRAPI;
}
if (!statement->varsDefined) {
BDM_Trace(0, "libdatabase", "DB_Statement_Fetch", "Aucune variable n'est d<>finie");
return DBS_ERRAPI;
}
if (!statement->isExecuted) {
BDM_Trace(0, "libdatabase", "DB_Statement_Fetch", "Le statement n'a pas encore <20>t<EFBFBD> ex<65>cut<75>");
return DBS_ERRAPI;
}
if (statement->isEOF) /* the end of data was seen during last fetch */
return DBS_END_OF_DATA;
BDM_Trace(3, "libdatabase", "DB_Statement_Fetch", "Fetch...");
OCIrc = OCIStmtFetch(statement->handle, statement->errhp, (unsigned)statement->rowsToFetch, OCI_FETCH_NEXT, OCI_DEFAULT);
return DB__CheckFetch(statement, OCIrc, result);
}
/*---------------------------------------------------------------------------------*/
/* Cancels implicit cursor associated to last fetch. */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_Fetch_Terminate(DBT_Statement *statement) {
sword OCIrc;
if (! statement->isPrepared) {
BDM_Trace(0, "libdatabase", "DB_Statement_Fetch", "Le statement n'a pas <20>t<EFBFBD> pr<70>par<61>");
return DBS_ERRAPI;
}
if (! statement->isSelect) {
BDM_Trace(0, "libdatabase", "DB_Statement_Fetch", "Le statement n'est pas un SELECT");
return DBS_ERRAPI;
}
OCIrc = OCIStmtFetch(statement->handle, statement->errhp, 0, OCI_FETCH_NEXT, OCI_DEFAULT);
if (OCIrc != OCI_SUCCESS) {
DB__Error_Statement(statement, OCIrc);
if (statement->errorCode == 1406) {
statement->errorCode = 0;
strcpy(statement->error, "");
BDM_Trace(1, "libdatabase", "DB_Statement_Fetch_Terminate", "Donn<EFBFBD>e tronqu<71>e");
return DBS_OK;
}
BDM_Trace(0, "libdatabase", "DB_Statement_FetchTerminate", "Erreur lors du fetch : %s", statement->error);
return DBS_ERROCI;
}
return DBS_OK;
}
static DBT_Status DB__Disconnect(DBT_Connection *connection, int reportErrors) {
sword OCIrc;
DBT_Status rc = DBS_OK;
char *err;
int errLen;
char localErr[512];
dword *errCode;
dword localErrCode;
if (connection->handlesAreValid) {
if (reportErrors) {
err = connection->error;
errLen = sizeof(connection->error);
errCode = &connection->errorCode;
} else {
err = localErr;
errLen = sizeof(localErr);
errCode = &localErrCode;
}
/* D<>connexion de la base de donn<6E>es */
OCIrc = OCISessionEnd(connection->svchp, connection->errhp, connection->authp, 0);
if (OCIrc != OCI_SUCCESS) {
DB__Error(connection->errhp, err, errLen, errCode, OCIrc);
BDM_Trace(0, "libdatabase", "DB__Disconnect", "Echec de la fermeture de la session : %s", err);
rc = DBS_ERROCI;
}
/* D<>tachement du serveur */
OCIrc = OCIServerDetach(connection->srvhp, connection->errhp, OCI_DEFAULT);
if (OCIrc != OCI_SUCCESS) {
DB__Error(connection->errhp, err, errLen, errCode, OCIrc);
BDM_Trace(0, "libdatabase", "DB__Disconnect", "Echec du d<>tachement du serveur : %s", err);
rc = DBS_ERROCI;
}
BDM_Trace(2, "libdatabase", "DB__Disconnect", "D<EFBFBD>connect<EFBFBD> d'Oracle");
}
if (connection->srvhp)
OCIHandleFree(connection->srvhp, OCI_HTYPE_SERVER);
if (connection->errhp)
OCIHandleFree(connection->errhp, OCI_HTYPE_ERROR);
if (connection->authp)
OCIHandleFree(connection->authp, OCI_HTYPE_SESSION);
if (connection->transp)
OCIHandleFree(connection->transp, OCI_HTYPE_TRANS);
if (connection->svchp)
OCIHandleFree(connection->svchp, OCI_HTYPE_SVCCTX);
memset(connection, 0, sizeof(DBT_Connection));
return rc;
}
static void DB__Error(OCIError *errhp, char *buffer, int length, dword *errorCode, sword status) {
unsigned int len = length;
*errorCode = 0;
switch (status) {
case OCI_SUCCESS:
buffer[0] = '\0';
break;
case OCI_SUCCESS_WITH_INFO:
snprintf(buffer, len, "ErrorOCI_SUCCESS_WITH_INFO");
break;
case OCI_NEED_DATA:
snprintf(buffer, len, "ErrorOCI_NEED_DATA");
break;
case OCI_NO_DATA:
snprintf(buffer, len, "ErrorOCI_NO_DATA");
break;
case OCI_ERROR:
OCIErrorGet(errhp, 1, NULL, errorCode, (text*)buffer, len, OCI_HTYPE_ERROR);
break;
case OCI_INVALID_HANDLE:
snprintf(buffer, len, "ErrorOCI_INVALID_HANDLE");
break;
case OCI_STILL_EXECUTING:
snprintf(buffer, len, "ErrorOCI_STILL_EXECUTE");
break;
case OCI_CONTINUE:
snprintf(buffer, len, "ErrorOCI_CONTINUE");
break;
default:
snprintf(buffer, len, "Unknown Error");
break;
}
}
static void DB__Error_Connection(DBT_Connection *connection, sword status) {
DB__Error(connection->errhp, connection->error, sizeof(connection->error), &connection->errorCode, status);
}
static void DB__Error_Statement(DBT_Statement *statement, sword status) {
DB__Error(statement->errhp, statement->error, sizeof(statement->error), &statement->errorCode, status);
}
static DBT_Status DB__CheckFetch(DBT_Statement *statement, sword OCIrc, DBT_Result *result) {
sword OCIrc2;
int fetched = 0;
result->rowsProcessed = 0; /* will set it later if possible */
if (OCIrc == OCI_ERROR) {
/* see whether the error is "Fetched column value is truncated" */
/* do not consider it an error -- indicators indicate truncated data */
DB__Error_Statement(statement, OCIrc);
if (statement->errorCode == 1406) {
OCIrc = OCI_SUCCESS;
BDM_Trace(1, "libdatabase", "DB__CheckFetch", "Donn<EFBFBD>e tronqu<71>e");
} else {
BDM_Trace(0, "libdatabase", "DB__CheckFetch", "Erreur <20> l'ex<65>cution du statement : %s", statement->error);
return DBS_ERROCI;
}
} else if (OCIrc != OCI_SUCCESS && OCIrc != OCI_SUCCESS_WITH_INFO && OCIrc != OCI_NO_DATA) {
DB__Error_Statement(statement, OCIrc);
BDM_Trace(0, "libdatabase", "DB__CheckFetch", "Erreur <20> l'ex<65>cution du statement : %s", statement->error);
return DBS_ERROCI;
}
/* OCI_SUCCESS_WITH_INFO may happen because of EOF reached or some
* data was truncated. I don't know how to distinguish these. */
/* get number of rows fetched -- this is mandatory */
OCIrc2 = OCIAttrGet(statement->handle, OCI_HTYPE_STMT, &fetched, NULL, OCI_ATTR_ROW_COUNT, statement->errhp);
if (OCIrc2 != OCI_SUCCESS) {
DB__Error_Statement(statement, OCIrc2);
BDM_Trace(0, "libdatabase", "DB__CheckFetch", "Echec de la r<>cup<75>ration du nombre de lignes du fetch : %sErreur <20> l'ex<65>cution du statement : %s", statement->error);
return DBS_ERROCI;
}
result->rowsProcessed = fetched - statement->rowsFetchedSoFar;
statement->rowsFetchedSoFar = fetched;
if (OCIrc == OCI_NO_DATA) {
statement->isEOF = 1;
return result->rowsProcessed ? DBS_OK : DBS_END_OF_DATA;
}
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Fonction pour compatibilit<69> avec les pr<70>c<EFBFBD>dentes versions de libdatabase */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Connection_SQLError_Get(DBT_Connection *connection, dword *errCode, char **errStr) {
BDM_Trace(1, "libdatabase", "DB_Connection_SQLError_Get", "Cette fonction est obsol<6F>te ; cf BDT_Connection");
*errCode = connection->errorCode;
*errStr = connection->error;
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Fonction pour compatibilit<69> avec les pr<70>c<EFBFBD>dentes versions de libdatabase */
/*---------------------------------------------------------------------------------*/
void DB_Debug_Level_Set(int level) {
BDM_Trace(1, "libdatabase", "DB_Debug_Level_Set", "Cette fonction est obsol<6F>te ; cf BDM_Trace_SetLevel");
BDM_Trace_SetLevel(level, "libdatabase");
}
/*---------------------------------------------------------------------------------*/
/* Fonction pour compatibilit<69> avec les pr<70>c<EFBFBD>dentes versions de libdatabase */
/*---------------------------------------------------------------------------------*/
DBT_Status DB_Statement_SQLError_Get(DBT_Statement *st, dword *errCode, char **errStr) {
BDM_Trace(1, "libdatabase", "DB_Statement_SQLError_Get", "Cette fonction est obsol<6F>te ; cf BDT_Statement");
*errCode = st->errorCode;
*errStr = st->error;
return DBS_OK;
}
/*---------------------------------------------------------------------------------*/
/* Fonction pour compatibilit<69> avec les pr<70>c<EFBFBD>dentes versions de libdatabase */
/*---------------------------------------------------------------------------------*/
const char *DB_Error_Message_Get() {
BDM_Trace(1, "libdatabase", "DB_Error_Message_Get", "Cette fonction est obsol<6F>te ; cf BDM_Trace_LastError");
return BDM_Trace_LastError();
}