Salut, va prezint un alt proiect mai vechi, de aceasta data legat de operarea cu bazele de date access - asta aveam nevoie la timpul respectiv. Sunt doar 4 functii simple una pentru "select" din sql, alta pentru "insert", una pentru recuperarea erori/erorilor si una pentru a face curat dupa celelalte 3 functii. Functiile sunt foarte simple, dar pentru proiectul la care am avut nevoie la momentul respectiv si-au facut treaba cu brio. Pentru a le utiliza aveti nevoie sa legati (link) libraria odbc32. Functiile se folosesc de api-ul ODBC, si pot fi modificate pentru a putea rula cu orice tip de baza de date la care va puteti conecta cu ODBC. Pentru a le utiliza nu trebuie decat sa adaugati cele 2 fisiere proiectului vostru.
functii_odbc.h
#ifndef FUNCTII_ODBC_H_INCLUDED #define FUNCTII_ODBC_H_INCLUDED #include <iostream> #include <string> #include <sstream> #include <fstream> #include <vector> #include <windows.h> #include <sql.h> #include <sqlext.h> int select(std::string dns, int numarCampuri, std::string querry, std::vector<std::vector< std::string> >& array, std::string& erroare); void getErrors(SQLSMALLINT type,SQLHANDLE handle, std::string& error); void freeDbResources(HSTMT& hStmt, HENV& hEnv, HDBC& hDbc); int insert(std::string dns, std::string num_Tabel, std::vector<std::string> Campuri, std::vector<std::string> Valori, std::string& error); #endif // FUNCTII_ODBC_H_INCLUDED
functii_odbc.cpp
#include "functii_odbc.h"
/**
* Lista argumente:
* string dns = adresa fisierului acces mdb/accdb pe hard-disk
* numarCampuri = numarul de campuri care urmeaza a fi selectate prin querry-ul respectiv
* vector<vector<string> >& array = este un vector de vectori de stringuri, trimis prin referinta care va contine datele extrase
* in felul urmator: array[0][0] va fi prima coloana returnata pe primul rand
* array[0][1] va fi a 2-a coloana
* array[0][2] va fi a 3-a coloana smd
* iar array[x][0], x va desemna randul de date extrase
* string& eroare = va contine mesajele de eroare, in situatia in care apare o eroare in executia functiei sau a querry-ului atunci aceasta va fi salvata in acest string
* Functia are doar 2 valori de return 1 = succes in executarea querry-ului, sau -1 eroare
* Functia face doar cateva verificari de baza in privinta corectitudinii querry-ului
* De asta trebuie sa se asigure utilizatorul inainte
* Functia va verifica doar daca exista un posibil insert in querry caz in care iese.
*/
using namespace std;
int select(string dns, int numarCampuri, string querry, vector<vector<string> >& array, string& erroare)
{
if(querry.find("SELECT") == string::npos || querry.find("INSERT") != string::npos)
{
erroare = "Nu ai introdus o comanda de select;";
}
else
{
SQLCHAR valoareCustomers[numarCampuri][128];
HSTMT querryStmt;
string tempNume;
int retCode[numarCampuri];
HENV hEnv;
HDBC hDbc;
RETCODE rc;
int len = querry.size();
char szConnStrOut[255];
int iConnStrLength2Ptr;
char szDSN[256] = "Driver={Microsoft Access Driver (*.mdb)};DSN='';DBQ=";
unsigned char* statementQuerry = (unsigned char*)querry.c_str();
//vStrArray.clear(); // golim vectorul primit ca container de output (este datoria utilizatorului sa se asigure ca nu are nimic important in vector inainte de a fi trimis acestei functii
strcat(szDSN, dns.c_str());
rc = SQLAllocEnv(&hEnv);
if(!SQL_SUCCEEDED(rc))
{
erroare = "Nu am putut aloca Envorimentul.";
cout << "Nu am putut aloca envorimentul.";
freeDbResources(querryStmt, hEnv, hDbc);
return -1;
}
rc = SQLAllocConnect(hEnv, &hDbc);
if(!SQL_SUCCEEDED(rc))
{
erroare = "Nu am putut aloca conexiunea.";
cout << "Nu am putut aloca conexiunea.";
freeDbResources(querryStmt, hEnv, hDbc);
return -1;
}
rc = SQLDriverConnect(hDbc, NULL, (SQLCHAR*)szDSN,
SQL_NTS, (SQLCHAR*)szConnStrOut,
255, (SQLSMALLINT*)&iConnStrLength2Ptr, SQL_DRIVER_NOPROMPT);
if(!SQL_SUCCEEDED(rc))
{
getErrors(SQL_HANDLE_DBC , hDbc, erroare);
freeDbResources(querryStmt, hEnv, hDbc);
return -1;
}
rc = SQLAllocStmt(hDbc, &querryStmt);
if(!SQL_SUCCEEDED(rc))
{
erroare = "Nu am putut aloca statementul.";
cout << "Nu am putut aloca statementul.";
freeDbResources(querryStmt, hEnv, hDbc);
return -1;
}
rc = SQLPrepare(querryStmt, statementQuerry, SQL_NTS);
if(!SQL_SUCCEEDED(rc))
{
erroare = "Nu am putut pregatii statementul.";
cout << "Nu am putut pregatii statementul.";
freeDbResources(querryStmt, hEnv, hDbc);
return -1;
}
for(int i = 0; i < numarCampuri; i++)
{
rc = SQLBindCol(querryStmt, (i + 1), SQL_C_CHAR, valoareCustomers[i], 128, (SQLINTEGER*)&retCode[i]);
if(!SQL_SUCCEEDED(rc))
{
erroare = "Nu am putut bindui coloana.";
cout << "Nu am putut bindui coloana.";
freeDbResources(querryStmt, hEnv, hDbc);
return -1;
}
}
cout << endl << endl << statementQuerry << endl;
rc = SQLExecute(querryStmt);
if(SQL_ERROR == rc || SQL_SUCCESS_WITH_INFO == rc)
{
cout << "Eroare in executare statement, sau SQL_SUCCESS_WITH_INFO";
erroare = "Eroare in executare statement, sau SQL_SUCCESS_WITH_INFO";
getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
return -1;
}
else if(SQL_NEED_DATA == rc)
{
cout << "SQL_NEED_DATA.";
erroare = "SQL_NEED_DATA.";
getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
return -1;
}
else if(SQL_STILL_EXECUTING == rc)
{
cout << "SQL_STILL_EXECUTING.";
erroare = "SQL_STILL_EXECUTING.";
getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
return -1;
}
else if(SQL_NO_DATA == rc)
{
cout << "SQL_NO_DATA.";
erroare = "SQL_NO_DATA.";
getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
return -1;
}
else if(SQL_INVALID_HANDLE == rc)
{
cout << "SQL_INVALID_HANDLE.";
erroare = "SQL_INVALID_HANDLE";
getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
return -1;
}
rc = SQLFetch(querryStmt);
if(SQL_SUCCESS == rc)
{
while(SQL_SUCCEEDED(rc))
{
vector<string> vString;
string temp;
for(int i = 0; i < numarCampuri; i++)
{
temp = (char*)valoareCustomers[i];
vString.push_back(temp);
}
array.push_back(vString);
rc = SQLFetch(querryStmt);
}
freeDbResources(querryStmt, hEnv, hDbc);
return 1;
}
else if(SQL_SUCCESS_WITH_INFO == rc || SQL_ERROR == rc)
{
cout << "Nu am putut executa SQLFetch.\n";
cout << "Nu am putut executa SQLFetch.\n";
getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
freeDbResources(querryStmt, hEnv, hDbc);
return -1;
}
else if(SQL_STILL_EXECUTING == rc)
{
cout << "Inca se executa statementul.\n";
erroare = "Inca se executa statementul.\n";
freeDbResources(querryStmt, hEnv, hDbc);
return -1;
}
else if(SQL_NO_DATA == rc)
{
cout << "Nu exista date de returnat.\n";
erroare = "Nu exista date de returnat.\n";
freeDbResources(querryStmt, hEnv, hDbc);
return -1;
}
}
return -1;
}
/**
* Functia preia prin referinta handle-urile pentru conexiune, mediu si statement si le elibereaza
*/
void freeDbResources(HSTMT& hStmt, HENV& hEnv, HDBC& hDbc)
{
SQLFreeStmt(hStmt, SQL_UNBIND);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
SQLDisconnect(hDbc);
SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
}
/**
* Functia preia datele necesare pentru a putea prelua o eroare legata de un handle pentru conexiune, statement sau mediu
* SQLSMALLINT type poate fi: QL_HANDLE_DBC, SQL_HANDLE_DBC_INFO_TOKEN, SQL_HANDLE_DESC, SQL_HANDLE_ENV, SQL_HANDLE_STMT
* SQLHANDLE handle va fi de fapt handle-ul pentru care aven nevoie sa preluam eroarea
* string& eroare = va tine textul erori
* Functia preia doar erorile care pot fi preluate prin SQLGetDiagRec.
* Mai multe informatii despre SQLGetDiagRec aici: http://msdn.microsoft.com/en-us/library/windows/desktop/ms716256(v=vs.85).aspx
*/
void getErrors(SQLSMALLINT type,SQLHANDLE handle, string& eroare)
{
SQLINTEGER i = 1;
SQLINTEGER native; // codul nativ al erori ( depinde de sistemul de operare si de driver
SQLCHAR state[7]; // starea driverului mai multe informatii despre state : http://msdn.microsoft.com/en-us/library/windows/desktop/ms716412(v=vs.85).aspx
SQLCHAR text[256]; // textul erori
SQLSMALLINT len; // marimea textului erori
SQLRETURN ret; // stocheaza raspunsul functiei SQLGetDiagRec
ret = SQLGetDiagRec(type, handle, i, (SQLCHAR*)state, &native, (SQLCHAR*)text, sizeof(text), &len );
if(SQL_SUCCESS == ret || SQL_SUCCESS_WITH_INFO == ret)
{
stringstream buffer(stringstream::in | stringstream::out);
string temp;
eroare = "Eroare:\n";
eroare.append("State: ");
cout << "State: " << state << endl;
buffer << state;
buffer >> temp;
eroare.append(temp);
buffer.flush();
string temp3((char*)text);
eroare.append("\nTextul eroarei: ");
eroare.append(temp3);
buffer.flush();
cout << "Textul erori: " << text << endl;
}
else if(SQL_ERROR == ret)
{
eroare = "eroare Raporting: SQLGetDiagRec returneaza o eroare.\n";
}
else if(SQL_INVALID_HANDLE == ret)
{
eroare = "eroare Raporting: Handleul furnizat pentru preluarea erori a fost incorect.\n";
}
else if(SQL_NO_DATA == ret)
{
eroare = "eroare Raporting: Nu au fost date de returnat.\n";
}
}
/**
* Lista argumente:
* string dns = adresa pe disk a fisierului access
* string num_Tabel = numele tabelului in care se face inserarea
* vector<string> Campuri = Un vector ce contine campurile in care se va face inserarea
* vector<string> Valori = Un vector cu valorile ce vor fi introduse in fiecare camp
* string& eroare va tine textul oricarei posibile erori
* Pentru cei doi vectori marimea lor trebuie sa fie identica.
* Exemplu Campuri va contine stringurile "Nume", "Prenume" "telefon"
* vectorul Valori va contine "ION", "VASILE", "02345241590"
* Functia are doar 2 valori de return 1 = succes in inserarea datelor, sau -1 eroare
*/
int insert(string dns, string num_Tabel, vector<string> Campuri, vector<string> Valori, string& eroare)
{
//Incepem pregatirea statmentului
string comanda = "INSERT INTO ";
string comanda2 = ") VALUES ('";
string comanda3 = ");";
string campuri_tabel;
string campuri_valori;
//verificam daca numarul de campuri si de valori e identic
if(Campuri.size() != Valori.size())
{
eroare = "Numarul de campuri si de valori nu este identic. Pentru ca inserarea sa se poata efectua este necesar ca cele 2 sa fie identice.\n";
return -1;
}
// pregatim lista cu campurile in care se introduc datele
for(unsigned int i = 0; i < Campuri.size(); i++)
{
if(i == (Campuri.size() - 1))
{
campuri_tabel.append(Campuri[i]);
//campuri_tabel.append(")");
}
else
{
campuri_tabel.append(Campuri[i]);
campuri_tabel.append(", ");
}
}
// pregatim lista cu valorile de introdus
for(unsigned int i = 0; i < Valori.size(); i++)
{
if(i == (Valori.size() - 1))
{
campuri_valori.append(Valori[i]);
campuri_valori.append("'");
}
else
{
campuri_valori.append(Valori[i]);
campuri_valori.append("', '");
}
}
string sql_statement;
sql_statement.append(comanda);
sql_statement.append(num_Tabel);
sql_statement.append("(");
sql_statement.append(campuri_tabel);
sql_statement.append(comanda2);
sql_statement.append(campuri_valori);
sql_statement.append(");");
// am terminat pregatirea statementului
// pregatim conexiunea la bd
// inceput definitii variabile necesare conexiunii la db
HENV hEnv;
HDBC hDbc;
HSTMT hStmt;
RETCODE rc;
char szConnStrOut[255];
int iConnStrLength2Ptr;
char szDSN[256] = "Driver={Microsoft Access Driver (*.mdb)};DSN='';DBQ=";
// incheiere definitii
// Initiem conexiunea
strcat(szDSN, dns.c_str());
rc = SQLAllocEnv(&hEnv);
if(!SQL_SUCCEEDED(rc))
{
eroare = "Nu am putut aloca Envorimentul.";
cout << "Nu am putut aloca envorimentul.";
return -1;
}
rc = SQLAllocConnect(hEnv, &hDbc);
if(!SQL_SUCCEEDED(rc))
{
eroare = "Nu am putut aloca conexiunea.";
cout << "Nu am putut aloca conexiunea.";
return -1;
}
rc = SQLDriverConnect(hDbc, NULL, (SQLCHAR*)szDSN,
SQL_NTS, (SQLCHAR*)szConnStrOut,
255, (SQLSMALLINT*)&iConnStrLength2Ptr, SQL_DRIVER_NOPROMPT);
if(!SQL_SUCCEEDED(rc))
{
getErrors(SQL_HANDLE_DBC , hDbc, eroare);
freeDbResources(hStmt, hEnv, hDbc);
return -1;
}
rc = SQLAllocStmt(hDbc, &hStmt);
rc = SQLPrepare(hStmt, (SQLCHAR*)sql_statement.c_str(), SQL_NTS);
rc = SQLExecute(hStmt);
if(!SQL_SUCCEEDED(rc))
{
eroare = "Inserarea nu a putut fi efectuata.";
cout << "Inserarea nu a putut fi efectuata.";
getErrors(SQL_HANDLE_STMT, hStmt, eroare);
freeDbResources(hStmt, hEnv, hDbc);
return -1;
}
else if(SQL_SUCCEEDED(rc))
{
freeDbResources(hStmt, hEnv, hDbc);
return 1;
}
return -1;
}
Sper sa va fie utile.