/*---------------------------------------------------------------------------------*/ /* $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 #include #include #include extern char *strdup (const char *); #include #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é */ if (flags) BDM_Trace(1, "libdatabase", "DB_Library_Open", "Le paramètre flags (%d) est ignoré car il est obsolè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 à 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 été initialisé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é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érencement de la session dans le handle de contexte : %s", connection->error); DB__Disconnect(connection, 0); return DBS_ERROCI; } /* Réfé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érencement de la transaction"); DB__Disconnect(connection, 0); return DBS_ERROCI; } BDM_Trace(2, "libdatabase", "DB_Connect", "Connecté à 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ètre size n'est utilisé que pour le type DBD_STRING"); switch (type) { case DBD_STRING: if (size <= 0) { BDM_Trace(0, "libdatabase", "DB_HostVar_Setup", "Le paramètre size doit ê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è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é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é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 été préparé"); 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 été préparé"); 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 été préparé"); 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é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ètre startingRow est invalide (0 à %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ètre count est invalide (1 à %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écution du statement, iter = %d, à 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ération du nombre de lignes traité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é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 été préparé"); 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 été exécuté"); 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 été préparé"); 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ée tronqué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é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éconnecté 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ée tronquée"); } else { BDM_Trace(0, "libdatabase", "DB__CheckFetch", "Erreur à l'exé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 à l'exé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ération du nombre de lignes du fetch : %sErreur à l'exé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é avec les précé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ète ; cf BDT_Connection"); *errCode = connection->errorCode; *errStr = connection->error; return DBS_OK; } /*---------------------------------------------------------------------------------*/ /* Fonction pour compatibilité avec les précédentes versions de libdatabase */ /*---------------------------------------------------------------------------------*/ void DB_Debug_Level_Set(int level) { BDM_Trace(1, "libdatabase", "DB_Debug_Level_Set", "Cette fonction est obsolète ; cf BDM_Trace_SetLevel"); BDM_Trace_SetLevel(level, "libdatabase"); } /*---------------------------------------------------------------------------------*/ /* Fonction pour compatibilité avec les précé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ète ; cf BDT_Statement"); *errCode = st->errorCode; *errStr = st->error; return DBS_OK; } /*---------------------------------------------------------------------------------*/ /* Fonction pour compatibilité avec les précédentes versions de libdatabase */ /*---------------------------------------------------------------------------------*/ const char *DB_Error_Message_Get() { BDM_Trace(1, "libdatabase", "DB_Error_Message_Get", "Cette fonction est obsolète ; cf BDM_Trace_LastError"); return BDM_Trace_LastError(); }