× Aktuálně z oboru

Vychází Game Ready ovladače pro Far Cry 5 [ clanek/2018040603-vychazi-game-ready-ovladace-pro-far-cry-5/ ]
Celá zprávička [ clanek/2018040603-vychazi-game-ready-ovladace-pro-far-cry-5/ ]

Oracle C++ Call Interface – 4. Select a metadáta

[ http://programujte.com/profil/6101-zdenko-vrabel/ ]Google [ ?rel=author ]       [ http://programujte.com/profil/118-zdenek-lehocky/ ]Google [ ?rel=author ]       14. 12. 2007       17 364×

Neviem ako vy, ale ja som z toho, že sa Oracle vydal cestou Express edícií, nadšený a taktiež som nadšený z toho, ako je navrhnuté OCCI. Páči sa mi jeho jednoduchosť a ľahká pochopiteľnosť. Dúfam, že ste toho istého názoru a chápanie OCCI vám ide ako po masle. V tomto diely si ukážeme, ako dáta z databázy vyťahovať, ako spracovávať SELECT-y a povieme si aj niečo o metadátach.

Podstatou a hlavnou myšlienkou každej databázy je základná práca s dátami. Ich ukladanie, modifikovanie, mazanie a v neposlednom rade aj ich získavanie. Vo svete SQL sa dá povedať, že ide o INSERT, UPDATE, DELETE a SELECT. Ostatne prvky ako triggre, procedúry, funkcie už len ďalej rozširujú možnosti DB, no podstata stále ostáva. V predchádzajúcich dieloch sme si ukázali, ako vykonávať prvé 3 operácie pomocou OCCI. Chýba nám už len dáta získavať pomocou SELECT-ov. To sa realizuje podobne ako predchádzajúce operácie, rozdiel je iba v spracovaní výsledku.

Výsledok SELECT-u a trieda ResultSet

SELECT je určitá forma dotazu na DB, u ktorej je dôležitý práve jej výsledok. Výsledok nieje jednoduchá veličina, číslo alebo text. Ide o množinu dát reprezentujúcu stĺpce a riadky. To, ako vykonávať dotaz, ako bindovať hodnoty, už vieme. Čo však nevieme je to, ako pristupovať k tejto množine dát. Pre SELECT-y sa používa špeciálne na tuto činnosť určená funkcia executeQuery(). Funkcia vráti inštanciu triedy ResulSet. Práve trieda ResultSet nám umožní pristupovať k riadkom a stĺpcom.

Trieda ResultSet spracováva riadok po riadku tzv. fetchovaním. Fetchovanie nasledujúceho riadku sa vykonáva volaním funkcie next(). V prípade, že už nieje čo fetchovať a dostali sme sa na samotný koniec, funkcia next() vráti hodnotu ResultSet::END_OF_FETCH. Je to podobný princíp ako sa používa pri načítaní súborov dovtedy, kým sa nenarazí na EOF. Riadok ma určité stĺpce. ResultSet k stĺpcom pristupuje pomocou indexov. To znamená pomocou poradia v SELECT-e. Jednotlivé hodnoty tak vieme získať opäť pomocou členských funkcii ResultSet-u ako getInt(), getString() atd.

Spracovanie SELECT-u je rovnako jednoduchá vec ako vykonanie UPDATE-u, o čom sa môžete presvedčiť na nasledujúcom príklade. Príklad vypíše výsledok jednoduchého SELECT-u.

main.cpp:
#include <iostream>
#include <occi.h>

using namespace std;
using namespace oracle::occi;

#define USER  "OCCIUSER"
#define PASS  "****"
#define TNS   "XE"

int main()
{
	Environment* env;
	Connection*  con;
	Statement*   stm;
	ResultSet*   rs;
	string       meno;
	string       priezvisko;
	int          vek;


	try {
		//pripojenie na DB
		env = Environment::createEnvironment();
		con = env->createConnection(USER, PASS, TNS);

		//priprava a vykonanie Query
		stm = con->createStatement();
		stm->setSQL("SELECT MENO, PRIEZVISKO, VEK FROM OSOBY");
		rs = stm->executeQuery();
		
		//spracovanie dat
		while (rs->next() != ResultSet::END_OF_FETCH) {
			meno       = rs->getString(1);
			priezvisko = rs->getString(2);
			vek        = rs->getInt(3);

			cout << "fetched row" << endl;
			cout << " column MENO: " << meno << endl;
			cout << " column PRIEZVISKO: " << priezvisko << endl;
			cout << " column VEK:" << vek << endl;
		}

		//updratanie
		stm->closeResultSet(rs);
		env->terminateConnection(con);
		Environment::terminateEnvironment(env);

	} catch (SQLException& e) {
		cout << "ORACLE ERROR:" << e.getErrorCode() << " " << " - " << e.getMessage() << endl;
	}

	return 0;
}

Program jednoduchou formou vypíše cely obsah tabuľky OSOBY. V tomto programe je dôležitý cyklus spracovávania dát.

while (rs->next() != ResultSet::END_OF_FETCH) {
	...
}

Tento cyklus je vykonávaný dovtedy, kým sa nespracujú všetky dáta, čiže funkcia next() nevráti hodnotu ResultSet::END_OF_FETCH. Vo vnútri cyklu pracujeme už s konkrétnym riadkom.

...
meno       = rs->getString(1);
priezvisko = rs->getString(2);
vek        = rs->getInt(3);
...

Hodnoty meno a priezvisko získavame pomocou funkcie getString(), nakoľko ide o text. Hodnotu vek zase získavame volaním funkcie getInt(). Keďže sa k stĺpcom pristupuje pomocou indexov, je potrebne si uvedomiť v akom poradí sa v SELECT-e nachádzajú. V našom prípade je napríklad prvý stĺpec MENO, preto použijeme getString(1). Po spracovaní všetkých riadkov je potrebne vykonať určitú očistu od ResultSet-u, uvolniť pamäť. To vykonáme pomocou closeResultSet() funkcie statementu.

Metadáta – kľuč k potrebným informáciám

V príklade sme k dátam v jednotlivých stĺpcoch pristupovali pomocou indexov. Tento spôsob je fajn, keď vieme čosi o SELECT-e. Sú situácie, kedy však potrebujeme väčšiu dynamiku a nevieme nič o tom, koľko stĺpov ma riadok, akého typu sú a aké sú veľké. Týmto informáciám sa hovory metadáta. Na tento účel sa v OCCI nachádza rovnomenná trieda MetaData a trieda ResultSet disponuje funkciou getColumnListMetaData(), ktorá vracia vektor metadát. Každý prvok v tomto vektore reprezentuje stĺpec.

Trieda MetaData je postavená na dvoch základných informáciách. Na type metadát a atribútov metadát. Typ metadát určuje, aké informácie MetaData obsahuje, napríklad informácie o tabuľke (PTYPE_TABLE), stĺpci (PTYPE_COL) alebo procedúre (PTYPE_PROC). Každý typ metadát má svoje atribúty. Atribúty slúžia na prístup ku konkrétnym informáciám, ktoré potrebujeme vedieť, napríklad meno stĺpca (ATTR_NAME) alebo typ stĺpca (ATTR_DATA_TYPE). Teraz si upravíme náš kód tak, aby vypisoval obsah stĺpcov dynamickejšie, kde typ dát meno stĺpca bude získavané pomocou metadát.

main.cpp:
#include <iostream>
#include <vector>
#include <occi.h>

using namespace std;
using namespace oracle::occi;

#define USER  "OCCIUSER"
#define PASS  "****"
#define TNS   "XE"

void processResult(ResultSet* rs)
{
   vector<MetaData> meta_data = rs->getColumnListMetaData();

   //spracovanie riadkov
   while (rs->next() != ResultSet::END_OF_FETCH) {
      cout << "fetched row" << endl;

      //spracovanie stlpcov
      for (unsigned int i = 0; i < meta_data.size(); i++) {
         cout << "  column " << meta_data[i].getString(MetaData::ATTR_NAME);

         switch (meta_data[i].getInt(MetaData::ATTR_DATA_TYPE)) {
            case OCCI_SQLT_CHR:
               cout << ":" << rs->getString(i+1);
               break;

            case OCCI_SQLT_NUM:
               cout << ":" << rs->getInt(i+1);
               break;
         }
         cout << endl;
      }
   }
}

int main()
{
   Environment* env;
   Connection*  con;
   Statement*   stm;
   ResultSet*   rs;

   try {
      env = Environment::createEnvironment();
      con = env->createConnection(USER, PASS, TNS);

      stm = con->createStatement();
      stm->setSQL("SELECT MENO, PRIEZVISKO, VEK FROM OSOBY");

      rs = stm->executeQuery();
      processResult(rs);

      stm->closeResultSet(rs);
      env->terminateConnection(con);
      Environment::terminateEnvironment(env);

   } catch (SQLException& e) {
      cout << "ORACLE ERROR:" << e.getErrorCode() << " " << " - " << e.getMessage() << endl;
   }

   return 0;
}

Výsledok je prakticky ten istý, program opäť vypíše obsah tabuľky OSOBY. Dôležité u tohto programu je to, že už niesme viazaný na presné poradie stĺpcov v SELECT-e, čiže akonáhle by sme zmenili poradie, pridali novy stĺpec do SELECT-u, program na základe metadát vykoná korektný výpis.

Zmena nastala v spracovaní jednotlivých riadkov. Cele spracovanie riadkov som umiestnil do funkcie processResult(). Dôvodom je použitie práve metadáta. Ak si všimnete, metadáta sú alokované na zásobníku. Ak by som pracú z MetaData umiestnil do main() funkcie, kde je volané terminateEnvironment(), došlo by k segmentation fault chybe, pretože metadáta by sa uvoľňovali až po terminateEnfironment(). Ďalej bol pridaný cyklus, ktorý prechádza všetky metadáta vo vektore, ktoré obsahujú informácie o jednotlivých stĺpcov. Ďalej v kóde zisťujeme pomocou getString() hodnotu atribútu ATTR_NAME, čo je v konečnom dôsledku meno stĺpca.

         for (unsigned int i = 0; i < meta_data.size(); i++) {
            cout << "  column " << meta_data[i].getString(MetaData::ATTR_NAME);

Okrem mena stĺpca zisťujeme ešte typ stĺpca. Je to dôležité preto, aby sme vedeli, akým spôsobom k dátam pristupovať, čí sa jedna o číslo, text, alebo dátum. K tomuto zase slúži atribút ATTR_DATA_TYPE.

switch (meta_data[i].getInt(MetaData::ATTR_DATA_TYPE)) {
               case OCCI_SQLT_CHR:
                  cout << ":" << rs->getString(i+1);
                  break;

               case OCCI_SQLT_NUM:
                  cout << ":" << rs->getInt(i+1);
                  break;
}

Ten však nevracia textovú formu typu, ale int číslo. Preto sme použili funkciu getInt(). Toto číslo ďalej spracujeme pomocou switch-case podmienky. OCCI ma v header-och definície týchto čísel o aké typy ide. V tomto prípade ak pôjde o oracle typ VARCHAR2 , čiže OCCI_SQLT_CHR, dáta získame zo statementu pomocou getString(). Ak pôjde o oracle typ NUMBER, čiže OCCI_SQLT_NUM, dáta získame pomocou getInt().

Pomocou metadát môžeme zisťovať rôzne informácie o tabuľkách, samotnej databáze, schéme, procedúre. Spôsob je jednoduchý. Základ je získať metadáta. Objekt Connection ma pravé na tento účel funkciu getMetaData(). V nasej experimentálnej databáze mame s predchádzajúceho článku vytvorenú procedúru MYPROC. Metadáta tejto procedúry získame nasledujúcim riadkom:

MetaData mt_proc      = con->getMetaData("MYPROC", MetaData::PTYPE_PROC);

Ak budete pátrať v dokumentácii ORACLE, zistíte, že typ PTYPE_PROC obsahuje jeden zaujímavý atribút ATTR_LIST_ARGUMENTS. Ide o atribút, ktorý vráti opäť pole metadát reprezentujúcich jednotlivé argumenty. Keďže ide o pole, vektor MetaData, pristupujeme k nemu pomocou funkcie getVector().

vector<MetaData> args = mt_proc.getVector(MetaData::ATTR_LIST_ARGUMENTS);

Argumenty procedúr a funkcii majú množstvo atribútov. Nasledujúci krátky príklad pracuje len s niektorými a jeho úlohou je vypísať mena argumentov a ich mód pomocou atribútu ATTR_IOMODE, čí sú vstupné, výstupné alebo vstupne-výstupné.

main.cpp:
#include <iostream>
#include <vector>
#include <occi.h>

using namespace std;
using namespace oracle::occi;

#define USER  "OCCIUSER"
#define PASS  "****"
#define TNS   "XE"

void processMyprocAttrs(Connection* con)
{
   //spracovanie metadat procedury
   MetaData mt_proc      = con->getMetaData("MYPROC", MetaData::PTYPE_PROC);
   vector<MetaData> args = mt_proc.getVector(MetaData::ATTR_LIST_ARGUMENTS);

   //spracovanie metadat argumentov procedury
   for (unsigned int i = 0; i < args.size(); i++) {
      cout << "Parameter " << args[i].getString(MetaData::ATTR_NAME);

      switch (args[i].getInt(MetaData::ATTR_IOMODE)) {
         case 0:
            cout << " IN";
            break;

         case 1:
            cout << " OUT";
            break;

         case 2:
            cout << " IN/OUT";
            break;
      }
      cout << endl;
   }
}

int main()
{
   Environment* env;
   Connection*  con;

   try {
      //vytvorenie spojenia
      env = Environment::createEnvironment();
      con = env->createConnection(USER, PASS, TNS);

      processMyprocAttrs(con);

      //upratanie
      env->terminateConnection(con);
      Environment::terminateEnvironment(env);

   } catch (SQLException& e) {
      cout << "ORACLE ERROR:" << e.getErrorCode() << " " << " - " << e.getMessage() << endl;
   }

   return 0;
}

Popisovať tu každý typ metadát a každý atribút je mimo rámec článku, nakoľko sa takýmto spôsobom dá pristupovať k množstvu informácii. Teraz by ste už však mali vedieť o tom, že nejaké metadáta existujú a ako ich približne používať. Popis atribútov a typov už nájdete v ORACLE dokumentácii na www.oracle.com [ http://download-uk.oracle.com/docs/cd/B12037_01/appdev.101/b10778/metadata.htm#sthref570 ].


Článek stažen z webu Programujte.com [ http://programujte.com/clanek/2007053102-oracle-c-call-interface-4-select-a-metadata/ ].