Sunteți pe pagina 1din 256

DEZVOLTAREA APLICAŢIILOR DE BAZE

DE DATE POSTGRESQL CU INTERFAŢĂ


GRAFICĂ ÎN C++

DEZVOLTARE ÎN OPEN SOURCE


Procesare computerizată a textului şi imaginii
Procesare text: LIVIU ŞERBĂNESCU
Redactor: Viorica Cerasela POSTOLACHE

Editura Fair Partners


Sector 2, Bucureşti
telefon: 021.346.05.23
e-mail: editura@fairpartners.ro
http://www.fairpartners.ro

Descrierea CIP a Bibliotecii Naţionale a României


ŞERBĂNESCU, LIVIU
Dezvoltarea aplicaţiilor de baze de date PostgreSQL cu interfaţă
grafică în C++ / Liviu Şerbănescu. - Bucureşti : Fair Partners, 2014
Bibliogr.
ISBN 978-606-718-006-0

004.43
lector dr.ing. Liviu ŞERBĂNESCU

DEZVOLTAREA APLICAŢIILOR DE BAZE


DE DATE POSTGRESQL CU INTERFAŢĂ
GRAFICĂ ÎN C++

DEZVOLTARE ÎN OPEN SOURCE

Editura FAIR PARTNERS


Bucureşti, 2014
Manuale. Tratate. Monografii (vol. 142)

Editor şef:
prof. univ. dr. MIHAI POSTOLACHE
Universitatea ”Politehnica” din Bucureşti

Referenţi ştiinţifici:
prof. univ. dr. ing. ANGELICA BACIVAROV
Universitatea ”Politehnica” din Bucureşti
conf. univ. dr. ing. CIPRIAN LUPU
Universitatea ”Politehnica” din Bucureşti

©2014 Liviu Şerbănescu


CUPRINS

I.Introducere............................................................................................ 10
1.Baze de date şi sisteme de gestiune a bazelor de date...................................10
2.Structura PostgreSQL. Interfaţa pgAdmin3.......................................................11
2.1.Structura PostgreSQL............................................................................................... 11
2.2.Interfaţa pgAdmin3.................................................................................................. 13
3.Mediul CodeLite pentru dezvoltarea aplicaţiilor în C/C++..............................14
II.Limbajul SQL........................................................................................ 17
1.Tabele persistente şi tabele temporare.............................................................17
1.1.Crearea tabelelor...................................................................................................... 17
1.2.Ştergerea tabelelor................................................................................................... 19
1.3.Tabele temporare...................................................................................................... 19
2.Operaţii de editare ce implică un singur tabel..................................................20
2.1.Adăugarea înregistrărilor într-un tabel..................................................................20
2.2.Tipuri de date frecvent utilizate..............................................................................21
2.3.Adăugarea mai multor înregistrări dintr-un alt tabel..........................................21
2.4.Importul datelor în tabel.......................................................................................... 22
2.5.Actualizarea datelor dintr-un tabel........................................................................23
2.6.Ştergerea înregistrărilor dintr-un tabel..................................................................24
3.Operaţii de interogare ce implică un singur tabel............................................24
3.1.Preluarea datelor din tabel......................................................................................25
3.2.Căutarea parţială în şirurile de caractere..............................................................26
3.3.Funcţii de agregare................................................................................................... 26
3.4.Operatorul CAST....................................................................................................... 28
3.5.Expresii condiţionale................................................................................................. 28
3.6.Comenzi SQL ce nu implică tabele..........................................................................29
4.Tipuri de asocieri între înregistrările unor tabele.............................................29
4.1.Asocierea de tip unu la unu (1:1).............................................................................30
4.2.Asocierea de tip unu la mai mulţi (1:N)..................................................................30
4.3.Asocierea de tip mulţi la mulţi (M:N)......................................................................31
4.4.Crearea cheilor primare şi a cheilor externe..........................................................31
4.5.Asigurarea integrităţii referenţiale........................................................................31
5.Operaţii de editare cu mai multe tabele.............................................................32
5.1.Actualizarea datelor din tabele prin utilizarea unor date din alte tabele.........32
5.2.Ştergerea înregistrărilor din tabele prin utilizarea unor date din alte tabele...32
6.Operaţii de interogare cu mai multe tabele. Vederi........................................33
6.1.Combinarea interogărilor (UNION, EXCEPT, INTERSECT)...................................33
6.2.Interogarea mai multor tabele................................................................................34
7.Subinterogări...........................................................................................................38
8.Operaţii cu tablouri de date..................................................................................39
8.1.Tablouri unidimensionale......................................................................................... 39

5
8.2.Tablouri multidimensionale.....................................................................................40
8.3.Căutarea în tablouri.................................................................................................. 40
9.Operaţii cu date de tip XML..................................................................................41
10.Funcţii PostgreSQL...............................................................................................42
10.1.Operaţii cu data calendaristică.............................................................................42
10.2.Operaţii cu dată de tip oră, minut, secundă şi miimi de secundă......................43
10.3.Date de tip UUID / GUID......................................................................................... 44
10.4.Funcţii pentru înlocuirea caracterelor. Căutarea cu şi fără diacritice..............44
11.Tranzacţii. Interogări multiserver......................................................................46
12.Teste rezolvate – gestiune bibliotecă...............................................................48
III.Programarea la nivel de server – plpgSQL.................................52
1.Funcţii de tipul SQL.................................................................................................52
1.1.Funcţii de tip SQL ce nu preiau şi nu întorc nimic..................................................52
1.2.Funcţii SQL ce nu preiau nimic şi întorc un set de înregistrări.............................53
1.3.Funcţii SQL cu transfer prin argument şi cu returnare subset de înregistrări. . .54
1.4.Funcţii SQL cu transfer prin argument şi cu returnare de tip boolean...............55
2.Funcţii de tipul plpgSQL.........................................................................................55
3.Declararea variabilelor. Secvenţe de decizie.....................................................56
4.Parametri de intrare şi ieşire.................................................................................56
5.Implementarea notificărilor..................................................................................58
6.Structuri repetitive.................................................................................................59
7.Construcţia dinamică a interogărilor...................................................................61
8.Funcţii de tip trigger...............................................................................................63
9.Definirea de noi tipuri de date şi supraîncărcarea operatorilor SQL............66
10.Teste rezolvate......................................................................................................71
10.1.Funcţie pentru contorizarea timpilor de parcare................................................71
10.2.Calculul debitelor medii zilnice.............................................................................72
IV.Introducere în limbajul C.................................................................73
1.Organizarea programelor......................................................................................73
1.1.Structura unui program în limbajul C/C++.............................................................74
2.Variabile, alocări, tipuri de date, operatori C/C++............................................74
2.1.Noţiunile de variabilă şi constantă.........................................................................74
2.2.Tipuri de alocare a variabilelor................................................................................75
2.3.Tipuri de variabile...................................................................................................... 75
2.4.Operatorii C/C++....................................................................................................... 76
2.5.Teste........................................................................................................................... 77
3.Implementarea structurilor de control...............................................................77
3.1.Implementarea structurii secvenţiale.....................................................................77
3.2.Implementarea structurii de decizie (alternative, de selecţie)............................78
3.3.Implementarea structurilor repetitive (ciclice)......................................................80
4.Tablouri şi pointeri. Aritmetica pointerilor........................................................83
4.1.Alocarea dinamică. Noţiunea de pointer................................................................83

6
4.2.Noţiunea de tablou................................................................................................... 84
4.3.Alocarea dinamică a memoriei................................................................................85
4.4.Aritmetica pointerilor............................................................................................... 88
4.5.Teste........................................................................................................................... 88
5.Funcţii. Aria de vizibilitate a variabilelor.............................................................89
5.1.Noţiunea de funcţie.................................................................................................. 89
5.2.Structura unei funcţii................................................................................................ 90
5.3.Declararea, explicitarea şi apelul funcţiilor...........................................................90
5.4.Domeniul de vizibilitate al variabilelor..................................................................91
6.Transferul parametrilor în cadrul funcţiilor.......................................................92
6.1.Transferul parametrilor prin valoare......................................................................92
6.2.Transferul parametrilor prin adresă.......................................................................93
6.3.Transferul parametrilor prin referinţă...................................................................93
6.4.Funcţii cu număr variabil de argumente................................................................95
7.Structuri de date. Liste...........................................................................................96
7.1.Noţiunea de structură.............................................................................................. 96
7.2.Declaraţii de tip......................................................................................................... 96
7.3.Liste............................................................................................................................. 97
8.Funcţii recursive. Parcurgerea arborilor...........................................................101
9.Funcţii uzuale din bibliotecile C. Lucrul cu fişiere text..................................103
9.1.Operaţii cu şiruri de caractere...............................................................................103
9.2.Operaţii cu funcţii din biblioteca matematică....................................................104
9.3.Operaţii cu fişiere.................................................................................................... 105
10.Implementarea unui analizor sintactic pentru expresii matematice........105
V.Programarea orientată pe obiecte Limbajul C++....................109
1.Clase şi moşteniri..................................................................................................109
1.1.Noţiunea de clasă şi obiect....................................................................................109
1.2.Operatori specifici C++...........................................................................................109
1.3.Noţiunea de moştenire........................................................................................... 110
2.Constructori şi destructori..................................................................................110
2.1.Constructori............................................................................................................. 110
2.2.Destructori............................................................................................................... 111
2.3.Exemple.................................................................................................................... 111
2.4.Exemple privind ordinea de apel a constructorilor şi destructorilor................113
3.Modificatori şi specificatori de acces. Exemple..............................................115
3.1.Modificatori şi specificatori de acces....................................................................115
3.2.Exemplu privind accesul......................................................................................... 116
4.Funcţii de tip friend şi de tip static....................................................................118
4.1.Funcţii de tipul friend............................................................................................. 118
4.2.Exemplu cu funcţie de tip friend...........................................................................118
4.3.Membrii statici......................................................................................................... 119
4.4.Exemplu cu date de tip static şi funcţii de tip static...........................................120
5.Polimorfism, funcţii virtuale...............................................................................121

7
5.1.Obiecte polimorfice................................................................................................121
5.2.Variabile de tip pointer care punctează la clasele derivate..............................121
5.3.Funcţii virtuale........................................................................................................ 121
5.4.Exemple – polimorfism şi clase abstracte............................................................122
6.Moştenirea multiplă.............................................................................................124
6.1.Exemplu pentru moştenire multiplă simplă.........................................................125
6.2.Exemplu privind moştenirea de tip diamant........................................................125
7.Supraîncărcarea operatorilor..............................................................................126
7.1.Restricţii privind supraîncărcarea operatorilor...................................................126
7.2.Sintaxa formei de apel...........................................................................................127
7.3.Exemple privind supraîncărcarea operatorilor....................................................127
8.Fluxuri de intrare/ieşire.......................................................................................133
8.1.Fluxuri de intrare/ieşire şi obiecte standard........................................................133
8.2.Operaţii de intrare/ieşire cu fişiere.......................................................................134
9.Introducere în funcţii şi clase template............................................................135
10.Tratarea excepţiilor............................................................................................137
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor.........138
1.Biblioteca wxWidgets şi dezvoltarea interfeţelor grafice în C++................138
1.1.Structura unei aplicaţii...........................................................................................138
1.2.Gestiunea evenimentelor.......................................................................................141
1.3.Utilizarea claselor generate de template-uri......................................................143
1.4. Exemplu cu primitive grafice şi evenimente.......................................................148
2.Interfaţarea client-server PostgreSQL în limbajul C/C++..............................152
2.1.Descrierea bibliotecii libpq....................................................................................152
2.2.Exemplu privind apelul funcţiilor libpq în cadrul unui cod C standard.............155
2.3.Exemplu privind utilizarea bibliotecii libpq în cadrul wxWidgets.....................156
3.Generarea în limbajul C a rapoartelor în fişiere format PDF........................161
3.1.Introducere în utilizarea bibliotecii libHARU.......................................................161
3.2.Funcţii uzuale oferite de bibliotecă......................................................................161
3.3.Exemplu de construcţie a unui fişier PDF.............................................................164
VII.Dezvoltarea unor module pentru logistica stocurilor.........168
1.Arhitectura aplicaţiei – LogX...............................................................................168
1.1.Structura de bază a aplicaţiei................................................................................168
1.2.Fluxul informaţional............................................................................................... 170
1.3.Structura tabelelor PostgreSQL............................................................................172
2.Dezvoltarea modulelor SQL/plpgSQL...............................................................175
2.1.Dezvoltarea modulelor SQL/plpgSQL pentru editare........................................175
2.2.Dezvoltarea modulelor plpgSQL pentru rapoarte..............................................176
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul.......177
3.1.Fereastra de acces iFACC....................................................................................... 177
3.2.Fereastra ce conţine panoul cu butoane iFPB.....................................................179
3.3.Fereastra de editare intrări (recepţii) iFEI...........................................................181
3.4.Fereastra de editare ieşiri/livrări iFEBP...............................................................185

8
3.5.Fereastra editare/selecţie nomenclator iFNOM.................................................188
3.6.Fereastra pentru rapoarte / selecţie stoc iFSTSEL..............................................191
4.Dezvoltarea interfeţei SQL/plpgSQL – controale grafice.............................197
4.1.Clasa lxCtrl............................................................................................................... 198
4.2.Clasa lxComboBox.................................................................................................. 199
4.3.Clasa lxDPC.............................................................................................................. 199
4.4.Clasa lxEditN........................................................................................................... 200
4.5.Clasa lxEditT............................................................................................................ 201
4.6.Clasa lxGrid.............................................................................................................. 201
5.Integrarea modulelor aplicaţiei LogX...............................................................203
ANEXE...................................................................................................... 206
1.Secvenţe din sintaxa SQL/postgreSQL simplificată.......................................206
2.Principalele tipuri de variabile în limbajul C.....................................................210
3.Operatori C/C++....................................................................................................211
4.Codul sursă SQL pentru aplicaţia LogX.............................................................213
5.Codul sursă pentru biblioteca LX.......................................................................223
6.Codul sursă pentru aplicaţia LogX.....................................................................230
Bibliografie............................................................................................. 254

9
I.Introducere

I. Introducere
Notă: Toate aplicaţiile necesare realizării testelor şi
dezvoltării aplicaţiei din această carte sunt open sources şi de
asemenea sunt de tip cross-platform (rulează pe sisteme
UNIX/Linux, Windows şi MAC). Cartea se limitează la dezvoltarea
aplicaţiilor de tip desktop, aplicaţii ce prezintă un grad de
siguranţă ridicat comparativ cu aplicaţiile de tip WEB.

1. Baze de date şi sisteme de gestiune a bazelor de date


Informaţiile, indiferent de structura acestora, pot fi
stocate pe diverse suporturi fizice. Cel mai simplu mod de stocare
şi acces a informaţiilor îl asigură fişierele de tip text (sunt
recunoscute/interpretate codurile pentru spaţiu, tab şi linie
nouă). În acest caz informaţiile nu sunt corelate şi nu se oferă
funcţii de acces la date, iar dimensiunea fişierelor este limitată
de tipul partiţiei suportului de stocare. Există numeroase moduri
de a stoca informaţiile, însă puţine dintre acestea s-au impus în
timp.
Termenul de bază de date (database) reprezintă o colecţie de
informaţii corelate, relaţiile logice dintre aceste informaţii şi
tehnicile de prelucrare corespunzătoare (căutare, sortare, ştergere,
inserare, modificare etc.).
Sistemul de gestiune a bazelor de date S.G.B.D. (Database
Management System − DBMS) reprezintă sistemul de programe care
permite accesarea şi realizarea de operaţii asupra bazei de date de
către utilizatori (ex.: aplicaţia software PostgreSQL).
Cel mai răspândit model de baze de date este modelul
relaţional (pentru volum mare de date şi tipuri de date simple) −
structura de bază a datelor este aceea de relaţie–tabel, limbajul
SQL (Structured Query Language) este specializat în comenzi de
manipulare la nivel de tabel.
Majoritatea SGBD-urilor asigură lucrul cu sisteme multi-
utilizator. Putem avea baze de date centralizate (toate datele,
inclusiv SGBD, sunt memorate pe un singur host) sau baze de date
distribuite (atât datele, cât şi SGBD-ul sunt memorate pe mai multe
host-uri, conectate printr-o reţea de comunicaţie).
➢ Arhitectura client-server (cele actuale) cuprinde:
✗ Serverul (back-end): SGBD-ul şi baza de date;
✗ Clienţii (front-end): program (programe) de aplicaţie ce
asigură interfaţarea utilizator–bază de date.
➢ Arhitectură locală (sistemele vechi utilizează fişiere comune
mapate în interiorul reţelei) – pentru controlul accesului se
folosesc fişiere sistem, iar întreruperea conexiunii avea

10
1.Baze de date şi sisteme de gestiune a bazelor de date

repercusiuni asupra integrităţii datelor.


O bază de date trebuie să asigure:
✔ abstractizarea datelor (date unice);
✔ integrarea datelor (controlul corelaţiei datelor);
✔ integritatea datelor (respectarea restricţiile de
integritate a datelor pe parcursul operaţiilor SQL);
✔ securitatea datelor (controlul accesului la baza de date);
✔ partajarea datelor (datele pot fi accesate de mai mulţi
utilizatori, eventual în acelaşi timp).
Un SGBD trebuie să asigure următoarele activităţi:
✗ definirea şi descrierea structurii bazei de date – se
realizează printr-un limbaj propriu, limbaj de definire a
datelor (LDD);
✗ încărcarea datelor în baza de date – se realizează prin
comenzi în limbaj propriu (limbaj de manipulare a datelor
(LMD));
✗ accesul la date – se realizează prin comenzi specifice din
limbajul de manipulare a datelor (SQL). Accesul la date se
referă la operaţiile de interogare şi actualizare.
Interogarea presupune vizualizarea, consultarea, editarea de
situaţii de ieşire (rapoarte, liste, regăsiri punctuale).
Actualizarea presupune trei operaţiuni: adăugare, modificare
efectuată prin respectarea restricţiilor de integritate ale
BD şi ştergere;
✗ întreţinerea bazei de date – se realizează prin utilitare
proprii ale SGBD;
✗ securitatea datelor – se referă la asigurarea confidenţi-
alităţii datelor prin autorizarea şi controlul accesului la
date pe mai multe nivele, criptarea datelor.

2. Structura PostgreSQL. Interfaţa pgAdmin3

2.1. Structura PostgreSQL


PostgreSQL utilizează un model client/server. O sesiune
PostgreSQL constă în mai multe procese:
➢ Un proces server, care gestionează fişierele bazei de date,
acceptă conexiuni la bazele de date dinspre aplicaţiile client,
realizează acţiunile cerute de client pe bazele de date.
Serverul de baze de date se numeşte postgres;
➢ Aplicaţia client a utilizatorului care doreşte să execute
operaţii în cadrul bazei de date. Aplicaţiile client pot fi
aplicaţii de tip web sau aplicaţii de tip desktop.
Pentru ca un client să se conecteze la server-ul de baze de
date sunt necesari următorii parametri de conectare: IP-ul server-

11
I.Introducere

ului pe care se află server-ul PostgreSQL, portul pe care rulează


server-ul PostgreSQL (implicit este 5432), numele bazei de date ce
va fi accesată, numele utilizatorului (alias-ul) şi parola.
Serverul PostgreSQL poate gestiona conexiuni multiple
concurente de la clienţi. Pentru aceasta el porneşte câte un nou
proces pentru fiecare conexiune. Din acel moment, clientul şi un
proces al server-ului comunică fără intervenţia procesului iniţial
postgres. Astfel procesul principal al server-ului continuă să
aştepte noi clienţi. PostgreSQL foloseşte un model client/server
numit proces per-user. O sesiune Postgres constă din cooperarea
următoarelor procese (programe): un proces, rezident în memorie, de
supervizare (postmaster), aplicaţia frontend a userului şi unul sau
mai multe servere backend de baze de date (procesul postgres). Un
singur proces postmaster conduce o colecţie de baze de date pe o
singură gazdă. Aplicaţiile frontend care doresc să acceseze o bază
de date printr-o instalare apelează la funcţiile din cadrul
librăriei libpq.
Atât serverul SGBD postgreSQL, cât şi interfaţa de tip
desktop pgAdmin3 pot fi descărcate de la adresa www.postgresql.org.
În momentul instalării, PostgreSQL îşi creează un cont
postgres care nu este şters la dezinstalare. O nouă reinstalare
fără ştergerea contului postgres (se poate şterge ca orice alt cont
de pe calculator) va cere vechea parolă pentru contul postgres.
Fiecare server PostgreSQL are asignat:
☑ Un IP static specific calculatorului/staţiei. În cazul
accesării server-ului PostgreSQL chiar de pe calculatorul pe
care rulează server-ul, acest IP va fi localhost (de ex.:
127.0.0.1);
☑ Un port (implicit 5432) ce reprezintă un număr ataşat
proceselor ce implementeză SGBD-ul PostgreSQL (se poate
modifica în fişierul de configurare). Aceste porturi sunt
gestionate de sistemul de operare;
☑ O parolă de administrare la nivel SGBD.
În ceea ce priveşte bazele de date:
➢ Un server de baze de date poate gestiona mai multe baze de date;
➢ Fiecare bază de date poate avea propria parolă de administrare;
➢ Fiecare bază de date poate avea propriul encoding (implicit UTF8);
➢ Fiecare bază de date poate avea propriul tablespace (implicit
pg_default). Acesta oferă posibilitatea stocării bazei de date pe
mai multe suporturi fizice sau pe mai multe staţii de lucru;
➢ De obicei, o aplicaţie corespunde unei singure baze de date cu
excepţia cazurilor când sunt importate date din afara aplicaţiei;
➢ Fiecare server are o bază de date implicită, denumită postgres,
utilizată pentru administrare;
➢ Fiecare bază de date cuprinde una sau mai multe scheme. Schemele
grupează mai multe obiecte, inclusiv tabele;
➢ Fiecare bază de date conţine câte o schemă (grupare de obiecte)

12
2.Structura PostgreSQL. Interfaţa pgAdmin3

denumită public. Utilizarea schemei public nu necesită prefixarea


denumirii obiectelor cu denumirea schemei (public.nume_obiect).
În vederea accesului, avem grupuri de acces şi utlizatori,
ambele sunt referite prin comanda ROLE (diferenţa constă în faptul că
doar utilizatorii se pot conecta la server).
➢ Un grup de acces cuprinde mai mulţi utlilizatori;
➢ Drepturile de acces sunt la nivel de tabel sau alt obiect, schemă
sau bază de date;
➢ Drepturile de acces se pot atribui fie grupurilor de acces, fie
utilizatorilor individuali;
➢ Odată ce a fost asignat un drept de acces pentru un grup, toţi
utilizatorii din acel grup vor avea acel drept;
➢ Elementele dintr-un server (baze de date, tabele, funcţii,
secvenţe, vederi, coloane, reguli, indecşi, operatori, scheme etc.)
sunt denumite generic obiecte.
2.2. Interfaţa pgAdmin3
Utilitarul pgAdmin3 este o interfaţă de tip desktop (pentru
web există phppgAdmin) ce permite gestionarea server-ului de baze
de date PostgreSQL. Acesta este utilizat doar pentru întreţinerea
bazelor de date, el nu este necesar în rularea unei aplicaţii de
baze de date.
Versiunile mai noi ale pgAdmin3 nu sunt compatibile cu toate
versiunile vechi de postgreSQL, de exemplu pgAdmin3 (sau altfel
scris pgAdmin III) versiunea 1.18 nu este compatibil cu versiunile
PostgreSQL mai vechi decât versiunea 8.4.
În continuare sunt prezentate pe scurt principalele ferestre
din cadrul interfeţei pgAdmin3.
☑ Fereastra Object browswer cuprinde ierarhia de obiecte din
cadrul PostgreSQL. În vârful ierarhiei se află obiectul
server, urmat de bazele de date împreună cu utilizatorii şi
grupurile de acces. Bazele de date se ramifică pe următorul
nivel în scheme, iar acestea se ramifică în tabele, funcţii,
secvenţe, operatori etc. La rândul lor, tabelele se ramifică
în coloane etc. În File>Options...>Browser>Display se pot
seta obiectele care să fie afişate sau nu. Prin realizarea
unui click pe butonul dreapta al mouse-ului, având obiectul
selectat, se pot stabili proprietăţile obiectului. Opţiunea
de vizualizare a tabelelor permite editarea acestora, cu
condiţia să existe definită o cheie primară pentru acel
tabel. Prin selecţia bazei de date şi click pe butonul
dreapta se poate realiza back-upul bazei de date sau
restaurarea acesteia. Dacă salvarea datelor se realizează
pentru a fi utilizată de un alt server PostgreSQL, recomand
ca acesta să fie în format plain (în cod SQL), iar la
salvare să fie bifată opţiunea Use insert comands;

13
I.Introducere

☑ Fereastra SQL pane afişează codul SQL corespunzător creării


obiectului selectat. Această fereastră este utilă pentru
înţelegerea modului de construcţie al obiectelor. De obicei,
pentru construcţia obiectului nu este necesar întregul cod
afişat, acesta conţinând şi partea implicită din cadrul
comenzii de creare a obiectului;
☑ Prin selecţia File>Open postgresql.conf şi căutarea efectivă
a fişierului postgresql.conf în directorul data, se deschide
fişierul ce permite setările pentru server-ul de date (ex.:
regional setting, numărul maxim de conexiuni simultane,
portul utilizat etc.);
☑ Prin selecţia File>Open postgresql.conf şi căutarea efectivă
a fişierului pg_hba.conf în directorul data, se deschide
fişierul ce permite întroducerea adreselor de IP pentru
clienţii ce accesează serverul. Orice adresă pentru IPv4
este urmată de ”/32”. Dacă dorim să dăm acces unei reţele,
se introduce adresa de reţea urmată de simbolul “/” şi de
numărul de biţi semnificativi pentru adresa reţelei. Prin
punerea simbolului “*” se oferă acces pentru orice host de
la orice adresă IP, însă acest lucru nu este recomandat;
☑ Fereastra Query este fereastra de lucru, este fereastra în
care lansăm comenzile SQL şi urmărim rezultatele (se poate
lansa şi prin Ctrl+E). Este deosebit de utilă pentru
întreţinerea bazelor de date. În această fereastră putem
construi bazele de date şi de asemenea le putem testa.
Săgeata verde este pentru lansarea comenzilor din fereastră
sau din blocul selectat (în caz de selecţie vor fi executate
doar comenzile din blocul selectat). Comenzile sunt separate
între ele prin simbolul semicolon “;”.

3. Mediul CodeLite pentru dezvoltarea aplicaţiilor în C/C++


O aplicaţie C/C++ poate fi scrisă utilizând orice editor de
texte, cu condiţia ca acel editor să nu adauge simboluri specifice
acestuia în fişierele cu cod C/C++. Editoarele specializate pentru
limbaje de programare oferă facilităţi de editare specifice
limbajului. De exemplu, în momentul în care scriem numele unui
obiect şi vrem să facem referire la un element din cadrul
obiectului, apare automat lista cu elementele din cadrul obiectului
şi dezvoltatorul de aplicaţie doar selectează acel obiect (în
cadrul CodeLite, colecţia cu aceste date se realizează prin seleţia
Workspace>Retag workspace, iar datele cu aceste corespondenţe sunt
memorate în fişierul temporar cu extensia *.tag). Mediile pentru
dezvoltarea aplicaţiilor pe lângă editor conţin legături cu
rutinele pentru compilare şi link-editare precum şi bibliotecile
standard (en.cppreference.com/w/ sau www.delorie.com/djgpp/doc/libc/)

14
3.Mediul CodeLite pentru dezvoltarea aplicaţiilor în C/C++

CodeLite utilizează pentru compilare şi link-editare utilitarul


g++, comun şi altor medii de dezvoltare cross-platform.
Din punct de vedere al aplicaţiei, se defineşte un workspace
(spaţiu de lucru) în care sunt salvate fişierele, iar în cadrul
unui workspace avem proiecte (projects), fiecare proiect corespunde
unei aplicaţii sau biblioteci. Fiecare proiect este alcătuit din
unul sau mai multe fişiere (de obicei fişiere sursă). În momentul
în care realizăm dublu-click pe pictograma cu numele proiectului
aceasta va deveni verde, iar în acel moment proiectul este
selectat.
Din punct de vedere al organizării ferestrelor, distingem în
stânga o fereastră de gestiune pentru workspace-ul curent, o
fereastră în dreapta unde sunt deschise fişierele cu codul sursă şi
o fereastră în partea de jos în care sunt detaliate rezulatele în
urma diverselor acţiuni, inclusiv mesajele generate în urma
compilării şi link-editării proiectului.
Pentru compilarea şi link-editarea unui proiect, urmată de
lansarea aplicaţiei rezultate se utilizează comanda Ctrl + F9.
Mediul CodeLite oferă posibilitatea utilizării unui debug-er
pentru a putea urmări evoluţia conţinutului variabilelor pas cu
pas. În acest sens, se stabileşte o linie de întrerupere în
aplicaţie (clik în dreapta numărului liniei) şi se apasă tasta F5
sau săgeata verde (timpii de aşteptare cresc semnificativ). În
modul debug-er se poate intra în funcţii (Step in) sau se poate
ieşi din acestea (Step out). Se observă apariţia unei ferestre
Debuger ce conţine mai multe tab-uri. Unul de aceste taburi este
Locals şi conţine variabilele din zona de întrerupere. În tab-ul
Watches putem vizualiza anumite variabile sau putem evalua
expresii. În tab-ul Call Stack putem vizualiza stiva din cadrul
aplicaţiei pentru acel moment (în limbajul C stiva este de tip
LIFO – ultimul intrat, primul ieşit).
La nivelul unui proiect se pot crea directoare virtuale
(acestea nu există pe suportul fizic) pentru a grupa pe categorii
fişierele (ex.: include, source, res etc.).
Mediul de lucru CodeLite se poate descărca de la adresa
http://codelite.org/, iar după instalarea acestuia se instalează
biblioteca wxWidgets de la adresa https://www.wxwidgets.org/ (la
instalarea sub S.O. Windows se va limita denumirea directorului la
wxwidgets – fără celelalte puncte, spaţii etc.). De asemenea poate
fi utilă descărcarea utilitarului FormBuilder de la adresa
http://sourceforge.net/projects/wxformbuilder/ .
Pentru aplicaţiile de tip consolă (cele în care funcţia
main() apare explicit), când se creează un nou proiect se
selectează: NewProject> Console> SimpleExecutable(g++). Pentru
aplicaţii în care utilizăm wxWidgets, vom selecta: NewProject>
GUI> Executable(wxWidgets enabled), iar dacă utilizăm şi

15
I.Introducere

wxFormBuider vom selecta: NewProject> GUI> Executable(wxWidgets +


wxFB frame). În ultimul caz vom selecta din cadrul proiectului nou
fişierul resources/gui.fbp şi vom efectua dublu click pe acesta
pentru deschiderea acestuia în wxFormBuilder. După efectuarea
modificărilor în wxFormBuilder, se va apăsa tasta F8 pentru
generarea codului corespunzător interfeţei create. Practic, vom
deriva această clasă creată de wxFormBuider şi vom lucra cu clasa
derivată.

16
II.Limbajul SQL

L
Notă: Pentru o înţelegere mai bună, recomand scrierea comenzilor
SQL (din dreptunghiurile cu colţuri rotunjite) în fereastra pentru
execuţia comenzilor SQL din PgAdmin3 şi testarea acestora prin
modificarea datelor şi/sau a structurii comenzilor.
În PostgreSQL schemele, tabelele, coloanele, secvenţele,
vederile, cheile etc. sunt considerate obiecte, fiecare având un
număr unic asociat (OID). Limbajul SQL este “case insensitive” (nu
ţine cont de literă mare sau mică) însă denumirile obiectelor sunt
“case sensitive”. Denumirile obiectelor ce conţin litere mari vor
fi trecute între ghilimele.

1. Tabele persistente şi tabele temporare


Tabelele sunt entităţi logice de reprezentare a informaţiei,
stocate asemănător foilor de calcul tabelare. Pentru bazele de date
avansate, ele nu au o corespondenţă fizică cu un anumit fişier.
Coloanele dintr-un tabel corespund câmpurilor (fields) din cadrul
bazelor de date, iar rândurile/liniile din tabel corespund
înregistrărilor (records). Crearea unei baze de date începe cu
definirea şi crearea tabelelor necesare.
1.1. Crearea tabelelor
În PostgreSQL tabelele aparţin unei scheme, dacă nu este
specificată schema, atunci se face referire automată la schema
public (schemă implicită). Două scheme din cadrul aceleiaşi baze de
date pot să conţină tabele cu denumiri identice.
Referirea la un tabel se va face prin nume_schemă.
nume_tabel. Dacă schema este public, atunci nu mai este necesar
numele schemei.
Începem prin crearea unui tabel cu două coloane, prima
coloană va conţine date de tip întreg (tipul int sau integer), iar
a doua va conţine date de tip alfanumeric (şir de caractere) având
maxim 20 de caractere. Comanda DROP şterge obiectul, iar opţiunea
CASCADE specifică faptul că sunt şterse şi legăturile acestui
obiect cu alte obiecte.
DROP
DROP SCHEMA
SCHEMA IF
IF EXISTS
EXISTS pic
pic CASCADE;
CASCADE; create
create schema
schema pic;
pic;
create
create table
table pic.t1(a
pic.t1(a int,
int, bb varchar(20));
varchar(20));
Sintaxa (simplificată):

17
II.Limbajul SQL

CREATE
CREATE [TEMPORARY|TEMP]
[TEMPORARY|TEMP] TABLE
TABLE
table_name
table_name ([{
([{ column_name
column_name data_type
data_type
[[ column_constraint
column_constraint [[ ...
... ]] ]}])
]}])
Notă: parantezele drepte din expresie indică o expresie opţională,
iar acoladele o secvenţă ce se repetă şi este separată prin
virgulă. De asemenea simbolul “|” semnifică “sau”, iar
column_constraint [ ... ] poate fi:
[[ CONSTRAINT
CONSTRAINT constraint_name
constraint_name ]]
{NOT
{NOT NULL|NULL|UNIQUE
NULL|NULL|UNIQUE index_parameters|
index_parameters|
PRIMARY
PRIMARY KEY
KEY index_parameters
index_parameters || CHECK
CHECK (( expression
expression )}
)}
unde:
• PRIMARY KEY reprezintă cheia primară de ordonare (câmpul sau
câmpurile după care se face ordonarea);
• NOT NULL semnifică faptul că nu se acceptă valori vide (fără
date) pentru câmpul respectiv;
• CHECK permite verificări suplimentare (de exemplu: temp_max<50).
Ex.: Crearea unui tabel cu denumirea parm în cadrul schemei c2,
câmpul data este de tipul dată calendaristică și va fi inițializat
cu data curentă atunci când se adaugă o nouă înregistrare, la fel
se procedează și pentru câmpul moment, câmp ce conţine ora, minutul
secunda și fracţiunea de secundă. Tabelul conține valorile măsurate
dintr-un proces.
CREATE
CREATE TABLE
TABLE pic.parm(data
pic.parm(data date
date DEFAULT
DEFAULT CURRENT_DATE,
CURRENT_DATE,
moment
moment time DEFAULT CURRENT_TIME, modul
time DEFAULT CURRENT_TIME, modul varchar(10),
varchar(10),
pct
pct varchar(50),par
varchar(50),par varchar(15),val
varchar(15),val numeric,um
numeric,um char(10),
char(10),
idx
idx serial
serial PRIMARY
PRIMARY KEY);
KEY);
Notă: Câmpul sau câmpurile care constituie cheia primară nu pot
avea valori NULL
CREATE
CREATE TABLE
TABLE pic.hidro
pic.hidro
(( afluent
afluent character
character varying(80),
varying(80),
temp_min
temp_min integer,
integer, nivel_min
nivel_min int,
int,
temp_max
temp_max integer,
integer, nivel_max
nivel_max int,
int,
precipitatii
precipitatii numeric,
numeric,
datac
datac date,
date,
CONSTRAINT
CONSTRAINT k_hidro
k_hidro PRIMARY
PRIMARY KEY(afluent,
KEY(afluent, datac),
datac),
CONSTRAINT
CONSTRAINT hidro_check
hidro_check CHECK
CHECK (temp_min
(temp_min << temp_max),
temp_max),
CONSTRAINT
CONSTRAINT hidro_precipitatii_check
hidro_precipitatii_check
CHECK
CHECK (precipitatii
(precipitatii >=>= 0::numeric),
0::numeric),
CONSTRAINT
CONSTRAINT hidro_temp_max_check CHECK (temp_max << 50)
hidro_temp_max_check CHECK (temp_max 50)
);
);
Operatorul rezoluţie “::” este utilizat pentru specificarea
tipului unui câmp. Tipul int este acelaşi cu integer.
Cheia primară poate fi constituită din unul sau mai multe câmpuri,
iar declaraţia acesteia are forma:

18
1.Tabele persistente şi tabele temporare

CONSTRAINT
CONSTRAINT nume_cheie_primara
nume_cheie_primara PRIMARY
PRIMARY KEY(câmp1,câmp2,câmp3,..)
KEY(câmp1,câmp2,câmp3,..)
Crearea unui tabel cu structură identică se va realiza prin
instrucţiunile: create table pic.hidro2 as table pic.hidro;
create table pic.hidro2 as table pic.hidro;

1.2. Ştergerea tabelelor


Tabelele pot fi şterse (atenţie: inclusiv structura
acestora) prin utilizarea instrucţiunii DROP TABLE.
Ex.: DROP DROP TABLE tablename
DROP TABLE pic.tabel Sintaxa: DROP TABLE tablename
TABLE pic.tabel
Se recomandă testarea verificării existenţei tabelului
înainte ca acesta să fie şters, altfel dacă tabelul nu există,
execuţia instrucţiunii DROP va genera o eroare şi va fi oprită
execuţia întregului bloc de instrucţiuni.
Ex.: DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.tabel
pic.tabel
Sintaxa: DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS tablename
tablename

1.3. Tabele temporare


Acestea NU sunt sub o anumită schemă, sunt VIZIBILE DOAR de
către utilizatorul care le-a şi creat. Tabelul temporar se şterge
automat în momentul delogării utilizatorului/închiderii conexiunii.
Este recomandat ca iniţial să verificăm dacă tabelul există, iar

Ex.: DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS tx1;
tx1;
CREATE
CREATE TEMPORARY
TEMPORARY TABLE
TABLE tx1(a
tx1(a serial,
serial, bb int);
int);
--sau
--sau
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS tx1;
tx1;
CREATE
CREATE TEMP TABLE tx1(a
TEMP TABLE tx1(a serial,
serial, bb int);
int);
Pentru testarea vizibilităţii tabelului temporar se poate
realiza testul următor:
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS tx1;
tx1;
CREATE
CREATE TEMPORARY
TEMPORARY TABLE
TABLE tx1(a
tx1(a serial,
serial, bb int);
int);
--
-- sau
sau CREATE
CREATE TEMP
TEMP TABLE
TABLE tx1(a
tx1(a serial,
serial, bb int);
int);
INSERT
INSERT INTO
INTO tx1(b)
tx1(b) VALUES
VALUES (12),(22),(1),(2);
(12),(22),(1),(2);
SELECT
SELECT ** FROM
FROM tx1;
tx1; SELECT
SELECT USER;--
USER;-- afiseaza
afiseaza utilizatorul
utilizatorul curent
curent
--
-- crează
crează un
un nou
nou utilizator
utilizator bubu
bubu
DROP
DROP ROLE
ROLE IF
IF EXISTS
EXISTS bubu;
bubu; –-–- se
se presupune
presupune că
că există
există acest
acest drept
drept
CREATE
CREATE ROLE
ROLE bubu
bubu PASSWORD
PASSWORD '1234';
'1234';
SET
SET ROLE
ROLE bubu;
bubu; -–-– sau
sau delogare
delogare şişi logare
logare cu
cu noul
noul utilizator
utilizator
SELECT
SELECT USER;
USER; SELECT
SELECT ** FROM
FROM tx1;
tx1; --tabelul
--tabelul tx1
tx1 nunu mai
mai există
există
--
-- delogare
delogare sisi logare
logare cucu vechiul
vechiul utilizator
utilizator sau
sau
SET
SET ROLE
ROLE postgres
postgres –- –- sau
sau utilizatorul
utilizatorul afişat
afişat lala început
început
SELECT
SELECT USER;
USER; SELECT
SELECT ** FROM
FROM tx1;
tx1;
La reconectare, tabelul temporar nu mai există, în momentul

19
II.Limbajul SQL

închiderii conexiunii/delogării acesta este distrus. Se pot crea


legături cu celelalte tabele (foreign key).
În PostgreSQL atât utilizatorii, cât şi grupurile de acces
poartă denumirea de ROLE, deosebirea între acestea constă în
atributul can login pentru utilizatori (grupurile nu se pot loga şi
nu au parolă/cod acces).
Notă: În momentul distrugerii tabelului temporar, acesta nu
trebuie să fie tabel master faţă de tabelele persistente din
cadrul schemelor. Tabelele temporare de tip master pot fi în
relaţii master-slave doar cu tabelele temporare.

2. Operaţii de editare ce implică un singur tabel


2.1. Adăugarea înregistrărilor într-un tabel
Operaţia prin care sunt adăugate înregistrări într-un tabel se
realizează prin instrucţiunea INSERT.
INSERT
INSERT INTO
INTO table
table [[ (( column
column [,
[, ...]
...] )) ]]
Sintaxa simplificată: {{ DEFAULT
DEFAULT VALUES
VALUES || VALUES
VALUES
(( {{ expression
expression || DEFAULT
DEFAULT }} [,
[, ...]
...] ))
[,
[, ...]
...] || query
query }}
CREATE
CREATE TABLE
TABLE pic.t2(a1
pic.t2(a1 serial
serial PRIMARY
PRIMARY KEY,b1
KEY,b1 varchar(20)
varchar(20) DEFAULT
DEFAULT 'V1'
'V1'
,c1 date DEFAULT CURRENT_DATE, d1 time DEFAULT CURRENT_TIME);
,c1 date DEFAULT CURRENT_DATE, d1 time DEFAULT CURRENT_TIME);
--adauga
--adauga doua
doua inregistrari
inregistrari
INSERT
INSERT INTO pic.t2(b1)
INTO pic.t2(b1) VALUES
VALUES ('un
('un test1'),('un
test1'),('un test2');
test2');
--pentru
--pentru verificare
verificare
SELECT
SELECT ** from
from pic.t2;
pic.t2;
Pentru verificare s-a utilizat instrucţiunea: SELECT * from
c1.t2; ceea ce are ca rezultat afişarea tuturor câmpurilor şi
înregistrărilor din cadrul tabelului.
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t1;
pic.t1;
CREATE
CREATE TABLE
TABLE pic.t1(a1
pic.t1(a1 serial
serial PRIMARY
PRIMARY KEY,
KEY, a2
a2 varchar(20),
varchar(20), a3
a3 int);
int);
--adauga trei inregistrări
--adauga trei inregistrări
INSERT
INSERT INTO
INTO pic.t1(a2,a3)
pic.t1(a2,a3) VALUES
VALUES ('A1',11),('C1',22),('B1',33);
('A1',11),('C1',22),('B1',33);
--sau
--sau
INSERT
INSERT INTO
INTO pic.t1
pic.t1 VALUES
VALUES (101,'A1',11),(102,'C1',22),(103,'B1',33);
(101,'A1',11),(102,'C1',22),(103,'B1',33);
--pentru verificare
--pentru verificare
SELECT
SELECT ** FROM
FROM pic.t1;
pic.t1;
În ultima linie de inserare din Ex.2, numărul, tipul şi
ordinea valorilor introduse trebuie să corespundă cu cea din CREATE
TABLE pentru c2.t1

20
2.Operaţii de editare ce implică un singur tabel

INSERT
INSERT INTO
INTO pic.hidro(afluent,precipitatii,datac,
pic.hidro(afluent,precipitatii,datac, temp_min,temp_max)
temp_min,temp_max)
VALUES
VALUES ('Tuşnad',
('Tuşnad', 0.25,
0.25, '30.12.2007',
'30.12.2007', 4,
4, 8),
8),
('Tuşnad',
('Tuşnad', 0.25,
0.25, '31.12.2007',
'31.12.2007', 5,
5, 8),('Malnaş',
8),('Malnaş', NULL,
NULL, '31.12.2007',3,9),
'31.12.2007',3,9),
('Sebeş',
('Sebeş', 0.11,
0.11, '29.12.2007',4,
'29.12.2007',4, 8),('Sebeş',
8),('Sebeş', 0.11,
0.11, '30.12.2007',3,
'30.12.2007',3, 7);
7);

2.2. Tipuri de date frecvent utilizate


Considerăm un exemplu în care apar câteva tipuri de date uzuale
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t3;
pic.t3;
CREATE
CREATE TABLE
TABLE pic.t3(a
pic.t3(a int,b
int,b numeric,c
numeric,c boolean,d
boolean,d varchar(20),
varchar(20),
ee char(20),
char(20), ff text,
text, gg date,
date, hh time,
time, ii timestamp,
timestamp, jj uuid,
uuid,
kk xml,
xml, ll bytea,
bytea, mm serial
serial PRIMARY
PRIMARY KEY);
KEY);
--şirurile
--şirurile de de caractere
caractere şi şi data
data calendaristică
calendaristică se se introduc
introduc între
între apostrof.
apostrof.
INSERT
INSERT INTO
INTO pic.t3(a,b,c,d,f,g,h,k)
pic.t3(a,b,c,d,f,g,h,k) VALUES VALUES (34,56.89,false,
(34,56.89,false,
'abcde','xyz','2012-08-31','11:23:51.0','<test>abcd</test>');
'abcde','xyz','2012-08-31','11:23:51.0','<test>abcd</test>');
SELECT
SELECT ** FROM
FROM pic.t3;
pic.t3;
--întoarce
--întoarce data,ora
data,ora curenta,utlizator
curenta,utlizator
select
select CURRENT_DATE,
CURRENT_DATE, CURRENT_TIME,CURRENT_USER;
CURRENT_TIME,CURRENT_USER;
INSERT
INSERT INTO
INTO pic.t3(a,b,c,d,f,g,h,k)
pic.t3(a,b,c,d,f,g,h,k) VALUES VALUES
(10,1.2,true,'un
(10,1.2,true,'un sir',NULL,CURRENT_DATE,CURRENT_TIME,NULL),
sir',NULL,CURRENT_DATE,CURRENT_TIME,NULL),
(11,NULL,NULL,'alt
(11,NULL,NULL,'alt sir',NULL,NULL,NULL,
sir',NULL,NULL,NULL,
'<test1><t1>tttx</t1><t2>tty</t2></test1>');
'<test1><t1>tttx</t1><t2>tty</t2></test1>');
--
-- in
in caz
caz de
de eroare
eroare nu
nu se
se modifica
modifica nici
nici unun camp
camp
--
-- dacă
dacă şirul
şirul XML
XML conţine
conţine erori
erori înregistrarea
înregistrarea nu nu va
va fi
fi validată/salvată
validată/salvată
INSERT
INSERT INTO
INTO pic.t3(a,b,c,d,f,g,h,k)
pic.t3(a,b,c,d,f,g,h,k) VALUES VALUES
(11,NULL,NULL,'alt
(11,NULL,NULL,'alt sir',NULL,NULL,NULL,
sir',NULL,NULL,NULL,
'<test1><t1>tttx</t1><t2>tty</xt2></test1>');
'<test1><t1>tttx</t1><t2>tty</xt2></test1>');
--
-- apare
apare oo EROARE
EROARE deoarece
deoarece sirul
sirul XML
XML NU
NU este
este CORECT
CORECT
Tipul serial este echivalent cu tipul autoincrement din alte
sisteme de gestiune a bazelor de date, practic câmpul este de tip
integer şi are asociat un obiect de tip sequence.
2.3. Adăugarea mai multor înregistrări dintr-un alt tabel
Presupunem că există un alt tabel, denumit hidro2 cu o
structură asemănătoare cu primul tabel (hidro):
INSERT
INSERT INTO
INTO pic.hidro2(afluent,temp_min,temp_max,
pic.hidro2(afluent,temp_min,temp_max, precipitatii,datac)
precipitatii,datac)
SELECT
SELECT afluent,temp_min,temp_max,precipitatii,
afluent,temp_min,temp_max,precipitatii,
datac
datac FROM
FROM pic.hidro
pic.hidro WHERE
WHERE datac
datac >> '20.11.2007';
'20.11.2007';
În cazul în care cele două tabele au aceeaşi structură de date şi
aceeaşi ordine a câmpurilor, se poate omite detalierea câmpurilor
din cadrul instrucţiunii INSERT INTO.
DELETE
DELETE FROM
FROM pic.hidro
pic.hidro WHERE
WHERE datac
datac >> '20.11.2007';--
'20.11.2007';-- ENTER
ENTER
INSERT
INSERT INTO
INTO pic.hidro
pic.hidro
SELECT
SELECT ** FROM
FROM pic.hidro2
pic.hidro2 WHERE
WHERE datac
datac >> '20.11.2007';
'20.11.2007';

21
II.Limbajul SQL

Dacă se inserează toate câmpurile, în ordinea dată la crearea


DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t4;
pic.t4;
CREATE
CREATE TABLE pic.t4(regiune varchar(50),localitate
TABLE pic.t4(regiune varchar(50),localitate varchar(50),
varchar(50),
nr_locuitori
nr_locuitori int,
int,
CONSTRAINT
CONSTRAINT k_t4
k_t4 PRIMARY
PRIMARY KEY(regiune,localitate));
KEY(regiune,localitate));
INSERT
INSERT INTO
INTO pic.t4
pic.t4 VALUES('R1','LR1',100),('R1','LR2',15),
VALUES('R1','LR1',100),('R1','LR2',15),
('R1','L3',20),('R2','L1',150),('R2','LX2',25),('R2','L3',10),
('R1','L3',20),('R2','L1',150),('R2','LX2',25),('R2','L3',10),
('R2','L4',5),('R3','LR1',100),('R3','L2',15);
('R2','L4',5),('R3','LR1',100),('R3','L2',15);
SELECT
SELECT ** FROM
FROM pic.t4;
pic.t4;
tabelului, nu mai este necesară specificarea acestora.

2.4. Importul datelor în tabel


Se pot copia fişiere întregi cu date într-un tabel. Aceste
fişiere, de regulă, sunt fişiere format CSV (comma-separated
values) − fişierele text având valorile separate prin virgulă, iar
valorile ce conţin caracterul “,” sunt scrise între ghilimele.
COPY
COPY pic.hidro
pic.hidro FROM
FROM ’D:/test/temphidro.csv’
’D:/test/temphidro.csv’ WITH
WITH csv;
csv;
În acest exemplu, tabelul hidro este populat cu datele din
fişierul temphidro.csv. Sintaxa simplificată:
COPY
COPY tablename
tablename [[ (( column
column [,[, ...]
...] )) ]] FROM
FROM ’filename’
’filename’
[[ WITH
[[ WITH ] ]
[[ DELIMITER
DELIMITER [[ ASAS ]] ’delimiter’
’delimiter’ ]]
[[ NULL
NULL [ AS
[ AS ]] ’null
’null string’
string’ ]]
[[ CSV
CSV [[ HEADER
HEADER ]] [[ QUOTE
QUOTE [[ AS
AS ]] ’quote’
’quote’ ]]
unde:
✗ DELIMITER specifică un alt delimitator între valorile din
fişierul CSV (implicit fiind virgula);
✗ NULL specifică şirul ce reprezintă valoarea NULL (implicit
nu se scrie nimic – adică după virgulă urmează tot virgulă);
✗ CSV specifică faptul că este vorba de un fişier format CSV
(pot exista transferuri şi cu fişiere binare sau alte
fişiere text – aceste cazuri nu sunt tratate în prezentul
curs);
✗ HEADER specifică dacă primul rând din cadrul fişierului
format CSV este reprezentat de numele coloanelor/câmpurilor;
✗ QUOTE specifică caracterele între care sunt puse valorile ce
conţin şi caracterul separator (implicit caracterul “).
De obicei, această comandă este utilizată la preluarea iniţială a
datelor, date existente în alte baze de date sau în fişiere de tip
XLS sau text. Comanda COPY adaugă noile date la cele vechi. Există
şi comanda (asemănătoare) pentru export a datelor dintr-un tabel
sau rezultat al unei interogări într-un fisier CSV.
Fişierele CSV trebuie să se afle pe suportul fizic de

22
2.Operaţii de editare ce implică un singur tabel

stocare (ex.:HDD) în aria de vizibilitate a contului postgres (cont


creat automat la instalarea server-ului bazei de date PostgreSQL).
2.5. Actualizarea datelor dintr-un tabel
Actualizarea/modificarea datelor deja existente (chiar dacă
celula din tabel nu are valori, adică este NULL) se realizează cu
instrucţiunea UPDATE.
Comanda UPDATE actualizează valorile din tabelul dat, pentru
înregistrările rezultate în urma aplicării condiţiei din cadrul
WHERE, cu valorile şi pentru câmpurile explicitate în secţiunea
SET.
UPDATE
UPDATE pic.hidro2
pic.hidro2 SET
SET temp_min
temp_min == temp_min-1,
temp_min-1,
temp_max
temp_max == temp_min
temp_min ++ 20,
20, precipitatii
precipitatii == 00
WHERE
WHERE (afluent
(afluent == 'Malnaş')
'Malnaş') AND
AND (datac
(datac >> '1.07.2007')
'1.07.2007')
AND
AND (datac
(datac << '31.08.2008');
'31.08.2008');
Sunt modificate doar valorile câmpurilor date de SET pentru
înregistrările date de WHERE
UPDATE
UPDATE [[ ONLY
ONLY ]] table
table [[ [[ AS
AS ]] alias
alias ]]
Sintaxa simplificată: SET{ column = {expression | DEFAULT}}
SET{ column = {expression | DEFAULT}}
[,...][FROM
[,...][FROM fromlist][WHERE
fromlist][WHERE condition]
condition]
unde:
✗ ONLY este utilizat în cazul tabelelor ierarhizate pentru a
nu modifica şi înregistrările din “tabelele copil” ce depind
de tabelul respectiv (nu sunt tratate în această carte);
✗ FROM specifică alte tabele utilizate în condiţia WHERE;
✗ DEFAULT – preia valorile implicite sau NULL dacă nu sunt
specificate.
Presupunem că vrem să utilizăm datele dintr-un alt tabel în vederea
actualizării. În acest sens, constituim următorul tabel:
drop
drop table
table if
if exists
exists pic.rauri;
pic.rauri;
CREATE
CREATE TABLE
TABLE pic.rauri(rau
pic.rauri(rau varchar(100),
varchar(100), afluent
afluent varchar(80),
varchar(80),
CONSTRAINT k_rauri PRIMARY KEY(rau,afluent));
CONSTRAINT k_rauri PRIMARY KEY(rau,afluent));
INSERT
INSERT INTO
INTO pic.rauri
pic.rauri VALUES('Argeş','Neajlov'),('Olt','Malnaş'),
VALUES('Argeş','Neajlov'),('Olt','Malnaş'),
('Olt','Hurez'),('Olt','Sebeş'),('Olt','Tuşnad'),('Argeş','Tutana'),
('Olt','Hurez'),('Olt','Sebeş'),('Olt','Tuşnad'),('Argeş','Tutana'),
('Mureş','Târnava'),('Mureş','Borzia'),('Mureş','Fântânele');
('Mureş','Târnava'),('Mureş','Borzia'),('Mureş','Fântânele');
În acest caz, comanda de actualizare va fi:
UPDATE
UPDATE pic.hidro
pic.hidro SET
SET temp_min
temp_min == temp_min
temp_min -- 1,
1,
temp_max
temp_max == temp_min
temp_min ++ 20,
20, precipitatii
precipitatii == DEFAULT
DEFAULT
FROM
FROM pic.rauri
pic.rauri
WHERE(hidro.afluent
WHERE(hidro.afluent == rauri.afluent
rauri.afluent ))
AND(data
AND(data >'1.07.2007')
>'1.07.2007') ANDAND (data<'31.08.2007');
(data<'31.08.2007');
Alte exemple de utilizare:

23
II.Limbajul SQL

--
-- pune
pune valoarea
valoarea 14 14 acolo
acolo unde
unde câmpul
câmpul a2a2 are
are valoarea
valoarea 'C1'
'C1'
update
update pic.t1
pic.t1 SET
SET a3
a3 == 14
14 WHERE
WHERE a2a2 == 'C1';
'C1';
--
-- poate
poate să
să nu
nu fie
fie afectata
afectata nicio
nicio inregistrare
inregistrare sau sau
--
-- pot
pot fi
fi modificate
modificate toate
toate înreg.
înreg.
--
-- ORDONARE
ORDONARE după
după mai
mai multe
multe coloane
coloane
select
select ** from
from pic.t1
pic.t1 order
order byby a1a1 ASC,
ASC, a2
a2 DESC;
DESC;
-- se poate seta si cu valoarea
-- se poate seta si cu valoarea NULL NULL
UPDATE
UPDATE pic.t1
pic.t1 SET
SET a3
a3 == NULL
NULL WHERE
WHERE a2='B1'
a2='B1' AND
AND a3a3 >> 100;
100;
UPDATE pic.t1 SET a3 = 77 WHERE a3
UPDATE pic.t1 SET a3 = 77 WHERE a3 IS NULL; IS NULL;
--
-- adauga
adauga valoarea
valoarea 11 lala toate
toate inregistrarile
inregistrarile al al caror
caror camp
camp a3
a3 conţine
conţine
-- o valoare impară (a3%2 este diferit de zero,
-- o valoare impară (a3%2 este diferit de zero, adica TRUE) adica TRUE)
UPDATE
UPDATE pic.t1
pic.t1 SET
SET a3
a3 == a3
a3 ++ 11 WHERE
WHERE a3
a3 %% 22 <>
<> 0;
0;
-- concatenare
-- concatenare
UPDATE
UPDATE pic.t1
pic.t1 SET
SET a2
a2 == a2
a2 ||
|| '_Q'
'_Q' WHERE a3 <=
WHERE a3 <= 100;
100;

2.6. Ştergerea înregistrărilor dintr-un tabel


Se pot şterge înregistrări din tabele
Ex.: DELETE FROM pic.hidro ; şterge toate înregistrările din tabelul
DELETE FROM pic.hidro ;
hidro.
Ex.: DELETE
DELETE FROM
FROM pic.hidro
pic.hidro WHERE
WHERE data
data << '20.11.2007';
'20.11.2007';
şterge toate
înregistrările anterioare datei specificate.
Sintaxa simplificată:
DELETE
DELETE FROM
FROM table[USING
table[USING usinglist][WHERE
usinglist][WHERE condition]
condition]
unde USING specifică alte tabele utilizate în condiţia WHERE.
De exemplu, dorim să ştergem toate înregistrările ce conţin
afluenţii Oltului:
DELETE
DELETE FROM
FROM pic.hidro2
pic.hidro2 USING
USING pic.rauri
pic.rauri
WHERE
WHERE hidro2.afluent
hidro2.afluent == rauri.afluent
rauri.afluent AND
AND rauri.rau='Olt';
rauri.rau='Olt';

3. Operaţii de interogare ce implică un singur tabel


Instrucţiunea SELECT permite formularea unei varietăţi de
comenzi SQL chiar şi fără a implica tabele.
Select
Select 5;5; --
-- afisează
afisează valoarea
valoarea 55
SeLecT
SeLecT 5+4;
5+4; --
-- afişează
afişează rezultatul
rezultatul expresiei
expresiei :: 99
--
--instrucţiunile
instrucţiunile SQLSQL nu
nu depind
depind de
de litera
litera mare
mare sau
sau mică
mică (case
(case insensitive)
insensitive)
Select
Select 33 ++ 5;
5; --
-- operatorul
operatorul de de adunare
adunare
select
select '3'||'5';
'3'||'5'; --concatenare
--concatenare şiruri
şiruri
SELECT
SELECT 1717 >>
>> 2;
2; ---- afişează
afişează rezultatul
rezultatul operaţiei
operaţiei de de deplasare
deplasare
--
-- cu
cu 22 biţi
biţi la
la dreapta
dreapta valorii
valorii 17 17
select
select current_date-5;
current_date-5; --afişează
--afişează datadata calendaristică
calendaristică cu cu 55 zile
zile în
în urmă
urmă

24
3.Operaţii de interogare ce implică un singur tabel

3.1. Preluarea datelor din tabel


Preluarea datelor din tabel (interogarea bazei de date) se
realizează prin utilizarea instrucţiunii SELECT.
Notă: Nu contează dacă codul SQL este scris pe un singur rând sau
pe mai multe rânduri şi nici dacă între cuvinte există un spaţiu
sau mai multe. Se recomandă scrierea astfel încât codul să se
citească cât mai uşor. Comenzile transmise către server sunt
separate prin “semicolon” (;).
Ex.:preia întregul tabel hidro SELECT
SELECT ** FROM
FROM pic.hidro;
pic.hidro;
(Operatorul * specifică preluarea tuturor câmpurilor)
Ex.:preia doar câmpurile specificate în interogare
SELECT
SELECT afluent,
afluent, datac
datac FROM
FROM pic.hidro;
pic.hidro;
✗ preia numai ce îndeplineşte condiţiile din cadrul “WHERE”
(condiţii la nivel de înregistrare)
✗ ordonează datele descrescător
SELECT
SELECT afluent,
afluent, datac,
datac, precipitatii
precipitatii
FROM
FROM pic.hidro
pic.hidro
WHERE((temp_min
WHERE((temp_min >=0>=0 )) AND
AND (temp_max
(temp_max <10
<10 ))
)) OR
OR (precipitatii>0)
(precipitatii>0)
ORDER
ORDER BY
BY datac
datac DESC,
DESC, afluent;
afluent;
✗ grupează datele după anumite câmpuri în vederea realizării
anumitor operaţii (ex.:calc. mediei temp. min.)
SELECT
SELECT afluent,
afluent, avg(temp_min)
avg(temp_min) FROM
FROM pic.hidro
pic.hidro GROUP
GROUP BY
BY afluent;
afluent;
✗ aplică condiţii la nivel de grup (ex.:doar oraşele cu media
temp. min. > 5)
SELECT
SELECT afluent,
afluent, avg(temp_min)
avg(temp_min)
FROM
FROM pic.hidro
pic.hidro GROUP
GROUP BY
BY afluent
afluent HAVING
HAVING avg(temp_min)
avg(temp_min) >> 5;
5;
✗ aplică condiţii/restricţii atât la nivel de grup, cât şi la
nivel de înregistrare
SELECT
SELECT afluent,
afluent, avg(temp_min)
avg(temp_min) WHERE
WHERE data
data >> '20.11.2007'
'20.11.2007'
FROM
FROM pic.hidro
pic.hidro GROUP
GROUP BY
BY afluent
afluent HAVING
HAVING avg(temp_min)
avg(temp_min) >> 5;
5;
Notă: Toate câmpurile din cadrul SELECT trebuie să se regăsească
în GROUP BY, altfel nu este posibilă gruparea acestora.
✗ efectuează operaţii între câmpuri şi afişează rezultatul
într-un câmp nou
SELECT
SELECT afluent,
afluent, temp_min,
temp_min, temp_max,
temp_max,
(temp_min+temp_max)/2.0
(temp_min+temp_max)/2.0 ASAS media
media FROM
FROM pic.hidro;
pic.hidro;
✗ întoarce un număr maxim de înregistrări (ex.: primele 10
obţinute în urma ordonării)
SELECT
SELECT afluent,
afluent, data,
data, precipitatii
precipitatii FROM
FROM pic.hidro
pic.hidro
ORDER
ORDER BY
BY data,
data, afluent
afluent LIMIT
LIMIT 10;
10;

25
II.Limbajul SQL

3.2. Căutarea parţială în şirurile de caractere


Căutarea parţială în şirurile de caractere este asigurată de
funcţiile LIKE (case sensitive) şi ILIKE (case insensitive).Sintaxa
(simplificată):
Ex.
Ex. expresie
expresie LIKE
LIKE
SELECT
SELECT [[ ALL
ALL || DISTINCT
DISTINCT ]] Rezultat
** || expression
expression [[ ASAS output_name
output_name ]] Rezultat
'abcd'
'abcd' LIKE
LIKE 'abcd'
'abcd' truetrue
[[ FROM
FROM from_item
from_item [, [, ...]
...] ]] 'abcd'
[[ WHERE
WHERE condition
condition ]] 'abcd' LIKE
LIKE 'a%'
'a%' true
true
'abcd'
'abcd' LIKE
LIKE '_b__'
'_b__' truetrue
[[ GROUP
GROUP BY
BY expression
expression [, [, ...]
...] ]] 'abcd'
[[ HAVING
HAVING condition
condition [,[, ...]
...] ]] 'abcd' LIKE
LIKE 'abc'
'abc' false
false
'abcd'
'abcd' LIKE
LIKE '_b%'
'_b%' true
true
[[ ORDER
ORDER BY
BY expression
expression [[ ASC
ASC || DESC
DESC ]] 'abcd'
[[ NULLS
NULLS {{ FIRST
FIRST || LAST
LAST }} ]]
]] 'abcd' LIKE
LIKE '__b%'
'__b%' false
false
Nota:În
Nota:În loc
loc de
de 'abcd'
'abcd' va
va
[[ LIMIT
LIMIT {{ count
count || ALL
ALL }} ]] fi
fi denumirea
denumirea câmpului
câmpului
DROP
DROP TABLE
TABLE IF IF EXISTS
EXISTS pic.t5;
pic.t5;
create
create table
table pic.t5(a1
pic.t5(a1 serial
serial PRIMARY
PRIMARY KEY,a2
KEY,a2 varchar(20),a3
varchar(20),a3 int);
int);
INSERT INTO pic.t5(a2,a3) VALUES ('A1',11),('C1',22),('B1',33);
INSERT INTO pic.t5(a2,a3) VALUES ('A1',11),('C1',22),('B1',33);
SELECT
SELECT ** FROM
FROM pic.t5;
pic.t5;
SELECT
SELECT t5.* FROM
t5.* FROM pic.t5;
pic.t5;
SELECT
SELECT a1,a2,a3
a1,a2,a3 FROM
FROM pic.t5;
pic.t5;
SELECT
SELECT t5.a1,
t5.a1, t5.a2,
t5.a2, t5.a3
t5.a3 FROM
FROM pic.t5;
pic.t5;
SELECT
SELECT t5.a2
t5.a2 FROM
FROM pic.t5;
pic.t5;
SELECT
SELECT a1,a2,a3
a1,a2,a3 FROM
FROM pic.t5
pic.t5 ORDER
ORDER BY
BY a2;
a2;
SELECT
SELECT a1,a2,a3
a1,a2,a3 FROM
FROM pic.t5
pic.t5 ORDER
ORDER BY
BY a2
a2 DESC,a1
DESC,a1 ASCASC ;;
INSERT INTO pic.t5(a2,a3) VALUES ('B1',21),('B2',24),('D1',30);
INSERT INTO pic.t5(a2,a3) VALUES ('B1',21),('B2',24),('D1',30);
--afiseaza
--afiseaza doardoar inregistrarile
inregistrarile al al caror
caror camp
camp a2
a2 incepe
incepe cucu lit.B
lit.B
SELECT * FROM pic.t5 WHERE a2 LIKE
SELECT * FROM pic.t5 WHERE a2 LIKE 'B%'; 'B%';
--
-- daca
daca nunu tine
tine cont
cont de
de litera
litera mare
mare mica
mica (case
(case insensitive)
insensitive)
SELECT * FROM pic.t5 WHERE a2 ILIKE
SELECT * FROM pic.t5 WHERE a2 ILIKE 'b%'; 'b%';
--afişează
--afişează toate
toate înreg.
înreg. care
care incep
incep cu
cu BB sau
sau bb si
si ptr.care
ptr.care a3<30
a3<30
SELECT * FROM pic.t5 WHERE a2 ILIKE 'b%'
SELECT * FROM pic.t5 WHERE a2 ILIKE 'b%' AND a3<30;AND a3<30;
SELECT
SELECT ** FROM
FROM pic.t5
pic.t5 WHERE
WHERE a3<30
a3<30 AND
AND a2a2 ILIKE
ILIKE 'b%';
'b%';
SELECT
SELECT * FROM pic.t5 WHERE (a2 ILIKE 'b%') AND
* FROM pic.t5 WHERE (a2 ILIKE 'b%') AND (a3<30);
(a3<30);

3.3. Funcţii de agregare


Funcţiile de agregare permit operaţiile de mai jos pe
grupuri de înregistrări. Un exemplu de structură a comenzii privind
utilizarea funcţiilor de agregare este:
SELECT
SELECT câmpGrup1,…,câmpGrupS
câmpGrup1,…,câmpGrupS etc,funcţieAgregare1,...funcţieAgregareT
etc,funcţieAgregare1,...funcţieAgregareT
FROM tabele
FROM tabele
WHERE
WHERE filtru
filtru la
la nivel
nivel de
de înregistrare
înregistrare şi
şi legături
legături între
între tabele
tabele
GROUP
GROUP BY câmpGrup1, …,câmpGrupS HAVING filtru la nivel de
BY câmpGrup1, …,câmpGrupS HAVING filtru la nivel de grup;
grup;
Câmpurile din cadrul instrucţiunii SELECT trebuie să se
regăsească în instrucţiunea GROUP BY.
Funcţii de agregare ce pot fi aplicate asupra câmpurilor în

26
3.Operaţii de interogare ce implică un singur tabel

cadrul SELECT:
• avg(expresie): media aritmetică a expresiei;
• count(expresie): numărul de înregistrări pentru care valoarea expresiei nu este
nulă, de ex. count(*):total înregistrări;
• max(expresie): valoarea maximă pentru expresie;
• min(expresie): valoarea minimă pentru expresie;
• sum(expresie): suma expresiei;
Notă: (expresie) poate fi un câmp sau rezultatul unor operaţii
aplicate mai multor câmpuri
Ex.1:
--
-- SUMA
SUMA
SELECT
SELECT sum(a3)
sum(a3) from
from pic.t1;
pic.t1; select
select sum(a3)
sum(a3) as
as xx from
from pic.t1;
pic.t1;
--MEDIA ARITMETICA
--MEDIA ARITMETICA
select avg(a3) as media from pic.t1;
select avg(a3) as media from pic.t1;
--
-- numărul
numărul total
total de
de înreg.
înreg.
select
select count(*) as nr_t
count(*) as nr_t from
from pic.t1;
pic.t1;
Ex.2:
/*suma
/*suma după
după câmpul
câmpul a2
a2 atunci
atunci când
când acesta
acesta începe
începe cu
cu BB sau
sau b*/
b*/
SELECT
SELECT a2,SUM(a3) AS suma FROM pic.t1 WHERE a2 ILIKE 'b%' GROUP
a2,SUM(a3) AS suma FROM pic.t1 WHERE a2 ILIKE 'b%' GROUP BY
BY a2;
a2;
-- calculeaza media aritmetica ptr. a3 grupata
-- calculeaza media aritmetica ptr. a3 grupata dupa a2 dupa a2
SELECT
SELECT a2,
a2, AVG(a3)
AVG(a3) AS
AS media
media FROM
FROM pic.t1
pic.t1 GROUP
GROUP BY
BY a2;
a2;
-- determina val max ptr. a3 grupata dupa
-- determina val max ptr. a3 grupata dupa a2 a2
SELECT
SELECT a2,
a2, MAX(a3)
MAX(a3) AS
AS maximum
maximum FROM
FROM pic.t1
pic.t1 GROUP
GROUP BY
BY a2;
a2;
-- determina val max ptr a3 ptr. intregul
-- determina val max ptr a3 ptr. intregul tabel tabel
SELECT
SELECT MAX(a3)
MAX(a3) AS
AS maximum2
maximum2 FROM
FROM pic.t1;
pic.t1;
-- calc. numarul de inregistrari
-- calc. numarul de inregistrari
SELECT
SELECT COUNT(*)
COUNT(*) FROM
FROM pic.t1;
pic.t1;
--calc.
--calc. nr.valori identice
nr.valori identice pentru
pentru câmpul
câmpul a2,
a2, grupate
grupate după
după a2
a2
SELECT
SELECT a2,COUNT(a2) as nr_val_identice FROM pic.t1 GROUP BY
a2,COUNT(a2) as nr_val_identice FROM pic.t1 GROUP BY a2;
a2;
Ex.3:
--numărul
--numărul dede locuitori
locuitori din
din fiecare
fiecare regiune
regiune
SELECT
SELECT regiune,
regiune, SUM(nr_locuitori)
SUM(nr_locuitori) as as nrRegLoc
nrRegLoc FROM
FROM pic.t4
pic.t4 GROUP
GROUP BYBY
regiune;
regiune;
/*
/* campurile
campurile din
din GROUP
GROUP BY
BY vor
vor fi
fi campurile
campurile din
din cadrul
cadrul SELECT
SELECT (cu
(cu
exceptia functiilor de agregare)*/
exceptia functiilor de agregare)*/
--afiseaza
--afiseaza DOAR
DOAR regiunile
regiunile ce ce au
au peste
peste 150
150 de
de locuitori
locuitori
SELECT
SELECT regiune, SUM(nr_locuitori) as nrRegLoc FROM
regiune, SUM(nr_locuitori) as nrRegLoc FROM pic.t4
pic.t4 GROUP
GROUP BYBY
regiune
regiune HAVING
HAVING SUM(nr_locuitori)
SUM(nr_locuitori) >> 150;150;
--
-- WHERE
WHERE se
se referă
referă la
la FIECARE
FIECARE înregistrare
înregistrare în în parte
parte
--
-- HAVING
HAVING se
se referă
referă la
la rezultatul
rezultatul unei
unei funcţii
funcţii dede agregare
agregare
--(sum,avg,count,min,max,etc)
--(sum,avg,count,min,max,etc)
SELECT
SELECT localitate
localitate FROM
FROM pic.t4;
pic.t4;
--
-- se va elimina afisarea de
se va elimina afisarea de mai
mai multe
multe ori
ori aa înregistrărilor
înregistrărilor identice
identice
SELECT
SELECT DISTINCT
DISTINCT localitate
localitate FROM
FROM pic.t4;
pic.t4;
--
-- DISTINCT
DISTINCT -- elimină
elimină inregistrările
inregistrările multiple
multiple ---- pastreaza
pastreaza oo singura
singura
inregistrare
inregistrare

27
II.Limbajul SQL

Un exemplu de utilizare simultană a instrucţiunilor HAVING


şi WHERE − afişează numărul total de locuitori pe regiuni, luând în
considerare numai localităţile a căror denumire se termină cu 1 şi
care nu depăşesc 120 locuitori pe regiune.
SELECT
SELECT regiune,
regiune, SUM(nr_locuitori)
SUM(nr_locuitori) as
as nrRegLoc
nrRegLoc FROM
FROM pic.t4
pic.t4 WHERE
WHERE
localitate LIKE '%1' GROUP BY regiune HAVING SUM(nr_locuitori) <=
localitate LIKE '%1' GROUP BY regiune HAVING SUM(nr_locuitori) <=
120;
120;
S-a realizat mai întâi filtrarea, dată prin instrucţiunea
WHERE, la nivel de înregistrare şi apoi s-au realizat sumele la
nivel de grup.
3.4. Operatorul CAST
Este utilizat pentru conversia tipurilor de date, în vederea
evaluării unor expresii în care intră datele respective sau pentru
adăugarea acestora în tabele ce au un alt format de date.
CAST
CAST (( expression
expression AS
AS type
type ))
SELECT
SELECT afluent,
afluent, cast(temp_min
cast(temp_min as
as numeric),
numeric),
cast(precipitatii
cast(precipitatii asas integer)
integer) as
as prec_int,
prec_int,
cast(temp_max
cast(temp_max as as varchar(15))
varchar(15)) FROM
FROM pic.hidro;
pic.hidro;
Un caz aparte de cast este funcţia to_date(text, text), care
realizează conversia din şir de caractere în format de tip dată
calendaristică.
INSERT
INSERT INTO
INTO pic.hidro(afluent,
pic.hidro(afluent, datac)
datac)
VALUES('Tutana',
VALUES('Tutana', to_date('29.05.1996','dd.mm.yyyy'));
to_date('29.05.1996','dd.mm.yyyy'));
Aceeaşi operaţie este asigurată de utilizarea operatorului
rezoluţie “::”. Exemplul de mai sus devine:
SELECT
SELECT afluent,
afluent, temp_min::numeric,
temp_min::numeric, precipitatii::integer
precipitatii::integer as
as prec_int,
prec_int,
temp_max::varchar(15)
temp_max::varchar(15) FROM
FROM pic.hidro;
pic.hidro;
Un alt exemplu în care sunt prezentate în paralel cele două forme:
select
select cast('3'
cast('3' as
as integer)
integer) ++ cast('5'
cast('5' as
as integer);
integer);
--echivalent cu
--echivalent cu
select
select '3'::integer
'3'::integer ++ '5'::integer;
'5'::integer;
---------------------------------------------
---------------------------------------------
select
select '2013-12-07'::date
'2013-12-07'::date ++ 5;5;
--echivalent
--echivalent cu
cu
select
select cast('2013-12-07'
cast('2013-12-07' asas date)
date) ++ 5;
5;

3.5. Expresii condiţionale


Dacă valoarea dintr-un câmp depinde de alte valori din alte
câmpuri după reguli de tipul if then else, atunci se poate utiliza
următoarea construcţie.

28
3.Operaţii de interogare ce implică un singur tabel

CASE
CASE WHEN
WHEN condition
condition THEN
THEN result
result
Sintaxa simplificată: [WHEN
[WHEN ...]
...]
[ELSE
[ELSE result]
result]
END
END
SELECT
SELECT (temp_min+temp_max)/2.0
(temp_min+temp_max)/2.0 asas temp_medie,
temp_medie,
CASE
CASE WHEN( (temp_min+temp_max)/2.0 )) <=10
WHEN( (temp_min+temp_max)/2.0 <=10 THEN
THEN 'F.RECE'
'F.RECE'
WHEN(
WHEN( ((temp_min+temp_max)/2.0 ) > 10 )AND
((temp_min+temp_max)/2.0 ) > 10 )AND
(((temp_min+temp_max)/2.0
(((temp_min+temp_max)/2.0 )) <15
<15 )) THEN
THEN 'RECE'
'RECE'
WHEN
WHEN ((temp_min+temp_max)/2.0
((temp_min+temp_max)/2.0 )) >> 25
25 THEN
THEN 'CALDA'
'CALDA'
ELSE
ELSE 'NORMALA'
'NORMALA'
END
END AS
AS evaluare
evaluare
FROM
FROM pic.hidro
pic.hidro
Rezultatul expresiei este pus în noul câmp evaluare.
3.6. Comenzi SQL ce nu implică tabele
Sunt prezentate câteva comenzi SQL ce nu implică tabele:
SELECT
SELECT 3;
3; --
-- afişează
afişează 33
SELECT
SELECT 3+2;
3+2; --afişează
--afişează 55
select
select CURRENT_DATE;
CURRENT_DATE; --data
--data curenta
curenta ---- de
de tip
tip date
date
select
select CURRENT_TIME;
CURRENT_TIME; --ora
--ora curenta
curenta --
-- de
de tip
tip time
time
select
select CURRENT_DATE+CURRENT_TIME;
CURRENT_DATE+CURRENT_TIME; -- -- rezultatul
rezultatul este
este dede tip
tip timestamp
timestamp
SELECT
SELECT CURRENT_DATE+1;
CURRENT_DATE+1; -- -- ziua
ziua urmatoare
urmatoare dede tip
tip date
date
SELECT
SELECT CURRENT_DATE-1;
CURRENT_DATE-1; -- -- ziua
ziua anterioara
anterioara
SELECT
SELECT CURRENT_TIME+'2:3'::time;
CURRENT_TIME+'2:3'::time; -- -- peste
peste 22 ore
ore si
si 33 minute
minute (timestamp)
(timestamp)
SELECT
SELECT CURRENT_TIME-'2:3'::time;
CURRENT_TIME-'2:3'::time; -- -- acum
acum 22 ore
ore si
si 33 minute
minute
SELECT
SELECT '2012-05-27'::date
'2012-05-27'::date -- '2:15'::time;
'2:15'::time;
--rezultat
--rezultat timestamp
timestamp ziua
ziua 26.05.2012
26.05.2012 ora
ora 21:45
21:45
SELECT
SELECT '2:23'::time
'2:23'::time -- '1:15'::time;
'1:15'::time; --rezultat
--rezultat de de tip
tip interval
interval

4. Tipuri de asocieri între înregistrările unor tabele


Într-o bază de date relaţională tabelele sunt corelate,
pentru ca datele memorate în tabele diferite să poată fi asociate
corect atunci când din baza de date se solicită anumite informaţii.
Se pot realiza asocieri între datele din tabele după criterii de
funcţionalitate sau structură a datelor. Asocierile sunt posibile
în faza de definire a structurii tabelelor. Câmpurile comune prin
care se face corelarea sunt date de cheia primară (PRIMARY KEY)
pentru un tabel şi, respectiv, chei externe (FOREIGN KEY) pentru
tabelele asociate.
Orice tabel poate să cuprindă unul sau mai multe câmpuri,
care intră în componenţa unei chei primare, utilizată pentru dife-
renţierea unei înregistrări de celelalte. Asocierea a două tabele

29
II.Limbajul SQL

(tabel părinte şi tabel copil)/master-slave se face printr-un câmp


special cu o trimitere la cheia primară a tabelului subordonat
(tabelul copil).
O bază de date poate fi formată din mai multe tabele având
diferite legături între acestea. Tipurile de legături dintre două
tabele pot fi:
● asocierea (legătura) de tip unu la unu (1:1) – unei
înregistrări dintr-un tabel îi corespunde o singură
înregistrare în celălalt tabel;
● asocierea (legătura) de tip unu la mai mulţi (1:M) – unei
înregistrări dintr-un tabel îi corespund mai multe
înregistrări în celălalt tabel;
● asocierea (legătura) de tip mulţi la mulţi (M:N) – un grup
cu mai multe înregistrări dintr-un tabel corespunde cu alt
grup format din mai multe înregistrări din celălalt tabel.
4.1. Asocierea de tip unu la unu (1:1)
Înregistrările din două tabele se află în asocierea unu la
unu dacă unei înregistrări dintr-un tabel îi corespunde (sau nu) o
singură înregistrare din celălalt tabel. Legătura dintre cele două
tabele se face pe baza cheilor primare.
Acest tip de asociere este utilizată mai rar. Există,
totuşi, cazuri în care este necesară şi utilă stabilirea unei
astfel de relaţii.
ANGAJATI 1:1 DATE PERSONALE
cnp cnp
sectia tel
colectivul adresa

Figura II-1: Exemplu de asociere 1:1

4.2. Asocierea de tip unu la mai mulţi (1:N)


Două tabele A şi B se află în asociere 1:N dacă unei
înregistrări din tabelul A îi corespund mai multe înregistrări în
tabelul B. Cheia primară din tabelul părinte (A) se adaugă în
tabelul copil (B) sub forma unei chei externe.
JUDET 1:N LOCALITATE
nume denumire
suprafata suprafata
populatie populatie
pozitie_geogr.
Figura II-2: Exemplu de asociere 1:N

30
4.Tipuri de asocieri între înregistrările unor tabele

4.3. Asocierea de tip mulţi la mulţi (M:N)


Mai multor înregistrări dintr-un tabel le corespund mai
multe înregistrări în cealălalt tabel. În vederea implementării
practice, se adaugă un tabel suplimentar, care va conţine cheile
primare ale tabelelor iniţiale şi câmpuri referitoare la asocierea
dintre tabelele părinţi. Cheia primară din tabela intermediară va
fi o cheie compusă.
M:N BIBLIOTECA
AUTORI cota
nume titlu
titlu autor1
editura editura
an_aparitie an_aparitie
Figura II-3: Exemplu de asociere M:N
4.4. Crearea cheilor primare şi a cheilor externe
Cheia primară din tabel impune unicitatea valorilor
câmpurilor din cadrul cheii primare pentru fiecare înregistrare.
Specificarea cheii primare se face cu modificatorul PRIMARY KEY.
Cheia străină/externă se declară prin modificatorul FOREIGN KEY.
drop
drop table
table if
if exists
exists pic.rauri;
pic.rauri;
CREATE
CREATE TABLE
TABLE pic.rauri(rau
pic.rauri(rau varchar(100),
varchar(100), afluent
afluent varchar(80),
varchar(80),
CONSTRAINT k_rauri PRIMARY KEY(rau,afluent));
CONSTRAINT k_rauri PRIMARY KEY(rau,afluent));
INSERT
INSERT INTO
INTO pic.rauri
pic.rauri VALUES('Argeş','Neajlov'),('Olt','Malnaş'),
VALUES('Argeş','Neajlov'),('Olt','Malnaş'),
('Olt','Hurez'),('Olt','Sebeş'),('Olt','Tuşnad'),('Argeş','Tutana'),
('Olt','Hurez'),('Olt','Sebeş'),('Olt','Tuşnad'),('Argeş','Tutana'),
('Mureş','Târnava'),('Mureş','Borzia'),('Mureş','Fântânele');
('Mureş','Târnava'),('Mureş','Borzia'),('Mureş','Fântânele');
Atât pentru PRIMARY KEY, cât şi pentru FOREIGN KEY putem
avea unul sau mai multe câmpuri, separate prin virgulă.
CREATE
CREATE TABLE
TABLE pic.hidro
pic.hidro
(( afluent
afluent character
character varying(80),temp_min
varying(80),temp_min integer,
integer, nivel_min
nivel_min int,
int,
temp_max
temp_max integer,
integer, nivel_max
nivel_max int,
int, precipitatii
precipitatii numeric,
numeric,
datac
datac date,
date, CONSTRAINT
CONSTRAINT k_hidro
k_hidro PRIMARY
PRIMARY KEY(afluent,
KEY(afluent, datac),
datac),
CONSTRAINT
CONSTRAINT f_rauri
f_rauri FOREIGN
FOREIGN KEY(rau)
KEY(rau) REFERENCES
REFERENCES pic.rauri(rau)
pic.rauri(rau)
);
);

4.5. Asigurarea integrităţii referenţiale


Una dintre problemele importante ale unei baze de date este
asigurarea consistenţei şi corelării datelor. Pentru aceasta putem
impune respectarea unei restricţii la adăugarea sau eliminarea
înregistrărilor în tabelele corelate. Aplicarea acestor restricţii
ne dă posibilitatea să asigurăm o proprietate importantă a bazelor
de date relaţionale, numită integritate referenţială.

31
II.Limbajul SQL

Aceste restricţii se referă la ştergerea datelor, la


inserarea/adăugarea datelor sau modificarea lor.
Operaţiile de ştergere şi inserare au în clauza WHERE şi
câmpurile ce constituie cheile primare. De asemenea, operaţia de
adăugare impune specificarea obligatorie a câmpurilor din cheia
primară, acestea fiind diferite de NULL şi fiind unice în cadrul
tabelului.

5. Operaţii de editare cu mai multe tabele


Operaţiile de inserare au ca destinaţie întotdeauna un
singur tabel, indiferent dacă sursa este explicită (cu operatorul
VALUES) sau rezultă dintr-o interogare (cu operatorul SELECT),
operaţie ce poate să implice unul sau mai multe tabele.
create
create table
table pic.t11(a
pic.t11(a varchar(10),
varchar(10), bb int);
int);
create
create table
table pic.t12(c
pic.t12(c varchar(10),
varchar(10), dd int);
int);
INSERT
INSERT INTO
INTO pic.t11
pic.t11 VALUES('ABC11',10),('ACD12',20),('BCD13',30);
VALUES('ABC11',10),('ACD12',20),('BCD13',30);
INSERT
INSERT INTO
INTO pic.t12
pic.t12 VALUES('AXC11',11),
VALUES('AXC11',11),
('ACD12',25),('BD19',30),('XCD18',45);
('ACD12',25),('BD19',30),('XCD18',45);
select
select ** from
from pic.t11;--echivalent
pic.t11;--echivalent cu: cu:
select
select t11.* from pic.t11;--echivalent
t11.* from pic.t11;--echivalent cu: cu:
select
select a,b
a,b from
from pic.t11;--echivalent
pic.t11;--echivalent cu:cu:
select
select t11.a,
t11.a, t11.b
t11.b from
from pic.t11;--
pic.t11;-- echivalent
echivalent cu:
cu:
select
select tx.a,tx.b
tx.a,tx.b from
from pic.t11
pic.t11 tx;
tx;
--tx
--tx se
se comporta
comporta caca un
un alias
alias ptr.tabelul
ptr.tabelul t11
t11

5.1. Actualizarea datelor din tabele prin utilizarea unor date


din alte tabele
Forma generală simplificată:
UPDATE
UPDATE table
table [[ [[ AS
AS ]] alias
alias ]]
SET
SET { column = { expression || DEFAULT
{ column = { expression DEFAULT }} }} [,
[, ...]
...]
[[ FROM
FROM fromlist
fromlist ]] [[ WHEREWHERE condition
condition ]]
Ex.:mărim valoarea lui t3.b cu valoarea din t4.d pentru care t3.a =
t4.c
UPDATE
UPDATE pic.t11
pic.t11 SET
SET b=t11.b+t12.d
b=t11.b+t12.d FROM
FROM pic.t12
pic.t12 WHERE
WHERE t11.a=t12.c;
t11.a=t12.c;

5.2. Ştergerea înregistrărilor din tabele prin utilizarea unor


date din alte tabele
Forma generală simplificată:
DELETE
DELETE FROM
FROM table
table [[ USING
USING usinglist
usinglist ][
][ WHERE
WHERE condition
condition ]]
Ex.:Ştergem din tabelul t3 înregistrările a căror valoare din
câmpul a se regăseşte şi în tabelul t4 (a=c) şi a căror valoare din
câmpul d este mai mică decât valoarea 30.

32
5.Operaţii de editare cu mai multe tabele

DELETE
DELETE FROM
FROM pic.t11
pic.t11 USING
USING pic.t12
pic.t12 WHERE
WHERE t11.a=t12.c
t11.a=t12.c AND
AND t12.d<30;
t12.d<30;

6. Operaţii de interogare cu mai multe tabele. Vederi

6.1. Combinarea interogărilor (UNION, EXCEPT, INTERSECT)


a) UNIUNEA: concatenarea tabelelor (operaţie pe verticală – la
nivel de înregistrări)
interogare#1
interogare#1 UNION
UNION [ALL]
[ALL] interogare#2
interogare#2
În mod implicit prin uniunea celor două interogări,
înregistrările multiple, identice, vor apărea o singură dată
(echivalent cu utilizarea instrucţiunii DISTINCT). Prin utilizarea
comenzii ALL, acestea nu vor mai fi eliminate. De exemplu:
SELECT
SELECT afluent
afluent FROM
FROM pic.hidro
pic.hidro
UNION
UNION
SELECT
SELECT afluent
afluent FROM
FROM pic.rauri
pic.rauri
SELECT
SELECT a,b
a,b FROM
FROM pic.t11
pic.t11
sau: UNION
UNION
SELECT
SELECT c,d
c,d FROM
FROM pic.t12;
pic.t12;
Putem avea:
interogare#1
interogare#1 UNION
UNION interogare#2
interogare#2 ...
... UNION
UNION interogare#n
interogare#n
Denumirea câmpului rezultat va fi cea din prima interogare.
Înregistrările multiple vor apărea o singură dată. Este
echivalent cu a scrie:
SELECT
SELECT c,d
c,d FROM
FROM pic.t12
pic.t12
UNION
UNION
SELECT
SELECT a,b
a,b FROM
FROM pic.t11;
pic.t11;
Notă: Atenţie la operatorul semicolon ; deoarece acesta semnifică
sfârşitul şirului ce constituie comanda SQL.
Ex.: Afişarea doar a valorilor mai mari de 20:
SELECT
SELECT a,b
a,b FROM
FROM pic.t11
pic.t11 WHERE
WHERE b>20
b>20
UNION
UNION
SELECT
SELECT c,d
c,d FROM
FROM pic.t12
pic.t12 WHERE
WHERE d>20;
d>20;
echivalent cu a scrie:
select
select par.x,
par.x, par.y
par.y from
from
(select
(select aa as
as x,b
x,b asas yy from
from pic.t11
pic.t11
UNION
UNION
select
select cc as
as x,
x, dd as
as yy from
from pic.t12)
pic.t12) par
par
WHERE
WHERE par.y
par.y >> 20;
20;
b) INTERSECŢIA: intersecţia dintre două tabele cu aceeaşi
structură (operaţie pe verticală – la nivel de înregistrări)

33
II.Limbajul SQL

interogare#1
interogare#1 INTERSECT
INTERSECT [ALL]
[ALL] interogare#2
interogare#2
Prin utilizarea comenzii INTERSECT, vor fi selectate doar
înregistrările comune din cadrul interogărilor, de exemplu:
SELECT
SELECT afluent
afluent FROM
FROM pic.hidro
pic.hidro INTERSECT
INTERSECT
SELECT afluent FROM pic.rauri;
SELECT afluent FROM pic.rauri;
c) DIFERENŢA: diferenţa între două tabele cu aceeaşi structură
(operaţie pe verticală – la nivel de înregistrări)
interogare#1
interogare#1 EXCEPT
EXCEPT [ALL]
[ALL] interogare#2
interogare#2
Prin utilizarea comenzii EXCEPT vor fi selectate doar
înregistrările care se regăsesc în rezultatul primei interogări,
dar care nu se regăsec şi în rezultatul celei de a doua interogări.
SELECT
SELECT afluent
afluent FROM
FROM pic.rauri
pic.rauri
EXCEPT
EXCEPT
SELECT
SELECT afluent
afluent FROM
FROM pic.hidro;
pic.hidro;
SELECT
SELECT a,b
a,b FROM
FROM pic.t11
pic.t11
EXCEPT
EXCEPT
SELECT
SELECT c,d
c,d FROM
FROM pic.t12;
pic.t12;
--
-- t12
t12 except
except t11
t11
select
select c,d
c,d from
from pic.t12
pic.t12
except
except
select
select a,b
a,b from
from pic.t11;
pic.t11;
În toate cazurile, câmpurile din cadrul selecţiilor trebuie
să fie de acelaşi tip, altfel fiind necesară convertirea acestora
la tipul respectiv prin utilizarea operatorului CAST.
Pentru ordonarea rezultatului introducem un alias (în acest
caz se numeşte par) pentru tabelul rezultat.
select
select par.x,par.y
par.x,par.y FROMFROM
(select
(select cc as
as x,d
x,d asas yy from
from pic.t12
pic.t12
except
except
select
select aa as
as x,b
x,b as
as yy from
from pic.t11)
pic.t11) par
par
ORDER
ORDER BY
BY par.x;
par.x;

6.2. Interogarea mai multor tabele


6.2.1. Clauza JOIN
Atunci când în clauza FROM a unei comenzi SELECT apar mai
multe tabele, se realizează produsul cartezian al acestora. De
aceea numărul de linii rezultat creşte considerabil, fiind necesară
restricţionarea acestora cu clauza WHERE.
Atunci când este necesară obţinerea de informaţii din mai
multe tabele, se utilizează clauza JOIN. În acest fel liniile
dintr-un tabel pot fi puse în legătură cu cele din alt tabel,
conform valorilor comune ale unor coloane.

34
6.Operaţii de interogare cu mai multe tabele. Vederi

Clauza JOIN este utilizată pentru preluarea informaţiilor


din două sau mai multe tabele, în condiţiile existenţei unor
legături logice între anumite câmpuri din cadrul tabelelor.
JOIN întoarce înregistrările ce respectă condiţiile impuse
de JOIN între tabele. JOIN nu se referă la intersecţii, ci la
produse carteziene. Diversele tipuri de JOIN, reduc produsele,
punând diverse condiţii pe una sau mai multe mulţimi.
Presupunem că avem un tabel T1 cu N înregistrări şi un tabel
T2 cu M înregistrări.
Clauza CROSS JOIN are forma: T1 CROSS JOIN T2 şi întoarce pentru
fiecare înregistrare din T1 toate înregistrările din T2. Tabelul
rezultat va avea NxM înregistrări. Sintaxa este:
T1
T1 {{ [INNER]
[INNER] || {{ LEFT
LEFT || RIGHT
RIGHT || FULL
FULL }} [OUTER]
[OUTER] }}
JOIN
JOIN T2
T2 ON
ON boolean_expression
boolean_expression
T1
T1 {{ [INNER]
[INNER] || {{ LEFT
LEFT || RIGHT
RIGHT || FULL
FULL }} [OUTER]
[OUTER] }}
JOIN
JOIN T2T2 USING
USING (( join
join column
column list
list ))
T1
T1 NATURAL
NATURAL {{ [INNER]
[INNER] || {{ LEFT
LEFT || RIGHT
RIGHT || FULL
FULL }} [OUTER]
[OUTER] }} JOIN
JOIN T2
T2
Clauzele INNER şi OUTER sunt opţionale. INNER este implicit,
iar LEFT, RIGHT sau FULL implică OUTER.
Clauza ON este asemănătoare cu WHERE.
Principalele forme ale clauzei JOIN:
✔ INNER JOIN (joncţiune internă – de tip egalitate) - pentru
fiecare rând (înregistrare) R1 din tabelul T1, tabelul
rezultat în urma aplicării clauzei JOIN va avea o
înregistrare din tabelul R2 care îndeplineşte condiţia de
joncţiune cu R1.
Ex.:
--
-- 22 tabele
tabele FARA
FARA legatura
legatura intre
intre ele
ele !!!
!!!
select
select t11.*,
t11.*, t12.*
t12.* FROM
FROM pic.t11,
pic.t11, pic.t12;
pic.t12;
--
-- rezultat:
rezultat: PRODUSUL
PRODUSUL CARTEZIAN
CARTEZIAN (nr_inreg_t11
(nr_inreg_t11 XX nr_inreg_t12)
nr_inreg_t12)
--
-- oo conditie
conditie de
de legatura
legatura intre
intre tabele:
tabele:
select
select t11.*,
t11.*, t12.*
t12.* from
from pic.t11,
pic.t11, pic.t12
pic.t12 WHERE
WHERE t11.a=t12.c;
t11.a=t12.c;
--
-- având
având în
în vedere
vedere den.
den. diferite
diferite ale
ale campurilor
campurilor sese poate
poate scrie:
scrie:
select t11.*, t12.* from pic.t11, pic.t12 WHERE
select t11.*, t12.* from pic.t11, pic.t12 WHERE a=c; a=c;
--
-- echivalent
echivalent cucu aa scrie:
scrie:
select
select t11.*, t12.* from
t11.*, t12.* from pic.t11
pic.t11 INNER
INNER JOIN
JOIN pic.t12
pic.t12 ON
ON t11.a=t12.c;
t11.a=t12.c;
--sau
--sau
select
select t11.*,
t11.*, t12.*
t12.* from
from pic.t12
pic.t12 INNER
INNER JOIN
JOIN pic.t11
pic.t11 ON
ON t11.a=t12.c;
t11.a=t12.c;
echivalent d.p.d.v. al scrierii cu:
SELECT
SELECT v.afluent,
v.afluent, v.datac
v.datac FROM
FROM pic.hidro
pic.hidro vv
INNER
INNER JOIN
JOIN pic.rauri
pic.rauri mm ON
ON v.afluent=m.afluent;
v.afluent=m.afluent;
sau SELECT
SELECT v.afluent,
v.afluent, v.datac
v.datac FROM
FROM pic.hidro
pic.hidro v,
v, pic.rauri
pic.rauri mm
WHERE
WHERE v.afluent=m.afluent;
v.afluent=m.afluent;

35
II.Limbajul SQL

✔ LEFT OUTER JOIN (joncţiune externă la stânga)


LEFT OUTER JOIN − preia toate înregistrările din tabelul din
stânga, iar din tabelul din dreapta va prelua doar cele pentru care
este adevărată condiţia de legătură, restul câmpurilor provenite
din tabelul din dreapta sunt completate cu NULL (câmp gol), unirea
celor două tabele se face PE ORIZONTALĂ
sau
LEFT OUTER JOIN − realizează, în primul rând, o joncţiune
internă. Apoi, fiecare rând din T1 (T1 stânga, T2 dreapta) care nu
satisface joncţiunea cu T2 este adăugat, având valori NULL pentru
câmpurile corespondente din T2. Tabelul rezultat va avea, cel puţin
rândurile din tabelul T1. De exemplu:
SELECT
SELECT v.afluent,v.datac
v.afluent,v.datac FROM
FROM pic.hidro
pic.hidro vv
LEFT
LEFT OUTER JOIN pic.rauri
OUTER JOIN pic.rauri mm ON
ON v.afluent=m.afluent;
v.afluent=m.afluent;
select
select t11.*,t12.*
t11.*,t12.* FROM
FROM pic.t11
pic.t11
LEFT
LEFT OUTER
OUTER JOIN
JOIN pic.t12
pic.t12 ON
ON t11.b=t12.d;
t11.b=t12.d;
✔ RIGHT OUTER JOIN (joncţiune externă la stânga)
RIGHT OUTER JOIN realizează, pentru început, o joncţiune
internă. Apoi, fiecare rând din T2 (T1 stânga, T2 dreapta) care nu
satisface joncţiunea cu T1 este adăugat având valori NULL pentru
câmpurile corespondente din T1. Tabelul rezultat va avea, cel puţin
rândurile din tabelul T2.
sau
RIGHT OUTER JOIN − preia toate înregistrările din tabelul
din dreapta, iar din tabelul din stânga va prelua doar cele pentru
care este adevărată condiţia de legătură, restul câmpurilor
provenite din tabelul din stânga sunt completate cu NULL (câmp gol)
− unirea celor două tabele se face PE ORIZONTALĂ. De exemplu:
SELECT
SELECT v.afluent,
v.afluent, v.datac,
v.datac, m.rau
m.rau
FROM
FROM pic.hidro vv RIGHT
pic.hidro RIGHT OUTER
OUTER JOIN
JOIN pic.rauri
pic.rauri mm
ON
ON v.afluent
v.afluent == m.afluent;
m.afluent;
✔ FULL OUTER JOIN (joncţiune totală)
select
select t11.*,
t11.*, t12.*
t12.* FROM
FROM pic.t11
pic.t11
RIGHT
RIGHT OUTER
OUTER JOIN
JOIN pic.t12
pic.t12 ON
ON t11.a=t12.c;
t11.a=t12.c;
FULL OUTER JOIN, pentru început, realizează o joncţiune
internă, apoi fiecare rând din T2 (T1 stânga, T2 dreapta) care nu
satisface joncţiunea cu T1 este adăugat având valori NULL pentru
câmpurile corespondente din T1. În final, fiecare rând din T2 (T1
stânga, T2 dreapta) care nu satisface joncţiunea cu T1 este
adăugat, având valori NULL pentru câmpurile corespondente din T1.
sau
FULL OUTER JOIN − preia toate înregistrările din tabelul din
stânga şi toate înregistrările din tabelul din dreapta.

36
6.Operaţii de interogare cu mai multe tabele. Vederi

Înregistrările pentru care există condiţia de legătură cu valoarea


de adevăr TRUE vor apărea o singură dată (rândul stânga-dreapta va
fi complet). Pentru celelalte înregistrări pentru care nu există
condiţie de legătură cu valoarea TRUE va fi completat doar tabelul
de care aparţin, în celălalt tabel vor avea valoarea NULL.
SELECT
SELECT v.afluent,
v.afluent, v.datac,
v.datac, m.rau
m.rau
FROM
FROM pic.hidro
pic.hidro vv FULL
FULL OUTER
OUTER JOIN
JOIN pic.rauri
pic.rauri mm
ON
ON v.afluent=m.afluent;
v.afluent=m.afluent;
select
select t11.*,t12.*
t11.*,t12.* FROM
FROM pic.t11
pic.t11
FULL
FULL OUTER
OUTER JOIN
JOIN pic.t12
pic.t12 ON
ON t11.a
t11.a == t12.c;
t12.c;
De asemenea, se pot lega mai multe clauze de tipul ON într-o
instrucțiune de tipul JOIN, utilizând, de exemplu, sintaxa:
SELECT
SELECT câmpuri
câmpuri
FROM
FROM tabel1
tabel1 INNER
INNER JOIN
JOIN tabel2
tabel2
(( (ON
(ON tabel1.câmp1 operator
tabel1.câmp1 operator de
de comparație
comparație tabel2.câmp1
tabel2.câmp1 AND
AND
ON
ON tabel1.câmp2
tabel1.câmp2 operator
operator de
de comparație
comparație tabel2.câmp2)
tabel2.câmp2) OR
OR
ON
ON tabel1.câmp3
tabel1.câmp3 operator
operator de
de comparație
comparație tabel2.câmp3)
tabel2.câmp3) ;;
O operație de tip LEFT JOIN sau RIGHT JOIN se poate imbrica
într-o operație de tipul INNER JOIN, dar o operație de tipul INNER
JOIN nu se poate imbrica într-o operație de tipul LEFT JOIN sau
RIGHT JOIN.
6.2.2. DIFERENŢA ÎNTRE DOUĂ TABELE
Diferenţa între două tabele cu aceeaşi structură (operaţie
pe vericală) − apar toate înregistrările din primul tabel minus/cu
excepţia celor comune pentru cele două tabele
DROP
DROP TABLE
TABLE IFIF EXISTS
EXISTS pic.t13;
pic.t13; DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t14;
pic.t14;
create
create table
table pic.t13(a
pic.t13(a varchar(10),
varchar(10), bb int);
int);
create
create table
table pic.t14(c
pic.t14(c varchar(10),
varchar(10), dd int);
int);
INSERT
INSERT INTO
INTO pic.t13
pic.t13 VALUES('ABC11',10),('ACD12',20),('BCD13',30);
VALUES('ABC11',10),('ACD12',20),('BCD13',30);
INSERT
INSERT INTO
INTO pic.t14
pic.t14 VALUES('AXC11',15),('ACD12',20),
VALUES('AXC11',15),('ACD12',20),
('BCD13',35),('XCD18',45);
('BCD13',35),('XCD18',45);
--
-- t13
t13 except
except t14
t14
select
select ** from
from pic.t13;
pic.t13; select
select ** from
from pic.t14;
pic.t14;
SELECT
SELECT a,b
a,b FROM
FROM pic.t13
pic.t13 EXCEPT
EXCEPT SELECT
SELECT c,d
c,d FROM
FROM pic.t14;
pic.t14; --
-- sau
sau
SELECT
SELECT a,b
a,b FROM
FROM pic.t13
pic.t13
EXCEPT
EXCEPT
SELECT
SELECT c,d
c,d FROM
FROM pic.t14;
pic.t14;
--
-- t14
t14 except
except t13
t13
select
select c,d
c,d from
from pic.t14
pic.t14 except
except select
select a,b
a,b from
from pic.t13;
pic.t13;
Pentru utilizarea comenzii SQL EXCEPT este necesar ca cele
două tabele să aibă aceeaşi structură a câmpurilor.

37
II.Limbajul SQL

7. Subinterogări
Subinterogările permit crearea unui tabel în cadrul unei
interogări. Acestea sunt scrise între paranteze rotunde şi au un
ALIAS.
Subinterogările scalare sunt interogările care întorc un
singur rând şi o singură coloană. Ele se scriu între paranteze
rotunde. În cazul în care subinterogarea întoarce mai multe
înregistrări, vom avea eroare. În cazul în care nu întoarce nicio
înregistrare, valoarea rezultată a interogării este considerată
NULL. În exemplul următor sunt afişaţi toţi afluenţii din tabelul
pic.rauri şi mediile precipitaţilor acolo unde acestea au fost
introduse:
SELECT
SELECT rau,
rau, afluent,
afluent,
(SELECT
(SELECT avg(precipitatii)
avg(precipitatii) FROMFROM pic.hidro
pic.hidro
WHERE
WHERE rauri.afluent
rauri.afluent == hidro.afluent)
hidro.afluent)
AS
AS media
media FROM
FROM pic.rauri;
pic.rauri;
Expresii specifice subinterogărilor:
 EXISTS EXISTS (subquery) − întoarce true dacă subinterogarea
EXISTS (subquery)
are ca rezultat cel puţin o înregistrare, altfel întoarce
false;
 IN expression
expression IN
IN (subquery)
(subquery) − caz în care subinterogarea
întoarce o singură coloană. Verifică dacă expresia se află
în rezultatul subinterogării sau Row Row constructor
constructor IN
IN (subquery)
(subquery)
− caz în care subinterogarea întoarce acelaşi număr de
coloane ca şi constructorul înregistrare (row constructor).
Notă: Constructorul de înregistrare formează o înregistrare
utilizând cuvântul ROW, de ex.: ROW(12,14,'TEST')
ROW(12,14,'TEST') .
Un alt exemplu:
SELECT
SELECT ROW(3,9,'Malnaş')
ROW(3,9,'Malnaş')
IN(SELECT
IN(SELECT temp_min,temp_max,afluent
temp_min,temp_max,afluent
FROM
FROM pic.hidro)
pic.hidro) AS
AS VERIFICA;
VERIFICA;
 NOT IN – asemănătoare cu IN, dar cu rezultatul negat.
 ALL expression
expression ALL (subquery) – caz în care subinterogarea
ALL (subquery)
întoarce o singură coloană – verifică dacă expresia
satisface condiţia pentru fiecare înregistrare din
subinterogare sau Row constructor ALL (subquery)
Row constructor ALL (subquery) – caz în care
subinterogarea întoarce acelaşi număr de coloane ca şi
constructorul de înregistrare row constructor.
Ex.: Adaugă în pic.t13, înregistrările din pic.t14 pentru care
valoarea câmpului NU se regăseşte în pic.t13

38
7.Subinterogări

INSERT
INSERT INTO
INTO pic.t13
pic.t13
select
select par1.x,
par1.x, t14.d
t14.d as
as yy FROM
FROM
(SELECT
(SELECT t14.c
t14.c asas xx from
from pic.t14
pic.t14
except
except
select
select t13.a
t13.a asas xx from
from pic.t13
pic.t13 )par1,
)par1,
pic.t14
pic.t14 WHERE
WHERE par1.x == t14.c;
par1.x t14.c;
Ex.: Actualizează valorile b pentru înregistrările a căror valoare
din câmpul a începe cu litera x. Valoarea cu care se înlocuieşte
este suma tuturor valorilor câmpului d din tabelul pic.t14
update
update pic.t13
pic.t13 set
set bb == (select
(select sum(t14.d)
sum(t14.d) from
from pic.t14)
pic.t14)
where
where aa ilike
ilike 'x%';
'x%';

8. Operaţii cu tablouri de date


Tablourile de date sunt structuri secvenţiale cu un anumit
tip de dată. Acestea pot fi cu o dimensiune (vectori) sau cu mai
multe dimensiuni.
create
create temporary
temporary table
table tx(a
tx(a varchar(3),
varchar(3), bb varchar[3]);
varchar[3]);
În exemplul de mai sus, câmpul a este un şir cu maxim 3
caractere, iar b este un vector cu 3 şiruri de tip varchar cu
lungime nedefinită (tip text).
Fiecare tablou se află în câte o celulă a "tabelului".
create
create table
table pic.t15(a
pic.t15(a serial
serial primary
primary key,
key, bb varchar(10)[],
varchar(10)[], cc int[]);
int[]);
În exemplul de mai sus, câmpul b este un vector cu un număr
nedefinit de celule, iar fiecare celulă din cadrul vectorului
conţine câte un şir cu maxim 10 caractere. Câmpul c este un vector
cu dimensiune necunoscută, fiecare element din cadrul vectorului
fiind de tip întreg.
8.1. Tablouri unidimensionale
Pentru tablourile unidimensionale (vectori) nu este necesară
specificarea dimensiunii acestora. Popularea cu date se poate
realiza în mai multe moduri.
O primă variantă este prezentată în ceea ce urmează:
INSERT
INSERT INTO
INTO pic.t15(b,c)
pic.t15(b,c) VALUES
VALUES
('{"A01","A02","A03"}','{12,
('{"A01","A02","A03"}','{12, 14,9,7,5}'),
14,9,7,5}'),
--
-- bb cu
cu 33 elemente
elemente si
si cc cu
cu 55 elemente
elemente
('{"A01","A02","A03","A04"}','{9,7}'),
('{"A01","A02","A03","A04"}','{9,7}'),
--
-- bb cu
cu 44 elemente
elemente si
si cc cu
cu 22 elemente
elemente
('{"A04"}','{9,7,9,9,9,9}');
('{"A04"}','{9,7,9,9,9,9}');
--
-- bb cu
cu un
un element
element si
si cc cu
cu 66 elemente
elemente

39
II.Limbajul SQL

O altă variantă de populare cu date este:


INSERT
INSERT INTO
INTO pic.t15(b,c)
pic.t15(b,c) VALUES
VALUES
(ARRAY['B01','B02','B03'],
(ARRAY['B01','B02','B03'], ARRAY[17,20,5,4]),
ARRAY[17,20,5,4]),
(ARRAY['B02','B05'],
(ARRAY['B02','B05'], ARRAY[7]),
ARRAY[7]), (ARRAY['B03'],
(ARRAY['B03'], ARRAY[1,2,3,4,5]);
ARRAY[1,2,3,4,5]);
Pentru selecţie se poate utiliza:
SELECT
SELECT ** FROM
FROM pic.t15;
pic.t15;
--este
--este selectat
selectat primul
primul element
element din
din vect.b
vect.b şi
şi primul
primul element
element din
din vect.
vect. cc
select
select b[1],c[1]
b[1],c[1] FROM
FROM pic.t15;
pic.t15;
SELECT
SELECT b[2],c[1]
b[2],c[1] from
from pic.t15;
pic.t15;
/*
/* selecteaza
selecteaza toate
toate inreg.
inreg. din
din t1
t1 ptr.
ptr. care
care elementul
elementul alal doilea
doilea al
al
vectorului
vectorului bb are
are caracterele
caracterele 22 si
si 33 egale
egale cu
cu 0,
0, respectiv
respectiv 22 */
*/
select
select ** from
from pic.t15
pic.t15 where
where b[2]
b[2] like
like '_02';
'_02';
Dacă elementul vectorului nu există, celula aceasta va fi
reprezentată prin NULL.
8.2. Tablouri multidimensionale
Tablorile multidimensionale (dim.>=2) trebuie să aibă
dimensiunea stabilită în momentul declarării acestora. De exemplu:
--matrice
--matrice 33 linii
linii xx 44 coloane
coloane
create
create table
table pic.t16(a
pic.t16(a serial
serial primary
primary key,
key,
bb varchar(10)[3][4],
varchar(10)[3][4], cc int[3][4]);
int[3][4]);
Pentru popularea cu date recomand utilizarea celei de a două
variante:
INSERT
INSERT INTO
INTO pic.t16(b,c)
pic.t16(b,c) VALUES
VALUES
(ARRAY[['A11','A12','A13','A14'],['A21','A22','A23','A24'],
(ARRAY[['A11','A12','A13','A14'],['A21','A22','A23','A24'],
['A31','A32','A33','A34']],
['A31','A32','A33','A34']], --b
--b (din
(din prima
prima înregistrare)
înregistrare)
ARRAY[[11,12,13,14],[21,22,23,24],[31,32,33,34]]),--c(prima
ARRAY[[11,12,13,14],[21,22,23,24],[31,32,33,34]]),--c(prima înreg.)înreg.)
(ARRAY[['B11','B12','B13','B14'],['B21','B22','B23','B24'],
(ARRAY[['B11','B12','B13','B14'],['B21','B22','B23','B24'],
['B31','B32','B33','B34']],--
['B31','B32','B33','B34']],-- bb (din
(din aa doua
doua înregistrare)
înregistrare)
ARRAY[[11,12,13,14],[21,22,23,24],[31,32,33,34]]);--c(a
ARRAY[[11,12,13,14],[21,22,23,24],[31,32,33,34]]);--c(a doua doua înreg.)
înreg.)
Pentru selecţie, se pot extrage doar datele aflate în
anumite zone ale tablourilor.
select
select ** from
from pic.t16;
pic.t16;
SELECT
SELECT b[1][1],b[2][3],c[2][1]
b[1][1],b[2][3],c[2][1] FROM FROM pic.t16;
pic.t16;
/*
/* extrage valorile ce se află pe primul
extrage valorile ce se află pe primul rând
rând şi
şi între
între
coloanele
coloanele 22 şi
şi 33 ale
ale tabloului
tabloului bb */
*/
SELECT b[1][2:3] FROM pic.t16;
SELECT b[1][2:3] FROM pic.t16;
/*
/* extrage
extrage valorile
valorile ce
ce se
se afla
afla între
între rândurile
rândurile 11 şi şi 22 (inclusiv)
(inclusiv)
şi
şi între coloanele 2 şi 3 (inclusiv) ale
între coloanele 2 şi 3 (inclusiv) ale tabolului
tabolului bb */
*/
SELECT
SELECT b[1:2][2:3]
b[1:2][2:3] FROM
FROM pic.t16;
pic.t16;

8.3. Căutarea în tablouri


Pentru a utiliza elemente dintr-o dată de tip tablou, se

40
8.Operaţii cu tablouri de date

utilizează funcţia generate_subscripts, funcţie ce expandează


tabloul, în sensul că fiecare nouă înregistrare va conţine câte o
celulă. Funcţia are forma:
generate_subscripts(tablou
generate_subscripts(tablou anyarray,
anyarray, dimensiune_tablou
dimensiune_tablou int)
int)
Pentru exemplificare, considerăm tabelul de mai jos, populat
cu date de tip tablou:
drop
drop table
table if
if exists
exists t1;
t1;
create
create temp table t1(a
temp table t1(a serial
serial primary
primary key,
key, bb int[]);
int[]);
insert
insert into
into t1(b)
t1(b) values
values
(array[10]),(array[10,20]),(array[20,30,40]),(array[10,20,30]);
(array[10]),(array[10,20]),(array[20,30,40]),(array[10,20,30]);
select
select ** from
from t1;
t1;
Selectează toate celulele care au valoarea 30:
select
select b,b, s,
s, b[s]
b[s] as
as "celula
"celula b[s]"
b[s]"
FROM
FROM (select
(select b,b, generate_subscripts(b,1)
generate_subscripts(b,1) as
as ss from
from t1)par
t1)par
WHERE
WHERE b[s]=30;
b[s]=30;
Un alt exemplu ar fi atunci când sunt căutate corespondenţe
între celulele tabloului. Considerăm un tabel cu coduri şi
descrieri asociate, ambele câmpuri fiind de tip tablou. Tipul
câmpului cod este un vector ce conţine şiruri cu maxim 30 de
caractere, iar tipul câmpului descriere (descr) este tot vector cu
elemente şiruri de caractere de maxim 50 de caractere.
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS t2;
t2;
CREATE
CREATE TEMP
TEMP TABLE
TABLE t2(a
t2(a serial
serial PRIMARY
PRIMARY KEY,
KEY, cod
cod varchar(30)[],
varchar(30)[],
descr
descr varchar(50)[]);
varchar(50)[]);
INSERT
INSERT INTO
INTO t2(cod,descr)
t2(cod,descr) VALUES
VALUES
(array['A1','A2','A3'],
(array['A1','A2','A3'], array['descrA1','descrA2','descrA3']),
array['descrA1','descrA2','descrA3']),
(array['B1','B2','B3','B4'],
(array['B1','B2','B3','B4'], array['descrB1','descrB2','descrB3']),
array['descrB1','descrB2','descrB3']),
(array['C1','C2'],
(array['C1','C2'], array['descrC1','descriereC2']);
array['descrC1','descriereC2']);
SELECT
SELECT ** FROM
FROM t2;
t2;
Ex.:Să se afişeze toate descrierile pentru codurile care se
termină cu cifra 3:
select
select cod[s]
cod[s] as
as cod_s,
cod_s, descr[s]
descr[s] as
as descriere_s
descriere_s
FROM
FROM (select
(select cod,descr,
cod,descr, generate_subscripts(cod,1)
generate_subscripts(cod,1) as
as ss from
from t2)par
t2)par
WHERE
WHERE cod[s]
cod[s] ILIKE
ILIKE '%3';
'%3';

9. Operaţii cu date de tip XML


Tipul de dată XML permite stocarea informaţiei aferente unui
fişier de tip XML (întreaga structură arborescentă) într-o singură
celulă (un câmp al unei înregistrări). PostgreSQL oferă funcţii
unice de căutare atât la nivelul arborelui de tip XML, cât şi la
nivelul tuturor înregistrărilor din tabel. Aceste funcţii de

41
II.Limbajul SQL

căutare sunt lente şi recomand o primă căutare doar ca text, apoi


rafinarea căutării, pe rezultatul obţinut, prin utilizarea acestor
funcţii.
CREATE
CREATE TABLE
TABLE pic.t17(a
pic.t17(a serial
serial PRIMARY
PRIMARY KEY,
KEY, bb xml);
xml);
--
-- adauga
adauga un
un xml
xml cu
cu un
un singur
singur nod
nod
INSERT
INSERT INTO
INTO pic.t17(b)
pic.t17(b) VALUES
VALUES ('<A0>
('<A0> UNUN NOD
NOD </A0>');
</A0>');
--
-- adauga
adauga un
un xml
xml cu
cu un
un nod
nod si
si doua
doua atribute
atribute
INSERT
INSERT INTO
INTO pic.t17(b)
pic.t17(b) VALUES
VALUES ('<A0
('<A0 m="123"
m="123" n="76">
n="76"> UN
UN TEST
TEST </A0>');
</A0>');
--
-- adauga
adauga un
un xml
xml cu
cu un
un nod
nod parinte
parinte sisi noduri
noduri copil
copil
INSERT
INSERT INTO
INTO pic.t17(b)
pic.t17(b) VALUES
VALUES
('<A0>
('<A0> <A11/>
<A11/> <A12>Al
<A12>Al doilea
doilea rand</A12>
rand</A12> </A0>');
</A0>');
--<A11/>
--<A11/> este
este echivalent
echivalent cu cu aa scrie
scrie <A11></A11>
<A11></A11>
select
select ** from
from pic.t17;
pic.t17;
Căutarea se realizează cu funcţia xpath
xpath(val_căutată,câmp_XML)
xpath(val_căutată,câmp_XML)
--afşează
--afşează valoarea
valoarea din
din nodurile
nodurile A0 A0
SELECT
SELECT xpath('//A0/text()',b)
xpath('//A0/text()',b) FROM
FROM pic.t17;
pic.t17;
--
-- afiseaza
afiseaza nodurile
nodurile XML
XML de
de sub
sub A0
A0
SELECT
SELECT xpath('//A0',b)
xpath('//A0',b) FROM
FROM pic.t17;
pic.t17;

10. Funcţii PostgreSQL


Pe lângă operatorii SQL standard, PostgreSQL oferă o serie
de funcţii pentru completarea acestora.
--afişează
--afişează valoarea
valoarea dindin nodurile
nodurile A12
A12
SELECT
SELECT xpath('//A12/text()',b)
xpath('//A12/text()',b) FROM FROM pic.t17;
pic.t17;
--afişare
--afişare XML
XML cucu oo anumită
anumită valoare
valoare aa nodurilor;
nodurilor; afişare
afişare XML
XML ca
ca şir
şir
de
de caractere;
caractere; valoarea
valoarea din
din cadrul
cadrul nodului
nodului A12
A12 conţine
conţine şirul
şirul "ra"
"ra"
SELECT
SELECT xpath('//A12/text()',b)
xpath('//A12/text()',b) FROM FROM pic.t17
pic.t17
WHERE
WHERE CAST(xpath('//A12/text()',b)
CAST(xpath('//A12/text()',b) as as text)
text) ILIKE
ILIKE '%ra%';
'%ra%';
--
-- valoarea
valoarea din
din cadrul
cadrul nodului
nodului A12
A12 sa
sa conţină
conţină şirul
şirul "ra"
"ra"
--
-- afişare
afişare XML
XML simplă
simplă (noduri
(noduri XML)
XML)
SELECT
SELECT xpath('//A12',b)
xpath('//A12',b) FROM FROM pic.t17
pic.t17
WHERE
WHERE CAST(xpath('//A12/text()',b)
CAST(xpath('//A12/text()',b) as as text)
text) ILIKE
ILIKE '%ra%';
'%ra%';

10.1. Operaţii cu data calendaristică


Din data calendaristică se poate extrage anul, ziua, luna,
ziua săptămânii etc. De asemenea se pot utiliza operatorii
aritmetici “+” şi ”-”.

42
10.Funcţii PostgreSQL

select
select CURRENT_DATE;
CURRENT_DATE; ---- data
data curentă
curentă
--
-- extrage numărul zilei din săptămână
extrage numărul zilei din săptămână
select
select extract(DOW
extract(DOW from
from current_date);
current_date);
select
select extract(YEAR from
extract(YEAR from current_date);--extrage
current_date);--extrage anulanul
--
-- extrage
extrage luna
luna calendaristică
calendaristică
select
select extract(MONTH
extract(MONTH from
from current_date);
current_date);
select
select extract(DAY
extract(DAY from
from current_date);--
current_date);-- extrage
extrage ziua
ziua
select
select current_date - 22; -- scade din data curentă
current_date - 22; -- scade din data curentă 22
22 de
de zile
zile
select
select current_date
current_date ++ 1000;
1000; --adună
--adună la
la data
data curentă
curentă 1000
1000 de
de zile
zile
Ex.:Să se afişeze în clar numele zilei curente din săptămână (luni,
marţi etc.), apoi pentru ziua de joi să se extindă pentru AM, PM:
select
select CASE
CASE
WHEN
WHEN extract(DOW
extract(DOW from
from current_date)
current_date) == 11 THEN
THEN 'Luni'
'Luni'
WHEN
WHEN extract(DOW
extract(DOW from
from current_date)
current_date) == 22 THEN
THEN 'Marţi'
'Marţi'
WHEN
WHEN extract(DOW
extract(DOW from
from current_date)
current_date) == 33 THEN
THEN 'Miercuri'
'Miercuri'
WHEN
WHEN extract(DOW
extract(DOW from
from current_date)
current_date) == 44 AND
AND
current_time
current_time << '12:00:00.0'
'12:00:00.0' THEN
THEN 'JoiAM'
'JoiAM'
WHEN
WHEN extract(DOW
extract(DOW from
from current_date)
current_date) == 44 AND
AND
current_time >=
current_time >= '09:00:00.0'
'09:00:00.0' THEN
THEN 'JoiPM'
'JoiPM'
WHEN extract(DOW from current_date) = 5 THEN 'Vineri'
WHEN extract(DOW from current_date) = 5 THEN 'Vineri'
WHEN
WHEN extract(DOW
extract(DOW from
from current_date)
current_date) == 66 THEN
THEN 'Sambata'
'Sambata'
WHEN
WHEN extract(DOW from current_date) = 7 THEN 'Duminica'
extract(DOW from current_date) = 7 THEN 'Duminica'
ELSE
ELSE 'Incorect'
'Incorect' END
END AS
AS Ziua;
Ziua;
Ex.:Pentru exemplul cu biblioteca: Să se calculeze câte zile a fost
împrumutată fiecare carte (tabel generat în ultimul exemplu):
SELECT
SELECT cota,
cota,
SUM(
SUM( CASE
CASE WHEN
WHEN data_rest
data_rest IS
IS NOT
NOT NULL
NULL THEN
THEN data_rest-data_impr
data_rest-data_impr
WHEN data_rest IS NULL
WHEN data_rest IS NULL THEN
THEN CURRENT_DATE
CURRENT_DATE -- data_impr
data_impr
END
END )) as
as nr_zile
nr_zile
FROM
FROM biblio.oper
biblio.oper GROUP
GROUP BY
BY cota;
cota;
sau:
SELECT
SELECT cota,SUM(CASE
cota,SUM(CASE WHEN
WHEN data_rest
data_rest IS
IS NULL
NULL THEN
THEN
CURRENT_DATE-data_impr
CURRENT_DATE-data_impr ELSE ELSE data_rest
data_rest -- data_impr
data_impr
END
END )) as
as nr_zile
nr_zile
FROM
FROM biblio.oper
biblio.oper GROUP
GROUP BY
BY cota;
cota;

10.2. Operaţii cu dată de tip oră, minut, secundă şi miimi de


secundă
Cu datele de tip time se pot realiza operaţii de tipul:

43
II.Limbajul SQL

select
select current_time;--
current_time;-- afiş.
afiş. ora,min.,sec.
ora,min.,sec. şişi fracţiunea
fracţiunea de de sec.
sec.
select
select cast('05:20:20.0' as time);-- transformă: char[] -> time
cast('05:20:20.0' as time);-- transformă: char[] -> time
select
select current_time
current_time as
as timp1,
timp1, current_time
current_time ++
cast('05:20:20.0'
cast('05:20:20.0' as as time)
time) as
as timp2;
timp2;
select
select current_date
current_date ++ current_time
current_time as
as a;--
a;-- date
date ++ time
time =>
=> timestamp
timestamp
select
select '2009-03-11'::date
'2009-03-11'::date ++ '12:20'::time
'12:20'::time as
as a;
a;
select
select to_date('20.05.1984','dd.mm.yyyy');
to_date('20.05.1984','dd.mm.yyyy');
--
-- util
util ptr.
ptr. preluare
preluare format
format ro/fr/ge
ro/fr/ge
select
select to_date('05/20/1984','mm/dd/yyyy');--
to_date('05/20/1984','mm/dd/yyyy');-- en en

10.3. Date de tip UUID / GUID


Datele de tip UUID (în postgreSQL) sau GUID în alte SGBD-uri
sunt reprezentate pe 128 de biţi (32 simboluri în baza 16). Acestea
sunt generate aleator şi se consideră că sunt practic unice. De
obicei este utilizat pentru identificarea unică a unor resurse
hardware sau software. Înainte de primul apel al acestei funcţii,
este necesară lansarea comenzii SQL: CREATE
CREATE EXTENSION
EXTENSION "uuid-ossp";
"uuid-ossp";
deoarece această funcţie se regăseşte ca extensie. De exemplu:
CREATE
CREATE TABLE
TABLE pic.t18(a
pic.t18(a serial,
serial, bb uuid);
uuid); SELECT
SELECT ** FROM
FROM pic.t18;
pic.t18;
--
-- datele
datele de
de tip
tip uuid
uuid sunt
sunt generate
generate aleator--SELECT
aleator--SELECT ** FROM
FROM pic.t18
pic.t18
INSERT
INSERT INTO
INTO pic.t18(b)
pic.t18(b) SELECT
SELECT uuid_generate_v1();
uuid_generate_v1(); ;;
Exemplu de dată de tip uuid: "0c6f0514-9204-11e2-81ec-83bcce9043a7"
10.4. Funcţii pentru înlocuirea caracterelor. Căutarea cu şi fără
diacritice
Presupunem următorul exemplu:
CREATE
CREATE TABLE
TABLE pic.t19
pic.t19 (( aa int
int PRIMARY
PRIMARY KEY,
KEY, bb int,
int, cc varchar(50));
varchar(50));
INSERT
INSERT INTO
INTO pic.t19
pic.t19 VALUES
VALUES (1,
(1, 10,
10, 'usa'),
'usa'), (2,
(2, 15,
15, 'uşa'),
'uşa'),
(3,
(3, 20,
20, 'uşă'),
'uşă'), (4,
(4, 30,
30, 'Uşa'),
'Uşa'), (5,
(5, 35,
35, 'uŞa'),
'uŞa'), (6,
(6, 40,
40, 'uŞĂ'),
'uŞĂ'),
(7,
(7, 50,
50, 'UŞĂ'),
'UŞĂ'), (8,
(8, 60,
60, 'USA'),
'USA'), (9,
(9, 70,
70, 'Xsa');
'Xsa');
select
select ** from
from pic.t19;
pic.t19;
Actualizare tabel: UPDATE
UPDATE pic.t19
pic.t19 SET
SET c='Xsă'
c='Xsă' WHERE
WHERE b=70;
b=70;
sau: SELECT
SELECT ** FROM
FROM pic.t19
pic.t19 WHERE
WHERE cc == 'usa';
'usa'; --
-- oo înregistrare
înregistrare
Funcţia translate(câmp, şir iniţial de caractere, şir final
de caractere) înlocuieşte caracterele din argumentul al doilea cu
cele din argumentul al treilea, corespunzător funcţiei (translate).
Practic, primul caracter din argumentul al doilea este înlocuit cu
primul caracter din argumentul al treilea, la fel şi pentru
următoarele caractere.

44
10.Funcţii PostgreSQL

SELECT
SELECT ** FROM
FROM pic.t19
pic.t19 WHERE
WHERE translate(c,'ăîşţâ','aista')
translate(c,'ăîşţâ','aista') ='usa';
='usa';
-- 3 înregistrări
-- 3 înregistrări
Dacă se caută cu şi fără diacritice:
SELECT
SELECT ** FROM
FROM pic.t19
pic.t19 WHERE
WHERE translate(c,'ăîşţâ','aista')
translate(c,'ăîşţâ','aista') ='uşa';
='uşa';
--
-- 00 înregistrări
înregistrări
În acest caz, convertim ambii termeni în expresii
independente de diacritice:
SELECT
SELECT ** FROM
FROM pic.t19
pic.t19 WHERE
WHERE translate(c,'ăîşţâ','aista')
translate(c,'ăîşţâ','aista') ==
translate('uşa','ăîşţâ','aista');
translate('uşa','ăîşţâ','aista');
--
-- 33 înregistrări
înregistrări
Dar tot nu sunt luate în considerare şi literele mari.
Pentru a rezolva aceasta:
SELECT
SELECT ** FROM
FROM pic.t19
pic.t19 WHERE
WHERE
translate(t19.c,'ăîşţâĂÎŞŢÂ','aistaAISTA')
translate(t19.c,'ăîşţâĂÎŞŢÂ','aistaAISTA')
== translate('usa','ăîşţâĂÎŞŢÂ','aistaAISTA');
translate('usa','ăîşţâĂÎŞŢÂ','aistaAISTA');
--
-- 33 înregistrări
înregistrări
Pentru a avea o căutare parţială, avem:
SELECT
SELECT ** FROM
FROM pic.t19
pic.t19 WHERE
WHERE translate(t19.c,'ăîşţâ','aista')
translate(t19.c,'ăîşţâ','aista') LIKE
LIKE
translate('uşa','ăîşţâ','aista');
translate('uşa','ăîşţâ','aista');
--
-- 33 înregistrări
înregistrări
Termenul de comparaţie este şirul fără diacritice, cu litere mici.
Pentru:
SELECT
SELECT ** FROM
FROM pic.t19
pic.t19 WHERE
WHERE translate(t19.c,'ăîşţâ','aista')
translate(t19.c,'ăîşţâ','aista') ILIKE
ILIKE
translate('uşa','ăîşţâ','aista');
translate('uşa','ăîşţâ','aista');
--5
--5 înregistrări
înregistrări
Termenul de comparaţie este şirul cu diacritice – litere mici şi
fără diacritice – litere mari.
Iar dacă avem:
SELECT
SELECT ** FROM
FROM pic.t19
pic.t19 WHERE
WHERE translate(t19.c,'ăîşţâĂÎŞŢÂ','aistaAISTA')
translate(t19.c,'ăîşţâĂÎŞŢÂ','aistaAISTA')
LIKE
LIKE translate('uşa','ăîşţâĂÎŞŢÂ','aistaAISTA');
translate('uşa','ăîşţâĂÎŞŢÂ','aistaAISTA');
--3
--3 înregistrări
înregistrări
comparaţia ţine cont de litera mare sau litera mică şi nu de
diacritice.
Cazul general:
SELECT
SELECT ** FROM
FROM pic.t19
pic.t19
WHERE
WHERE translate(t19.c,'ăîşţâĂÎŞŢÂ','aistaAISTA')
translate(t19.c,'ăîşţâĂÎŞŢÂ','aistaAISTA')
ILIKE
ILIKE translate('uşa','ăîşţâĂÎŞŢÂ','aistaAISTA');
translate('uşa','ăîşţâĂÎŞŢÂ','aistaAISTA');
--8
--8 înregistrări
înregistrări
comparaţia NU ţine cont de literă mare sau literă mică ŞI NICI de

45
II.Limbajul SQL

diacritice.

11. Tranzacţii. Interogări multiserver


Prin gestiunea tranzacţiilor poate fi controlată
validarea/salvarea unui grup de comenzi SQL. De exemplu, într-o
aplicaţie de gestiune a stocurilor, în momentul în care adăugăm o
linie în tabelul cu ieşirile de materiale şi o ştergem din stoc,
este important ca ambele comenzi să fie finalizate cu succes,
altfel pot apărea surplusuri în stoc.
Implicit, execuţia unui bloc de comenzi (separate prin
semicolon „;”) presupune execuţia tuturor comenzilor din cadrul
blocului într-o singură tranzacţie. Considerăm exemplul:
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t20;
pic.t20;
CREATE
CREATE table
table pic.t20(datam
pic.t20(datam date,
date, ora
ora int,
int, val
val numeric,
numeric,
CONSTRAINT
CONSTRAINT k_d1
k_d1 PRIMARY
PRIMARY KEY(datam,ora));
KEY(datam,ora));
INSERT
INSERT INTO
INTO pic.t20
pic.t20 VALUES
VALUES ('2000-01-01',6,8),('2000-01-01',14,12),
('2000-01-01',6,8),('2000-01-01',14,12),
('2000-01-01',22,16),
('2000-01-01',22,16), ('2000-01-02',6,24),
('2000-01-02',6,24), ('2000-01-02',14,26),
('2000-01-02',14,26),
('2000-01-02',22,32),
('2000-01-02',22,32), ('2000-01-03',6,44),
('2000-01-03',6,44), ('2000-01-03',14,90),
('2000-01-03',14,90),
('2000-01-03',22,120),
('2000-01-03',22,120), ('2000-01-04',6,122),
('2000-01-04',6,122), ('2000-01-04',14,129),
('2000-01-04',14,129),
('2000-01-04',22,135);
('2000-01-04',22,135); selectselect ** from
from pic.t20;
pic.t20;
Instrucţiunea COMMIT validează şi închide actuala tranzacţie:
BEGIN
BEGIN TRANSACTION;
TRANSACTION;
UPDATE
UPDATE pic.t20
pic.t20 SET
SET val
val == val
val ++ 1000
1000 WHERE
WHERE ora
ora == 22;
22;
COMMIT;--închide tranzacţia
COMMIT;--închide tranzacţia
UPDATE
UPDATE pic.t20
pic.t20 SET
SET val
val == val
val -- 1000
1000 WHERE
WHERE ora
ora == 14;
14;
ROLLBACK;-- nu are ce să închidă; rămân modificate
ROLLBACK;-- nu are ce să închidă; rămân modificate
Instrucţiunea ROLLBACK fără etichetă/punct salvare are
acelaşi efect ca şi instrucţiunea COMMIT.
BEGIN
BEGIN TRANSACTION;
TRANSACTION;
UPDATE
UPDATE pic.t20
pic.t20 SET
SET val
val == val
val ++ 1000
1000 WHERE
WHERE ora
ora == 22;
22;
UPDATE
UPDATE pic.t20
pic.t20 SET
SET val
val == val
val -- 1000
1000 WHERE
WHERE ora
ora == 14;
14;
ROLLBACK;--
ROLLBACK;-- ne ne intoarce
intoarce in
in punctul
punctul BEGIN
BEGIN TRANSACTION;
TRANSACTION;
--
-- NU
NU SUNT
SUNT modificate
modificate inregistrarile
inregistrarile
Se pot defini puncte/etichete în care să revină în caz de
eşec al unei anumite comenzi:
BEGIN
BEGIN TRANSACTION;
TRANSACTION;
UPDATE
UPDATE pic.t20
pic.t20 SET
SET val
val == val
val ++ 1000
1000 WHERE
WHERE ora
ora == 22;
22;
SAVEPOINT
SAVEPOINT x1;
x1;
UPDATE
UPDATE pic.t20
pic.t20 SET
SET val
val == val
val -- 1000
1000 WHERE
WHERE ora
ora == 14;
14;
ROLLBACK
ROLLBACK TO
TO SAVEPOINT
SAVEPOINT x1;--ne
x1;--ne întoarce
întoarce inin punctul
punctul x1
x1
ROLLBACK;--
ROLLBACK;-- nene intoarce
intoarce în
în punctul
punctul BEGIN
BEGIN TRANSACTION;
TRANSACTION;
Instrucţiunea ROLLBACK, pe lângă anularea tranzacţiei până

46
11.Tranzacţii. Interogări multiserver

în punctul specificat, are şi efect de validare a restului de


comenzi, situate în afara zonei de anulare.
BEGIN
BEGIN TRANSACTION;
TRANSACTION;
UPDATE
UPDATE pic.t20
pic.t20 SET
SET val
val == val
val ++ 1000
1000 WHERE
WHERE ora
ora == 22;
22;
ROLLBACK;
ROLLBACK; --
-- închide
închide tranzacţia
tranzacţia
UPDATE
UPDATE pic.t20
pic.t20 SET
SET val
val == val
val -- 1000
1000 WHERE
WHERE ora
ora == 14;
14;
ROLLBACK;--nu
ROLLBACK;--nu are
are efect
efect
Reapelarea instrucţiunii “BEGIN TRANSACTION” fără ca
tranzacţia anterioară să fie închisă nu are efect.
BEGIN
BEGIN TRANSACTION;
TRANSACTION;
UPDATE
UPDATE pic.t20
pic.t20 SET
SET val
val == val
val ++ 1000
1000 WHERE
WHERE ora
ora == 22;
22;
SAVEPOINT
SAVEPOINT x1;
x1;
BEGIN
BEGIN TRANSACTION;--
TRANSACTION;-- acest
acest apel
apel este
este ignorat
ignorat
UPDATE
UPDATE pic.t20
pic.t20 SET
SET val
val == val
val -- 1000
1000 WHERE
WHERE ora
ora == 14;
14;
ROLLBACK
ROLLBACK TO
TO SAVEPOINT
SAVEPOINT x1;--
x1;-- nene intoarce
intoarce inin punctul
punctul x1x1
ROLLBACK;--
ROLLBACK;-- nene intoarce
intoarce in
in punctul
punctul BEGIN
BEGIN TRANSACTION;
TRANSACTION;
Pentru preluarea datelor situate pe un alt server de baze de
date, se utilizează funcţia dblink. Prima comandă lansată în acest
sens va fi din interfaţa pgAdmin3 şi va fi: CREATE
CREATE EXTENSION
EXTENSION dblink
dblink
deoarece această funcţie este o extensie a PostgreSQL. Sintaxa
este: dblink(şir_conectare,bloc_comenzi_sql
dblink(şir_conectare,bloc_comenzi_sql ))
Pentru exemplificare, constituim un tabel pic.t21 pe
server-ul ce furnizează datele.
create
create table
table pic.t21
pic.t21 (a
(a int
int PRIMARY
PRIMARY KEY,
KEY, bb varchar(20));
varchar(20));
insert
insert into pic.t21 (a,b) values (1, 'alb');
into pic.t21 (a,b) values (1, 'alb');
select
select ** from
from pic.t21;
pic.t21;
Pe celălalt server vom lansa comanda:
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t22
pic.t22 CASCADE;
CASCADE;
CREATE
CREATE TABLE
TABLE pic.t22
pic.t22 ASAS SELECT
SELECT aa FROM
FROM
dblink('dbname
dblink('dbname == picdb
picdb password
password == abcd
abcd port
port == 5435',
5435',
'select
'select aa from
from pic.t21')
pic.t21') AS
AS t(a
t(a int);
int);
SELECT
SELECT ** FROM
FROM pic.t22;
pic.t22;
Se poate realiza un view pentru a vizualiza modificările
tabelul pic.t21 în timp real:
CREATE
CREATE OR
OR REPLACE
REPLACE VIEW
VIEW pic.z1
pic.z1 AS
AS SELECT
SELECT aa FROM
FROM
dblink('dbname
dblink('dbname = picdb password
= picdb password == abcd
abcd port
port == 5435',
5435',
'select
'select aa from
from pic.t21')
pic.t21') as
as t(a
t(a int)
int)
WHERE a NOT IN (SELECT a FROM pic.t22);
WHERE a NOT IN (SELECT a FROM pic.t22);
SELECT
SELECT ** FROM
FROM pic.z1;
pic.z1;
Există funcţii asemănătoare pentru interfaţare şi cu alte
servere de baze de date, cum ar fi cel de Oracle.

47
II.Limbajul SQL

12. Teste rezolvate – gestiune bibliotecă


Se consideră o arhitectură simplă pentru o aplicaţie ce
gestionează operaţiile de împrumut şi restituire dintr-o
bibliotecă. Astfel avem:
☑ un tabel cu utilizatorii, având structura: utiliz(cnp, nume,
prenume adresa, tel, email, data_inscrierii);
☑ un tabel ce conţine cărţile din bibliotecă, având structura:
inv(cota, titlu, editura, an_ap, nr_pag,autori, nr_vol,
limba);
☑ un tabel cu operaţiile de împrumumut şi restituire, cu
structura: oper(data_impr, ora_impr, cnp, cota, data_rest,
ora_rest, nr_zile_termen, operator_impr, oper_rest),
unde câmpul nr_zile_termen reprezintă numărul de zile pentru
care a fost împrumutată cartea şi se completează în momentul
împrumutului, iar câmurile operator_impr şi oper_rest conţin
numele bibliotecarului ce a împrumutat cartea respectiv a celui
care a primit cartea.
1. Să se scrie codul SQL pentru constituirea celor trei tabele.
DROP
DROP SCHEMA
SCHEMA IF
IF EXISTS
EXISTS biblio;
biblio; CREATE
CREATE SCHEMA
SCHEMA biblio;
biblio;
CREATE
CREATE TABLE
TABLE biblio.utiliz(cnp
biblio.utiliz(cnp char(13)
char(13) PRIMARY
PRIMARY KEY,
KEY,
nume
nume varchar(50),prenume
varchar(50),prenume varchar(50),
varchar(50), adresa
adresa varchar(250),
varchar(250),
tel
tel varchar(50),email
varchar(50),email varchar(50),
varchar(50),
data_inscrierii
data_inscrierii date
date DEFAULT
DEFAULT CURRENT_DATE,
CURRENT_DATE, data_n
data_n date);
date);
CREATE TABLE biblio.inv(cota
CREATE TABLE biblio.inv(cota varchar(50)
varchar(50) PRIMARY KEY,titlu
PRIMARY KEY ,titlu varchar(100),
varchar(100),
ditura
ditura varchar(50),
varchar(50), an_ap
an_ap int,
int, nr_pag
nr_pag int,
int, autori
autori varchar(200),
varchar(200),
nr_vol
nr_vol int,
int, limba
limba varchar(30),
varchar(30), imprumutata
imprumutata boolean
boolean DEFAULT
DEFAULT false);
false);
CREATE
CREATE TABLE
TABLE biblio.oper(data_impr
biblio.oper(data_impr date
date DEFAULT
DEFAULT CURRENT_DATE,
CURRENT_DATE,
ora_impr time DEFAULT CURRENT_TIME,
ora_impr time DEFAULT CURRENT_TIME,
cnp
cnp char(13),cota
char(13),cota varchar(50),data_rest
varchar(50),data_rest date,
date, ora_rest
ora_rest time,
time,
nr_zile_termen int DEFAULT 30,oper_impr varchar(50),
nr_zile_termen int DEFAULT 30,oper_impr varchar(50),
oper_rest
oper_rest varchar(50),
varchar(50),
CONSTRAINT
CONSTRAINT k_oper
k_oper PRIMARY
PRIMARY KEY(data_impr,ora_impr,cnp,cota),
KEY(data_impr,ora_impr,cnp,cota),
CONSTRAINT
CONSTRAINT f1_oper
f1_oper FOREIGN
FOREIGN KEY(cota)
KEY(cota) REFERENCES
REFERENCES biblio.inv
biblio.inv (cota),
(cota),
CONSTRAINT f2_oper FOREIGN KEY(cnp) REFERENCES biblio.utiliz(cnp));
CONSTRAINT f2_oper FOREIGN KEY(cnp) REFERENCES biblio.utiliz(cnp));
Notă: Cheile externe (FOREIGN KEY) au următoarele efecte:
● NU putem avea în tabelul oper o cota care NU se regăseşte şi în
tabelul inv;
● NU putem avea în tabelul oper un cnp care să nu existe şi în
tabelul utiliz.
2. Să se scrie codul pentru popularea tabelelor cu câteva
înregistrări.

48
12.Teste rezolvate – gestiune bibliotecă

INSERT
INSERT INTO
INTO biblio.utiliz(cnp,nume,data_n)
biblio.utiliz(cnp,nume,data_n) VALUES
VALUES
('cnp0000000001','Ion','1992-05-21'),
('cnp0000000001','Ion','1992-05-21'), ('cnp0000000002','Vasile',
('cnp0000000002','Vasile',
'1991-04-21'),
'1991-04-21'), ('cnp0000000003','Ion','1996-05-21'),
('cnp0000000003','Ion','1996-05-21'),
('cnp0000000004','Grigore','1982-05-21'),
('cnp0000000004','Grigore','1982-05-21'), ('cnp0000000005','Ion',
('cnp0000000005','Ion',
'1972-09-21'),('cnp0000000006','Geo','1993-09-21'),
'1972-09-21'),('cnp0000000006','Geo','1993-09-21'),
('cnp0000000007','Ionescu','1992-05-21'),
('cnp0000000007','Ionescu','1992-05-21'), ('cnp0000000008','Vlad',
('cnp0000000008','Vlad',
'1999-05-21'),
'1999-05-21'), ('cnp0000000009','Alin','1992-08-21');
('cnp0000000009','Alin','1992-08-21');
INSERT
INSERT INTO
INTO biblio.inv(cota,
biblio.inv(cota, titlu,
titlu, editura,
editura, an_ap,
an_ap, nr_pag
nr_pag )) VALUES
VALUES
('001','T1','ALL',1977,150),('002','T21','Nemira',1977,190),
('001','T1','ALL',1977,150),('002','T21','Nemira',1977,190),
('003','T1','Tehnica',1977,250),('004','T54','Albatros',1977,220),
('003','T1','Tehnica',1977,250),('004','T54','Albatros',1977,220),
('005','T97','Nemira',1977,80),('006','T1','ALL',1977,50),
('005','T97','Nemira',1977,80),('006','T1','ALL',1977,50),
('007','T77','Tehnica',1977,120),('008','T21','Albatros',1987,100);
('007','T77','Tehnica',1977,120),('008','T21','Albatros',1987,100);
INSERT
INSERT INTO
INTO biblio.oper(data_impr,
biblio.oper(data_impr, cnp,
cnp, cota,
cota,
data_rest,
data_rest, nr_zile_termen, oper_impr, oper_rest)
nr_zile_termen, oper_impr, oper_rest) VALUES
VALUES
('2013-01-22','cnp0000000002','001','2013-02-22',55,'X1','X1'),
('2013-01-22','cnp0000000002','001','2013-02-22',55,'X1','X1'),
('2012-12-22','cnp0000000003','003','2013-02-22',17,'X1','X2'),
('2012-12-22','cnp0000000003','003','2013-02-22',17,'X1','X2'),
('2013-01-22','cnp0000000002','002','2013-02-25',55,'X1','X1'),
('2013-01-22','cnp0000000002','002','2013-02-25',55,'X1','X1'),
('2013-01-22','cnp0000000004','001','2013-02-24',5,'X1','X1');
('2013-01-22','cnp0000000004','001','2013-02-24',5,'X1','X1');
INSERT
INSERT INTO
INTO biblio.oper(data_impr,cnp,cota,nr_zile_termen,oper_impr)
biblio.oper(data_impr,cnp,cota,nr_zile_termen,oper_impr)
VALUES
VALUES ('2013-03-22','cnp0000000002','006',17,'X1'),
('2013-03-22','cnp0000000002','006',17,'X1'), ('2012-05-22',
('2012-05-22',
'cnp0000000002','006',127,'X1'),
'cnp0000000002','006',127,'X1'), ('2013-05-22','cnp0000000002','001',
('2013-05-22','cnp0000000002','001',
27,'X1'),('2013-01-21','cnp0000000001','005',117,'X1'),('2012-05-22',
27,'X1'),('2013-01-21','cnp0000000001','005',117,'X1'),('2012-05-22',
'cnp0000000007','002',71,'X2'),
'cnp0000000007','002',71,'X2'), ('2009-05-22','cnp0000000002','006',
('2009-05-22','cnp0000000002','006',
577,'X2'),('2013-05-22','cnp0000000005',
577,'X2'),('2013-05-22','cnp0000000005', '006',7,'X1'),('2012-05-22',
'006',7,'X1'),('2012-05-22',
'cnp0000000005','006',17,'X1'),
'cnp0000000005','006',17,'X1'), ('2012-11-22','cnp0000000002','006',
('2012-11-22','cnp0000000002','006',
57,'X1'),
57,'X1'), ('2013-03-01','cnp0000000002','005',19,'X1');
('2013-03-01','cnp0000000002','005',19,'X1');
Notă: Sunt populate cu date mai întâi tabelele de tip master (sau
părinte), altfel cheia din tabelul slave (sau copil) nu se va
regăsi şi în tabelul părinte.
3 Să se afişeze cotele, titlurile, cnp-urile şi numele persoanelor
pentru toate cărţile împrumutate (au fost sau sunt imprumutate).
SELECT
SELECT oper.cota,inv.titlu,oper.cnp,utiliz.nume
oper.cota,inv.titlu,oper.cnp,utiliz.nume
FROM
FROM biblio.oper,
biblio.oper, biblio.utiliz,
biblio.utiliz, biblio.inv
biblio.inv
WHERE
WHERE oper.cota
oper.cota == inv.cota
inv.cota AND
AND oper.cnp
oper.cnp == utiliz.cnp;
utiliz.cnp;
sau
SELECT
SELECT inv.cota,inv.titlu,utiliz.cnp,utiliz.nume
inv.cota,inv.titlu,utiliz.cnp,utiliz.nume
FROM
FROM biblio.oper,
biblio.oper, biblio.utiliz,
biblio.utiliz, biblio.inv
biblio.inv
WHERE
WHERE oper.cota
oper.cota == inv.cota
inv.cota AND
AND oper.cnp
oper.cnp == utiliz.cnp;
utiliz.cnp;
4. Să se afişeze toate cărţile (cota,titlu,data_impr) împrumutate
de către utilizatorii al căror nume conţine secvenţa 'ion':

49
II.Limbajul SQL

SELECT
SELECT oper.cota,inv.titlu,oper.data_impr,utiliz.nume
oper.cota,inv.titlu,oper.data_impr,utiliz.nume
FROM
FROM biblio.oper,
biblio.oper, biblio.inv,
biblio.inv, biblio.utiliz
biblio.utiliz
WHERE
WHERE oper.cota=inv.cota
oper.cota=inv.cota AND
AND oper.cnp=utiliz.cnp
oper.cnp=utiliz.cnp
AND
AND utiliz.nume
utiliz.nume ilike
ilike '%ion%';
'%ion%';
5. Să se afişeze toate cărţile (cota, titlu) care se află în
bibliotecă în momentul de faţă (ce cărţi mai există pe raft):
select
select inv.cota,inv.titlu
inv.cota,inv.titlu from
from biblio.inv
biblio.inv where
where inv.imprumutata=false;
inv.imprumutata=false;
--sau
--sau
SELECT
SELECT inv.cota,inv.titlu
inv.cota,inv.titlu FROM
FROM biblio.inv
biblio.inv WHERE
WHERE NOT
NOT inv.imprumutata;
inv.imprumutata;
sau (toate cărţile din inv mai puţin cele care se află în oper şi
nu au completat câmpul data pentru restituire):
select
select inv.cota,inv.titlu
inv.cota,inv.titlu FROM
FROM biblio.inv
biblio.inv
EXCEPT
EXCEPT
select
select distinct
distinct inv.cota,inv.titlu
inv.cota,inv.titlu from
from biblio.oper,
biblio.oper, biblio.inv
biblio.inv
WHERE
WHERE oper.cota=inv.cota
oper.cota=inv.cota AND
AND oper.data_rest
oper.data_rest IS
IS NULL;
NULL;
sau prin utilizarea subinterogărilor:
SELECT
SELECT par.cota,
par.cota, inv.titlu
inv.titlu FROM
FROM
(select
(select cota
cota from
from biblio.inv
biblio.inv EXCEPT
EXCEPT select
select distinct
distinct cota
cota
from
from biblio.oper
biblio.oper WHERE
WHERE oper.data_rest
oper.data_rest IS
IS NULL)par,
NULL)par,
biblio.inv
biblio.inv WHERE
WHERE par.cota=inv.cota;
par.cota=inv.cota;
6. Să se afişeze toţi restanţierii (nume,cnp,cota,titlu,data_impr,
nr_zile_depasire):
SELECT
SELECT utiliz.nume,oper.cnp,oper.cota,inv.titlu,data_impr,
utiliz.nume,oper.cnp,oper.cota,inv.titlu,data_impr,
oper.nr_zile_termen,CURRENT_DATE
oper.nr_zile_termen,CURRENT_DATE --
(oper.data_impr+oper.nr_zile_termen)
(oper.data_impr+oper.nr_zile_termen) as as nr_zile_depasire
nr_zile_depasire
FROM
FROM biblio.oper,
biblio.oper, biblio.inv,
biblio.inv, biblio.utiliz
biblio.utiliz
WHERE
WHERE oper.cnp
oper.cnp == utiliz.cnp
utiliz.cnp AND
AND oper.cota
oper.cota == inv.cota
inv.cota
AND
AND oper.data_rest
oper.data_rest ISIS NULL
NULL
AND
AND CURRENT_DATE
CURRENT_DATE >> (oper.data_impr+oper.nr_zile_termen);
(oper.data_impr+oper.nr_zile_termen);
7. Să se afişeze numărul total de pagini citite de utilizatori pe
fiecare an (afişează anul şi numărul total de pagini). Se preia
anul din data împrumutului:
SELECT
SELECT extract(year
extract(year from
from oper.data_impr),
oper.data_impr),
sum(inv.nr_pag)
sum(inv.nr_pag) asas nr_t_pag
nr_t_pag FROM
FROM biblio.oper,
biblio.oper, biblio.inv
biblio.inv
WHERE
WHERE oper.cota
oper.cota == inv.cota
inv.cota
GROUP
GROUP BY
BY extract(year
extract(year from
from oper.data_impr);
oper.data_impr);
8. Să se afişeze numărul total de pagini citite de fiecare
utilizator în fiecare an (afişează anul, nr. total de pagini, nume
utilizator, cnp):

50
12.Teste rezolvate – gestiune bibliotecă

SELECT
SELECT extract(year
extract(year from
from oper.data_impr),
oper.data_impr),
sum(inv.nr_pag)
sum(inv.nr_pag) asas nr_t_pag,utiliz.nume,oper.cnp
nr_t_pag,utiliz.nume,oper.cnp
FROM
FROM biblio.oper,
biblio.oper, biblio.utiliz,
biblio.utiliz, biblio.inv
biblio.inv
WHERE
WHERE oper.cota
oper.cota == inv.cota
inv.cota AND
AND oper.cnp
oper.cnp == utiliz.cnp
utiliz.cnp
GROUP
GROUP BY
BY extract(year
extract(year from
from oper.data_impr),
oper.data_impr), utiliz.nume,
utiliz.nume, oper.cnp
oper.cnp
ORDER
ORDER BY
BY extract(year
extract(year from
from oper.data_impr);
oper.data_impr);
9. Să se afişeze de câte ori a fost împrumutată fiecare carte
(cota, titlu, nr_imprumuturi).
SELECT
SELECT oper.cota,
oper.cota, inv.titlu,count(oper.*)
inv.titlu,count(oper.*) as
as nr_impr
nr_impr
FROM
FROM biblio.oper, biblio.inv WHERE oper.cota
biblio.oper, biblio.inv WHERE oper.cota == inv.cota
inv.cota
GROUP
GROUP BY
BY oper.cota,
oper.cota, inv.titlu;
inv.titlu;

51
III.Programarea la nivel de server – plpgSQL

III. Programarea la nivel de server – plpgSQL


Programarea la nivel de server (server side) constă în
dezvoltarea unor funcţii ce sunt apelate utilizând comenzi SQL.
Aceste funcţii, pot înlocui blocurile de comenzi SQL şi pot fi
scrise în diverse limbaje de programare – inclusiv SQL. Aceste
funcţii sunt stocate în cadrul server-ului de baze de date, în
acest caz PostgreSQL. De asemenea, funcţiile pot realiza operaţii
specifice fiecărei înregistrări (chiar diferite la nivel de
înregistrare), ceea ce limbajul SQL nu permite. În acest capitol
prezint doar limbajul plpgSQL (procedural language postgreSQL). Un
alt mare avantaj constă în posibiltatea modificării corpului
funcţiei fără a modifica interfaţa software.

1. Funcţii de tipul SQL


Funcţiile sunt la nivel de schemă, se deosebesc între ele
prin nume, numărul sau tipul argumentelor. Funcţiile tratate în
acest capitol sunt cele de tip SQL şi de tip plpgSQL. Structura
simplificată a unei funcţii de tip SQL este:
CREATE
CREATE [OR
[OR REPLACE]
REPLACE] function
function nume_functie
nume_functie --
-- in
in loc
loc de
de DROP
DROP ...
...
RETURNS
RETURNS tipul_de_data_intors_de_functie
tipul_de_data_intors_de_functie AS AS
$$
$$
---
--- corpul
corpul funcţiei---
funcţiei---
$$
$$
LANGUAGE
LANGUAGE 'sql';
'sql';
Corpul funcţiei este încadrat de $$ sau $BODY$ .
1.1. Funcţii de tip SQL ce nu preiau şi nu întorc nimic
În acest caz, lista de parametrii este vidă, iar tipul
pentru RETURNS este void. De obicei sunt funcţii ce execută blocuri
de comenzi SQL de tipul: CREATE, UPDATE sau DELETE. Funcţia
pic.f0() creează un tabel pic.t1.
--Ex.
--Ex. 1(1):
1(1): varianta
varianta 11 D+A
D+A -- DEFINIRE
DEFINIRE ++ APEL
APEL
CREATE
CREATE OR
OR REPLACE
REPLACE function
function pic.f0()
pic.f0()
RETURNS
RETURNS void
void AS
AS
$$
$$
CREATE
CREATE TABLE
TABLE pic.t1(a1
pic.t1(a1 serial
serial PRIMARY
PRIMARY KEY,
KEY, a2
a2 int,
int, a3
a3 int,
int,
a4
a4 varchar(20));
varchar(20));
$$
$$ LANGUAGE
LANGUAGE 'sql';
'sql';
--
-- apelul
apelul functiei
functiei (executia
(executia funcţiei)
funcţiei)
DROP
DROP TABLE IF EXISTS c1.t1;
TABLE IF EXISTS c1.t1; select
select pic.ftest0()
pic.ftest0() asas x1;
x1;
Funcţia pic.f0() nu preia şi nu întoarce valori. Pentru

52
1.Funcţii de tipul SQL

popularea cu date utilizăm funcţia pic.f1()


/*Ex.
/*Ex. 1(2):D+A
1(2):D+A varianta
varianta 22 D+A*/
D+A*/
CREATE
CREATE OR
OR REPLACE
REPLACE function
function pic.f1()
pic.f1() RETURNS
RETURNS void
void AS
AS $$
$$
INSERT
INSERT INTO
INTO pic.t1(a2,a3,a4)
pic.t1(a2,a3,a4)
VALUES(7,8,'AA1'),(12,7,'AA2'),(11,5,'AA3');
VALUES(7,8,'AA1'),(12,7,'AA2'),(11,5,'AA3');
$$
$$ LANGUAGE
LANGUAGE 'sql';
'sql';
SELECT
SELECT pic.f1();
pic.f1(); --apel
--apel functie
functie varianta
varianta 11
Pentru funcţii de tip SQL (language sql) nu putem avea
operaţiile pentru crearea tabelului şi pentru inserarea de
înregistrări în aceeaşi funcţie. Aceasta este datorată faptului că
secvenţa SQL este realizată într-un singur bloc, adică
validarea(COMMIT) este realizată la terminarea funcţiei, iar până
în acel moment tabelul nu există. Sintaxa pentru funcţii de tip SQL
(language SQL) este verificată în momentul definirii funcţiei.
1.2. Funcţii SQL ce nu preiau nimic şi întorc un set de
înregistrări
Structura seturilor de înregistrări întoarse poate fi
preluată de la un tabel existent sau poate fi constituită în
interiorul funcţiei. În acest sens, avem mai multe variante:
☑ Funcţia f1_1() selectează toate câmpurile din tabel şi
întoarce înregistrările selectate prin definirea strucurii
tabelului selectat. Este valabil doar la funcţii de tip SQL,
altfel ar fi trebuit să întoarcă înregistrare cu
înregistrare.
--Ex.
--Ex. 1(3D)
1(3D) DEFINITIE:functie
DEFINITIE:functie SQLSQL ptr.afisarea
ptr.afisarea înreg.din
înreg.din t1–VARIANTA
t1–VARIANTA 11
CREATE
CREATE OR REPLACE function pic.f1_1() RETURNS SETOF pic.t1 AS
OR REPLACE function pic.f1_1() RETURNS SETOF pic.t1 AS
$$
$$ SELECT
SELECT ** FROM
FROM pic.t1
pic.t1 $$
$$ LANGUAGE
LANGUAGE 'sql';
'sql';
Apelul funcţiei poate fi realizat în modurile următoare:
--Ex.
--Ex. 1(3A)
1(3A) APEL
APEL pic.f1_1()
pic.f1_1() –VARIANTA
–VARIANTA 11 de
de selecţie
selecţie
SELECT
SELECT pic.f1_1();
pic.f1_1(); ---- (1,7,...)
(1,7,...) are
are semnificatie
semnificatie de
de structura
structura sau
sau
SELECT
SELECT a1,a2,a3,a4
a1,a2,a3,a4 FROM
FROM pic.f2();
pic.f2();
--
-- a1,a2,a3,a4
a1,a2,a3,a4 :: afişează
afişează fiecare
fiecare coloana
coloana (a1,a2
(a1,a2 …)
…) din
din SETOF
SETOF pic.t1
pic.t1
Deşi câmpurile din selecţie au fost specificate (prin
denumire), primul apel al funcţiei f1_1() întoarce o singură
coloană de tip structură având ca elemente câmpurile din
cadrul selecţiei. Al doilea apel al funcţiei f1_1() întoarce
separat câmpurile a1, a2, a3 şi a4;
☑ În a doua variantă de selecţie (funcţia f1_2()), în cadrul
definiţiei nu mai este specificat tipul datelor din cadrul
structurii, ci numai tipul „structură” (SETOF RECORD):

53
III.Programarea la nivel de server – plpgSQL

--Ex.
--Ex. 1(4D)
1(4D) -functie
-functie SQL
SQL ptr.
ptr. afişarea
afişarea conţinutului
conţinutului lui
lui t1
t1 –VARIANTA
–VARIANTA 22
CREATE OR REPLACE function pic.f1_2()
CREATE OR REPLACE function pic.f1_2()
RETURNS
RETURNS SETOF
SETOF RECORD
RECORD AS
AS $$
$$ SELECT
SELECT a1,a2,a3,a4
a1,a2,a3,a4 FROM
FROM pic.t1
pic.t1 $$
$$
LANGUAGE sql;
LANGUAGE sql;
Funcţia f1_2() selectează câmpuri din tabel şi întoarce
înregistrările selectate prin definirea structurii tabelului
selectat. Dacă apelăm “SELECT a1,a2,a3,a4 FROM pic.f1_2()” nu
este cunoscut numele câmpurilor. Apelul funcţiei pic.f1_2()
se poate realiza în două moduri:
--Ex.
--Ex. 1(4A)
1(4A) APEL
APEL pic.f1_2()
pic.f1_2() –VARIANTA
–VARIANTA 22 dede selecţie
selecţie
SELECT
SELECT pic.f1_2();
pic.f1_2(); -- -- rezultat
rezultat pe
pe oo singura
singura coloană
coloană sau
sau
SELECT
SELECT a1,a2,a3,a4
a1,a2,a3,a4 FROM
FROM c1.f3()
c1.f3()
AS
AS (a1
(a1 int,a2
int,a2 int,a3
int,a3 int,a4
int,a4 varchar(20));
varchar(20));
--
-- după
după AS
AS este
este definita
definita structura
structura RECORD
RECORD -- patru
patru coloane
coloane
☑ O altă variantă este dată de definiţia funcţiei pic.f1_3()
în care este specificată structura întoarsă ca fiind
structura tabelului pic.t1:
--Ex.
--Ex. 1(5D):functie
1(5D):functie SQL
SQL ptr.
ptr. afişarea
afişarea conţinutului
conţinutului t1
t1 –VARIANTA
–VARIANTA 33
CREATE
CREATE OR
OR REPLACE
REPLACE function
function pic.f1_3()
pic.f1_3() RETURNS
RETURNS SETOF
SETOF pic.t1
pic.t1 AS
AS
$$
$$ SELECT
SELECT a1,a2,a3,a4
a1,a2,a3,a4 FROM
FROM pic.t1
pic.t1 $$
$$ LANGUAGE
LANGUAGE 'sql';
'sql';
şi exemplul de apel:
--Ex.
--Ex. 1(5A)
1(5A) APEL
APEL pic.f1_3()
pic.f1_3() –VARIANTA
–VARIANTA 33 de
de selecţie
selecţie
SELECT
SELECT c1.ftest4();
c1.ftest4(); ---- afişează
afişează un
un singur
singur câmp
câmp de
de tip
tip structură
structură SAU
SAU
SELECT
SELECT ** FROM
FROM pic.f1_3();--afişeză
pic.f1_3();--afişeză a1,a2,a3,a4
a1,a2,a3,a4 -- sunt
sunt deja
deja definite
definite
SELECT
SELECT a1,a2,a3,a4
a1,a2,a3,a4 FROM
FROM c1.ftest4();
c1.ftest4();
Câmpurile a1, a2, a3 şi a4 sunt afişate distinct, tipul
fiecarui câmp este cunoscut din RETURNS SETOF pic.t1.
1.3. Funcţii SQL cu transfer prin argument şi cu returnare
subset de înregistrări
Dacă dorim să punem un filtru asupra datelor din tabelul
pic.t1 şi să întoarcem doar un subset al înregistrărilor, vom avea:
--Ex.
--Ex. 1.1(6D):fcţ.
1.1(6D):fcţ. SQL
SQL ce
ce preia
preia 22 param.
param. şi
şi întoarce
întoarce unun set
set de
de înreg.
înreg.
CREATE OR REPLACE function c1.f3(b2 int,
CREATE OR REPLACE function c1.f3(b2 int, b3 int)b3 int)
RETURNS
RETURNS SETOF
SETOF RECORD
RECORD AS
AS
$$
$$ SELECT a1,a2,a3,a4 FROM
SELECT a1,a2,a3,a4 FROM c1.t1
c1.t1 WHERE
WHERE a2
a2 >> $1
$1 AND
AND a3
a3 << $2;
$2;
$$
$$ LANGUAGE
LANGUAGE 'sql';
'sql';
şi exemplul de apel:
--Ex.
--Ex. 1.1(6A)
1.1(6A) APEL
APEL pic.f3(int,int)
pic.f3(int,int)
SELECT
SELECT a1,a2,a3,a4
a1,a2,a3,a4 FROM
FROM c1.f3(10,15)
c1.f3(10,15) AS AS
(a1
(a1 int,
int, a2
a2 int,
int, a3
a3 int,
int, a4
a4 varchar(20));
varchar(20));

54
1.Funcţii de tipul SQL

Tipul variabilei se pune după numele acesteia (invers faţă


de limbajul C). Simbolul $1 semnifică primul argument (b2), iar
simbolul $2 semnifică al doilea argument (b3).
1.4. Funcţii SQL cu transfer prin argument şi cu returnare de tip
boolean
În acest caz funcţia are forma din exemplul următor, unde
întoarce valoarea de adevăr TRUE dacă există cel puţin o
înregistrare în cadrul celui de al doilea SELECT, altfel întoarce
FALSE:
--Ex.
--Ex. 1.1(7D):funcţie
1.1(7D):funcţie SQL
SQL ce
ce preia
preia 11 param.
param. şi
şi întoarce
întoarce val.
val. bool.
bool.
CREATE
CREATE OR
OR REPLACE
REPLACE function
function pic.f4(b2
pic.f4(b2 int)
int) RETURNS
RETURNS BOOLEAN
BOOLEAN AS
AS
$$
$$ SELECT
SELECT EXISTS(SELECT
EXISTS(SELECT ** FROM
FROM c1.t1
c1.t1 WHERE
WHERE a2>$1);
a2>$1); $$
$$ LANGUAGE
LANGUAGE 'sql';
'sql';
iar apelul are forma:
/*Ex.
/*Ex. 1.1(7A)
1.1(7A) APEL
APEL pic.f4()
pic.f4() */
*/ SELECT
SELECT pic.f4(2)
pic.f4(2) as
as f1;
f1;

2. Funcţii de tipul plpgSQL


Funcţiile (procedurile) de tipul plpgSQL sunt de câteva ori
mai lente decat funcţiile SQL însă sunt mult mai flexibile, acestea
permiţând operaţii variate pentru fiecare înregistrare în parte.
Structura simplificată a unei funcţii de tip plpgSQL este:
CREATE
CREATE [OR
[OR REPLACE]
REPLACE] function
function nume_functie
nume_functie ---- în
în loc
loc de
de DROP
DROP ...
...
RETURNS
RETURNS tipul_de_data_intors_de_functie
tipul_de_data_intors_de_functie AS AS
$$
$$
DECLARE
DECLARE –-
–- urmează
urmează declaraţii
declaraţii variabile
variabile locale
locale
BEGIN
BEGIN ---
--- corpul
corpul funcţiei---
funcţiei---
END
END
$$
$$ LANGUAGE
LANGUAGE 'sql';
'sql';
Un exemplu de funcţie plpgSQL ce nu preia şi nu întoarce date:
--Ex.
--Ex. 2.1(1D):funcţie
2.1(1D):funcţie SQLSQL ce
ce nu
nu preia
preia param.
param. şi
şi nu
nu întoarce
întoarce date.
date.
CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION pic.f2_1()
pic.f2_1() RETURNS
RETURNS void
void ASAS
$$
$$ BEGIN
BEGIN
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t2;
pic.t2;
CREATE TABLE pic.t2(a1 serial
CREATE TABLE pic.t2(a1 serial PRIMARY KEY,a2
PRIMARY KEY ,a2 int,a3
int,a3 int,a4
int,a4 varchar(20));
varchar(20));
INSERT
INSERT INTO
INTO c1.t2(a2,a3,a4)
c1.t2(a2,a3,a4)
VALUES
VALUES (17,18,'AA11'),(112,17,'AA12'),(111,15,'AA13');
(17,18,'AA11'),(112,17,'AA12'),(111,15,'AA13');
END
END $$
$$ LANGUAGE
LANGUAGE 'plpgsql';
'plpgsql';
iar exemplul de apel:
/*Ex.
/*Ex. 2.1(1A)
2.1(1A) APEL
APEL pic.f2_1()
pic.f2_1() */
*/ SELECT
SELECT pic.f2_1();
pic.f2_1();

55
III.Programarea la nivel de server – plpgSQL

O funcţie de tip SQL nu ar fi permis crearea tabelului şi


popularea acestuia în cadrul aceluiaşi bloc de instrucţiuni. În
continuare ne vom referi doar la plpgSQL.

3. Declararea variabilelor. Secvenţe de decizie


În plpgSQL se pot declara variabile de tipul celor întâlnite
în limbajul SQL. Aceste variabile se pot declara în secţiunile
precedate de cuvântul cheie DECLARE ce apare înaintea blocului de
date delimitat de BEGIN şi END. Cuvintele cheie din plpgSQL, în mod
asemănător cu cele din SQL, sunt case insensitive (nu ţin cont de
literă mare sau mică).
În plpgSQL este recunoscută instrucţiunea de decizie IF iar
principalele forme sunt prezentate în cele ce urmează:
IF
IF <expresie>
<expresie> THEN
THEN << BLOC
BLOC INSTRUCŢIUNI
INSTRUCŢIUNI >> END
END IF
IF –-
–- sau
sau
IF
IF <expresie>
<expresie> THEN
THEN << BLOC
BLOC INSTRUCŢIUNI_1
INSTRUCŢIUNI_1 >> ELSE
ELSE
<< BLOC
BLOC INSTRUCŢIUNI_2
INSTRUCŢIUNI_2 >> END
END IF
IF
În următorul exemplu este creată o funcţie care adaugă o
înregistrare dacă numărul acestora (înainte de adăugare) este
impar şi două înregistrări dacă numărul acestora este par.
/*Ex.
/*Ex. 3(1D):*/
3(1D):*/ CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION pic.f3_1()
pic.f3_1() RETURNS
RETURNS void
void AS
AS
$$
$$ DECLARE
DECLARE nr_inreg
nr_inreg int;--
int;-- deschide
deschide blocul
blocul pentru
pentru declaratii
declaratii
BEGIN
BEGIN SELECT
SELECT count(*)
count(*) FROM
FROM pic.t1
pic.t1 INTO
INTO nr_inreg;
nr_inreg;
IF
IF NOT
NOT (nr_inreg
(nr_inreg %% 2)
2) == 00 THEN
THEN --
-- daca
daca este
este impar
impar
INSERT
INSERT INTO
INTO pic.t1(a2,a3,a4)
pic.t1(a2,a3,a4) VALUES
VALUES (1001,0,'---');
(1001,0,'---');
ELSE
ELSE INSERT
INSERT INTO
INTO pic.t1(a2,a3,a4)
pic.t1(a2,a3,a4) VALUES
VALUES (1002,0,'M'),(1003,17,'N');
(1002,0,'M'),(1003,17,'N');
END
END IF;
IF; END
END $$
$$ LANGUAGE
LANGUAGE plpgsql;
plpgsql;
iar apelul are forma:
/*Ex.
/*Ex. 3(1A)
3(1A) APEL
APEL pic.f3_1()
pic.f3_1() */
*/ select
select pic.f3_1();
pic.f3_1();
Comanda SQL:“SELECT count(*) FROM pic.t1 INTO nr_inreg;” are ca
efect determinarea numărului total de înregistrări cu ajutorul
funcţiei de agregare COUNT(*) şi transferarea valorii rezultate în
variabila nr_inreg.

4. Parametri de intrare şi ieşire


Parametrii de ieşire (variabilele ce conţin valorile
returnate de funcţie) pot fi specificaţi şi prin lista de argumente
a funcţiei, prin utilizarea cuvântului cheie OUT în faţa acestora.
Parametrii de intrare (preluaţi de către funcţie) au
cuvântul cheie IN în faţa acestora sau nu au nimic (în mod implicit
parametrii din lista de argumente sunt parametri de intrare).
În exemplul următor, valorile date prin variabilele a şi b

56
4.Parametri de intrare şi ieşire

sunt transmise funcţiei, iar aceasta întoarce valoarea c, valoare


ce conţine suma celor două variabile.
/*Ex.
/*Ex. 4(1D):*/
4(1D):*/ create
create or
or replace
replace function
function
pic.f4_1(IN
pic.f4_1(IN aa int,
int, IN
IN bb int,
int, OUT
OUT cc int)
int) returns
returns int
int as
as
$$ BEGIN c:=a+b;
$$ BEGIN c:=a+b; ENDEND $$ language plpgsql;.
$$ language plpgsql;.
Notă: Expresia c:=a+b; înseamnă asignarea rezultatului adunării în
variabila c (operatorul de asignare).
Nu este necesară scrierea instrucţiunii return c; deoarece
există OUT c.
Pentru apelul funcţiei se transmit doar parametrii de
intrare, în acest caz câmpul rezultat va avea numele funcţiei.
/*Ex.
/*Ex. 4(1A-1)
4(1A-1) APEL
APEL pic.f4_1()
pic.f4_1() */
*/ SELECT
SELECT pic.f1(5,7);
pic.f1(5,7);
Dacă apelul are forma de mai jos, câmpul rezultat are numele
variabilei de ieşire.
/*Ex
/*Ex 4(1A-2)
4(1A-2) APEL
APEL pic.f4_1()
pic.f4_1() */
*/ SELECT
SELECT ** FROM
FROM pic.f1(4,9);
pic.f1(4,9);
În continuare este prezentat un alt exemplu cu doi parametri
de intrare şi trei parametri de ieşire. În prima variantă a
definirii funcţiei nu a fost necesară specificaţia RETURNS, iar
denumirile variabilelor de ieşire sunt recunoscute (precum şi cele
de intrare) şi pot fi utilizate în corpul funcţiei:
/*
/* Ex.
Ex. 4(2D-1):
4(2D-1): */
*/ CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION pic.f4_2(IN
pic.f4_2(IN aa int,
int, IN
IN bb
int,
int, OUT
OUT suma
suma int,
int, OUT
OUT produs
produs int,
int, OUT
OUT impartire
impartire numeric)
numeric) AS
AS
$$
$$ BEGIN
BEGIN suma:=a+b;
suma:=a+b; produs:=a*b;
produs:=a*b; impartire:=a/(b*1.0);
impartire:=a/(b*1.0);
END
END $$
$$ language
language plpgsql;
plpgsql;
Apelul funcţiei se realizează fie prin menţionarea
parametrilor de ieşire:
/*Ex.
/*Ex. 4(2A-1)
4(2A-1) APEL
APEL pic.f4_2()
pic.f4_2() */*/ ,
SELECT
SELECT suma,
suma, produs,
produs, impartire
impartire FROM
FROM pic.f2(5,7);
pic.f2(5,7);
fie prin selecţia tuturor câmpurilor existente:
/*Ex.
/*Ex. 4(2A-2)
4(2A-2) APEL
APEL pic.f4_2()
pic.f4_2() */
*/
SELECT
SELECT ** FROM
FROM pic.f2(5,7);
pic.f2(5,7);
În exemplul de mai sus se poate adăuga RETURNS RECORD fără a
avea vreun efect asupra execuţiei funcţiei.
/*
/* Ex.
Ex. 4(2D-2):
4(2D-2): */*/ CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION pic.f4_2(a
pic.f4_2(a int,
int, IN
IN bb
int,
int, OUT
OUT suma
suma int,
int, OUT
OUT produs
produs int,
int, OUT
OUT impartire
impartire numeric)
numeric) RETURNS
RETURNS
RECORD
RECORD ASAS
$$
$$ BEGIN
BEGIN suma:=a+b;
suma:=a+b; produs:=a*b;
produs:=a*b; impartire:=a/(b*1.0);
impartire:=a/(b*1.0);
END
END $$
$$ LANGUAGE
LANGUAGE plpgsql;
plpgsql;
Cuvântul cheie IN (INPUT) este opţional, acesta fiind sensul
implicit pentru argumente.

57
III.Programarea la nivel de server – plpgSQL

5. Implementarea notificărilor
Uneori sunt necesare informaţii despre valorile variabilelor
în diverse puncte sau sunt necesare informaţii despre anumite stări
din cadrul funcţiei (de exemplu, numărul de înregistrări afectate
de o operaţie SQL de tipul UPDATE sau DELETE), iar aceste informaţii
sunt necesare fără a ieşi din funcţie. În aceste condiţii sunt
utilizate notificările, acestea se transmit de la server către
client.
În exemplul următor sunt adăugate câteva înregistrări
într-un tabel t1, apoi sunt realizate operaţii de adăugare
înregistrări şi actualizare. Se construiesc notificări pentru a
transmite către client numărul de înregistrări adăugate şi numărul
de înregistrări afectate de comanda UPDATE. De asemenea se testează
o expresie şi se transmite o notificare către client.
/*
/* Ex.
Ex. 5(1D):
5(1D): */*/ CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION pic.f5_1()
pic.f5_1() RETURNS
RETURNS TEXT
TEXT AS
AS
$$
$$ DECLARE
DECLARE nr_ins
nr_ins int;
int; nr_up
nr_up int;
int; achR
achR text;
text; total
total int;
int;
BEGIN
BEGIN achR:='OK';
achR:='OK'; DROPDROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t1;
pic.t1;
CREATE
CREATE TABLE
TABLE pic.t1(a
pic.t1(a serial
serial PRIMARY
PRIMARY KEY,
KEY, bb int,
int, cc varchar(10));
varchar(10));
INSERT INTO pic.t1(b,c)
INSERT INTO VALUES (11,'A1'),(21,'B2'),(31,'B3'),(41,'C3');
pic.t1(b,c) VALUES (11,'A1'),(21,'B2'),(31,'B3'),(41,'C3');
GET
GET DIAGNOSTICS
DIAGNOSTICS nr_ins
nr_ins == ROW_COUNT;
ROW_COUNT;
RAISE
RAISE NOTICE
NOTICE 'Au
'Au fost
fost adaugate:
adaugate: %% inregistrari',nr_ins;
inregistrari',nr_ins;
UPDATE
UPDATE pic.t1
pic.t1 SET
SET b=b+2
b=b+2 WHERE
WHERE cc LIKE
LIKE 'B%';
'B%';
GET
GET DIAGNOSTICS
DIAGNOSTICS nr_up
nr_up == ROW_COUNT;
ROW_COUNT; RAISE
RAISE NOTICE
NOTICE 'Au
'Au fost
fost
actualizate:
actualizate: %% inregistrari',nr_up;
inregistrari',nr_up;
SELECT
SELECT SUM(b)
SUM(b) FROM
FROM pic.t1
pic.t1 INTO
INTO total;
total;
IF
IF total
total << 60
60 THEN
THEN achR:='NOK';
achR:='NOK'; ENDEND IF;
IF; RETURN
RETURN achR;
achR;
END
END $$
$$ LANGUAGE
LANGUAGE plpgsql;
plpgsql;
Prin instrucţiunea GET DIAGNOSTICS este preluat conţinutul
variabilei ROW_COUNT din cadrul server-ului, variabilă ce conţine
numărul de înregistrări afectate de ultima comandă SQL.
Instrucţiunea RAISE NOTICE transmite notificarea către
client. În şirul ce urmează este specificat textul transmis către
client, iar în locul simbolurilor “%” din cadrul şirului se vor
transmite valorile variabilelor din finalul instrucţiunii. De
exemplu, dacă avem: “RAISE NOTICE 'A= % ...B=% ...C=%',a,b,c;”, iar
dacă a=10, b='TEST' şi c=15.7, atunci textul afişat va fi: “NOTICE:
A=10 … B=TEST ...C=15.7”.
Notificările pot să apară (în funcţie de configurarea
serverului de baze de date) în fişierele cu istoricul operaţiilor
la nivel de server de baze de date şi pot fi utile pentru
interpretarea anumitor situaţii apărute.
/*Ex.
/*Ex. 5(1A)
5(1A) APEL
APEL pic.f5_1()
pic.f5_1() */
*/ SELECT
SELECT pic.f5_1();
pic.f5_1();
După apelul funcţiei pic.f5_1() în fereastra SQL din

58
5.Implementarea notificărilor

pgAdmin3 în tab-ul Messages se pot urmări notificările apărute.


Există metode de preluare a notificărilor în cadrul interfeţei
client.

6. Structuri repetitive
O buclă cu instrucţiuni, în plpgSQL are forma:
LOOP
LOOP
--
-- bloc
bloc instructiuni
instructiuni
END
END LOOP;
LOOP;
Ieşirea din buclă se realizează cu instrucţiunea EXIT
(echivalent cu instrucţiunea break din limbajul C), iar revenirea
la începutul buclei (fară executarea restului de instrucţiuni) se
realizează cu instrucţiunea CONTINUE (idem în limbajul C),
utilizarea acestora fiind exemplificată în cele ce urmează.
i:=0;
i:=0; –-
–- iniţializare
iniţializare variabilă
variabilă
LOOP
LOOP –-
–- începutul
începutul buclei
buclei
--
-- instrucţiuni
instrucţiuni
IF
IF i<10
i<10 THEN
THEN CONTINUE;
CONTINUE; END
END IF;
IF;
IF
IF i>100
i>100 THEN
THEN EXIT;
EXIT; END
END IF;
IF;
--
-- instrucţiuni
instrucţiuni înîn care
care varibila
varibila ii are
are valori
valori între
între 10
10 şi
şi 100
100
END
END LOOP;
LOOP; –-
–- sfârşitul
sfârşitul buclei
buclei
Bucla de tip WHILE va avea forma:
WHILE
WHILE
--
-- condiţie
condiţie (expresie
(expresie evaluată
evaluată la
la TRUE
TRUE ---
--- adică
adică diferită
diferită de
de 0)
0)
LOOP
LOOP –-–- începutul
începutul buclei
buclei
--instrucţiuni
--instrucţiuni
END
END LOOP;–-
LOOP;–- sfârşitul
sfârşitul buclei
buclei
iar bucla de tip FOR va avea forma:
FOR
FOR nume
nume IN
IN expresieDomeniu
expresieDomeniu
LOOP
LOOP
--instructiuni
--instructiuni
END
END LOOP;
LOOP;
Pentru bucla de tip FOR putem avea formele:
FOR
FOR ii IN
IN 1..100
1..100
LOOP
LOOP
--
-- ii are
are valori
valori de
de la
la 11 la
la 100
100
END
END LOOP;
LOOP;
sau:

59
III.Programarea la nivel de server – plpgSQL

FOR
FOR rr IN
IN DENUMIRE_TABEL
DENUMIRE_TABEL
--aici
--aici „expresieDomeniu”
„expresieDomeniu” esteeste alcatuit
alcatuit din
din mulţimea
mulţimea liniilor
liniilor tabelului
tabelului
LOOP
LOOP -- r este variabila ce conţine o înregistrare (linie din
-- r este variabila ce conţine o înregistrare (linie din tabel)
tabel)
--
-- rr parcurge
parcurge tot
tot tabelul
tabelul (la
(la fiecare
fiecare ciclu
ciclu preia
preia altă
altă inregistrare)
inregistrare)
–-
–- rr este
este de
de tip
tip RECORD
RECORD
END
END LOOP;
LOOP;
În continuare este prezentat un exemplu de citire dintr-un
tabel. În acest sens, este contruit un tabel t1 în care sunt
adăugate câteva înregistrări.
/*
/* Ex.
Ex. 6(1D):
6(1D): */
*/ DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t1;
pic.t1;
create
create table
table pic.t1(a
pic.t1(a serial
serial PRIMARY
PRIMARY KEY,
KEY, bb int,
int, cc varchar(10));
varchar(10));
INSERT
INSERT INTO
INTO pic.t1(b,c)
pic.t1(b,c) VALUES(10,'A10'),(17,'A17'),(14,'A14'),
VALUES(10,'A10'),(17,'A17'),(14,'A14'),
(13,'A13'),(11,'A11');
(13,'A13'),(11,'A11'); select
select ** from
from pic.t1;
pic.t1;
Funcţia pentru citirea din tabel va fi:
/*
/* Ex.
Ex. 6(1D):
6(1D): */*/
CREATE
CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION pic.f1()
pic.f1() RETURNS
RETURNS SETOF
SETOF RECORD
RECORD AS
AS
$$
$$ DECLARE
DECLARE rr record;--
record;-- rr este
este de
de tip
tip înregistrare
înregistrare (RECORD)
(RECORD)
BEGIN
BEGIN
FOR
FOR rr IN
IN SELECT
SELECT a,b,c
a,b,c FROM
FROM pic.t1
pic.t1 ---- rr este
este variabila
variabila de
de parcurgere
parcurgere
LOOP
LOOP ---- inceput
inceput buclă
buclă
RETURN
RETURN NEXT
NEXT r;
r; --
-- întoarce
întoarce înregistrare
înregistrare cu cu înregistrare
înregistrare
END
END LOOP;--sfârşit
LOOP;--sfârşit buclă
buclă
END
END $$
$$ LANGUAGE
LANGUAGE plpgsql;
plpgsql;
Bucla se opreşte în momentul în care variabila r devine
NULL, adică atunci când nu mai sunt înregistrări de parcurs. Fără
secvenţa RETURN NEXT r; funcţia nu adaugă în tabelul întors
înregistrarea curentă, practic pentru fiecare apel al instrucţiunii
next este adăugată o înregistrare la tabelul întors. Pentru a apela
funcţia este necesară specificarea structurii câmpurilor rezultate:
/*Ex.
/*Ex. 6(1A)
6(1A) APEL
APEL pic.f6_1()
pic.f6_1() */
*/
select
select a,b,c
a,b,c FROM
FROM pic.f6_1()
pic.f6_1() AS
AS (a
(a int,
int, bb int,
int, cc varchar(10));
varchar(10));
În cadrul buclei prin care sunt întoarse înregistrările, se
pot efectua diverse operaţii. În acest sens, este prezentat un
exemplu de funcţie care afişează numai înregistrările pentru care
valoarea câmpului b este mai mică decât valoarea anterioară pentru
acelaşi câmp:
/*Ex.
/*Ex. 6(2D):*/
6(2D):*/
CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION pic.f6_2()
pic.f6_2() RETURNS
RETURNS SETOF
SETOF RECORD
RECORD AS
AS $$
$$
DECLARE
DECLARE rr record;
record; b1
b1 int;
int; –-
–- b1
b1 memoreaza
memoreaza valoarea
valoarea anterioara
anterioara
BEGIN
BEGIN b1:=0;
b1:=0; FOR
FOR rr IN
IN SELECT
SELECT a,b,c
a,b,c FROM
FROM pic.t1
pic.t1
LOOP
LOOP IF IF r.b
r.b << b1
b1 THEN
THEN b1:=r.b;
b1:=r.b; RETURN
RETURN NEXT
NEXT r;r;
ELSE
ELSE b1:=r.b;
b1:=r.b;
END
END IF;
IF;
END
END LOOP;
LOOP;
END
END $$
$$ LANGUAGE
LANGUAGE plpgsql;
plpgsql;
Tabelul parcurs este dat de :"SELECT a,b,c FROM pic.t1".

60
6.Structuri repetitive

Apelul funcţiei:
/*Ex.
/*Ex. 6(2A)
6(2A) APEL
APEL pic.f6_2()
pic.f6_2() */
*/
SELECT
SELECT a,b,c
a,b,c FROM
FROM pic.f6_2()
pic.f6_2() AS
AS (a
(a int,
int, bb int,
int, cc varchar(10));
varchar(10));
În continuare este prezentat un exemplu prin care sunt
înlocuite funcţiile SQL de agregare MIN şi MAX:
/*
/* Ex.
Ex. 6(3D):
6(3D): */*/
CREATE
CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION pic.f6_3()
pic.f6_3() RETURNS
RETURNS SETOF
SETOF RECORD
RECORD AS
AS
$$
$$ DECLARE
DECLARE rr record;
record; b_min
b_min int;
int; b_max
b_max int;--memorează
int;--memorează val.min
val.min şişi max.
max.
BEGIN
BEGIN b_min:=1000;
b_min:=1000; b_max:=-1000;
b_max:=-1000;
FOR
FOR rr IN
IN SELECT
SELECT a,b,c
a,b,c FROM
FROM pic.t1
pic.t1
LOOP
LOOP if
if r.b
r.b << b_min
b_min THEN
THEN b_min:=r.b;
b_min:=r.b; endend if;
if;
if
if r.b
r.b >=
>= b_max
b_max THEN
THEN b_max:=r.b;
b_max:=r.b; end
end if;
if;
END
END LOOP;
LOOP;
FOR
FOR rr IN
IN SELECT
SELECT a,b,c
a,b,c FROM
FROM pic.t1
pic.t1
LOOP
LOOP IFIF r.b
r.b == b_min
b_min OR
OR r.b
r.b == b_max
b_max THEN
THEN return
return next
next r;
r; END
END IF;
IF;
END
END LOOP;
LOOP;
END
END $$
$$ LANGUAGE
LANGUAGE plpgsql;
plpgsql;
iar apelul:
/*Ex.
/*Ex. 6(3A)
6(3A) APEL
APEL pic.f6_3()
pic.f6_3() */
*/
SELECT
SELECT a,b,c FROM pic.f6_3()
a,b,c FROM pic.f6_3() AS
AS (a
(a int,
int, bb int,
int, cc varchar(10));
varchar(10));
Funcţia f6_3 este echivalentă cu următoarea comandă SQL:
select
select t1.a,t1.b,t1.c
t1.a,t1.b,t1.c FROM
FROM
(SELECT
(SELECT min(b)
min(b) as
as b_min,
b_min, max(b)
max(b) as
as b_max
b_max FROM
FROM pic.t1)
pic.t1) par,
par, pic.t1
pic.t1
WHERE
WHERE t1.b=par.b_min
t1.b=par.b_min OROR t1.b=par.b_max;
t1.b=par.b_max;

7. Construcţia dinamică a interogărilor


Uneori structura unei interogări poate să depindă de anumiţi
parametri din lista de argumente a funcţiei sau de un anumit
context. De exemplu, iniţial punem un filtru pentru un anumit câmp
(denumire_camp=<parametru>) prin înlocuirea efectivă a valorii
parametrului, apoi vrem să afişăm înregistrările care nu conţin
valori (denumire_camp IS NULL). Se observă necesitatea schimbării
structurii interogării. În acest caz se construieşte un şir de
caractere ce reprezintă interogarea, apoi aceasta se execută prin
utilizarea instrucţiunii EXECUTE. În acest mod, comenzile vor fi
reprezentate prin şiruri de caractere ce se formează în momentul
apelării funcţiei.
În continuare este prezentat un exemplu de interogare
formată din mai multe şiruri de caractere:

61
III.Programarea la nivel de server – plpgSQL

/*Ex.
/*Ex. 7(1D):*/
7(1D):*/
CREATE
CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION pic.f7_1(a
pic.f7_1(a text)
text) RETURNS
RETURNS int
int as
as
$$
$$ DECLARE
DECLARE rez
rez int;
int; achQ
achQ text;
text;
BEGIN
BEGIN achQ='select
achQ='select '||a;
'||a; EXECUTE
EXECUTE achQ
achQ INTO
INTO rez;
rez; return
return rez;
rez;
END
END $$
$$ language
language plpgsql;
plpgsql;
Instrucţiunea “EXECUTE achQ INTO rez;” execută comanda SQL din
şirul achQ, iar rezultatul este memorat în variabila rez (aici
interogarea întoarce o singură valoare). Exemplu de apel:
/*Ex.
/*Ex. 7(1A)
7(1A) APEL
APEL pic.f7_1()
pic.f7_1() */
*/ SELECT
SELECT pic.f7_1('5+9');
pic.f7_1('5+9');
Un alt exemplu de funcţie cu interogare dinamică şi cu doi
parametri de intrare:
/*Ex.
/*Ex. 7(2D):*/
7(2D):*/
CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION pic.f7_2(a
pic.f7_2(a text,
text, bb text)
text) RETURNS
RETURNS int
int AS
AS
$$
$$ DECLARE
DECLARE rez
rez int;
int; achQ
achQ text;
text;
BEGIN
BEGIN achQ
achQ == 'select
'select '' ||
|| aa ||
|| '+'
'+' ||
|| b;
b; EXECUTE
EXECUTE achQ
achQ INTO
INTO rez;
rez;
return
return rez;
rez; END
END $$
$$ language
language plpgsql;
plpgsql;
iar apelul:
/*Ex.
/*Ex. 7(2A)
7(2A) APEL
APEL pic.f7_2()
pic.f7_2() */
*/ SELECT
SELECT pic.f7_2('5','9');
pic.f7_2('5','9');
Un alt exemplu, preia un vector cu şiruri (ultimul element
este NULL) şi întoarce un şir rezultat prin concatenarea
elementelor din cadrul vectorului cu şiruri:
/*Ex.
/*Ex. 7(3D):*/
7(3D):*/
CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTIOn
FUNCTIOn pic.f7_3(_sir_
pic.f7_3(_sir_ text[])
text[]) RETURNS
RETURNS text
text AS
AS
$$
$$ DECLARE
DECLARE ii int;
int; rez
rez text;
text;
BEGIN
BEGIN rez:='';
rez:=''; i:=1;
i:=1;
LOOP
LOOP IF
IF _sir_[i]
_sir_[i] ISIS NULL
NULL THEN
THEN RETURN
RETURN rez;
rez; END
END IF;
IF;
rez := rez || _sir_[i] ||
rez := rez || _sir_[i] || ';'; ';'; i:=i+1;
i:=i+1;
END
END LOOP;
LOOP; END
END $$
$$ LANGUAGE
LANGUAGE 'plpgsql';
'plpgsql';
iar apelul:
/*Ex
/*Ex 7(3A)
7(3A) APEL
APEL pic.f7_3()
pic.f7_3() */
*/
select
select pic.f7_3(ARRAY['UNU','DOI','TREI',NULL])
pic.f7_3(ARRAY['UNU','DOI','TREI',NULL]) as
as rez;
rez;
În continuare este prezentat un exemplu de funcţie ce preia
un şir, îl concatenează cu şirul “test” apoi întoarce şirul
rezultat în urma concatenării:
/*Ex
/*Ex 7(4D):*/
7(4D):*/CREATE
CREATE OR
OR REPLACE FUNCTION pic.f7_4(b
REPLACE FUNCTION pic.f7_4(b text) RETURNS text
text) RETURNS text AS
AS
$$
$$ DECLARE
DECLARE achQ
achQ text;
text; rez
rez text;
text;
BEGIN
BEGIN achQ
achQ ='SELECT
='SELECT '||quote_literal(b)||'||'||quote_literal('
'||quote_literal(b)||'||'||quote_literal(' test'); test');
EXECUTE
EXECUTE achQ
achQ INTO
INTO rez;
rez; RETURN
RETURN rez;END
rez;END $$$$ LANGUAGE
LANGUAGE 'plpgsql';
'plpgsql';
Funcţia quote_literal(b) întoarce valoarea lui b ca şir de
caractere şi nu mai este necesară specificarea acestuia ca şir de

62
7.Construcţia dinamică a interogărilor

caractere prin introducerea simbolurilor apostrof. Apelul are


forma:
/*Ex.
/*Ex. 7(4A)
7(4A) APEL
APEL pic.f7_4()
pic.f7_4() */
*/ select
select pic.f7_4('trei');
pic.f7_4('trei');
Un alt exemplu îl constuie o funcţie ce preia un tablou cu
şiruri de caractere ce reprezintă filtre. Funcţia preia date din
tabelul t1 şi creează un tabel t2 cu rezultatul selecţiei:
drop
drop table
table if
if exists
exists t1;
t1;
create
create temporary
temporary table
table t1(idx
t1(idx serial,
serial, val
val varchar(20));
varchar(20));
insert into t1(val) values ('A111'),('A222'),('A333'),('A222'),
insert into t1(val) values ('A111'),('A222'),('A333'),('A222'),
('B111');
('B111'); select
select ** from
from t1;
t1;
/*Ex.
/*Ex. 7(5D):*/
7(5D):*/ CREATE
CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION pic.f7_5(_filtru_
pic.f7_5(_filtru_ text[])
text[])
RETURNS
RETURNS void
void AS
AS
$$
$$ DECLARE
DECLARE achQ
achQ text;
text; ii int;
int;
BEGIN
BEGIN achQ:='CREATE
achQ:='CREATE TEMPTEMP TABLE
TABLE t2t2 as
as SELECT
SELECT ** FROM
FROM t1
t1 WHERE
WHERE ';
'; i:=1;
i:=1;
LOOP
LOOP
IF
IF _filtru_[i]
_filtru_[i] IS IS NULL
NULL
THEN
THEN achQ
achQ :=
:= achQ
achQ |||| ';';
';'; EXECUTE
EXECUTE achQ;
achQ; RETURN;
RETURN; END
END IF;
IF;
IF
IF i>1
i>1 THEN
THEN achQ
achQ :=
:= achQ
achQ |||| '' OR
OR ';
'; END
END IF;
IF;
achQ
achQ :=
:= achQ
achQ ||
|| '' val='
val=' ||
|| quote_literal(_filtru_[i]);
quote_literal(_filtru_[i]); i:=i+1; i:=i+1;
END
END LOOP;
LOOP; END
END $$
$$ LANGUAGE
LANGUAGE 'plpgsql';
'plpgsql';
Afişează doar liniile ce cuprind valorile A222 sau A333:
/*Ex.
/*Ex. 7(4A)
7(4A) APEL
APEL pic.f7_5()
pic.f7_5() */
*/ select
select ** from
from t1;
t1;
SELECT
SELECT pic.f7_5(ARRAY['A222','A333',NULL]);
pic.f7_5(ARRAY['A222','A333',NULL]); SELECT
SELECT ** FROM
FROM t2;
t2;

8. Funcţii de tip trigger


În scopul captării evenimentelor de editare specifice unui
tabel (INSERT, UPDATE, DELETE), pot fi utilizate funcţii/proceduri
speciale ce pot fi apelate automat de către sistemul de gestiune a
bazelor de date în momentul apariţiei unui eveniment de editare
asupra unui tabel. Aceste funcţii pot apărea imediat înainte de
validarea evenimentului de editare sau după validarea acestuia
(ex.: BEFORE UPDATE sau AFTER UPDATE).
De asemenea, în cazul unui eveniment de tip UPDATE aceste
funcţii speciale au acces atât la vechile valori, cât şi la noile
valori din cadrul câmpurilor înregistrării care se modifică.
De exemplu, construim un tabel care conţine un câmp cu data
calendaristică datac şi altul cu anul din cadrul datei
calendaristice an. Dacă modificăm câmpul cu data calenaristică
datac dorim să se actualizeze automat câmpul cu anul an
corespunzător câmpului datac, fară a apela explicit vreo funcţie.
În acest sens construim tabelul:

63
III.Programarea la nivel de server – plpgSQL

DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t1;
pic.t1;
CREATE
CREATE TABLE pic.t1(datac date,
TABLE pic.t1(datac date, an
an smallint,idx
smallint,idx serial
serial PRIMARY
PRIMARY KEY);
KEY);
Crearea funcţiei de tip trigger:
/*Ex.
/*Ex. 8(1D):*/
8(1D):*/ CREATE
CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION pic.f8_1()
pic.f8_1()
RETURNS
RETURNS trigger
trigger AS
AS
$$
$$ BEGIN
BEGIN
SELECT
SELECT EXTRACT(
EXTRACT( YEAR
YEAR FROM
FROM NEW.datac)
NEW.datac) INTO
INTO NEW.an;
NEW.an; return
return NEW;
NEW;
END $$ LANGUAGE 'plpgsql';
END $$ LANGUAGE 'plpgsql';
În funcţia de tip triger variabilele NEW şi OLD sunt
predefinite, sunt de tip înregistrare RECORD şi memorează noua,
respectiv vechea valoare a înregistrării afectate. Funcţia triger
poate întoarce şi înregistrarea OLD (vechile valori).
O funcţie de tip triger poate fi ataşată unuia sau mai
multor tabele. Ataşarea funcţiei f8_1() la tabelul t1 pentru
operaţia de UPDATE se realizează prin:
/*Ex.
/*Ex. 8(1T):*/
8(1T):*/ CREATE
CREATE TRIGGER
TRIGGER t8_1_trg
t8_1_trg
BEFORE
BEFORE UPDATE
UPDATE ON
ON pic.t1
pic.t1
FOR
FOR EACH
EACH ROW
ROW
WHEN
WHEN ((OLD.*
((OLD.* IS
IS DISTINCT
DISTINCT FROM
FROM NEW.*))
NEW.*)) EXECUTE
EXECUTE PROCEDURE
PROCEDURE pic.f8_1();
pic.f8_1();
Condiţia “WHEN ((OLD.* IS DISTINCT FROM NEW.*))” are ca efect
limitarea apelului funcţiei triger doar atunci când se modifică un
câmp (noile valori sunt diferite de vechile valori).
În vederea testării se adaugă înregistrări şi apoi se
execută o comandă UPDATE în vederea modificării valorii câmpului
datac dintr-o înregistrare.
/*Ex.
/*Ex. 8(1A)
8(1A) TESTARE
TESTARE pic.f8_1()
pic.f8_1() pentru
pentru pic.t1
pic.t1 */
*/
INSERT
INSERT INTO
INTO pic.t1(datac)
pic.t1(datac)
VALUES('2013-03-15'),('2013-03-17'),('2013-03-19');
VALUES('2013-03-15'),('2013-03-17'),('2013-03-19');
UPDATE
UPDATE pic.t1
pic.t1 SET
SET datac=datac+2
datac=datac+2 WHERE
WHERE datac='2013-03-17';
datac='2013-03-17';
select
select ** from
from pic.t1;
pic.t1;
În urma comenzii UPDATE funcţia triger f8_1() a fost apelată
automat.
Extindem exemplul anterior prin construcţia unui tabel
pic.t2, care pe lângă câmpul datac (data calendaristică) conţine
câmpul cu valoarea veche a anului (înainte de modificare) anul_v şi
câmpul anul_n cu valoarea nouă a anului, după modificare. În
momentul modificării câmpului datac, celelalte două câmpuri anul_v
şi anul_c trebuie să se actualizeze automat. În acest sens,
construim tabelul:
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t2;
pic.t2; CREATE
CREATE TABLE
TABLE pic.t2(datac
pic.t2(datac date,
date, an_v
an_v
smallint,
smallint, an_n
an_n smallint,idx
smallint,idx serial
serial PRIMARY
PRIMARY KEY);
KEY);
Funcţia triger va fi:

64
8.Funcţii de tip trigger

/*Ex.
/*Ex. 8(2D):*/CREATE
8(2D):*/CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION pic.f8_2()
pic.f8_2() RETURNS
RETURNS trigger
trigger AS
AS
$$ BEGIN SELECT EXTRACT( YEAR FROM OLD.datac) INTO NEW.an_v;
$$ BEGIN SELECT EXTRACT( YEAR FROM OLD.datac) INTO NEW.an_v;
SELECT
SELECT EXTRACT(
EXTRACT( YEAR
YEAR FROM
FROM NEW.datac)
NEW.datac) INTO
INTO NEW.an_n;
NEW.an_n;
return NEW; END $$ LANGUAGE 'plpgsql';
return NEW; END $$ LANGUAGE 'plpgsql';
Codul pentru ataşarea funcţiei triger f8_2() de tabelul
pic.t2 este:
/*Ex.
/*Ex. 8(2T):*/
8(2T):*/ CREATE
CREATE TRIGGER
TRIGGER t2_trg
t2_trg
BEFORE
BEFORE UPDATE
UPDATE ONON pic.t2
pic.t2 FORFOR EACH
EACH ROW
ROW
WHEN
WHEN ((old.*
((old.* IS
IS DISTINCT
DISTINCT FROM
FROM new.*))
new.*)) EXECUTE
EXECUTE PROCEDURE
PROCEDURE pic.f8_2();
pic.f8_2();
iar pentru testare:
/*Ex.
/*Ex. 8(2A)
8(2A) TESTARE
TESTARE pic.f8_2()
pic.f8_2() pentru
pentru pic.t2
pic.t2 */
*/
INSERT
INSERT INTO
INTO pic.t2(datac)
pic.t2(datac)
VALUES('2013-03-15'),('2013-03-17'),('2013-03-19');
VALUES('2013-03-15'),('2013-03-17'),('2013-03-19');
UPDATE
UPDATE pic.t1
pic.t1 SET
SET datac=datac+5
datac=datac+5 WHERE
WHERE datac='2013-03-17';
datac='2013-03-17';
select
select * from pic.t1 ;select * from pic.t2;
* from pic.t1 ;select * from pic.t2;
Un alt exemplu presupune un tabel pic.t3, cu următoarele
câmpuri: numărul zilei din săptamână nz, denumirea zilei denzi şi
un câmp idx de tip serial. În momentul introducerii numărului
zilei, dacă acesta este între 1 şi 7, se va afişa denumirea
acesteia, altfel numărul zilei rămâne nemodificat:
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.t3;
pic.t3; CREATE
CREATE TABLE
TABLE pic.t3(nz
pic.t3(nz smallint,denzi
smallint,denzi
varchar(15),idx
varchar(15),idx serial
serial PRIMARY
PRIMARY KEY);
KEY);
Funcţia triger pic.f8_3() va avea forma:
/*Ex
/*Ex 8(3D):*/
8(3D):*/ CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION pic.f8_3()
pic.f8_3() RETURNS
RETURNS trigger
trigger AS
AS
$$
$$ BEGIN
BEGIN IF
IF NEW.nz=1
NEW.nz=1 THEN
THEN NEW.denzi='Luni';
NEW.denzi='Luni'; END END IF;
IF;
IF
IF NEW.nz=2
NEW.nz=2 THEN
THEN NEW.denzi='Marti';
NEW.denzi='Marti'; END END IF;
IF;
IF
IF NEW.nz=3
NEW.nz=3 THEN
THEN NEW.denzi='Miercuri';
NEW.denzi='Miercuri'; END END IF;
IF;
IF
IF NEW.nz=4
NEW.nz=4 THEN
THEN NEW.denzi='Joi';
NEW.denzi='Joi'; END END IF;
IF;
IF
IF NEW.nz=5
NEW.nz=5 THEN
THEN NEW.denzi='Vineri';
NEW.denzi='Vineri'; END END IF;
IF;
IF
IF NEW.nz=6 THEN NEW.denzi='Sambata'; END
NEW.nz=6 THEN NEW.denzi='Sambata'; END IF;
IF;
IF
IF NEW.nz=7
NEW.nz=7 THEN
THEN NEW.denzi='Duminica';
NEW.denzi='Duminica'; END END IF;
IF;
IF
IF NEW.nz<1
NEW.nz<1 OROR NEW.nz>7
NEW.nz>7 THEN
THEN OLD.denzi=NULL;
OLD.denzi=NULL; return
return OLD;
OLD;
ELSE
ELSE return
return NEW;
NEW; END
END IF;
IF; END
END $$
$$ LANGUAGE
LANGUAGE 'plpgsql';
'plpgsql';
iar comenzile de asociere între funcţia triger şi tabel:
/*Ex.
/*Ex. 8(3T-1):*/
8(3T-1):*/ CREATE
CREATE TRIGGER
TRIGGER t8_3_1_trg
t8_3_1_trg
BEFORE
BEFORE UPDATE ON pic.t3 FOR
UPDATE ON pic.t3 FOR EACH
EACH ROW
ROW
WHEN
WHEN (old.*
(old.* IS
IS DISTINCT
DISTINCT FROM
FROM new.*)
new.*) EXECUTE
EXECUTE PROCEDURE
PROCEDURE pic.f8_3();
pic.f8_3();
În vederea testării, executăm comenzile:
/*Ex.
/*Ex. 8(3A-1)
8(3A-1) TESTARE
TESTARE pic.f8_3()
pic.f8_3() pentru
pentru pic.t3
pic.t3 */
*/
INSERT
INSERT INTO
INTO pic.t3(nz)
pic.t3(nz) VALUES(NULL);
VALUES(NULL); select
select ** from
from pic.t3;
pic.t3;
UPDATE
UPDATE pic.t3
pic.t3 SET
SET nz=2;
nz=2; select
select ** from
from pic.t3;
pic.t3;
UPDATE
UPDATE pic.t3
pic.t3 SET
SET nz=8;
nz=8; select
select ** from
from pic.t3;
pic.t3;

65
III.Programarea la nivel de server – plpgSQL

În momentul inserării nu este apelată funcţia triger


deoarece legătura cu tabelul este numai pentru evenimente de tipul
UPDATE. În continuare definim şi un triger pentru evenimentele de
tip INSERT. Acest triger va apela funcţia triger doar dacă numărul
pentru noua zi este mai mare decât 5.
/*Ex.
/*Ex. 8(3T):*/
8(3T):*/ DROP
DROP TRIGGER
TRIGGER IF
IF EXISTS
EXISTS t8_3_2_trg
t8_3_2_trg ON
ON pic.t3;
pic.t3;
CREATE
CREATE TRIGGER
TRIGGER t8_3_2_trg
t8_3_2_trg BEFORE
BEFORE INSERT
INSERT ON
ON pic.t3
pic.t3
FOR
FOR EACH
EACH ROW
ROW WHEN
WHEN ((old.*
((old.* IS
IS DISTINCT
DISTINCT FROM
FROM new.*)
new.*) AND
AND (new.nz>5))
(new.nz>5))
EXECUTE
EXECUTE PROCEDURE
PROCEDURE pic.f8_3();
pic.f8_3();
În vederea testării, executăm comenzile:
/*Ex.
/*Ex. 8(3A-2)
8(3A-2) TESTARE
TESTARE pic.f8_3()
pic.f8_3() pentru
pentru pic.t3
pic.t3 */*/ DELETE
DELETE FROM
FROM pic.t3;
pic.t3;
INSERT
INSERT INTO
INTO pic.t3(nz)
pic.t3(nz) VALUES(NULL);--
VALUES(NULL);-- nunu este
este apelata
apelata fct.:new.nz<5
fct.:new.nz<5
INSERT
INSERT INTO
INTO pic.t3(nz)
pic.t3(nz) VALUES(6);
VALUES(6); --
-- este
este apelata
apelata fct.:new.nz>5
fct.:new.nz>5 (6>5)
(6>5)
INSERT
INSERT INTO
INTO pic.t3(nz)
pic.t3(nz) VALUES(3);--nu
VALUES(3);--nu este
este apelată
apelată fct.new.nz<5
fct.new.nz<5 (3<5)
(3<5)
select
select ** from
from pic.t3;
pic.t3;

9. Definirea de noi tipuri de date şi supraîncărcarea


operatorilor SQL
Utilizatorul poate să îşi definească propriile tipuri de
date, acestea având forma unei structuri, iar odată cu aceasta
utilizatorul trebuie să supraîncarce operatorii uzuali pentru a
putea utiliza aceste noi tipuri de date.
Pentru exemplificare, presupunem că avem nevoie de un tip de
dată care să implementeze o structură de tip adresă.
/*Ex.
/*Ex. 9(1T):*/
9(1T):*/ DROP
DROP TYPE
TYPE IF
IF EXISTS
EXISTS adresa01
adresa01 CASCADE;
CASCADE;
CREATE
CREATE TYPE
TYPE adresa01
adresa01 AS
AS (tara
(tara varchar(30),regiune
varchar(30),regiune varchar(30),
varchar(30), oras
oras
varchar(40),
varchar(40), str
str varchar(50),
varchar(50), nrnr int,
int, cod_postal
cod_postal int,
int, tel
tel varchar(20),
varchar(20),
e_mail
e_mail varchar(20));
varchar(20));
Recomand ca tipurile de dată să fie definite în schema
public în vederea simplificării modulului de utilizare. Aceste
tipuri de dată se vor vedea la nivelul întregii baze de date. După
definirea noului tip, îl putem utiliza:
/*Ex.
/*Ex. 9(2T):*/
9(2T):*/ DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.pers;
pic.pers;
CREATE
CREATE TABLE
TABLE pic.pers(idx
pic.pers(idx serial
serial PRIMARY
PRIMARY KEY,
KEY, nume
nume varchar(40),
varchar(40),
adresa adresa01
adresa adresa01 ););
Pentru popularea cu date a tabelului pic.pers:
/*Ex.
/*Ex. 9(1A)
9(1A) TESTARE
TESTARE pentru
pentru pic.pers
pic.pers */*/
INSERT
INSERT INTO
INTO pic.pers(nume,adresa)
pic.pers(nume,adresa)
VALUES('Nume1','(''Ro'',''BV'',''BV'',''Str1'',5,0,''-'',''-'')');
VALUES('Nume1','(''Ro'',''BV'',''BV'',''Str1'',5,0,''-'',''-'')');
SELECT
SELECT ** FROM
FROM pic.pers;
pic.pers;
SELECT
SELECT nume,
nume, (adresa).tara
(adresa).tara asas tara,
tara, (adresa).oras
(adresa).oras FROM
FROM pic.pers;
pic.pers;

66
9.Definirea de noi tipuri de date şi supraîncărcarea operatorilor SQL

Se observă modul de preluare a informaţiei de tip adresa01.


Aceasta este delimitată de apostrof şi paranteze rotunde, iar
componentele din cadrul tipului, asemănătoare unei structuri, sunt
separate prin virgulă. Dublarea apostrofului pentru componentele
tipului de dată este cauzată de primul apostrof ce delimitează
tipul de dată.
Pentru accesarea unui element din cadrul tipului definit de
utilizator, variabila de tip adresa01 se va trece între paranteze
rotunde iar elementul se va referi prin utilizarea simbolului punct
(ex.: (adresa).oras)
/*Ex.
/*Ex. 9(2A)
9(2A) TESTARE
TESTARE pentru
pentru pic.pers
pic.pers */
*/
INSERT
INSERT INTO
INTO pic.pers(nume,adresa)
pic.pers(nume,adresa) VALUES
VALUES
('Nume5','(''Tara1'',''Reg3'',''Oras4'',''Str1'',5,5005,''-'',''-'')'),
('Nume5','(''Tara1'',''Reg3'',''Oras4'',''Str1'',5,5005,''-'',''-'')'),
('Nume3','(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')'),
('Nume3','(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')'),
('Nume7','(''Tara2'',''Reg5'',''Oras1'',''Str1'',5,5007,''-'',''-'')'),
('Nume7','(''Tara2'',''Reg5'',''Oras1'',''Str1'',5,5007,''-'',''-'')'),
('Nume9','(''Tara1'',''Reg5'',''Oras9'',''Str1'',5,5009,''-'',''-'')'),
('Nume9','(''Tara1'',''Reg5'',''Oras9'',''Str1'',5,5009,''-'',''-'')'),
('Nume8','(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5008,''-'',''-'')'),
('Nume8','(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5008,''-'',''-'')'),
('Nume8','(''Tara1'',''Reg3'',''Oras2'',''Str1'',5,5003,''-'',''-'')');
('Nume8','(''Tara1'',''Reg3'',''Oras2'',''Str1'',5,5003,''-'',''-'')');
Accesarea elementelor din cadrul datelor de tip adresa01 este
dată şi de exemplul următor:
/*Ex.
/*Ex. 9(3A)
9(3A) TESTARE
TESTARE pentru
pentru pic.pers
pic.pers */*/ SELECT
SELECT ** FROM
FROM pic.pers;
pic.pers;
SELECT
SELECT ** FROM
FROM pic.pers
pic.pers ORDER
ORDER BY
BY adresa;
adresa;
--echivalent
--echivalent cucu
SELECT
SELECT ** FROM
FROM pic.pers
pic.pers ORDER
ORDER BY
BY (adresa).tara,(adresa).regiune,
(adresa).tara,(adresa).regiune,
(adresa).oras,(adresa).str,(adresa).nr,(adresa).tel,(adresa).e_mail;
(adresa).oras,(adresa).str,(adresa).nr,(adresa).tel,(adresa).e_mail;
Implicit, fară a defini un operator de indexare, ordonarea
se realizează în ordinea câmpurilor din cadrul structurii de date
definite de utilizator (valabil începand cu vers. 9.x PostgreSQL).
Elementele structurii pot să apară şi la nivelul evaluării
expresiilor precum în exemplul următor:
/*Ex.
/*Ex. 9(4A)
9(4A) TESTARE
TESTARE pentru
pentru pic.pers
pic.pers */
*/
SELECT
SELECT ** FROM
FROM pic.pers
pic.pers WHERE
WHERE (adresa).oras
(adresa).oras ILIKE
ILIKE '%bv%';
'%bv%'; --sau
--sau
SELECT * FROM pic.pers WHERE adresa
SELECT * FROM pic.pers WHERE adresa = =
''(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')
(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')'::adresa01;
'::adresa01;
SELECT
SELECT ** FROM
FROM pic.pers
pic.pers WHERE
WHERE adresa
adresa <<
''(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')
(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')'::adresa01;
'::adresa01;
Fără a avea operatori definiţi explicit, pentru acest tip de
dată sunt utilizaţi operatorii standard, ce se aplică pe rând
fiecărui tip de dată din cadrul structurii definite de utilizator.
Supraîncărcarea operatorilor SQL lasă utilizatorului
posibilitatea de a stabili propriile reguli privind operaţiile. De
exemplu, dacă dorim să existe egalitate între două date de tip
adresa01 cu informaţii parţial introduse, dar suficiente pentru
identificare, este necesar să rescriem/supraîncărcăm operatorul
SQL de egalitate. Definim proceduri pentru fiecare operator astfel:

67
III.Programarea la nivel de server – plpgSQL

a) întoarce valoarea de adevăr TRUE dacă cele două adrese sunt din
aceeaşi ţară:
/*Ex.
/*Ex. 9(1D):*/
9(1D):*/ CREATE
CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION
pic.op_egal_niv_tara(_a_
pic.op_egal_niv_tara(_a_ adresa01,
adresa01, _b_
_b_ adresa01)
adresa01) RETURNS
RETURNS boolean
boolean AS
AS
$BODY$ BEGIN IF $1.tara = $2.tara OR $1.cod_postal=$2.cod_postal
$BODY$ BEGIN IF $1.tara = $2.tara OR $1.cod_postal=$2.cod_postal
THEN
THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF; RETURN
RETURN FALSE;
FALSE; END
END $BODY$
$BODY$ LANGUAGE
LANGUAGE 'plpgsql';
'plpgsql';
b) întoarce valoarea de adevăr TRUE dacă cele două adrese sunt din
acelaşi oraş:
/*Ex.
/*Ex. 9(2D):*/
9(2D):*/ CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION
pic.op_egal_niv_oras(_a_
pic.op_egal_niv_oras(_a_ adresa01,
adresa01, _b_
_b_ adresa01)
adresa01) RETURNS
RETURNS boolean
boolean AS
AS
$BODY$
$BODY$ BEGIN
BEGIN IF
IF ($1.tara
($1.tara == $2.tara
$2.tara AND
AND $1.oras=$2.oras)
$1.oras=$2.oras) OROR
$1.cod_postal=$2.cod_postal
$1.cod_postal=$2.cod_postal THEN THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF;
RETURN
RETURN FALSE;
FALSE; END
END $BODY$
$BODY$ LANGUAGE
LANGUAGE 'plpgsql';
'plpgsql';
c) întoarce valoarea de adevăr TRUE dacă cele două adrese sunt pe
aceeaşi stradă:
/*Ex.
/*Ex. 9(3D):*/
9(3D):*/ CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION
pic.op_egal_niv_str(_a_
pic.op_egal_niv_str(_a_ adresa01,
adresa01, _b_
_b_ adresa01)
adresa01) RETURNS
RETURNS boolean
boolean AS
AS
$BODY$
$BODY$ BEGIN
BEGIN IF
IF ($1.tara
($1.tara == $2.tara
$2.tara AND
AND $1.oras=$2.oras
$1.oras=$2.oras AND
AND
$1.str=$2.str)
$1.str=$2.str) OROR $1.cod_postal=$2.cod_postal
$1.cod_postal=$2.cod_postal THENTHEN RETURN
RETURN TRUE;END
TRUE;END IF;
IF;
RETURN
RETURN FALSE;
FALSE; END
END $BODY$
$BODY$ LANGUAGE
LANGUAGE 'plpgsql';
'plpgsql';
d) întoarce valoarea de adevăr TRUE dacă cele două adrese sunt
identice:
/*Ex.
/*Ex. 9(4D):*/
9(4D):*/ CREATE
CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION pic.op_egal_identice(_a_
pic.op_egal_identice(_a_
adresa01,
adresa01, _b_
_b_ adresa01)
adresa01) RETURNS
RETURNS boolean
boolean AS
AS $BODY$
$BODY$ BEGIN
BEGIN
IF
IF ($1.tara
($1.tara == $2.tara
$2.tara AND
AND $1.oras=$2.oras
$1.oras=$2.oras ANDAND $1.str=$2.str
$1.str=$2.str AND
AND
$1.nr=$2.nr)
$1.nr=$2.nr) OROR ($1.cod_postal=$2.cod_postal
($1.cod_postal=$2.cod_postal AND AND $1.nr=$2.nr)
$1.nr=$2.nr)
THEN
THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF; RETURN
RETURN FALSE;
FALSE; END
END $BODY$
$BODY$ LANGUAGE
LANGUAGE 'plpgsql';
'plpgsql';
e) este definit operatorul „mai mic” între două date de tip adresa01
în scopul ordonării alfanumerice:
/*Ex
/*Ex 9(5D):*/
9(5D):*/ CREATE
CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION pic.op_mai_mic(_a_
pic.op_mai_mic(_a_ adresa01,
adresa01,
_b_
_b_ adresa01)
adresa01) RETURNS
RETURNS boolean
boolean AS
AS
$BODY$
$BODY$ BEGIN
BEGIN IFIF $1.tara
$1.tara << $2.tara
$2.tara THEN
THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF;
IF
IF $1.tara=$2.tara
$1.tara=$2.tara THENTHEN
if
if $1.oras<$2.oras
$1.oras<$2.oras then
then return
return TRUE;end
TRUE;end if;
if; END
END IF;
IF;
IF
IF $1.tara
$1.tara == $2.tara
$2.tara andand $1.oras=$2.oras
$1.oras=$2.oras THEN
THEN
if
if $1.str
$1.str << $2.str
$2.str then
then return
return TRUE;
TRUE; end
end if;
if; END
END IF;
IF;
IF
IF $1.tara
$1.tara == $2.tara
$2.tara andand $1.oras=$2.oras
$1.oras=$2.oras andand $1.str=$2.str
$1.str=$2.str THEN
THEN
if
if $1.nr
$1.nr << $2.nr
$2.nr then
then return
return TRUE;
TRUE; end
end if;
if; END
END IF;
IF;
RETURN
RETURN FALSE;END
FALSE;END $BODY$
$BODY$ LANGUAGE
LANGUAGE plpgsql;
plpgsql;
f) este definit operatorul „mai mare” între două date de tip
adresa01 în scopul ordonării alfanumerice:

68
9.Definirea de noi tipuri de date şi supraîncărcarea operatorilor SQL

/*Ex.
/*Ex. 9(6D):*/
9(6D):*/ CREATE
CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION pic.op_mai_mare(_a_
pic.op_mai_mare(_a_
adresa01,
adresa01, _b_ adresa01) RETURNS boolean AS
_b_ adresa01) RETURNS boolean AS
$BODY$
$BODY$ BEGIN
BEGIN IF
IF $1.tara
$1.tara >> $2.tara
$2.tara THEN
THEN RETURN
RETURN TRUE;END
TRUE;END IF;
IF;
IF $1.tara=$2.tara THEN
IF $1.tara=$2.tara THEN
if
if $1.oras>$2.oras
$1.oras>$2.oras thenthen return
return TRUE;end
TRUE;end if;
if; END
END IF;
IF;
IF $1.tara = $2.tara and $1.oras = $2.oras
IF $1.tara = $2.tara and $1.oras = $2.oras THEN THEN
IF
IF $1.str
$1.str >> $2.str
$2.str THEN
THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF; END
END IF;
IF;
IF
IF $1.tara = $2.tara and $1.oras = $2.oras and $1.st r= $2.str THEN
$1.tara = $2.tara and $1.oras = $2.oras and $1.st r= $2.str THEN
IF
IF $1.nr
$1.nr >> $2.nr
$2.nr THEN
THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF; END END IF;
IF;
RETURN
RETURN FALSE;END
FALSE;END $BODY$
$BODY$ LANGUAGE
LANGUAGE plpgsql;
plpgsql;
g) este definit operatorul “mai mic sau egal” între două date de tip
adresa01 în scopul ordonării alfanumerice:
/*Ex.
/*Ex. 9(7D):*/
9(7D):*/ CREATE
CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION pic.op_mai_mic_egal(_a_
pic.op_mai_mic_egal(_a_
adresa01,
adresa01, _b_
_b_ adresa01)
adresa01) RETURNS
RETURNS boolean
boolean ASAS
$BODY$
$BODY$ BEGIN
BEGIN IFIF $1.tara
$1.tara <=
<= $2.tara
$2.tara THEN
THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF;
IF
IF $1.tara=$2.tara
$1.tara=$2.tara THENTHEN
if
if $1.oras<=$2.oras
$1.oras<=$2.oras then
then RETURN
RETURN TRUE;end
TRUE;end if;END
if;END IF;
IF;
IF
IF $1.tara
$1.tara == $2.tara
$2.tara andand $1.oras
$1.oras == $2.oras
$2.oras THEN
THEN
IF
IF $1.str
$1.str <=
<= $2.str
$2.str THEN
THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF; END
END IF;
IF;
IF
IF $1.tara
$1.tara == $2.tara
$2.tara andand $1.oras=$2.oras
$1.oras=$2.oras andand $1.str=$2.str
$1.str=$2.str THEN
THEN
IF
IF $1.nr
$1.nr <=
<= $2.nr
$2.nr THEN
THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF; END
END IF;
IF;
RETURN
RETURN FALSE;
FALSE; END
END $BODY$
$BODY$ LANGUAGE
LANGUAGE plpgsql;
plpgsql;
h) este definit operatorul “mai mare sau egal” între două date de
tip adresa01 în scopul ordonării alfanumerice:
/*Ex
/*Ex 9(8D):*/
9(8D):*/ CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION pic.op_mai_mare_egal(_a_
pic.op_mai_mare_egal(_a_
adresa01,
adresa01, _b_
_b_ adresa01)
adresa01) RETURNS
RETURNS boolean
boolean ASAS
$BODY$
$BODY$ BEGIN
BEGIN IFIF $1.tara
$1.tara >=
>= $2.tara
$2.tara THEN
THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF;
IF
IF $1.tara=$2.tara
$1.tara=$2.tara THEN
THEN
if
if $1.oras>=$2.oras
$1.oras>=$2.oras then
then RETURN
RETURN TRUE;end
TRUE;end if;
if; END
END IF;
IF;
IF
IF $1.tara
$1.tara == $2.tara
$2.tara andand $1.oras
$1.oras == $2.oras
$2.oras THEN
THEN
IF
IF $1.str
$1.str >=
>= $2.str
$2.str THEN
THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF; END
END IF;
IF;
IF
IF $1.tara
$1.tara == $2.tara
$2.tara andand $1.oras
$1.oras == $2.oras
$2.oras and
and $1.str
$1.str == $2.str
$2.str THEN
THEN
IF
IF $1.nr
$1.nr >=
>= $2.nr
$2.nr THEN
THEN RETURN
RETURN TRUE;
TRUE; END
END IF;
IF; END
END IF;
IF;
RETURN
RETURN FALSE;
FALSE; END
END $BODY$
$BODY$ LANGUAGE
LANGUAGE plpgsql;
plpgsql;
i) operatorul de indexare (implicit, pentru indexare, SGBD-ul
utilizează o tehnică ce utililizează arbori binari – btree)

69
III.Programarea la nivel de server – plpgSQL

/*Ex.
/*Ex. 9(9D):
9(9D): */*/ CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION pic.op_btree(_a_
pic.op_btree(_a_ adresa01,
adresa01,
_b_ adresa01) RETURNS integer AS $BODY$
_b_ adresa01) RETURNS integer AS $BODY$ BEGIN BEGIN
IF
IF $1.tara
$1.tara << $2.tara
$2.tara OROR ($1.tara
($1.tara == $2.tara
$2.tara and
and $1.oras
$1.oras << $2.oras)
$2.oras) OR
OR
($1.tara=$2.tara and $1.oras=$2.oras and $1.str<$2.str)
($1.tara=$2.tara and $1.oras=$2.oras and $1.str<$2.str) OR OR
($1.tara=$2.tara
($1.tara=$2.tara and and $1.oras=$2.oras
$1.oras=$2.oras and and $1.str=$2.str
$1.str=$2.str and
and
$1.nr<$2.nr)
$1.nr<$2.nr) THEN RETURN -1; END
THEN RETURN -1; END IF; IF;
IF
IF $1.tara
$1.tara >> $2.tara
$2.tara OROR ($1.tara
($1.tara == $2.tara
$2.tara and
and $1.oras
$1.oras >> $2.oras)
$2.oras) OR
OR
($1.tara
($1.tara = $2.tara and $1.oras = $2.oras and $1.str > $2.str) OR
= $2.tara and $1.oras = $2.oras and $1.str > $2.str) OR
($1.tara=$2.tara
($1.tara=$2.tara and and $1.oras=$2.oras
$1.oras=$2.oras and and $1.str=$2.str
$1.str=$2.str AND
AND
$1.nr>$2.nr) THEN RETURN 1; END
$1.nr>$2.nr) THEN RETURN 1; END IF; IF;
RETURN
RETURN 0;
0; END;
END; $BODY$
$BODY$ LANGUAGE
LANGUAGE plpgsql
plpgsql ;;
Funcţia operator de indexare întoarce valorile -1, +1 sau 0,
în funcţie de egalitate. Urmează etapa în care asignăm simboluri de
operatori SQL cu funcţiile operator definite mai sus. Astfel sunt
definiţi operatorii:
/*Ex.
/*Ex. 9(1L):*/
9(1L):*/ CREATE
CREATE OPERATOR
OPERATOR ==(
==( PROCEDURE
PROCEDURE == pic.op_egal_niv_tara,
pic.op_egal_niv_tara,
LEFTARG
LEFTARG == adresa01,
adresa01, RIGHTARG
RIGHTARG == adresa01);
adresa01);
/*Ex.
/*Ex. 9(2L):*/
9(2L):*/ CREATE
CREATE OPERATOR
OPERATOR ===(
===( PROCEDURE
PROCEDURE == pic.op_egal_niv_oras,
pic.op_egal_niv_oras,
LEFTARG
LEFTARG == adresa01,
adresa01, RIGHTARG
RIGHTARG == adresa01);
adresa01);
/*Ex.
/*Ex. 9(3L):*/
9(3L):*/ CREATE
CREATE OPERATOR
OPERATOR ====(
====( PROCEDURE
PROCEDURE == pic.op_egal_niv_str,
pic.op_egal_niv_str,
LEFTARG
LEFTARG == adresa01,
adresa01, RIGHTARG
RIGHTARG == adresa01);
adresa01);
/*Ex.
/*Ex. 9(4L):*/
9(4L):*/ CREATE
CREATE OPERATOR
OPERATOR ===*(PROCEDURE
===*(PROCEDURE == pic.op_egal_identice,
pic.op_egal_identice,
LEFTARG
LEFTARG == adresa01,
adresa01, RIGHTARG
RIGHTARG == adresa01);
adresa01);
/*Ex.
/*Ex. 9(5L):*/
9(5L):*/ CREATE
CREATE OPERATOR
OPERATOR <(PROCEDURE
<(PROCEDURE == pic.op_mai_mic,
pic.op_mai_mic,
LEFTARG
LEFTARG == adresa01,
adresa01, RIGHTARG
RIGHTARG == adresa01);
adresa01);
/*Ex.
/*Ex. 9(6L):*/
9(6L):*/ CREATE
CREATE OPERATOR
OPERATOR <=(
<=( PROCEDURE
PROCEDURE == pic.op_mai_mic_egal,
pic.op_mai_mic_egal,
LEFTARG = adresa01, RIGHTARG = adresa01);
LEFTARG = adresa01, RIGHTARG = adresa01);
/*Ex.
/*Ex. 9(7L):*/
9(7L):*/ CREATE
CREATE OPERATOR
OPERATOR >(
>( PROCEDURE
PROCEDURE == pic.op_mai_mare,
pic.op_mai_mare,
LEFTARG
LEFTARG == adresa01,
adresa01, RIGHTARG
RIGHTARG == adresa01);
adresa01);
/*Ex.
/*Ex. 9(8L):*/
9(8L):*/ CREATE
CREATE OPERATOR
OPERATOR >=(
>=( PROCEDURE
PROCEDURE == pic.op_mai_mare_egal,
pic.op_mai_mare_egal,
LEFTARG
LEFTARG == adresa01,
adresa01, RIGHTARG
RIGHTARG == adresa01);
adresa01);
Următoarea etapă constă în crearea clasei de operatori
pentru funcţia de indexare.
/*Ex.
/*Ex. 9(9L):*/
9(9L):*/ CREATE
CREATE OPERATOR
OPERATOR CLASS
CLASS adresa01_operatii
adresa01_operatii DEFAULT
DEFAULT
FOR TYPE adresa01 USING btree
FOR TYPE adresa01 USING btree AS AS
OPERATOR
OPERATOR 11 <,<, OPERATOR
OPERATOR 22 <=,
<=, OPERATOR
OPERATOR 33 ===*,
===*,
OPERATOR 4 >=,
OPERATOR 4 >=, OPERATOR 5 >,
OPERATOR 5 >,
FUNCTION
FUNCTION 11 pic.op_btree(adresa01,
pic.op_btree(adresa01, adresa01);
adresa01);
/*Ex.
/*Ex. 7(10L)*/CREATE
7(10L)*/CREATE OPERATOR
OPERATOR FAMILY
FAMILY adresa01_operatii
adresa01_operatii USING
USING btree;
btree;
Urmează etapa de testare:

70
9.Definirea de noi tipuri de date şi supraîncărcarea operatorilor SQL

/*Ex.
/*Ex. 9(5A)
9(5A) TESTARE
TESTARE pentru
pentru tipul
tipul adresa01
adresa01 */
*/
SELECT
SELECT ** FROM
FROM pic.pers
pic.pers WHERE
WHERE adresa
adresa ==
==
''(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')
(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')'::adresa01;
'::adresa01;
–-
–- pentru
pentru verificarea
verificarea operatorului
operatorului dede indexare
indexare realizăm
realizăm oo ordonare
ordonare
SELECT
SELECT ** FROM
FROM pic.pers
pic.pers ORDER
ORDER BY
BY adresa;
adresa;
În exemplul de mai sus, în prima interogare s-a testat
operatorul == (egalitate la nivel de ţară), interogarea trebuie să
întoarcă toate înregistrările pentru 'Tara2'. În a doua interogare
este testat operatorul de indexare prin utilizarea ordonării.
Pentru a testa intrarea în funcţia de indexare, se poate
introduce o notificare sau se poate simula o eroare în această
funcţie. De asemenea, se poate testa operatorul de indexare prin
utilizarea câmpului de tip adresa01 în construcţia pentru PRIMARY
KEY.
/*Ex.
/*Ex. 9(6A)TESTARE
9(6A)TESTARE pentru
pentru adresa01
adresa01 */
*/ DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.pers2;
pic.pers2;
CREATE
CREATE TABLE
TABLE pic.pers2(nume
pic.pers2(nume varchar(40),adresa
varchar(40),adresa adresa01
adresa01 PRIMARY
PRIMARY KEY);
KEY);

/*Ex.
/*Ex. 9(7A)TESTARE
9(7A)TESTARE pentru
pentru adresa01
adresa01 */
*/
INSERT
INSERT INTO
INTO pic.pers2(nume,adresa)
pic.pers2(nume,adresa) VALUES
VALUES
('Nume5','(''Tara1'',''Reg3'',''Oras4'',''Str1'',5,5005,''-'',''-'')'),
('Nume5','(''Tara1'',''Reg3'',''Oras4'',''Str1'',5,5005,''-'',''-'')'),
('Nume3','(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')'),
('Nume3','(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')'),
('Nume3','(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')'),
('Nume3','(''Tara2'',''Reg4'',''Oras3'',''Str1'',5,5006,''-'',''-'')'),
('Nume8','(''Tara1'',''Reg3'',''Oras2'',''Str1'',5,5003,''-'',''-'')');
('Nume8','(''Tara1'',''Reg3'',''Oras2'',''Str1'',5,5003,''-'',''-'')');
Înregistrările 2 şi 3 sunt identice, ceea ce va genera o
eroare în funcţia btree().

10. Teste rezolvate

10.1. Funcţie pentru contorizarea timpilor de parcare


Se dă un tabel cu structura: “data de intrare”, ”ora de
intrare (tip time)”, “nr. înmatriculare”, “data de ieşire”, “ora de
ieşire”. Să se scrie o funcţie care adaugă o înregistrare în
momentul unei intrări în parcare şi care în momentul ieşirii
întoarce timpul de staţionare.
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.parc;
pic.parc; CREATE
CREATE TABLE
TABLE pic.parc(datain
pic.parc(datain
date,orain
date,orain time,
time, nr_inmatr
nr_inmatr varchar(20),
varchar(20), dataout
dataout date,
date, oraout
oraout time);
time);
Verificăm dacă maşina cu numărul de înmatriculare transmis
prin argument există în parcare. Dacă nu există, adăugăm o
înregistrare în tabel, altfel actualizăm înregistrarea cu acel
număr de înmatriculare cu data şi ora curentă. Calculăm diferenţa
între cele două momente şi întoarcem valoarea de tip interval.

71
III.Programarea la nivel de server – plpgSQL

CREATE
CREATE OR
OR REPLACE
REPLACE FUNCTION
FUNCTION pic.parcare(_nr_inmatr_
pic.parcare(_nr_inmatr_ text,_data_
text,_data_ date
date
DEFAULT
DEFAULT CURRENT_DATE,
CURRENT_DATE, _ora_
_ora_ time
time DEFAULT
DEFAULT CURRENT_TIME)
CURRENT_TIME)
RETURNS
RETURNS interval
interval ASAS $$
$$ DECLARE
DECLARE ff boolean;
boolean; dif
dif interval;
interval;
BEGIN
BEGIN --verificăm
--verificăm dacă
dacă există
există deja
deja maşina
maşina în
în parcare
parcare
SELECT
SELECT EXISTS(SELECT
EXISTS(SELECT ** FROMFROM pic.parc
pic.parc
WHERE
WHERE nr_inmatr
nr_inmatr == _nr_inmatr_
_nr_inmatr_ AND
AND dataout
dataout IS
IS NULL)
NULL) INTO
INTO f;
f;
IF NOT f THEN INSERT INTO pic.parc(datain,orain,nr_inmatr)
IF NOT f THEN INSERT INTO pic.parc(datain,orain,nr_inmatr) VALUES VALUES
(_data_,_ora_,_nr_inmatr_);RETURN
(_data_,_ora_,_nr_inmatr_);RETURN '0'::interval;
'0'::interval; ENDEND IF;--intrare
IF;--intrare nouă
nouă
UPDATE pic.parc SET dataout = _data_, oraout
UPDATE pic.parc SET dataout = _data_, oraout = _ora_ = _ora_
WHERE
WHERE nr_inmatr
nr_inmatr == _nr_inmatr_
_nr_inmatr_ ANDAND dataout
dataout IS
IS NULL;
NULL;
SELECT dataout + oraout - datain - orain FROM pic.parc WHERE
SELECT dataout + oraout - datain - orain FROM pic.parc WHERE nr_inmatr nr_inmatr
== _nr_inmatr_
_nr_inmatr_ AND
AND dataout
dataout == _data_
_data_ AND
AND oraout
oraout == _ora_
_ora_ INTO
INTO dif;
dif;
RETURN dif; END; $$ LANGUAGE 'plpgsql';
RETURN dif; END; $$ LANGUAGE 'plpgsql';
Întodeauna trecem argumentele implicite (cu DEFAULT) după
cele obligatorii.
SELECT
SELECT pic.parcare('BC-45-AAA');
pic.parcare('BC-45-AAA'); SELECT
SELECT ** from
from pic.parc;
pic.parc;
SELECT
SELECT pic.parcare('BC-45-AAA');
pic.parcare('BC-45-AAA'); SELECT
SELECT ** from
from pic.parc;
pic.parc;

10.2. Calculul debitelor medii zilnice


Se realizează citiri ale unui contor de fluid la intervale
aleatoare. Se notează valoarea contorului şi momentul citirii. Să
se scrie o funcţie ce calculează debitele medii zilnice (de la ora
6:00 ziua curentă până a doua zi la ora 6:00).
DROP
DROP TABLE
TABLE IF
IF EXISTS
EXISTS pic.d1;
pic.d1; CREATE
CREATE table
table pic.d1(datam
pic.d1(datam date,
date, ora
ora int,
int,
val_contor
val_contor numeric,
numeric, CONSTRAINT
CONSTRAINT k_d1k_d1 PRIMARY
PRIMARY KEY(datam,
KEY(datam, ora));
ora));
INSERT
INSERT INTO
INTO pic.d1
pic.d1 VALUES
VALUES ('2000-01-01',6,8),
('2000-01-01',6,8), ('2000-01-01',14,12),
('2000-01-01',14,12),
('2000-01-01',22,16),
('2000-01-01',22,16), ('2000-01-02',6,24),
('2000-01-02',6,24), ('2000-01-02',14,26),
('2000-01-02',14,26),
('2000-01-02',22,32),
('2000-01-02',22,32), ('2000-01-03',6,44),
('2000-01-03',6,44), ('2000-01-03',14,90),
('2000-01-03',14,90),
('2000-01-03',22,120),
('2000-01-03',22,120), ('2000-01-04',6,122),
('2000-01-04',6,122), ('2000-01-04',14,129),
('2000-01-04',14,129),
('2000-01-04',22,135);
('2000-01-04',22,135); select select ** from
from pic.d1;
pic.d1;
CREATE
CREATE OROR REPLACE
REPLACE FUNCTION
FUNCTION pic.debit_m()
pic.debit_m() RETURNS
RETURNS SETOF
SETOF RECORD
RECORD AS
AS
$$
$$ DECLARE
DECLARE rr RECORD;r1
RECORD;r1 RECORD;
RECORD; kk int;
int;
BEGIN
BEGIN k:=0;FOR
k:=0;FOR rr ININ SELECT
SELECT datam,
datam, ora,
ora, val
val ,0.001
,0.001 as
as qq FROM
FROM pic.d1
pic.d1
LOOP
LOOP IF
IF r.ora=6
r.ora=6 and
and k>1
k>1 THEN
THEN k:=k+1;r.q:=(r.val-r1.val)/24.0;
k:=k+1;r.q:=(r.val-r1.val)/24.0;
r1:=r;
r1:=r; return
return next
next r;
r; END
END IF;
IF;
IF
IF r.ora
r.ora == 66 and
and kk == 11 THEN
THEN k:=k+1;
k:=k+1; r1.q:=r1.val/24.0;
r1.q:=r1.val/24.0; r.q:=(r.val-
r.q:=(r.val-
r1.val)/24.0;
r1.val)/24.0; return
return next
next r1;
r1; r1:=r;
r1:=r; return
return next
next r;
r; END
END IF;
IF;
IF
IF r.ora=6
r.ora=6 and
and k=0
k=0 THEN
THEN k:=k+1;
k:=k+1; r1:=r;
r1:=r; END
END IF;
IF;
END
END LOOP;
LOOP; END
END $$
$$ language
language plpgsql;
plpgsql;
Variabila de tip înregistrare r1 reprezintă ultima
înregistrare cu data de ieri, iar q reprezintă debitul mediu orar
la nivelul unei zile:
SELECT
SELECT datam,
datam, ora,
ora, val,
val, qq FROM
FROM pic.debit_m()
pic.debit_m() AS
AS (datam
(datam date,
date, ora
ora
int,
int, val
val numeric,q
numeric,q numeric);
numeric);

72
IV.Introducere în limbajul C

IV. Introducere în limbajul C


Notă: Pentru o mai bună înţelegere, recomand testarea exemplelor
din dreptunghiurile cu linie continuă şi colţuri rotunjite. După
testare se vor modifica valorile variabilelor şi se va urmări
rezultatul. În final se va încerca rescrierea liniilor pentru a
obţine acelaşi rezultat sau rezultate asemănătoare pentru alte
condiţii sau alte valori ale variabilelor. Se va crea iniţial un
workspace, apoi se va crea câte un proiect (de exemplu, în
codeLite) cu opţiunea de lucru în consolă. Codul din fişierul
iniţial creat pe post de şablon va fi înlocuit cu codul din
dreptunghi. Fiecare proiect se va compila şi rula (Ctrl+F9).
Recomand studierea individuală a lucrului cu debuger-ul pentru o
înţelegere mai aprofundată. În loc de /*\n*/ se va adăuga linie nouă.

1. Organizarea programelor
La baza oricărei aplicaţii software sau pentru cazurile
simple stă un algoritm (metodă de rezolvare). Acesta preia date din
memoria dinamică a calculatorului (RAM) sau de pe suporturi de
stocare şi le prelucrează. Rezultatul prelucrării (a execuţiei
algoritmilor) este stocat în memoria dinamică şi apoi poate fi
afişat, memorat pe suporturi de stocare a datelor, sau transmis
către alte aplicaţii software.
Codul sursă se scrie cu ajutorul unui editor de texte, acest
editor poate fi unul simplu gen notepad, notepad++, getit sau unul
specializat, înglobat într-un mediu integrat de dezvoltare pentru
software-uri (ex.: TuboC++, BorlandC++, C++Builder, VisualC++,
SymantecC++, DevC++, CodeLite, NetBean, EclipseC++, gt++, gtk++
etc.).
Compilatorul transformă codul sursă în cod obiect, altfel spus:
transformă textul literar în cod maşină – reprezentat de comenzi la
nivel de microprocesor).
Executabil / bibliotecă
Programator (*.exe,aplicaţie,*.dll,*.lib,*.so,*.a,…)
Editare Linkeditare Linkeditare
Editor de text Link-editor
Compilator
Cod sursă Cod obiect Biblioteci statice
(*.c,*.cpp) Compilare (*.obj/*.o) (*.lib/*.a)
Figura IV-1: Etapele pentru obţinerea unei aplicaţii software
În cadrul codului sursă se pot utiliza rutine
(proceduri/funcţii) definite în cadrul unor biblioteci statice

73
IV.Introducere în limbajul C

(*.lib – library sau *.a – archive), caz în care codul utilizat din
bibliotecile statice este încorporat în executabil.
Codul bibliotecilor dinamice (*.dll – dynamic link library
sau *.so – shared objects) este doar apelat în momentul execuţiei
sau al lansării aplicaţiei (diferenţele apar pentru Linux), acesta
nefiind inclus în fişierul executabil sau în biblioteca rezultată.
1.1. Structura unui program în limbajul C/C++
Un program C/C++ cuprinde următoarele elemente pricipale:
✗ operatori (aritmetici, logici etc.);
✗ instrucţiuni (de decizie, de parcurgere a unei bucle etc.);
✗ funcţii (apelate din cadrul bibliotecilor sau definite de
utilizator);
✗ variabile şi constante;
✗ funcţia main().
Textul unui program poate fi scris într-un fişier sau în mai
multe fişiere. Un program va avea o singură funcţie main(),
indiferent dacă este scris într-un singur fişier sau în mai multe
fişiere. Programul compilat şi linkeditat va începe întodeauna prin
lansarea în execuţie a instrucţiunilor şi funcţiilor din cadrul lui
main(). Prin ieşirea din funcţia main() se încheie şi execuţia
programului.
Comentariile sunt reprezentate prin simbolurile:
/*comentariu la nivel de bloc*/ iar //comentariu la nivel de linie.

2. Variabile, alocări, tipuri de date, operatori C/C++

2.1. Noţiunile de variabilă şi constantă


Datele din cadrul unui program sunt stocate în memoria
dinamică (RAM), ele vor fi marcate ca fiind şterse fie la un anumit
moment în timpul execuţiei programului, fie la sfârşitul acestuia.
VARIABILE pot fi reprezentate prin date ce urmează a se modifica în
cadrul programului în urma execuţiei unor operaţii asupra acestora.
Există şi date care rămân constante în urma execuţiei programului –
denumite CONSTANTE.
Pentru ca aceste date să poată fi accesate, este necesară
declararea acestora în cadrul programului. O variabilă va avea o
denumire, pe care o va pune utilizatorul în momentul declarării
acestora. De regulă, dimensiunea maximă a denumirii unei variabile
este de 32 de caractere, poate conţine atât litere cât şi cifre
(denumirea începe cu o literă sau cu simbolul underline, adică “_”)
Operaţiile din cadrul unui program se realizează asupra
variabilelor. Fiecare variabilă va avea o anumită structură în ceea
ce priveşte reprezentarea sa în memorie (aceasta dă tipul de

74
2.Variabile, alocări, tipuri de date, operatori C/C++

variabilă). În funcţie de reprezentarea în cadrul memoriei,


variabilele pot fi de mai multe tipuri. Acestea trebuie declarate
înainte de compilarea programului astfel încât compilatorul să le
rezerve memorie în momentul încărcării executabilului în memorie.
Variabilele la nivelul unui bloc de instrucţiuni trebuie să
aibă nume diferite.
2.2. Tipuri de alocare a variabilelor
Variabilele pot fi locale (declarate în cadrul funcţiilor)
sau globale (declarate în afara funcţiilor). La nivelul unui bloc
de instrucţiuni, o variabilă poate fi declarată în orice punct al
acestuia (facilitatea provine din C++), dar aceasta poate fi
utilizată doar în instrucţiunile situate după declararea acesteia.
Mulţi programatori declară variabilele la începutul blocului de
instrucţiuni sau a funcţiei (C standard).
2.2.1. Alocarea de tip automatic
Memoria RAM necesară datelor este rezervată în momentul
întâlnirii declaraţiei. De exemplu, declaraţia long a; rezervă
pentru variabila a 32 de biţi (4 octeţi). Memoria este eliberată
automat în momentul în care se închide blocul în care a fost
declarată variabila (adică se închide acolada “}”).
2.2.2. Alocarea de tip static
Alocarea de tip static presupune că variabila este alocată
de program în momentul declarării acesteia şi nu va fi ştearsă din
memorie decât în momentul terminării programului. Acestea sunt
tratate detaliat în capitolul despre vizibilitatea variabilelor.
2.2.3. Alocarea de tip dinamic
Alocarea dinamică presupune că variabila respectivă este
declarată de tip pointer în punctul respectiv al programului. În
acest caz, în momentul în care programul ajunge cu execuţia în acel
punct rezervă memorie doar pentru adresa unde se găsesc datele şi
NU pentru date. Pentru date se va aloca memorie printr-o funcţie
din biblioteca C (ex. funcţia malloc). Memoria astfel alocată va
necesita dealocare (eliberare) printr-o altă funcţie specializată
(ex.: free) în momentul în care aceste date nu mai sunt necesare.
2.3. Tipuri de variabile
Principalele tipuri de variabile sunt prezentate în anexă.
În momentul compilării programului, este cunoscut tipul
variabilelor. În acest mod programul „știe” cât ocupă în memorie
fiecare variabilă și cum să „utilizeze” variabila respectivă.

75
IV.Introducere în limbajul C

Consider că înţelegerea corectă a variabilelor reprezintă


jumătate din efortul de înţelegere a programării.
2.4. Operatorii C/C++
Operatorii sunt descrişi în anexă. În continuare este
prezentat un exemplu de interschimbare a două variabile fără
utilizarea unei variabile auxiliare. În acest sens, s-a utilizat
operatorul ^ (sau exclusiv / XOR) la nivel de bit.
/*Ex.3.1(1)
/*Ex.3.1(1) utilizare
utilizare “SAU“SAU EXCLUSIV”
EXCLUSIV” (XOR)*/
(XOR)*/
#include
#include <stdio.h>
<stdio.h>
int
int main()
main()
{{ short
short aa == 5,
5, bb == 10;
10;
printf("Inainte
printf("Inainte --- --- a:%d
a:%d b:%d
b:%d \n”,a,b);
\n”,a,b);
a=a^b;
a=a^b; //(1111)
//(1111)2=(0101)
=(0101)2
^(1010)
^(1010) , adica 7=5^10;
22, adica 7=5^10;
2 2
b=a^b;
b=a^b; //(0101)
//(0101)22=(1111)22^(1010)2,2, adica
=(1111) ^(1010) adica 5=15^10;
5=15^10;
a=a^b;
a=a^b; //(1010)
//(1010)2=(1111)
=(1111)2
^(0101)
^(0101)2
,, adica
adica 10=15^5;
10=15^5;
2 2 2
printf("Dupa
printf("Dupa --- --- a:%d
a:%d b:%d
b:%d \n”,a,b);
\n”,a,b);
}}
Un alt exemplu de lucru cu operatori binari constă în
extragerea informaţiei reprezentate pe biţii 16,15,14 (numerotaţi
de la dreapta la stânga începând cu 1) dintr-un număr reprezentat
pe 32 de biţi.
/*Ex.3.1(2)
/*Ex.3.1(2) extragere
extragere informaţii
informaţii dintr-un
dintr-un număr*/
număr*/
#include <stdio.h>
#include <stdio.h>
int
int main()
main()
{{ unsigned
unsigned long
long aa == 0x5B5F0;//declarare
0x5B5F0;//declarare urmatăurmată de
de atribuire
atribuire
//
// a=(1001
a=(1001 1011
1011 1001
1001 1111
1111 0000)
0000)22
unsigned
unsigned long
long masca
masca == 0xE000;//
0xE000;// masca=(1110
masca=(1110 0000
0000 0000
0000 0000)
0000)22
unsigned
unsigned long
long s;
s; ss == aa && masca;
masca;
//s=(0000
//s=(0000 1010
1010 0000
0000 0000
0000 0000)
0000)22 ΞΞ s=0xA000
s=0xA000
ss >>=
>>= 13; //deplasare la dreapta cu 13 biţi urmată de
13; //deplasare la dreapta cu 13 biţi urmată de atribuire
atribuire
printf(“X=%x\n”,s);
printf(“X=%x\n”,s); //s=(0000
//s=(0000 00000000 0000
0000 0000
0000 0101)
0101)22 adică
adică s=0x0005
s=0x0005
return 0;
return 0; }}
Cei trei biţi din variabila masca reprezintă biţii a căror
informaţie (dacă sunt 0 sau 1) este extrasă. Se realizează o
operaţie şi/AND (&) la nivel de bit între mască şi numărul de
analizat. După această operaţie se execută o deplasare (şiftare) la
dreapta cu 13 biţi astfel încât bit-ul 14 să devină primul bit din
stânga apoi se citeşte numărul rezultat.
Notă: Informaţia din număr se completează la stânga cu zero până
la reprezentarea numărului.

76
2.Variabile, alocări, tipuri de date, operatori C/C++

2.5. Teste
Scrieţi ce se afişează în urma execuţiei următoarelor
instrucţiuni (liniile sunt independente), iar în cazul existenţei
unor erori să se semnalezeze erorile apărute în urma execuţiei şi
sursa acestora:
1. inta = 19; (a %= +3)++; printf(“a=%d”,a);
2. inta = 19; (a%= -3)--; printf(“a=%d”,a);
3. inta = 33; a>>=2;printf(“%d”,a);
4. inta = 10,b; b = (a | 15) & (a ^ 12); printf(“%d”,b);
5. inta = 11, b, c, d; b = (a ^ 11) & (a | 22); c= a ^ 11; d=a | 22;
printf(“b=%d c=%d d=%d”,b,c,d);
6. int a,b = 13; a=(--b)++; printf(“a=%d”,a);
7. int a = 15,b=2;if((a>1)&&(b>=3)) a <<= b; else a >>= b; printf(“a=
%d”,a);
8. int a = 13,b; b = (a ^ 5) & (a | 2); printf(“b=%d”,b);
9 int a = 10; a >>= 2; printf(“a=%d”,a);
10.int a = 10, b, c, d; b=(a^10)&(a|11); c= a^10; d=a|2;
printf(“b=%d c=%d d=%d”,b,c,d);

3. Implementarea structurilor de control


Algoritmul proiectat pentru rezolvarea unei anumite probleme
trebuie implementat în limbajul de programare, iar prelucrarea
datelor se realizează cu ajutorul instrucţiunilor. Instrucţiunea
este transformată de compilator într-o secvenţă cod maşină, pe care
o execută microprocesorul cu scopul implementării instrucţiunii
respective. O instrucţiune este o construcţie validă (care respectă
sintaxa limbajului) urmată de „ ; ” (semicolon).
Un algoritm poate fi realizat prin combinarea a trei
structuri fundamentale:
➔ structura secvenţială;
➔ structura alternativă (de decizie, de selecţie);
➔ structura repetitivă (ciclică).

3.1. Implementarea structurii secvenţiale


Structura secvenţială este o înşiruire de instrucţiuni,
plasate una după alta, în ordinea execuţiei acestora.
I 1 I 2 I n
Pseudocodul: instr1; instr2;........; instr n;
instr1; instr2;........; instr n;
Implementarea structurii secvenţiale se realizează cu
ajutorul instrucţiunilor:
3.1.1. Instrucţiunea vidă
Sintaxa: ;

77
IV.Introducere în limbajul C

Descriere: Instrucţiunea vidă nu are niciun efect. Se


utilizează în construcţii în care se cere prezenţa unei
instrucţiuni, dar nu se execută nimic.
3.1.2. Instrucţiunea expresie
int
int a=3,b=4,
a=3,b=4, c;
c;
Sintaxa: expresie; double d;
double d;
sau: apel_funcţie; c=a+b;
c=a+b;
Ex.: sqrt() este o funcţie definită d=sqrt(b);
d=sqrt(b);
în biblioteca matematică
3.1.3. Instrucţiunea compusă (instrucţiunea bloc)
{ declaraţii
declaraţii variabile;
Sintaxa: { instr1; variabile;
instr1; instr2;........;
instr2;........; instr
instr n;
n;
}}
Într-un bloc de instrucţiuni se pot declara variabile care
pot fi accesate doar în corpul blocului. Instrucţiunea bloc este
utilizată în locurile în care este necesară prezenţa unei singure
instrucţiuni, însă procesul de calcul implică executarea mai multor
instrucţiuni.
3.2. Implementarea structurii de decizie (alternative, de
selecţie)
3.2.1. Instrucţiunea if
Sintaxa: if(<expresie>)
if(<expresie>) instr1;
instr1;
[else
[else instr2;]
instr2;]
unde [ ...] semnifică faptul că este opţional. Ramura else este
opţională.
La întâlnirea
instrucţiunii if, se evaluează
<expresie>. Dacă valoarea TRUE FALSE
expresiei are valoarea de adevăr if (<expresie>)
TRUE (≠0), se execută
instrucţiunea #1 (instr#1); dacă instr.1 instr.2
valoarea expresiei este FALSE
(=0), se execută instrucţiunea
#2 (instr#2). Se execută doar una
dintre cele două instrucţiuni:
fie instr#1, fie instr#2. După
execuţia instrucţiunii if, se
trece la execuţia instrucţiunii Figura IV-2: Instrucţiunea if
care urmează acesteia.
Instrucţiunile instr#1 şi instr#2 pot fi instrucţiuni compuse
(blocuri), sau chiar alte instrucţiuni if (if-uri imbricate).

78
3.Implementarea structurilor de control

Variabilele a şi b se int int a,b,c;


a,b,c;
citesc de la tastatură cu ajutorul scanf(“%d”,&a);
scanf(“%d”,&a); scanf(“%d”,&b),
scanf(“%d”,&b),
funcţiei scanf, unde %d specifică if(a<(b+1))
if(a<(b+1)) c++;
c++;
formatul datei de tip întreg, iar else
else {a--;
{a--; c=a+b;
c=a+b;
operatorul & semnifică adresa if(c)
if(c) a++;
a++; }}
variabilei.
3.2.2. Instrucţiunea switch
În unele cazuri este necesară o decizie multiplă specială.
Instrucţiunea switch permite acest lucru.
Se testează dacă valoarea pentru expresie este una dintre
constantele specificate
(expresie_const_1,
expresie_const_2, etc.) şi se case default
execută instrucţiunea de pe test expresie
ramura corespunzătoare. În
schema logică test_expresie este instrucţiune m
una din condiţiile:
expresie=expresie_const_1, instrucţiune 1
expresie=expresie_const_2 etc.
Este evaluată <expresie> break
(expresie aritmetică), iar
valoarea ei este comparată cu instrucţiune 2
valoarea expresiilor constante break
1, 2, n etc. (expresii
constante = expresii care nu
conţin variabile). Se execută
instrucţiunea corespunzătoare instrucţiune n
acelei ramuri (instrucţiune_
k), unde expresie_const_k este
egală cu rezultatul evaluării
<expresie>.
Dacă se întâlneşte in- Figura IV-3: Diagrama
strucţiunea break, parcurgerea instrucţiunii. switch
este întreruptă şi se va ieşi din blocul dat de switch. Dacă nu
este întâlnită instrucţiunea break, se parcurge şi instrucţiunea
următoare. În cazul în care valoarea expresiei nu este găsită
printre valorile expresiilor constante, se execută cazul marcat cu
eticheta default (opţională). Expresia <expresie>, trebuie să aibă
ca rezultat o valoare de tip întreg. În cazul în care sunt de alt
tip este necesară convertirea acestora la tipul întreg.
Dacă
Dacă expresie=expresie_const_1
expresie=expresie_const_1 instrucţiune1;
instrucţiune1; [ieşire;]
[ieşire;]
Dacă
Dacă expresie=expresie_const_2
expresie=expresie_const_2 instrucţiune2;
instrucţiune2; [ieşire;]
[ieşire;]
........................
........................
Dacă
Dacă expresie=expresie_const_n
expresie=expresie_const_n instrucţiune_n;
instrucţiune_n;
Altfel
Altfel instrucţiune_m;
instrucţiune_m;

79
IV.Introducere în limbajul C

switch
switch (<expresie>)//Sintaxa:
(<expresie>)//Sintaxa: int
int a=3,b=4;
a=3,b=4; cin>>luna;
cin>>luna;
{{ switch(luna)
switch(luna)
case
case expresie_const_1:
expresie_const_1: instr_1;[break;]
instr_1;[break;] {{ case
case 1:a++;
1:a++; break;
break;
case
case expresie_const_2: instr_2;[break;]
expresie_const_2: instr_2;[break;] case
case 2:{a--;b++;} break;
2:{a--;b++;} break;
.. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. case
case 3:{a--;b--;}
3:{a--;b--;}
case
case expresie_const_n:
expresie_const_n: instr_n; instr_n; case
case 4:a--;
4:a--;
[[ default:instrucţiune_m;
default:instrucţiune_m; ]] default:{a=0;
default:{a=0; b=0;}
b=0;}
}} }} cout<<”a=”<<a<<”b”<<b;
cout<<”a=”<<a<<”b”<<b;
Ex.:
• pentru luna=1 obţinem a=4, b=4
• pentru luna=2 obţinem a=2, b=5
• pentru luna=3 obţinem a=1, b=3
• pentru luna=4 obţinem a=2, b=4
• pentru luna>4 obţinem a=0, b=0
3.3. Implementarea structurilor repetitive (ciclice)
Există două categorii de
instrucţiuni ciclice: cu test
iniţial şi cu test final.
Structura ciclică cu test iniţial < EXPRESIE_1 >
este implementată prin (de obicei iniţializare
instrucţiunile for şi while, iar contori)
cea cu test final este
implementată prin instrucţiunea
do while.
3.3.1. Implementarea structurilor FALSE
Evaluare
ciclice cu test iniţial – < EXPRESIE_2 >
Instrucţiunea for
Limbajul C are o TRUE
instrucţiune for deosebit de
flexibilă. Instrucţiunea for BLOC_INSTRUCŢIUNI
implementează structura ciclică (blocul instr. for)
cu număr cunoscut de paşi.
Sintaxa:
for( EXPRESIE_1;;EXPRESIE_2
for(EXPRESIE_1 EXPRESIE_2;;EXPRESIE_3
EXPRESIE_3))
BLOC < EXPRESIE_3 >
BLOC INSTRUCŢIUNI;
INSTRUCŢIUNI; (de obicei
int
int aa == 0;
0; incrementare contori)
for(int
for(int i=0;i<100;i++)
i=0;i<100;i++) a++;
a++; Figura IV-4: Diagrama
În continuare este pre- instrucţiunii for
zentat pseudocodul şi un exemplu.

80
3.Implementarea structurilor de control

Evaluare
Evaluare EXPRESIE_1
EXPRESIE_1 int
int i,j,m,n,a,b;
i,j,m,n,a,b; int
ATÂT
ATÂT TIMP
TIMP cât
cât for(i=0;i<n;i++) int a=9;
a=9;
EXPRESIE_2 este TRUE for(i=0;i<n;i++) for(;;)
EXPRESIE_2 este TRUE for(j=0;j<m;j++) for(;;)
REPETĂ for(j=0;j<m;j++) {a--;
REPETĂ {a+=i*j; {a--;
begin {a+=i*j; if(a<5)
begin if(i<j)
if(i<j) b=a;
b=a;
if(a<5)
BLOC_INSTRUCŢIUNI
BLOC_INSTRUCŢIUNI else
break;
break;
Evaluare EXPRESIE_3 else b=-a;
b=-a; }}
Evaluare EXPRESIE_3 }}
end
end
Nu este obligatorie prezenţa expresiilor, ci doar a instruc-
ţiunilor vide. În exemplul de mai sus (dreapta) instrucţiunea for
implementează o buclă infinită, iar în exemplul de mai sus (stânga)
sunt implementate mai multe bucle for imbricate.

3.3.2. Instrucţiunea while


Sintaxa: while(<expresie>)
while(<expresie>)
instr1;
instr1;
La întâlnirea instrucţiunii while, se evaluează <EXPRESIE>.
Dacă aceasta are valoarea TRUE (diferită de ZERO), se execută
BLOC_INSTRUCŢIUNI. Se reevaluează valoarea expresiei. Dacă aceasta
are valoarea de adevăr TRUE, se
repetă instrucţiunea etc.
Astfel, blocul cu instrucţiuni
(corpul ciclului) se repetă
atât timp cât <EXPRESIE> are
valoarea de adevăr TRUE. În
momentul în care <EXPRESIE> are TRUE FALSE
valoarea de adevăr FALSE (sau Evaluare
egal cu ZERO), se iese din <EXPRESIE>
ciclu şi se trece la următoarea
instrucţiune din afara buclei
while.
În cazul în care la
prima evaluare a expresiei, BLOC_INSTRUCŢIUNI
aceasta are valoarea de adevăr
FALSE, corpul instrucţiunii
while nu va fi executat Figura IV-5: Diagrama
niciodată. Instrucţiunile din instrucţiunii while
corpul ciclului while trebuie
să modifice valoarea expresiei, altfel va fi un ciclu infinit.
Blocul cu instrucţiuni din corpul ciclului while poate fi
constituit dintr-o singură instrucţiune, caz în care nu mai sunt
necesare acoladele.

81
IV.Introducere în limbajul C

3.3.3. Instrucţiunea “do while”

Sintaxa: do
do BLOC_INSTRUCŢIUNI
BLOC_INSTRUCŢIUNI
while(<EXPRESIE>)
while(<EXPRESIE>) BLOC_INSTRUCŢIUNI
Se execută blocul de
instrucţiuni. Se evaluează apoi
<EXPRESIE>. Dacă aceasta are
valoarea de adevăr TRUE, se execută
din nou blocul de instrucţiuni,
altfel se iese din buclă. Se TRUE FALSE
Evaluare
testează din nou valoarea <EXPRESIE>
expresiei. Se repetă execuţia
blocului de instrucţiuni atât timp
cât valoarea de adevăr a expresiei
este TRUE. În cazul instrucţiunii Figura IV-6: Diagrama
do while, corpul ciclului se instrucţiunii do while
execută cel puţin o dată.
3.3.4. Instrucţiunea “break”

Aceasta forţează ieşirea din interiorul unei bucle fără a se


mai ţine seama de condiţia de menţinere în buclă. Instrucţiunile
situate în corpul buclei, după instrucţiunea break, nu vor mai fi
executate.
3.3.5. Intrucţiunea “continue”

Aceasta duce la ignorarea instrucţiunilor din buclă, situate


după aceasta, şi testarea din nou a expresiei de menţinere în
buclă. În cazul buclelor for se realizează şi evaluarea celei de a
treia expresii responsabilă cu incrementarea contorilor.
În continuare este prezentat un exemplu de implementare a
instrucţiunii for. Exemplul constă în calcularea valorii unei
integrale definite între două puncte a şi b. Aceasta se rezumă la
calculul ariei de sub grafic. În acest sens, intervalul ab este
împărţit într-un număr de segmente transmis ca şi parametrul
funcţiei (a se vedea capitolul funcţii). Fiecare segment obţinut
constituie înălţimea unui trapez, iar valorile funcţiei în cele
două capete ale segmentului se constituie în baza mare pentru
trapez, respectiv baza mică pentru trapez. Suma ariilor tuturor
trapezelor (pentru fiecare segment din intervalul ab) aproximează
valoarea integralei.

82
3.Implementarea structurilor de control

/*Ex.3.3(1):
/*Ex.3.3(1): calculul
calculul valorii
valorii unei
unei integrale
integrale definite
definite dede la
la aa la
la b*/
b*/
#include <stdio.h>
#include <stdio.h>
double
double f1(int
f1(int x)x) {return
{return x*x-5*x+2;}
x*x-5*x+2;} //f1 //f1 este
este un
un ex.
ex. de
de functie
functie
double
double integr(double a, double b, int n) // fct.ce întoarce val.integr.
integr(double a, double b, int n) // fct.ce întoarce val.integr.
{{ //pas
//pas –– inaltimea
inaltimea unuiunui trapez,
trapez, sum-suma
sum-suma partiala
partiala sisi cea
cea finala
finala
//
// aa bb sunt
sunt cele
cele doua
doua valori
valori intre
intre care
care sese calc.
calc. integrala
integrala
double
double q, q, sum,
sum, pas
pas == (b(b -- a)
a) // (n
(n ** 1.0);
1.0);
for(sum
for(sum == 0, 0, qq == aa ++ pas;
pas; qq <=
<= b;b; qq +=
+= pas)
pas)
sum
sum +=+= (( f1(q)
f1(q) ++ f1(q-pas)
f1(q-pas) )) ** pas pas // 2.0;
2.0;
return
return sum;
sum; }}
int
int main()
main()
{double
{double hh == integr(2,8,10000);
integr(2,8,10000); printf("Integrala:
printf("Integrala: %f\n",h);
%f\n",h);
return
return 0;}0;}

4. Tablouri şi pointeri. Aritmetica pointerilor.

4.1. Alocarea dinamică. Noţiunea de pointer


4.1.1. Când se utilizează alocarea dinamică?
Variabilele conţin datele ce se modifică în cadrul execuţiei
programului. Fiecare dintre acestea se află la o anumită adresă de
memorie şi este referită printr-o etichetă (nume). În cazul în care
dimensiunea acestor date este mare (raportată la memoria RAM a
calculatorului) sau nu este cunoscută la începutul programului, se
utilizează alocarea dinamică. De asemenea se mai utilizează şi în
alte cazuri, tratate la capitolul despre funcţii.
4.1.2. Noţiunea de pointer
Pointerul este o variabilă ce conţine adresa unui obiect.
Obiectul a cărui adresă este conţinută de pointer poate fi
variabilă sau funcţie.
În exemplul de mai jos (declararea a două variabile),
int a; int *b; este echivalent cu a scrie int a,*b; sau
int *b,a; .Variabila a este de tip întreg, iar variabila b este de
tip pointer, aceasta din urmă conţine adresa unei variabile de tip
întreg (adresa la care se află o valoare de tip întreg).
Adresele au lungimea tipului de dată int (întreg) şi coincid
cu dimensiunea magistralei de adresare pentru calculatoare (iniţial
8 biţi – microprocesoare Z80, 16 biţi microprocesoare 286,386,486,
32 biţi – mai recente, iar cele actuale sunt de 64 biţi. De
exemplu, memoria maximă adresabilă pentru microprocesoarele pe 32
de biţi va fi de 232 octeţi, adică aproximativ 4 Gb memorie RAM.
Memoria RAM în care sunt încărcate programele şi datele ne-o putem
imagina ca o riglă având 232 adrese, la fiecare adresă se va regăsi

83
IV.Introducere în limbajul C

o locaţie de memorie ce conţine 8 biţi (1 octet în franceză sau 1


byte în engleză). Din acest motiv, tipul de date boolean ce conţine
doar valorile de adevăr TRUE sau FALSE (codificabile doar cu un
bit) este reprezentat în memorie pe un octet. Nu pot fi
reprezentate date în memorie sub un octet.
În cazul declarării variabilei de tip pointer, semnul * din
stânga acesteia semnifică faptul ca acesta conţine o adresă
(variabila este de tip pointer) şi nu o valoare. În momentul
utilizării variabilei, semnul * din stânga acesteia are altă
semnificaţie, acesta semnifică faptul că se preia valoarea
variabilei de tip pointer. De exemplu, în instrucţiunea: a =* b;
variabila a preia valoarea de la adresa lui b (valoarea la care
pointează b).
Operatorul unar & este utilizat pentru obţinerea adresei. De
exemplu, variabila pointer b preia adresa lui a, iar a nu este de
tip pointer b =& a; .
Variabilele din declaraţiile int *b,****c,**d; sunt de
asemenea pointeri.
4.2. Noţiunea de tablou
Tablourile au acelaşi tip de dată ca şi variabilele simple
şi pot avea una sau mai multe dimensiuni (ex.: tablori cu o
dimensiune = vectori, tablouri cu două dimensiuni = matrici).
Declarea tablourilor se realizează prin utilizarea operatorului [].
Ex.: double b[10],c[10][5],d[5][6][3][4],a; , unde b este un tablou
unidimensional cu 10 elemente de tip double, c este un tablou
bidimensional cu 10x5 elemente de tip double, d este un tablou cu
patru dimensiuni având 5x6x3x4 elemente de tip double.
În cazul variabilelor de tip tablou, este alocată automat
memorie pentru întregul tablou. Elementele acestuia nu sunt
iniţializate (la fel ca la variabilele simple), acestea având
valori aleatorii. Referirea la un element din cadrul tabloului se

aa == d[1][2][0][3];
d[1][2][0][3];
//
// aa preia
preia valoarea
valoarea unei
unei celule
celule din
din cadrul
cadrul tabloului
tabloului dd
Indecşii încep cu valoarea 0. În memoria RAM tablourile sunt
repezentate prin secvenţe continue. Practic, d va conţine adresa
tabloului (un bloc compact de 5x6x3x4 elemente, ocupând în memorie
5x6x3x4x8 octeţi – double este reprezentat pe 8 octeţi).
În cazul alocării dinamice a tabloului d (double ****d),
primii 3 vectori din imagine conţin adrese şi doar ultimul vector
conţine valorile de tip double.

84
4.Tablouri şi pointeri. Aritmetica pointerilor.

d[4]
d[3] d[3][0] d[3][1] d[3][2] d[3][3] d[3][4] d[3][5]
d[2] d[3][2][0] d[3][2][0][0] d[3][2][0][1] d[3][2][0][2] d[3][2][0][3]
d[1] d[3][2][1]
d[0] d[3][2][2]
Figura IV-7: Referirea la celula d[3][2[0][3] într-un tablou
cu 4 dimensiuni alocat dinamic
Variabila d conţine adresa celulei d[0]. Celula d[3] conţine
adresa vectorului desenat în dreapta acesteia. Celula d[3][2]
conţine adresa vectorului desenat sub această celulă. Celula
d[3][2][0] conţine adresa vectorului desenat în dreapta acesteia.
Ultimul vector din dreapta conţine doar valori de tipul double. În
continuare este prezentat un exemplu de declaraţii de variabile,
inclusiv de tip tablou.
int
int a,
a, *a1,
*a1, a2[4],
a2[4], b1[3][4],
b1[3][4], *b2[5],
*b2[5], **b3;
**b3;
int
int c1[4][7][2], *c2[14][6], **c3[7], ***c4;
c1[4][7][2], *c2[14][6], **c3[7], ***c4;
În acest exemplu, a este o variabilă de tip întreg (conţine
o valoare de tip întreg), a1 este o variabilă pointer la tip
întreg (conţine adresa unde este memorată variabila de tip întreg),
a2 este un tablou unidimensional (vector) cu 4 elemente de tip
întreg, b1 este un tablou bidimensional (matrice) cu 3x4 elemente
de tip întreg, b2 este un vector cu 5 elemente de tip pointer la
întreg, b3 este un pointer la o matrice cu elemente de tip întreg,
c1 este un tablou tridimensional cu elemente de tip întreg şi cu
dimensiunea 4x7x2, c2 este o matrice 14x6 cu elementele de tip
pointer la tip întreg, c3 este un vector cu 7 elemente de tip
pointer la matrice de tip întreg, iar c4 este un pointer la tablou
tridimensional cu elemente de tip întreg. În continuare este
prezentat şi un exemplu de atribuiri cu elemente din cadrul
tablourilor.
aa == a2[1];
a2[1]; aa == b1[0][0];
b1[0][0]; aa == c1[1][0][1];
c1[1][0][1]; a1 a1 == a2;
a2;
a1
a1 = b1[1]; a1 = b2[3]; a1 = c1[1][2]; a1 = c2[1][0];
= b1[1]; a1 = b2[3]; a1 = c1[1][2]; a1 = c2[1][0];
b3
b3 == c2[3];
c2[3]; b3b3 == c3[1];
c3[1];
aa == *a1;
*a1; a = a1[0];
a = a1[0]; aa == b2[2][0];
b2[2][0]; aa == c2[1][0][0];
c2[1][0][0];
c3[2][0][0]
c3[2][0][0] == a; a; a1a1 == &a;
&a; b3[1]
b3[1] == a1;
a1; c4[0]
c4[0] == c3[1];
c3[1];
În urma execuţiei instrucţiunii a=a1[1];, variabila a va avea
aceeaşi valoare ca şi în cazul execuţiei instrucţiunii a=a2[1];.
4.3. Alocarea dinamică a memoriei
Variabilele de tip pointer presupun utilizarea alocării
dinamice pentru memorie, cu excepţia cazurilor când acestea
copiază adresa altor zone de memorie deja alocate. Utilizatorul

85
IV.Introducere în limbajul C

poate solicita în timpul execuţiei programului alocarea unei zone


de memorie. Această zonă de memorie poate fi eliberată în momentul
în care nu mai este necesară.
Alocarea memoriei se poate realiza cu funcţiile din
bibliotecă având prototipul (declaraţiile) în <alloc.h> sau prin
utilizarea operatorului new. O primă funcţie pentru alocare este:
void *malloc(unsigned n);. Funcţia alocă un bloc de memorie de n
octeţi şi întoarce un pointer la începutul zonei de memorie
alocată. În cazul în care nu este posibilă alocarea unei zone
compacte de n octeţi funcţia returnează NULL (valoarea 0x0000).
Memoria alocată nu este iniţializată. La sfârşitul operaţiilor,
memoria trebuie eliberată. În cazul alocării cu funcţia malloc,
dealocarea memoriei se face cu funcţia void free(void *p);. Funcţia
eliberează o zonă de memorie de la adresa stocată în variabila p,
zonă alocată în prealabil prin funcţia malloc.
Funcţiile de alocare întorc pointeri generici (void*) la zone
de memorie, în timp ce utilizatorul alocă memorie pentru un anumit
tip de dată. Pentru a putea accesa memoria alocată, indirect, prin
intermediul pointerului, acesta va trebui să fie un pointer cu tip,
ceea ce impune conversia explicită (prin operatorul cast) a
pointerului întors de funcţia de alocare într-un pointer cu tip.
int
int *p;
*p; pp == (int*)
(int*) malloc
malloc (( n*sizeof(int)
n*sizeof(int) );
);
if(p
if(p ==
== NULL)
NULL) {{ printf(„Memorie
printf(„Memorie insuficienta!”);}
insuficienta!”);}
//......
//......
if(
if( pp )) free(
free( pp );
); //
// identic
identic cu
cu if(p!=NULL)
if(p!=NULL)

int
int *p;
*p; pp == new
new int[
int[ nn ];
];
sau if( p == NULL ) { printf(„Memorie
if( p == NULL ) { printf(„Memorie insuficienta!”);}
insuficienta!”);}
//......
//......
if(
if( pp )) delete[]
delete[] p;p;
Notă: Operatorul delete se utilizează urmat de [] doar atunci când
variabila a cărei memorie rezervată urmează a fi eliberată este de
tip vector.
În continuare este prezentată o funcţie de alocarea dinamică
a unei matrice cu elemente de tipul double. Funcţia aloca2double
preia numărul de linii şi coloane a matricei ce urmează a fi
alocate dinamic şi întoarce adresa primului vector (echivalentul
adresei celulei d[0] din exemplul de mai sus):

86
4.Tablouri şi pointeri. Aritmetica pointerilor.

/*Ex.[4.1]*/
/*Ex.[4.1]*/ doubledouble **aloca2double(int
**aloca2double(int m,int m,int n)
n)
{double **A; int
{double **A; int k,i; k,i;
AA == (double**)
(double**) malloc(
malloc( mm ** sizeof(double*)
sizeof(double*) ); );
if( !A ) return
if( !A ) return NULL; NULL;
for(
for( kk == 0; 0; kk << m;
m; k++
k++ ))
{{ A[k]
A[k] = (double*) malloc(
= (double*) malloc( nn ** sizeof(double)
sizeof(double) ); );
if(
if( !A!A )) //alocare
//alocare esuata,
esuata, elibereaza
elibereaza memoria
memoria deja
deja alocată
alocată
{{ for( i = 0; i < k; i++ ) free(
for( i = 0; i < k; i++ ) free( A[i] ); A[i] );
free(
free( AA ); ); return
return NULL;
NULL;
}//endif
}//endif
}//endfor
}//endfor
for(
for( kk == 0; 0; kk << m;
m; k++
k++ ))
for(
for( ii == 0;0; ii << n;
n; i++
i++ )) A[k][i]
A[k][i] == 0;
0;
return A;}
return A;}
Funcţia de dealocare pentru matricea alocată dinamic va avea
forma:
/*Ex.[4.2]:*/
/*Ex.[4.2]:*/ voidvoid elib2double(
elib2double( double**A,
double**A, int
int mm ))
{int
{int k;
k;
for(
for( kk == 0;
0; kk << m;
m; k++
k++ )) if(
if( A[k]
A[k] )) free(
free( A[k]
A[k] ););
if(
if( AA )) free(
free( AA );); }}
Funcţia preia adresa matricei şi numărul de linii. În cadrul
contorului k sunt dealocaţi vectorii care au reuşit să fie alocaţi
dinamic. Utilizarea celor două funcţii este redată în exemplul
următor:
/*
/* ExEx #4.3:
#4.3: */*/ #include
#include <stdio.h>
<stdio.h>
#include
#include <stdlib.h>/*\n*/
<stdlib.h>/*\n*/ #include
#include <time.h>/*\n*/
<time.h>/*\n*/ #include
#include <bfd.h>
<bfd.h>
//declarare
//declarare şi şi explicitare
explicitare funcţie
funcţie alocare
alocare dinamică
dinamică --
-- aloca2double
aloca2double
//elibereaza
//elibereaza zona zona dede memorie
memorie alocata
alocata -- -- elib2double
elib2double
double
double **x;
**x; int
int m1m1 == 3,
3, n1
n1 == 4;
4;
srand(
srand( time(NULL)
time(NULL) ); ); //inţializare
//inţializare generare
generare numere
numere aleatoare
aleatoare
xx == aloca2double(
aloca2double( m1, m1, n1
n1 );
); //apel
//apel functie
functie alocare
alocare dinamica
dinamica
//iniţializarea
//iniţializarea variabilei
variabilei alocate
alocate dinamic
dinamic cu
cu valori
valori aleatoare
aleatoare
for(
for( intint ii == 0;
0; ii << m1;
m1; i++
i++ ))
for(
for( int
int jj == 0;
0; jj << n1;
n1; j++
j++ ))
x[i][j]
x[i][j] == rand()
rand() %% 100;//rand()
100;//rand() generează
generează nr.aleatoare:
nr.aleatoare: 1÷2^16
1÷2^16
for(
for( intint ii == 0;
0; ii << m1;
m1; i++
i++ ))
{{ for(
for( int
int jj == 0;0; jj << n1;
n1; j++
j++ )) printf("
printf(" %f%f ",x[i][j]);
",x[i][j]);
printf("\n");
printf("\n");
}}
elib2double(
elib2double( x, x, m1
m1 );); return
return 0;}0;}
Notă: Fie declaraţia int *a;. Alocarea a=new int; este echivalentă
cu alocarea a=new int[1];, adică dacă nu este vector putem considera
că alocăm memorie pentru un vector cu un singur element. Astfel,
aritmetica pointerilor prezentată în cele ce urmează devine mai
simplă.

87
IV.Introducere în limbajul C

4.4. Aritmetica pointerilor


Datorită faptului că adresele de memorie a elementelor
dintr-un vector sunt consecutive, se pot efectua operaţii cu aceste
adrese în cadrul tabloului. La incrementarea unei variabile
pointer, C/C++ incrementează automat adresa cu o valoare adecvată
(1 octet dacă tipul variabilei la care punctează este char, 2
octeţi pentru tipul de date short, patru octeţi pentru long sau
float etc.), astfel încât pointerul să indice următoarea valoare pe
care o are tipul pointerului (tipul datei la care punctează).
Pentru tipuri de date complexe (structuri, clase) se incrementează
cu dimensiunea structurii sau clasei la care punctează.
Pentru o înţelegere mai bună privind aritmetica pointerilor
sunt redate în continuare câteva exemple comentate.
/*Ex.[4.4]*/
/*Ex.[4.4]*/ #include
#include <stdio.h>
<stdio.h> //biblioteca
//biblioteca pentru
pentru funcţia
funcţia printf
printf
void
void main(void)
main(void)
{{ int
int t[4]
t[4] == {0,
{0, 1,
1, 2,
2, 3};//declaraţie
3};//declaraţie ++ iniţializare
iniţializare ptr.vector
ptr.vector 44 el.el.
int
int *p;
*p;
pp == &t[2];
&t[2]; //// pp preia
preia adresa
adresa celui
celui dede al
al treilea
treilea element
element din
din vect.t
vect.t
pp -=
-= 2;
2; printf("%d\n",
printf("%d\n", *p);*p); /*
/* afisează
afisează valoarea
valoarea lui
lui t[0]
t[0] */
*/
pp +=
+= 1;
1; printf("%d\n",
printf("%d\n", *p);*p); /*
/* afişeazîă
afişeazîă valoarea
valoarea lui
lui t[1]
t[1] */
*/
p++;
p++; printf("%d\n",
printf("%d\n", *p);*p); /*
/* afişează
afişează valoarea
valoarea lui
lui t[2]
t[2] */
*/
(*p)
(*p) +=+= 5;
5; printf("%d\n",
printf("%d\n", *p);
*p); /*/* afisează
afisează valoarea
valoarea lui
lui t[2]+5
t[2]+5 */*/
}}

4.5. Teste
Scrieţi ce se afişează în urma execuţiei următoarelor
instrucţiuni (liniile sunt independente), iar în cazul existenţei
unor erori, să se semnaleze erorile apărute în urma execuţiei şi
sursa acestora. Executaţi codul şi confruntaţi rezultatele afişate
cu cele scrise.
1. int a[2],b[7]={12,23,34,45,56,67,78};
a[0] = *(&b[2] + 1); a[1] = *b; printf(“a[0]=%d a[1]=%d”,a[0],
a[1]);
2. int a[3],c[4][3]={{10,23,34},{11,22,13},{31,42,33},{14,21,31}};
a[0] = 1 + *(&c[0][1] + 1); a[1] = **(&c[1] + 1); a[2]=**c;
printf(“a[0]=%d a[1]=%d a[2]=%d”,a[0],a[1],a[2]);
3. char *str = "ABCDEF"; str[1] = str[1]+1-'C'; str[3]=str[4]-
(str[3]+1); printf(“%s”,str);
4. int a,b[7]={11,13,15,17,19,21,23}; a = *(&b[6] - 6); printf(“a=
%d”,a);
5. int a,c[4][3]={{11,13,15},{21,23,25},{31,33,35},{41,43,45}};
a = *(c[3] - 1) + *(&c[0][1] - 1); printf(“a=%d”,a);
6. int a[2][3][2]={{{10,12},{13,15},{14,16}},{{50,52},{51,53},{54,
56}}},b; b = ***a + *(a[1][2] - 1) + **(a[0] + 1);
printf(“b=%d”, b);
7. char *str="ABCDEFG"; str[3]='G'-!(str[3]-str[1]); str[4]=!str[3]

88
4.Tablouri şi pointeri. Aritmetica pointerilor.

; printf(“%s”,str);
8. int a,b[7]={11,13,15,17,19,21,23}; a = *(&b[7] - 4); printf(“a=
%d”,a);
9. int a,c[4][3]={{11,13,15},{21,23,25},{31,33,35},{41,43,45}};
a = *(&c[0][0] + 2); printf(“a=%d”,a);
10. int a[2][3][2]={{{10,12},{13,15},{14,16}},{{50,52},{51,53},
{54,56}}}, b; b=***(a+1)+*(a[1][0]+1);printf(“b=%d”,b);
11. int a,c[4][3]={{11,13,15},{21,23,25},{31,33,35},{41,43,45}};
a = **(&c[1] - 1) + *(&c[1][2] - 1); cout<<a;
12. int a[2][3][2]={{{10,12},{13,15},{14,16}},{{50,52},{51,53},
{54,56}}}, b; b=*(**a+1)+*(a[1][2]-1)-**a[0]; printf(“b=%d”,b);
13. char *str="ABCDEFG"; str[0]='D'-str[3]+str[1];str[4]=!str[4];
printf(“%s”,str);
14. int a[2][3][2]={{{10,12},{13,15},{14,16}},{{50,52},
{51,53},54,56}}},b; b = *(**(a + 1) + 1) + *a[1][2] - **a[1];
printf(“b=%d”,b);
15. int a,c[4][3]={{11,13,15},{21,23,25},{31,33,35},{41,43,45}};
a=*(&c[3][2]-2)+**(&c[1]+1);printf(“a=%d”,a);
16. int a[2][3][2]={{{10,12},{13,15},{14,16}},{{50,52},
{51,53},54,56}}},b; b = ***a + **(*a + 1); printf(“b=%d”,b);

5. Funcţii. Aria de vizibilitate a variabilelor

5.1. Noţiunea de funcţie


Orice program C/C++ este structurat pe funcţii, astfel nu
vom avea instrucţiuni decât în cadrul funcţiilor. Orice program
C/C++ începe prin execuţia funcţiei main() sau similară. Un program
va avea o singură funcţie main(), indiferent de numărul de fişiere
în care este scris codul. Ieşirea din funcţia main() echivalează cu
ieşirea din program.
Funcţia int main(int argc, char **argv) preia numărul de
argumente (argc) şi lista de argumente (argv). Argumentele pot să
reprezinte lista de opţiuni specifică lansării în execuţie a unei
aplicaţii. Iesirea normală din aplicaţie corespunde cu return 0;.
Din punct de vedere conceptual, funcţia reprezintă o
aplicaţie definită pe o mulţime D (D = mulţimea, domeniul de
definiţie), cu valori în mulţimea C (C = mulţimea de valori,
codomeniul), care îndeplineşte condiţia că oricărui element din D
îi corespunde un unic element din C.
Funcţiile comunică prin lista de argumente. Ele primesc ca
parametri (argumente) datele de intrare, efectuează prelucrările
descrise în corpul funcţiei asupra acestora şi pot returna
rezultatul într-o singură variabilă, care poate fi şi de tip
pointer. În cazul în care funcţia trebuie să întoarcă mai multe
valori, se va utiliza transferul prin referinţă sau va întoarce
adresa unui obiect de tip structură sau clasă ce va conţine toate

89
IV.Introducere în limbajul C

datele. Execuţia programului începe cu funcţia principală, numită


main().
5.2. Structura unei funcţii
antet_funcţie
antet_funcţie
O funcţie este formată din antet şi corp: {corpul_funcţiei
{corpul_funcţiei
}}
Detaliat, structura unei funcţii este:
tip_val_return
tip_val_return nume_func(lista_declaraţiilor_param_formali)
nume_func(lista_declaraţiilor_param_formali)
{{ declaraţii_variabile_locale;
declaraţii_variabile_locale;
instrucţiuni;
instrucţiuni;
return
return valoare/pointer;
valoare/pointer;
}}
Prima linie reprezintă antetul funcţiei, în care se indică:
tipul funcţiei, numele acesteia şi lista declaraţiilor parametri-
lor formali. Dacă funcţia nu întoarce nicio valoare, în locul
tip_vali_return se specifică void.
Lista_declaraţiilor_param_formali (încadrată între paranteze
rotunde) constă într-o listă (enumerare), care conţine tipul şi
identificatorul fiecărui parametru de intrare, despărţite prin
virgulă. Tipul unui parametru poate fi oricare, chiar şi tipul
pointer. Dacă lista parametrilor formali este vidă, în antet, după
numele funcţiei, apar doar parantezele (), sau (void).
Corpul funcţiei este un bloc, care implementează algoritmul
de calcul folosit de către funcţie. În corpul funcţiei apar (în
orice ordine) declaraţii pentru variabilele locale şi instrucţiuni.
Dacă funcţia întoarce o valoare, se foloseşte instrucţiunea return
valoare. La execuţie, la întâlnirea acestei instrucţiuni, se revine
în funcţia apelantă. Într-o funcţie putem avea mai multe
intrucţiuni return, însă doar una din acestea va putea fi
executată.
5.3. Declararea, explicitarea şi apelul funcţiilor
Declaraţia unei funcţii conţine antetul funcţiei şi
informează compilatorul asupra tipului, numelui funcţiei şi a
listei parametrilor formali (în care se poate indica doar tipul
parametrilor formali, nu şi numele acestora). Declaraţiile de
funcţii se numesc prototipuri, şi sunt constituite din antetul
funcţiei, din care pot lipsi numele parametrilor formali (altfel
spus, în cadrul declaraţiilor, opţional, se pot scrie şi denumiri
de variabile, dar acestea sunt ignorate de compilator).
Explicitarea unei funcţii conţine antetul funcţiei şi corpul
acesteia. Nu este admisă definirea/explicitarea unei funcţii în
corpul altei funcţii.

90
5.Funcţii. Aria de vizibilitate a variabilelor

O funcţie poate fi declarată şi explicitată în cadrul


aceluiaşi fişier (dacă este explicitată/definită la începutul
fişierului, nu mai este necesară şi declararea acesteia), sau în
fişiere diferite. O funcţie nu poate fi spartă în mai multe
fişiere.
5.4. Domeniul de vizibilitate al variabilelor
Variabilele pot fi globale - declarate în afara unui bloc de
date (practic, în afara funcţiilor) sau locale – declarate în
cadrul blocurilor de date (blocuri ce se regăsesc în cadrul
funcţiilor). O variabilă globală poate fi vazută în orice funcţie
din fişierul respectiv dacă variabila este declarată înainte de
explicitarea funcţiei.
Variabilele declarate în interiorul unei funcţii, cât şi
parametrii formali ai acesteia nu pot fi accesaţi decât în
interiorul acesteia. Aceste variabile sunt numite variabile locale
şi nu pot fi accesate din alte funcţii. Domeniul de vizibilitate a
unei variabile este porţiunea (zona) de cod la a cărei execuţie
variabila respectivă este accesibilă. Deci, pentru variabilele
locale declarate la începutul funcţiei, domeniul de vizibilitate
este funcţia în care ea a fost definită sau zona de instrucţiuni
din cadrul funcţiei situată după declaraţia acesteia.
Dacă în interiorul unei funcţii există instrucţiuni compuse
(blocuri) care conţin declaraţii de variabile, aceste variabile nu
sunt vizibile în afara blocului.
Dacă înainte de apelul funcţiilor folosite, acestea au fost
definite (explicitate), acestea nu mai trebuiesc declarate la
începutul fişierului sau în fişierele de tip «header». De regulă
prototipurile (declaraţiile) funcţiilor se scriu în fişierele de
tip header, aceste fişiere au de regulă extensia “ *.h”. Utilizarea
unei astfel de funcţii impune doar includerea în program a
header-ului asociat, cu ajutorul directivei preprocesor #include.
Domeniul de vizibilitate a unei variabile sau a unei funcţii
este:
● întregul fişier sursă, dacă declaraţia este în fişier header
inclus prin directiva #include;
● fişierul sursă, după punctul de declaraţie sau explicitare dacă
declaraţia funcţiei sau variabilei apare în afara oricărei
funcţii (la nivel global);
● funcţia sau blocul în care apare declaraţia, după întâlnirea
declaraţiei.
O variabilă globală declarată într-un fişier va putea fi
văzută în alt fişier din cadrul aceluiaşi program numai dacă este
redeclarată tot la începutul noului fişier, dar având cuvântul
cheie extern în faţă.

91
IV.Introducere în limbajul C

În continuare considerăm un exemplu în care avem trei


fişiere cu diverse declaraţii de variabile şi funcţii.
/*
/* fişier
fişier /*fişier
/*fişier temp1.cpp*/
temp1.cpp*/ //
//fişier
fişier temp2.cpp
temp2.cpp
temp2.h
temp2.h */
*/ #include
#include “temp2.h”
“temp2.h” extern
extern int
int q2;
q2;
int static
static int
int a1;
a1; int
int d;//variabila
d;//variabila globală
globală
int int
f5(int);
f5(int); int q1,q2;
q1,q2; int
int f3(int
f3(int a, a, int
int b)
b)
int
int f2(int);
f2(int); {{ int
int m,n;//............
m,n;//............
int
int f1(int
f1(int a,a, int
int b)
b) }}
{{ int
int m,n;
m,n; /*...*/
/*...*/ }} int
int f4(void)
f4(void)
void
void main()
main() {int
{int x,y,d;//........
x,y,d;//........
{int
{int x,y,z1,z2;
x,y,z1,z2; //....
//.... xx == f3(q2,y);
f3(q2,y); yy == f5(q2);//..
f5(q2);//..
z1
z1 = f1(3, 4);
= f1(3, 4); }}
z2
z2 == f2(a1)
f2(a1) -- f5(q1);
f5(q1); int
int f5(int
f5(int d) d)
}} {int
{int a;a; aa == ::d-d;
::d-d; return
return a--;
a--;
int
int f2(int
f2(int d)d) }//prin
}//prin ::d::d sese preia
preia valoarea
valoarea
{{ return
return d++;
d++; }} //din variabila globală
//din variabila globală

Prin declararea static, variabila globală se vede doar în


fisierul respectiv şi nu poatre fi declarată prin cuvântul cheie
extern în alt fişier. Valoarea variabilei de tip static declarată
local la nivelul unei funcţii îşi va menţine valoarea chiar şi după
ieşirea din funcţia respectivă, la o nouă intrare în funcţie ea va
avea ultima valoare.
În cazul variabilelor locale, compilatorul alocă memorie în
momentul întâlnirii declaraţiei din cadrul blocului sau funcţiei în
care acestea sunt declarate. Când execuţia funcţiei sau blocului se
termină, se eliberează memoria pentru acestea (alocare de tip
automatic) şi valorile pentru variabilele locale se pierd.
Dacă variabila este de tip pointer şi a fost alocată dinamic
în interiorul funcţiei, ea va trebui dealocată cu funcţia cores-
punzătoare, altfel ea va ocupa în continuare spaţiul din memorie
chiar şi după ieşirea din funcţie.

6. Transferul parametrilor în cadrul funcţiilor


Funcţiile comunică între ele prin argumente (parametri).
Există următoarele moduri de transfer (transmitere) a parametrilor
către funcţiile apelate:
● Transfer prin valoare;
● Transfer prin adresă;
● Transfer prin referinţă.

6.1. Transferul parametrilor prin valoare


De la programul apelant către funcţia apelată, prin apel, se
transmit valorile parametrilor. Aceste valori vor fi atribuite, la
apel, parametrilor funcţiei. Procedeul de transmitere a para-

92
6.Transferul parametrilor în cadrul funcţiilor

metrilor prin valoare constă în încărcarea valorii parametrilor


efectivi în zona de memorie a parametrilor funcţiei (în stivă). La
apelul unei funcţii, parametrii reali trebuie să corespundă ca
ordine şi tip cu cei din declaraţie.
O modificare a valorii parametrului de intrare, în
interiorul funcţiei (printr-o operaţie din corpul funcţiei), nu va
modifica valoarea parametrului din afara funcţiei, ci doar a copiei
locale a parametrului din cadrul stivei.
/*Ex.6.1(1):transfer
/*Ex.6.1(1):transfer prin prin valoare*/
valoare*/
#include <stdio.h>
#include <stdio.h>
int
int f1(int
f1(int a,
a, int
int b)
b) {a++;
{a++; bb -=
-= 5;
5; return
return aa ++ b;}
b;}
void main()
void main()
{{ int
int x,y,z;
x,y,z;
printf("x=\n");
printf("x=\n"); scanf(“%d”,&x);/*ex:3*/
scanf(“%d”,&x);/*ex:3*/
printf("y=\n");
printf("y=\n"); scanf(“%d”,&y);//ex:4
scanf(“%d”,&y);//ex:4
zz == f1(x,
f1(x, y);
y); printf("z=%d\n",z);//ex:z=7
printf("z=%d\n",z);//ex:z=7
printf("Dupa
printf("Dupa iesire
iesire din
din f1:x=%d";y=%d,x,y);
f1:x=%d";y=%d,x,y); //ex:x=3,y=4
//ex:x=3,y=4
}}

6.2. Transferul parametrilor prin adresă


Parametrii transmişi unei funcţii pot fi pointeri (variabile
care conţin adrese). În aceste cazuri, parametrii funcţiei apelate
vor fi iniţializaţi cu valorile parametrilor efectivi, deci cu
valorile unor adrese. Astfel, funcţia apelată poate modifica
conţinutul locaţiilor spre care pointează argumentele (pointerii).
/*Ex.6.2(1):transfer
/*Ex.6.2(1):transfer prin prin adresă
adresă */
*/ #include
#include <stdio.h>
<stdio.h>
int
int f2(int
f2(int *a,
*a, int
int *b)
*b) {{ (*a)
(*a) ++;
++; (*b)
(*b) -=
-= 5;
5; return
return *a*a ++ *b;
*b; }}
void main()
void main()
{{ int
int x,y,z;
x,y,z; printf("x=\n");
printf("x=\n"); scanf(“%d”,
scanf(“%d”, &x);
&x); //ex:3
//ex:3
printf("y=\n"); scanf(“%d”, &y);
printf("y=\n"); scanf(“%d”, &y); //ex:4 //ex:4
zz == f2(&x,
f2(&x, &y);
&y); printf("z=%d\n",
printf("z=%d\n", z); z); //ex:z=3
//ex:z=3
printf("Dupa
printf("Dupa iesire din f1:x=%d";y=%d, x,
iesire din f1:x=%d";y=%d, x, y);
y); //ex:x=4;y=-1
//ex:x=4;y=-1
}}

6.3. Transferul parametrilor prin referinţă


Este asemănătoare cu transferul prin adresă/pointer cu deo-
sebirea că parametrului funcţiei i se poate asocia (atribui) chiar
obiectul (valoarea variabilei) parametrului transmis. Astfel,
parametrul transmis poate fi modificat direct prin operaţiile din
corpul funcţiei apelate.
Prin referinţă se creează un alias pentru variabila sursă,
alias ce este transmis prin lista de argumente.

93
IV.Introducere în limbajul C

/*Ex.6.3(1)://transfer
/*Ex.6.3(1)://transfer prin prin referinţă
referinţă */
*/ #include
#include <iostream.h>
<iostream.h>
int
int f3(int &a, int &b) {a++; b -= 5; return aa ++ b;
f3(int &a, int &b) {a++; b -= 5; return b; }}
void
void main()
main()
{{ int
int x,y,z;
x,y,z; printf("x=\n");
printf("x=\n"); scanf(“%d”
scanf(“%d” ,&x);
,&x); //ex:3
//ex:3
printf("y=\n");
printf("y=\n"); scanf(“%d”,&y);
scanf(“%d”,&y); //ex:4
//ex:4
zz == f3(x,y);
f3(x,y); printf("z=%d\n",z);
printf("z=%d\n",z); //ex:z=3
//ex:z=3
printf("Dupa
printf("Dupa iesire
iesire din
din f1:x=%d";y=%d,x,y);
f1:x=%d";y=%d,x,y); //ex:x=4;y=-1
//ex:x=4;y=-1
}}
Comparând cele trei moduri de transmitere a parametrilor
către o funcţie, se poate observa:
● La apelul prin valoare, transferul datelor este unidirecţional,
adică valorile se transferă numai de la funcţia apelantă către
cea apelată;
● La apelurile prin adresă şi referinţă, transferul datelor este
bidirecţional, deoarece o modificare a parametrilor funcţiei
determină modificarea parametrilor efectivi (au nume diferite,
dar referă aceleaşi locaţii de memorie);
● La transmiterea parametrilor prin valoare, ca parametri efec-
tivi pot apărea expresii sau nume de variabile;
● La transmiterea parametrilor prin referinţă, ca parametri efec-
tivi nu pot apărea expresii, ci doar nume de variabile;
● La transmiterea parametrilor prin pointeri, ca parametri efec-
tivi pot apărea expresii de pointeri.
În următorul exemplu avem transferul simultan prin cele trei
moduri.
În cazul în care funcţia trebuie să întoarcă mai multe
valori, iar acestea nu pot fi grupate sub o variabilă de tip
pointer (tablouri sau structuri), se poate utiliza transferul prin
referinţă.
/*Ex.6.3(2):Transfer
/*Ex.6.3(2):Transfer mixt mixt */*/ #include
#include <stdio.h>
<stdio.h>
#include
#include <malloc.h>
<malloc.h>
short
short f1(short
f1(short a, a, short
short *b,*b, short
short &c,
&c, short
short d[][2],
d[][2], short
short **e)
**e)
{{ d[0][0]++;
d[0][0]++; d[1][1]++;
d[1][1]++; e[0][0]++;
e[0][0]++; e[1][1]++;
e[1][1]++; //a++;
//a++; (*b)++;
(*b)++; c++;
c++;
return
return aa ++ (*b)
(*b) ++ c;}
c;}
int
int main()
main()
{short
{short x[3][2]
x[3][2] == {{10,20},{30,40},{50,60}},
{{10,20},{30,40},{50,60}}, r, r, **y;
**y;
yy == (short**)
(short**) malloc(
malloc( 33 ** sizeof(short*)
sizeof(short*) ); );
for(int
for(int ii == 0;i
0;i << 3;i++)
3;i++) y[i]=(short*)
y[i]=(short*) malloc(
malloc( 22 ** sizeof(short)
sizeof(short) ); );
for(int
for(int ii == 0;0; ii << 3;
3; i++
i++ ))
for(
for( int
int jj == 0;
0; jj << 2;
2; j++
j++ ))
y[i][j]
y[i][j] == x[i][j];
x[i][j]; //cele
//cele 22 tablouri(x,y)
tablouri(x,y) au au aceleasi
aceleasi valori
valori
rr == f1(
f1( x[0][0],
x[0][0], &x[0][0],
&x[0][0], x[0][0],
x[0][0], x,x, yy );
);
printf(
printf("r=%d
"r=%d x[0][0]=%d
x[0][0]=%d x[1][1]=%d x[2][1]=%d\n"",r,x[0][0],x[1][1],x[2][1])
x[1][1]=%d x[2][1]=%d\n ,r,x[0][0],x[1][1],x[2][1]);;
printf(
printf("y[0][0]=%d
"y[0][0]=%d y[1][1]=%d
y[1][1]=%d y[2][1]=%d\n",
y[2][1]=%d\n", y[0][0],
y[0][0], y[1][1], y[2][1]);;
y[1][1], y[2][1])
return
return 0;}
0;}

94
6.Transferul parametrilor în cadrul funcţiilor

6.4. Funcţii cu număr variabil de argumente


În limbajul C sunt o serie de funcţii din bibliotecile
standard care au un număr variabil de argumente; din acestea cele
mai cunoscute sunt printf şi scanf, acestea având sintaxa:
int
int printf(const
printf(const char
char *format[,
*format[, argument,
argument, ...]);
...]);
int
int scanf(const
scanf(const char
char *format[,
*format[, address,
address, ...]);
...]);
La început vor fi puse argumentele fixe, apoi vor fi trecute
argumentele, ce pot fi în număr variabil. Funcţiile cu număr
variabil de argumente implică utilizarea unui tip de dată va_list
şi a trei macroinstrucţiuni: va_start, va_arg şi va_end. Acestea au
următoarea semnificaţie:
☑ va_list − gestionează tipul de dată din lista de argumente
(de obicei int sau char*);
☑ void va_start(va_list ap, lastfix); − lastfix este ultimul
argument fix din lista de parametri. Această macroinstruc-
ţiune stabileşte de unde începe citirea argumentelor;
☑ type va_arg(va_list ap, type); − întoace valoarea
argumentului curent, la fiecare citire v_arg va avansa (va
puncta) la argumentul următor din lista de argumente a
funcţiei;
☑ void va_end(va_list ap); − va elibera resursele alocate
acestor citiri.
Exemplul următor adună numerele transmise prin argumente şi
le afişează. Citirea se opreşte în momentul în care argumentul are
valoarea zero.
/*Ex.6.4:
/*Ex.6.4: Funcţie
Funcţie cucu număr
număr variabil
variabil dede argumente
argumente */
*/
#include
#include <stdio.h>/*\n*/#include
<stdio.h>/*\n*/#include <stdarg.h>/*\n*/#include
<stdarg.h>/*\n*/#include <string.h>
<string.h>
void
void sum(char
sum(char *msg,
*msg, ...)
...)
{int
{int total
total == 0;
0; va_list
va_list ap;
ap; int
int arg;
arg; char
char sir[100],
sir[100], ch[50];
ch[50];
va_start(ap,
va_start(ap, msg);
msg); strcpy(sir,msg);
strcpy(sir,msg);
while
while ((arg
((arg == va_arg(ap,int))
va_arg(ap,int)) != != 0)
0) {{
sprintf(ch,"%d",arg);
sprintf(ch,"%d",arg);
if(strlen(sir)>strlen("Suma
if(strlen(sir)>strlen("Suma :")) :")) strcat(sir,"
strcat(sir," ++ ");
");
strcat(sir,ch);
strcat(sir,ch); total
total +=
+= arg;
arg; }}
printf("
printf(" %s
%s == %d\n",
%d\n", sir,
sir, total);
total); va_end(ap);}
va_end(ap);}
int
int main(void)
main(void) {{ sum("Suma
sum("Suma :",1,2,3,4,0);
:",1,2,3,4,0); return
return 0;
0; }}
Funcţia sprintf converteşte o variabilă dintr-un anumit tip
de dată (în acest caz tipul int) în şir de caractere (char*).
Funcţia strlen întoarce numărul de caractere din şir, iar funcţia
strcat(char* destinaţie, char* sursă) concatenează (adaugă) şirul
sursă în şirul destinaţie.
Acest mecanism se poate utiliza cu succes pentru trans-
miterea adreselor unor obiecte grafice.

95
IV.Introducere în limbajul C

7. Structuri de date. Liste

7.1. Noţiunea de structură


Structurile grupează date de tipuri diferite, constituind
definiţii ale unor noi tipuri de date. Componentele unei structuri
se numesc membrii (câmpurile) structurii. La declararea unei
structuri se pot preciza tipurile, identificatorii elementelor
componente şi numele structurii.
Forma generală de declarare a unei structuri este
struct
struct identificator_tip_structura
identificator_tip_structura {{
lista_de_declaratii_membrii;
lista_de_declaratii_membrii;
};
};
în care:
● struct este un cuvânt cheie (obligatoriu);
● identificator_tip_structura reprezintă numele noului tip (poate
lipsi);
● lista_de_declaratii_membri este o listă în care apar tipurile
şi identificatorii membrilor structurii.
Membrii unei structuri pot fi de orice tip, cu excepţia
tipului structură (struct), care se declară.
struct
struct data_c
data_c {int
{int zi;
zi; char
char luna[12];
luna[12]; int
int an;};
an;};
//..............
//..............
struct
struct data_c
data_c d1,*d2;
d1,*d2; d1.zi=4;d1->zi
d1.zi=4;d1->zi == 5;d1.an
5;d1.an == 1968;d2->an
1968;d2->an == 1969;
1969;
strcpy(d1.luna,”aprilie”);
strcpy(d1.luna,”aprilie”); strcpy(d2->luna,”mai”);
strcpy(d2->luna,”mai”);

7.2. Declaraţii de tip


Limbajul C permite atribuirea
typedef
typedef struct
struct NOD{
NOD{
unui nume pentru un tip (predefinit sau //.............
utilizator) de date. Pentru aceasta se //.............
NOD
NOD *urm,*ant;
*urm,*ant;
folosesc declaraţiile de tip. Forma }} NOD;
NOD;
generală a acestora este: NOD
NOD *cap,
*cap, *p;
*p;
typedef tip nume_tip;
typedef tip nume_tip; Nume_tip poate
fi folosit la declararea datelor în mod typedef
typedef struct{
struct{
similar cuvintelor cheie pentru double
double parte_reală;
parte_reală;
typedef int INTREG;
tipurile predefinite typedef int INTREG;. double
double parte_imaginară;
parte_imaginară;
INTREG
INTREG x,
x, y;
y; }} COMPLEX;
COMPLEX;
Uneori este necesar ca în inte- COMPLEX
COMPLEX x,
x, y;
y;
riorul unei structuri să avem variabile
de acelaşi tip cu structura (de exemplu, nodurile unei liste). În
acest caz, identificator_tip_structura se va repeta după închiderea
corpului/blocului structurii ca în exemplul din dreapta.

96
7.Structuri de date. Liste

7.3. Liste
Lista este o colecţie de elemente (celule) înlănţuite ce
formează o structură dinamică, situată în memoria dinamică, în care
toate elementele sunt de acelaşi tip; numărul de elemente este
variabil, chiar nul (listă vidă, pointează la NULL – adică adresa
primei celule din listă este NULL, adică nu există).
Altfel spus, lista este o secvenţă de zero sau mai multe
elemente, numite noduri, toate fiind de acelaşi tip. Spre deo-
sebire, tabloul este o structură statică, situată în memoria
dinamică.
Un nod al listei apare ca o structură recursivă, având o
componentă de tip pointer la structura, reprezentând legătura
(înlănţuirea) spre nodul următor. Fiecare element (celulă/nod) din
cadrul listei înlănţuite are o structură de tipul:
→→ Informaţie
Informaţie utilă;
utilă;
→→ Informaţie
Informaţie de
de înlănţuire
înlănţuire către
către elementul
elementul (celula)
(celula) următor;
următor;
//pentru
//pentru listele
listele dublu
dublu înlănţuite
înlănţuite
→→ Informaţie
Informaţie de
de înlănţuire
înlănţuire către
către elementul
elementul (celula)
(celula) anterior;
anterior;
Dacă în cadrul elementului/celulei/nodului se face referire
doar la elementul/celula/nodul următor atunci avem liste simplu
înlănţuite. Dacă se face şi referire la nodul anterior atunci avem
liste dublu înlănţuite.
Structura unui element/celulă/nod este:
ADRESA ADRESA
DATE
NODULUI NODULUI
NOD
ANTERIOR URMĂTOR
Figura IV-8: Structura unui nod
Înlănţuirea celulelor este prezentată în diagrama:
CAP
LISTĂ
Figura IV-9: Listă dublu înlănţuită
Pentru lucrul cu liste simplu înlănţuite este suficient să
se cunoască adresa capului listei, iar pentru listele dublu
înlănţuite se poate ajunge la orice nod dacă se cunoaşte adresa
unui singur nod din listă, oricare ar fi acesta.
7.3.1. Crearea unei liste dublu înlănţuite
Este necesară alocarea dinamică de memorie pentru orice nod
nou creat. Alocarea se poate face atât cu funcţia malloc, cât şi cu
operatorul new. Ştergerea elementelor din listă trebuie să asigure
şi dealocarea (eliberarea) memoriei corespunzătoare. Aceasta se
realizează cu funcţiia free, respectiv operatorul delete.

97
IV.Introducere în limbajul C

cap
cap == new
new NOD;
NOD; //crearea
//crearea capului
capului listei
listei sau
sau
//
// (cap=(NOD*)
(cap=(NOD*) malloc(sizeof(NOD));)
malloc(sizeof(NOD));)
memset(
memset( cap,
cap, 0,
0, sizeof(NOD)
sizeof(NOD) ); ); /*/* asigură
asigură iniţializarea
iniţializarea cu
cu val.0
val.0
respectiv
respectiv val.
val. NULL
NULL pentru
pentru pointeri
pointeri */
*/
//........................
//........................
//presupunem:
//presupunem: pp ultimul
ultimul nod
nod al
al listei
listei
p1
p1 == new
new NOD;
NOD; memset(cap,
memset(cap, 0, 0, sizeof(NOD)
sizeof(NOD) ); );
p->urm
p->urm == p1;
p1; p1->ant
p1->ant == p;
p; pp == p1;
p1;
Este obligatoriu ca la cele două capete cap->ante şi p->urm
să aibă valoarea NULL (p fiind ultimul nod din listă).
Dacă p->urm == cap şi cap->ante == p, atunci avem listă
circulară.
7.3.2. Parcurgerea unei liste
Presupunem că în acest caz variabila p este un pointer de
tip NOD folosit la parcurgerea listei.
//.......................
//.......................
. for(
for( pp == cap;
cap; p->urm;
p->urm; pp == p->urm
p->urm );
);
Bucla for execută o instrucţiune vidă, identificată prin
semicolon (;). Avansul se opreşte pe ultimul nod;
De exemplu, presupunem că avem o listă ale cărei noduri au
structura:
/*Ex.7.3.2(1)*/
/*Ex.7.3.2(1)*/
typedef
typedef struct
struct NOD2{
NOD2{ int
int id;
id; NOD2
NOD2 *urm,*ant;
*urm,*ant; }NOD2;
}NOD2;
Funcţia pentru crearea unei liste cu n noduri şi care
întoarce primul element al listei va fi:
/*Ex.7.3.2(2):
/*Ex.7.3.2(2): crearea
crearea unei
unei liste
liste cu
cu nn noduri,
noduri, întoarce
întoarce adresa
adresa capului
capului
listei*/
listei*/ NOD2*
NOD2* fc(int
fc(int n) n)
{NOD2
{NOD2 *p,
*p, *p1,
*p1, *caplista;
*caplista; pp == NULL;
NULL;
for(
for( int
int ii == 0;
0; ii << n;
n; i++
i++ ))
{{ p1
p1 == p;
p; pp == (NOD2*)
(NOD2*) malloc
malloc (( sizeof(NOD2)
sizeof(NOD2) ); );
if(
if( !p
!p )) return
return NULL;
NULL; //// eventual
eventual sese va
va realiza
realiza şi
şi dealocarea
dealocarea
memset(
memset( p,p, 0,0, sizeof(NOD2)
sizeof(NOD2) ); );
p->id
p->id == ii ++ 1000;
1000; // // ptr.
ptr. oo viitoare
viitoare identificare
identificare aa nodului
nodului
if(
if( !i
!i )) caplista
caplista == p; p;
else
else {{ p1->urm
p1->urm == p; p; p->ant
p->ant == p1;
p1; }}
}} return
return caplista;
caplista; }}
Dacă dorim să obţinem adresa ultimei celule/nod a/al listei,
utilizăm funcţia următoare:
/*Ex.7.3.2(3):parcurgerea
/*Ex.7.3.2(3):parcurgerea listeilistei cucu poziţionare
poziţionare pe
pe ultimul
ultimul nod
nod
(întoarce
(întoarce adresa
adresa ultimului
ultimului nod
nod din
din cadrul
cadrul listei)*/
listei)*/
NOD2*
NOD2* fp1(
fp1( NOD2
NOD2 *caplista
*caplista ))
{{ NOD2
NOD2 *p;
*p;
for(
for( pp == caplista;
caplista; p->urm;
p->urm; pp == p->urm
p->urm );//p->urm
);//p->urm sau
sau p->urm==NULL
p->urm==NULL
return
return p;}
p;}

98
7.Structuri de date. Liste

Dacă vrem ca pentru fiecare nod să avem anumite prelucrări,


utilizăm:
/*Ex.7.3.2(4):parcurgerea
/*Ex.7.3.2(4):parcurgerea listei listei cu
cu prelucrare
prelucrare pentru
pentru fiecare
fiecare nod*/
nod*/
void
void fp2
fp2 (NOD2
(NOD2 *caplista
*caplista ))
{{ NOD2
NOD2 *p;
*p;
for(
for( pp == caplista;
caplista; p;
p; pp == p->urm
p->urm )) {{ p->id
p->id == -p->id;
-p->id; }} //ex:
//ex: id=-id
id=-id
return;
return; }}
Pentru afişarea listei, avem:
/*Ex.7.3.2(5):
/*Ex.7.3.2(5): afişare
afişare listă
listă */*/
void
void afis(
afis( NOD2
NOD2 *caplista
*caplista ))
{{ NOD2
NOD2 *p;
*p; int
int i;
i; printf("\n");
printf("\n");
for(
for( pp == caplista,
caplista, ii == 0;
0; p;
p; pp == p->urm,
p->urm, i++
i++ ))
printf("
printf(" [%d]:%d",i,p->id);
[%d]:%d",i,p->id);
return;
return; }}
Dacă vrem să eliminăm din listă un nod a cărui adresă de
memorie este cunoscută:
/*Ex.7.3.2(6):stergerea
/*Ex.7.3.2(6):stergerea unui unui nod
nod din
din lista
lista dat
dat prin
prin adresa*/
adresa*/
void
void fd1(NOD2
fd1(NOD2 *d)*d)
{{ if(
if( d->urm
d->urm )) //nu//nu este
este ultimul
ultimul
{{ d->ant->urm
d->ant->urm == d->urm;
d->urm; d->urm->ant
d->urm->ant == d->ant;
d->ant; }}
else {d->ant->urm = NULL;}
else {d->ant->urm = NULL;}
free(
free( dd );
); }}
Iar dacă nodul ce va fi eliminat din memorie are un identi-
ficator cunoscut:
/*Ex.7.3.2(7):stergerea
/*Ex.7.3.2(7):stergerea unui unui nod
nod din
din lista
lista dat
dat prin
prin id*/
id*/
void fd2( int id, NOD2 caplista
void fd2( int id, NOD2 caplista ) )
{{ NOD2
NOD2 *p;
*p;
for(
for( pp == caplista;
caplista; p->id
p->id !=
!= id;
id; pp == p->urm
p->urm )) ;;
if(
if( p->id
p->id ==== id
id )) fd1(p);
fd1(p); }}
Pentru adăugarea unui nod la sfârşitul listei:
/*Ex.7.3.2(8):adaugarea
/*Ex.7.3.2(8):adaugarea unui unui nodnod (la
(la sfarsitul
sfarsitul listei)*/
listei)*/
void
void fa1(
fa1( NOD2
NOD2 *caplista,
*caplista, NOD2
NOD2 aa ))
{{ NOD2
NOD2 *p;
*p;
pp == fp1(
fp1( caplista
caplista );
); //pozitionare
//pozitionare pe
pe ultimul
ultimul nod
nod
p->urm
p->urm == a;a; a->ant
a->ant == p;
p; }}
Dacă vrem să adăugăm un nod în listă după un anumit nod, a
cărui adresă este cunoscută:
/*Ex.7.3.2(9):inserează
/*Ex.7.3.2(9):inserează nodnod (cu
(cu excepţia
excepţia primei
primei şi
şi ultimei
ultimei poziţii)
poziţii)
se
se dă
dă nodul
nodul anterior*/
anterior*/
void
void fi1(
fi1( NOD2
NOD2 *anterior,
*anterior, NOD2
NOD2 *ins
*ins ))
{{ ins->ant
ins->ant == anterior;
anterior; ins->urm
ins->urm == anterior->urm;
anterior->urm;
anterior->urm->ant
anterior->urm->ant == ins;
ins; anterior->urm
anterior->urm == ins;
ins; }}

99
IV.Introducere în limbajul C

Iar dacă vrem să adăugăm un nod în listă, după un anumit nod


al cărui identificator este cunoscut:
/*Ex.7.3.2(10):inserează
/*Ex.7.3.2(10):inserează nod nod -se
-se dă
dă id-ul
id-ul nodului
nodului anterior*/
anterior*/
void
void fi2(
fi2( int
int anterior_id,
anterior_id, NOD2
NOD2 *ins,
*ins, NOD2
NOD2 *caplista
*caplista ))
{{ NOD2
NOD2 *p;
*p;
for(
for( pp == caplista;
caplista; p->id
p->id !=
!= anterior_id;
anterior_id; pp == p->urm
p->urm )) ;;
if( p->id == anterior_id ) fi1(p,
if( p->id == anterior_id ) fi1(p, ins); }ins); }
Programul complet este dat de secvenţa următoare (în
prealabil se vor înlocui comentariile de tipul /*Ex.:...):
/*Ex.7.3.2(11):Exemplu
/*Ex.7.3.2(11):Exemplu liste liste */
*/
#include
#include <stdio.h>/*\n*/#include
<stdio.h>/*\n*/#include <malloc.h>/*\n*/#include
<malloc.h>/*\n*/#include <string.h>
<string.h>
/*se
/*se copiază
copiază codul
codul din
din exemplele:7.3.2(1),7.3.2(2),7.3.2(3),7.3.2(4),
exemplele:7.3.2(1),7.3.2(2),7.3.2(3),7.3.2(4),
7.3.2(5),7.3.2(6),7.3.2(7),7.3.2(8),7.3.2(9),7.3.2(10)*/
7.3.2(5),7.3.2(6),7.3.2(7),7.3.2(8),7.3.2(9),7.3.2(10)*/
int
int main(int
main(int argc,
argc, char
char **argv)//argc-nr
**argv)//argc-nr argumente,
argumente, argv-lista
argv-lista de de arg.
arg.
{NOD2
{NOD2 *c1,
*c1, *x,
*x, *y;
*y;
c1
c1 == fc(10);
fc(10); xx == fp1(c1);
fp1(c1); printf("\nUltimul
printf("\nUltimul nod:%d",x->id);//fp2(c1);
nod:%d",x->id);//fp2(c1);
//insereaza
//insereaza nodnod
yy == (NOD2*)
(NOD2*) malloc(
malloc( sizeof(NOD2)
sizeof(NOD2) ); );
memset(y
memset(y ,0,0 ,sizeof(NOD2)
,sizeof(NOD2) ); ); y->id
y->id == 999;
999; fi2(
fi2( 1004
1004 ,y
,y ,c1
,c1 );
);
afis(c1);
afis(c1); return
return 0;0; }}

100
7.Structuri de date. Liste

8. Funcţii recursive. Parcurgerea arborilor.


O funcţie este numită funcţie recursivă dacă ea se
autoapelează, fie direct (în definiţia ei se face apel la ea
însăşi), fie indirect (prin apelul altor funcţii). Pentru fiecare
apel al funcţiei, parametrii şi variabilele automatice se memorează
pe stivă, având valori distincte. Variabilele statice ocupă tot
timpul aceeaşi zonă de memorie (figurează într-un singur exemplar)
şi îşi păstrează valoarea de la un apel la altul. Orice apel al
unei funcţii conduce la o revenire în funcţia respectivă, în
punctul următor instrucţiunii de apel. La revenirea dintr-o
funcţie, stiva este curăţată (stiva revine la starea dinaintea
apelului).
Un exemplu de utilizare a funcţiilor recursive este cel de
parcurgere a arborilor binari. În acest sens se dă structura unui

/*Ex.8(1):
/*Ex.8(1): structura
structura arbore
arbore binar*/
binar*/
typedef struct NOD{
typedef struct NOD{
int
int id;
id; //identificator
//identificator nod
nod
NOD
NOD *st,*dr; //adresele
*st,*dr; //adresele nodurilor
nodurilor -- stânga,
stânga, dreapta
dreapta
}NOD;
}NOD;
Pentru început este creat un arbore cu şapte noduri. În
acest sens se alocă şapte adrese la structuri de tip NOD şi apoi se
realizează legăturile între aceste noduri.
/*Ex.8(2):creare
/*Ex.8(2):creare arbore
arbore binar*/
binar*/
NOD*
NOD* creare_arbore(void)
creare_arbore(void)
{NOD
{NOD *p[7];
*p[7]; //alocare
//alocare memorie
memorie
for(
for( int
int ii == 0;
0; ii << 7;
7; i++
i++ ))
{{ p[i]
p[i] == (NOD*)
(NOD*) malloc(
malloc( sizeof(NOD)
sizeof(NOD) );
);
memset(
memset( p[i],0,sizeof(NOD)
p[i],0,sizeof(NOD) ); ); p[i]->id
p[i]->id == ii ++ 1;}
1;}
p[0]->st=p[1];
p[0]->st=p[1]; p[0]->dr=p[2];
p[0]->dr=p[2]; p[2]->st=p[3];
p[2]->st=p[3];
p[2]->dr=p[4];
p[2]->dr=p[4]; p[3]->st=p[5];
p[3]->st=p[5]; p[3]->dr=p[6];
p[3]->dr=p[6];
return
return p[0];
p[0]; }//întoarce
}//întoarce rădăcina
rădăcina arborelui
arborelui
Funcţia de parcurgere a arborelui este o funcţie recursivă
şi se opreşte în momentul în care ajunge la nodurile frunză
(adresele următoare sunt NULL)
/*Ex.8(3):parcurge
/*Ex.8(3):parcurge arbore
arbore binar*/
binar*/
void
void parcurge(NOD
parcurge(NOD *p)*p) //preia
//preia rădăcina
rădăcina arborelui
arborelui curent
curent
{{ printf("-%d-", p->id);
printf("-%d-", p->id);
//if(p->st)
//if(p->st) parcurge(p->st);
parcurge(p->st); //dacă
//dacă începe
începe din
din stânga
stânga
if( p->dr ) parcurge( p->dr
if( p->dr ) parcurge( p->dr ); );
if(
if( p->st
p->st )) parcurge(
parcurge( p->st
p->st );
);
return;}
return;}

101
IV.Introducere în limbajul C

Căutarea unui anumit nod ce este identificat prin id se face


prin funcţia:
/*Ex.8(4):întoarce
/*Ex.8(4):întoarce adresa
adresa nodului
nodului cucu id-ul
id-ul transmis*/
transmis*/
NOD*
NOD* adresa_nod(
adresa_nod( intint id,
id, NOD
NOD *p
*p ))
{{ NOD
NOD *x
*x == NULL;
NULL; printf("#%d#",p->id);
printf("#%d#",p->id);
if(
if( p->id
p->id ==== id
id )) return
return p;
p;
if(p->st)
if(p->st) x = adresa_nod(id, p->st);
x = adresa_nod(id, p->st);
if(x)
if(x) return
return x;x; if(p->dr)
if(p->dr) xx == adresa_nod(id,
adresa_nod(id, p->dr);
p->dr);
if(x)
if(x) return x; return NULL; }}
return x; return NULL;
Ştergearea întregului arbore este asigurată de funcţia:
/*Ex.8(5):*/
/*Ex.8(5):*/
void
void sterge_arb(NOD
sterge_arb(NOD *p)*p)
{{ if(p->st)
if(p->st) sterge_arb(p->st);//avansează
sterge_arb(p->st);//avansează pe pe ramura
ramura din
din stânga
stânga
if(p->dr)
if(p->dr) sterge_arb(p->dr);
sterge_arb(p->dr);
free(p);
free(p); }} //ştergerea
//ştergerea începe
începe doar
doar când
când aa ajuns
ajuns la
la nodurile
nodurile
termiale
termiale (la
(la nodurile
nodurile frunză)
frunză)
Ştergerea legăturii dintre un nod părinte şi un nod fiu este
asigurată de funcţia:
/*Ex.8(6):
/*Ex.8(6): şterge
şterge legătura
legătura dintre
dintre nodul
nodul părinte
părinte şi
şi nodul
nodul pdel
pdel */
*/
void
void sterge_legatura(NOD
sterge_legatura(NOD *p,NOD
*p,NOD *pdel)
*pdel)
{{ if(p->dr==pdel)
if(p->dr==pdel) p->dr=NULL;
p->dr=NULL; else
else sterge_legatura(p->dr,
sterge_legatura(p->dr, pdel);
pdel);
if(p->st==pdel)
if(p->st==pdel) p->st=NULL;
p->st=NULL; else
else sterge_legatura(p->st,
sterge_legatura(p->st, pdel);
pdel);
}}
Ştergerea unui subarbore din cadrul arborelui este asigurată
de funcţia:
/*Ex.8(7):ştergerea
/*Ex.8(7):ştergerea unui unui subarbore*/
subarbore*/
void
void sterge_subarb(int
sterge_subarb(int id, id, NOD
NOD *p)
*p)
{{ NOD*
NOD* x;x; //în
//în final
final se
se va
va marca
marca cu
cu NULL
NULL legătura
legătura către
către subarbore
subarbore
xx == adresa_nod(id,p);//identificare
adresa_nod(id,p);//identificare adresa adresa nod
nod
sterge_arb(x);//şterge
sterge_arb(x);//şterge legătura
legătura dede la
la părinte
părinte la
la nodul
nodul xx
}}
Programul principal pentru testare:
/*Ex.8(8):programul
/*Ex.8(8):programul principal
principal pentru
pentru testare*/
testare*/
#include
#include <stdio.h>/*\n*/
<stdio.h>/*\n*/ #include
#include <string.h>/*\n*/#include
<string.h>/*\n*/#include <malloc.h>
<malloc.h>
/*se
/*se copiază
copiază codul
codul din
din exemplele:8(1),8(2),8(3),8(4),8(5),8(6),8(7)*/
exemplele:8(1),8(2),8(3),8(4),8(5),8(6),8(7)*/
int
int main()
main()
{{ NOD
NOD *rad,*x;
*rad,*x;
rad
rad == creare_arbore();
creare_arbore(); parcurge(rad);
parcurge(rad);
xx == adresa_nod(4,rad);
adresa_nod(4,rad); printf("\n
printf("\n id
id ptr.
ptr. id:4
id:4 este:
este: %d\n",
%d\n", x->id);
x->id);
sterge_subarb(4,rad);
sterge_subarb(4,rad); parcurge(rad);
parcurge(rad);
return
return 0;}
0;}
Aceste funcţii pot fi extinse asupra arborilor oarecare prin
utilizarea unui vector cu pointeri la structuri NOD în locul celor
două variabile (st şi dr). Vectorul cu pointeri la tip NOD va avea

102
8.Funcţii recursive. Parcurgerea arborilor.

ultimul element egal cu NULL pentru a opri căutarea în cadrul


vectorului.

9. Funcţii uzuale din bibliotecile C. Lucrul cu fişiere text

9.1. Operaţii cu şiruri de caractere


Vectorii (tablourile unidimensionale) ce conţin date de
tipul char au un statut aparte. Spaţiul de memorie asociat
variabilelor acestui tip este 1 octet; un caracter se memorează ca
un număr întreg, corespunzător codului ASCII al caracterului
respectiv. Prin urmare, în C, un caracter poate să apară în
operaţii numerice. Valorile acestui tip sunt cuprinse în intervalul
[–128...127] în cazul caracterelor cu semn, respectiv [0...255] în
cazul celor fără semn (corespunzător setului de caractere ASCII
EXTINS).
Orice şir de caractere se termină prin 0 numeric. Altfel
spus, un vector de caractere ce conţine pe ultima poziţie
caracterul cu codul 0 este un şir de caractere. Dacă într-un şir de
caractere setăm sau introducem codul 0, atunci şirul de caractere
se va termina în acel punct.
Biblioteca ce conţine funcţiile pentru şiruri de caractere
este dată de header-ul <string.h>. În continuare este prezentat un
exemplu cu câteva funcţii specifice şirurilor de caractere:
/*Ex.9.1(1):*/
/*Ex.9.1(1):*/ #include
#include <stdio.h>/*\n*/
<stdio.h>/*\n*/ #include
#include <string.h>
<string.h>
void
void f_siruri()
f_siruri()
{{ char
char b[50],
b[50], a[50]="Un
a[50]="Un şir
şir finit!";
finit!"; printf("şirul
printf("şirul a: a: %s\n",a);
%s\n",a);
strcpy(b,
strcpy(b, a);
a); //copiază
//copiază şirul
şirul aa în
în şirul
şirul b;
b;
printf("1.şirul
printf("1.şirul b: b: %s\n",b);
%s\n",b);
strcpy(
strcpy( b,b, &b[7]
&b[7] );
); b[6]=9;
b[6]=9; printf("2.şirul
printf("2.şirul b: b: %s\n",b);
%s\n",b);
strcpy( &a[7],"în"); strncat(a,b,1000);//concatenare
strcpy( &a[7],"în"); strncat(a,b,1000);//concatenare
}} //
// funcţia
funcţia strncat
strncat nu
nu adaugă
adaugă terminatorul
terminatorul de de şir
şir (codul
(codul 0)
0)
int main()
int main() { f_siruri(); return
{ f_siruri(); return 0;}0;}
Funcţiile utilizate au sintaxa:
● strcpy(char*
strcpy(char* destinatie,
destinatie, char*
char* sursa);
sursa); copiază şirul sursă în
destinaţie, dacă şirul destinaţie există acesta este înlocuit;
● strcat(char*
strcat(char* destinatie,char*sursa)
destinatie,char*sursa) concatenează la şirul
destinaţie şirul sursă;
● strncat(char* destinatie,char*sursa,int nr_caractere) adaugă la
strncat(char* destinatie,char*sursa,int nr_caractere)
şirul destinaţie primele nr_caractere din şirul sursă. Nu
adaugă automat la sfârşitul şirului terminatorul de şir (0),
altfel atunci când citim şirul există riscul să citim
informaţii rămase în memoria RAM până la întâlnirea valorii 0

103
IV.Introducere în limbajul C

pentru un octet din memorie.


De asemenea există funcţii ce permite conversia din şiruri
de caractere într-un număr de tip int sau double şi invers.
/*Ex:9.1(2):*/
/*Ex:9.1(2):*/
#include
#include <stdio.h>/*\n*/
<stdio.h>/*\n*/ #include
#include <stdlib.h>/*\n*/#include
<stdlib.h>/*\n*/#include <string.h>
<string.h>
void
void f_conversii()
f_conversii()
{{ char
char str[50];
str[50]; double
double x;
x; intint s;
s;
strcpy(str,"55");
strcpy(str,"55"); ss == atoi(str);
atoi(str); //ascii
//ascii toto integer
integer
printf("s=%d\n",s);
printf("s=%d\n",s);
strcpy(str,"899.457");
strcpy(str,"899.457"); xx == atof(str);
atof(str); //ascii
//ascii toto float
float
printf("x=%f\n",x);
printf("x=%f\n",x);
//invers
//invers –– din
din int
int şi
şi double
double în
în şir
şir de
de caractere
caractere
sprintf(str,"Un
sprintf(str,"Un sirsir double:%f
double:%f int:%d\n",x,s);
int:%d\n",x,s);
printf("Sirul:%s\n",str);
printf("Sirul:%s\n",str); }}
int
int main()
main() {{ f_conversii();
f_conversii(); returnreturn 0;}
0;}

9.2. Operaţii cu funcţii din biblioteca matematică


Biblioteca matematică este adăugată prin directiva
#include<math.h> şi utilizează tipul de dată double. În exemplul
următor sunt enumerate câteva funcţii din această bibliotecă:
/*Ex.9.2(1):*/
/*Ex.9.2(1):*/
#include
#include <math.h>
<math.h> /*\n*/#include
/*\n*/#include <stdio.h>/*\n*/#include
<stdio.h>/*\n*/#include <string.h>
<string.h>
main(){
main(){ char
char str[10];
str[10];
double
double aa == 0.5,
0.5, bb == 3,
3, c;
c;
cc == pow(a,b);
pow(a,b); printf(“pow(%f,%f)=%f\n”,
printf(“pow(%f,%f)=%f\n”, a,a, b,
b, c);
c);
cc == sqrt(b);
sqrt(b); printf(“sqrt(%f)=%f\n”,
printf(“sqrt(%f)=%f\n”, b,
b, c);
c);
cc == exp(b);
exp(b); printf(“exp(%f)=%f\n”,
printf(“exp(%f)=%f\n”, b,
b, c);
c);
cc == log(
log( exp(b)
exp(b) );printf(“log(exp(%f))=%f\n”,
);printf(“log(exp(%f))=%f\n”, b, b, c);
c);
strcpy(str,”15.9”);
strcpy(str,”15.9”);
cc == atof(c);
atof(c); printf(“atof(%f)=%f\n”,
printf(“atof(%f)=%f\n”, b,
b, c);
c); //ascii
//ascii to
to float
float
return
return 0;}
0;}
şi un alt exemplu:
/*Ex.9.2(2):*/
/*Ex.9.2(2):*/ #include
#include <math.h>/*\n*/#include
<math.h>/*\n*/#include <stdio.h>
<stdio.h>
#include
#include <string.h>
<string.h>
void
void functii_biblmath()
functii_biblmath()
{{ double
double aa == 25,
25, bb == 0.5,
0.5, c;
c;
cc == pow(a,b);
pow(a,b); printf("pow(%.2f,%.2f)=%.2f\n",
printf("pow(%.2f,%.2f)=%.2f\n", a, a, b,
b, c);
c);
cc == asin(b);
asin(b); printf("arcsin(%.2f)=%.2f\n",
printf("arcsin(%.2f)=%.2f\n", b, b, c);
c);
cc == sqrt(a);
sqrt(a); printf("sqrt(%.2f)=%.2f\n",
printf("sqrt(%.2f)=%.2f\n", a, a, c);
c);
}}
int
int main()
main() {{ functii_biblmath();
functii_biblmath(); return
return 0;
0; }}
Funcţia double
double pow(double
pow(double a,
a, double b); calculează a . Pentru
double b);
b

b = 0.5 obţinem funcţia sqrt, iar pentru a = e obţinem funcţia exp.

104
9.Funcţii uzuale din bibliotecile C. Lucrul cu fişiere text

9.3. Operaţii cu fişiere


Bibliotecile din C oferă funcţii atât pentru lucrul cu
fişiere binare, cât şi funcţii pentru lucrul cu fişiere de tip
text.
Fişierele de tip binar păstrează informaţiile la nivel de
bit exact cum au fost scrise. Funcţii de tipul write scriu în
fişier blocuri cu date în format binar, iar datele sunt citite pe
blocuri cu funcţii de tipul read.
Fişierele de tip text interpretează caractere precum spaţiu
sau linie nouă. Pentru a scrie într-un fişier sau pentru a citi
dintr-un fişier, este necesară deschiderea acestuia cu ajutorul
unei funcţii. După utilizare, fişierul se închide cu ajutorul unei
alte funcţii. Atât operaţiile de citire, cât şi operaţiile de
scriere setează o variabilă internă de poziţie în cadrul fişierului
(iniţializată în momentul deschiderii fişierului) cu 1L (valoarea
unu de tip unsigned long). Pe măsură ce datele sunt scrise sau
citite, această variabilă este actualizată automat. Când această
variabilă ajunge pe ultima poziţie din cadrul fişierului, este
activat un flag (de obicei o variabilă de tip boolean), ce
semnifică faptul că s-a ajuns la sfârşitul fişierului.
/*Ex.9.3(1):*/
/*Ex.9.3(1):*/ #include
#include <stdio.h>/*\n*/
<stdio.h>/*\n*/ #include
#include <stdlib.h>
<stdlib.h>
void
void fisiere_text()
fisiere_text()
{{ FILE
FILE *fis;
*fis; int
int x=0;
x=0; char
char s[100];
s[100];
if(
if( (fis
(fis == fopen("un_test.txt","wt")
fopen("un_test.txt","wt") )) == == NULL)//wt-scriere+citire
NULL)//wt-scriere+citire
{{ printf("Fisierul
printf("Fisierul nu nu aa putut
putut fi
fi deschis!");return;}
deschis!");return;}
fprintf(fis,"
fprintf(fis," Un Un text
text \n");
\n");
fprintf(fis,"Fisierul
fprintf(fis,"Fisierul este este de
de tip
tip text\n
text\n Un
Un numar
numar intreg:
intreg: 35");
35");
fseek(fis,0L,SEEK_SET);
fseek(fis,0L,SEEK_SET);
int
int mm == ftell(fis);
ftell(fis); printf("\n
printf("\n pozitia1:%d",m);//poziţie
pozitia1:%d",m);//poziţie cursor
cursor
fseek(fis,30L,SEEK_SET);
fseek(fis,30L,SEEK_SET);
mm == ftell(fis);
ftell(fis); printf("\n
printf("\n pozitia2:%d",m);
pozitia2:%d",m); //fscanf(fis,"%d",&x);
//fscanf(fis,"%d",&x);
fscanf(fis,"%s",s);
fscanf(fis,"%s",s); //printf("x
//printf("x tiparit:%d",x);
tiparit:%d",x);
printf("s
printf("s tiparit:%s",s);
tiparit:%s",s); fclose(fis);
fclose(fis);
}} int
int main()
main() {{ fisiere_text();
fisiere_text(); return
return 0;
0; }}

10. Implementarea unui analizor sintactic pentru expresii


matematice
Un alt exemplu sugestiv privind importanţa funcţiilor
recursive este reprezentat de analizoarele sintactice. În cele ce
urmează este exemplificat un model simplificat al unui analizor
pentru expresii matematice.
Ideea constă în împărţinea expresiei în alte expresii, în
funcţie de operatorii şi funcţiile matematice identificate. Prima
funcţie va genera alte apeluri recursive; fiecare funcţie va

105
IV.Introducere în limbajul C

întoarce, în final, fie o valoare de tip double, fie un cod ce va


fi ignorat în funcţia care a efectuat apelul. Operatorii şi
funcţiile matematice identificate vor fi utilizaţi împreună cu
rezultatele funcţiilor de evaluare – analizor1, lansate ca urmare a
apelurilor recursive.
Funcţia analizor1 preia un şir de caractere, ce reprezintă o
expresie matematică ce conţine operatori aritmetici şi funcţii din
biblioteca matematică, şi întoarce valoarea acestei expresii.
În primul rând este necesar să evaluăm dacă expresia nu este
chiar un număr. Funcţia double idnr(char* sir) implementează
această cerinţă:
/*Ex.10(1):
/*Ex.10(1): identintifică
identintifică dacă
dacă expresia
expresia este
este un
un număr*/
număr*/
double idnr(char* sir)
double idnr(char* sir)
{int
{int i,cnt;
i,cnt;
//testeaza
//testeaza daca
daca exista
exista mai
mai multe
multe caractere
caractere '.'
'.'
for(i=0,cnt=0;i<strlen(sir);i++)
for(i=0,cnt=0;i<strlen(sir);i++)
{{ if(sir[i]=='.')
if(sir[i]=='.') cnt++;
cnt++; if(cnt>1)
if(cnt>1) return
return 123456789L;}
123456789L;}
for(i=0;i<strlen(sir);i++)
for(i=0;i<strlen(sir);i++)
if((sir[i]<'0'
if((sir[i]<'0' |||| sir[i]>'9')&&(sir[0]!='-')
sir[i]>'9')&&(sir[0]!='-')
&&(sir[0]!='+')&&(sir[i]!='.'))
&&(sir[0]!='+')&&(sir[i]!='.')) return
return 123456789L;
123456789L;
return atof(sir);}
return atof(sir);}
În cazul în care nu este un număr, întoarce 123456789L,
altfel întoarce numărul convertit din şirul de caractere în tipul
double.
O funcţie utilă este cea prin care sunt căutaţi operatorii
bool cauta3(char operatorul,char* sir,char* sir1, char* sir2).
Această funcţie preia şirul cu expresia în care se caută şi
simbolul operatorului care este căutat. Funcţia identifică şirul
din stânga operatorului, din dreapta operatorului şi pune cele două
şiruri în variabilele sir1 şi sir2. Funcţia întoarce valoarea de
adevăr TRUE dacă găseşte operatorul, altfel întoarce FALSE. De
asemenea, funcţia contorizează numărul de paranteze închise şi
deschise, deoarece altfel nu ar fi posibilă împărţirea în două
expresii, fiecare din acestea urmând a fi reevaluate:
/*Ex.10(2):
/*Ex.10(2): identintifică
identintifică operatorii
operatorii şi
şi generează
generează subexpresiile
subexpresiile */*/
bool
bool cauta3(char
cauta3(char operatorul,
operatorul, char*
char* sir,
sir, char*
char* sir1,
sir1, char*
char* sir2)
sir2)
{int
{int i,t;
i,t;
for(i=strlen(sir)-1,t=0;i>=0;i--)
for(i=strlen(sir)-1,t=0;i>=0;i--)
{if(sir[i]=='(')
{if(sir[i]=='(') t++;t++; if(sir[i]==')')
if(sir[i]==')') t--;
t--;
if(i
if(i &&&& i!=(strlen(sir)-1)
i!=(strlen(sir)-1) &&&& sir[i]==operatorul
sir[i]==operatorul && && !t)
!t)
{strncpy(sir1,sir,i);
{strncpy(sir1,sir,i); sir1[i]=0;
sir1[i]=0; strcpy(sir2,&sir[i+1]);
strcpy(sir2,&sir[i+1]);
return
return true;}
true;}
}}
return
return false;}
false;}
Este necesar să verificăm dacă expresia este delimitată de
paranteze rotunde (în urma evaluării expresiilor se ajunge şi în

106
10.Implementarea unui analizor sintactic pentru expresii matematice

această situaţie) şi atunci este necesar să eliminăm simbolurile


pentru paranteze din cele două capete ale şirului. Acest lucru este
asigurat de funcţia bool cautap(char* sir,char* sir1):
/*Ex:10(3):
/*Ex:10(3): identifică
identifică dacă
dacă expresia
expresia este
este în
în întregime
întregime între
între paranteze
paranteze
şi
şi le
le elimină
elimină */
*/
bool
bool cautap(char*
cautap(char* sir,
sir, char*
char* sir1)
sir1)
{if(sir[0]=='('
{if(sir[0]=='(' && && sir[strlen(sir)-1]
sir[strlen(sir)-1] ==== ')')
')')
{{ strcpy(sir1,
strcpy(sir1, &sir[1]);
&sir[1]); sir1[strlen(sir)-2]
sir1[strlen(sir)-2] == 0; 0; return
return true;
true;
return
return false;}
false;}
Analizorul trebuie să verifice dacă expresia ce urmează a fi
evaluată este o funcţie din biblioteca matematică, caz în care
întoarce valoarea expresiei. Acest lucru este implementat prin
funcţia double cautfnc(char *buff).
/*Ex.10(4):
/*Ex.10(4): identintifică
identintifică şişi evaluează
evaluează funcţii
funcţii din
din bibl.
bibl. matematică*/
matematică*/
double
double cautfnc(char
cautfnc(char *buff)
*buff)
{static
{static int
int i,t,p;
i,t,p; double
double m,m1,m2;
m,m1,m2; charchar s1[MAX],s2[MAX];
s1[MAX],s2[MAX];
i=0;t=0;p=0;m=0;m1=0;m2=0;
i=0;t=0;p=0;m=0;m1=0;m2=0; if(buff[i]==0)
if(buff[i]==0) {return
{return -123456789L;}
-123456789L;}
for(i=0;i<MAX;i++)
for(i=0;i<MAX;i++) {if(buff[i]==0)
{if(buff[i]==0) break;
break; if(buff[i]==',')
if(buff[i]==',') p=i;}
p=i;}
t=i;
t=i; i=0;
i=0;
if(
if( (buff[0]=='e')
(buff[0]=='e') &&&& (buff[1]=='x')
(buff[1]=='x') && && (buff[2]=='p')
(buff[2]=='p') &&
&&
(buff[3]=='(')
(buff[3]=='(') &&&& (buff[strlen(buff)-1]==')')
(buff[strlen(buff)-1]==')') ))
return
return exp(analizor1(&buff[3]));
exp(analizor1(&buff[3]));
if(
if( (buff[i]=='l')
(buff[i]=='l') &&&& (buff[i+1]=='o')
(buff[i+1]=='o') && && (buff[i+2]=='g')
(buff[i+2]=='g') ))
{{ strncpy(s1,
strncpy(s1, &buff[i+3],t);
&buff[i+3],t); s1[t-3]=0;
s1[t-3]=0;
mm == log(analizor1(s1));
log(analizor1(s1)); return(m);
return(m); }}
if(
if( (buff[i]=='s')
(buff[i]=='s') &&&& (buff[i+1]=='i')
(buff[i+1]=='i') && && (buff[i+2]=='n')
(buff[i+2]=='n') ))
{{ strncpy(s1,
strncpy(s1, &buff[i+3],t);
&buff[i+3],t); s1[t-3]=0;
s1[t-3]=0;
mm == sin(analizor1(s1));
sin(analizor1(s1)); return(m);
return(m); }}
if(
if( (buff[i]=='c')
(buff[i]=='c') &&&& (buff[i+1]=='o')
(buff[i+1]=='o') && && (buff[i+2]=='s')
(buff[i+2]=='s') ))
{{ strncpy(s1,
strncpy(s1, &buff[i+3],t);
&buff[i+3],t); s1[t-3]=0;
s1[t-3]=0;
mm == cos(analizor1(s1));
cos(analizor1(s1)); return(m);
return(m); }}
if(
if( (buff[i]=='p')
(buff[i]=='p') &&&& (buff[i+1]=='o')
(buff[i+1]=='o') && && (buff[i+2]=='w')
(buff[i+2]=='w') ))
{{ strncpy(s1,&buff[i+4],p-4);
strncpy(s1,&buff[i+4],p-4); s1[p-4]=0;
s1[p-4]=0;
strncpy(s2,&buff[p+1],t-p);
strncpy(s2,&buff[p+1],t-p); s2[t-p-2]=0;
s2[t-p-2]=0;
m1=analizor1(s1);
m1=analizor1(s1); m2=analizor1(s2);
m2=analizor1(s2); mm == pow(m1,m2);
pow(m1,m2); return(m);
return(m); }}
return
return 123456789L;}
123456789L;}
Funcţia recursivă double analizor1(char *sir):
/*Ex.10(5):
/*Ex.10(5): funcţia
funcţia recursivă
recursivă de
de evaluare
evaluare */
*/
double
double analizor1(char
analizor1(char *sir)
*sir)
{{ double
double m;
m; char
char s1[100],s2[100];
s1[100],s2[100];
if((m=idnr(sir))!=123456789L)
if((m=idnr(sir))!=123456789L) return
return m;m;
if(cautap(sir,s1))
if(cautap(sir,s1)) return
return analizor1(s1);
analizor1(s1);
if(cauta3('+',sir,s1,s2))
if(cauta3('+',sir,s1,s2)) return
return analizor1(s1)+analizor1(s2);
analizor1(s1)+analizor1(s2);
if(cauta3('-',sir,s1,s2))
if(cauta3('-',sir,s1,s2)) return
return analizor1(s1)-analizor1(s2);
analizor1(s1)-analizor1(s2);
if(cauta3('*',sir,s1,s2))
if(cauta3('*',sir,s1,s2)) return
return analizor1(s1)*analizor1(s2);
analizor1(s1)*analizor1(s2);
if(cauta3('/',sir,s1,s2))
if(cauta3('/',sir,s1,s2)) return
return analizor1(s1)/analizor1(s2);
analizor1(s1)/analizor1(s2);
if((m=cautfnc(sir))!=123456789L)
if((m=cautfnc(sir))!=123456789L) return
return m;
m; }}
Apelul acestora este exemplificat în ceea ce urmează:

107
IV.Introducere în limbajul C

/*Ex:10(6):
/*Ex:10(6): apelul
apelul în
în vederea
vederea testării
testării */
*/
#include
#include <stdio.h>
<stdio.h> /*\n*/#include
/*\n*/#include <stdlib.h>/*\n*/#include
<stdlib.h>/*\n*/#include <string.h>
<string.h>
#include
#include <math.h>
<math.h> /*\n*/
/*\n*/ #define
#define MAX
MAX 500
500
double
double analizor1(char*);
analizor1(char*);
/*
/* se
se copiază
copiază explicitările
explicitările pentru
pentru funcţiile:
funcţiile:
idnr(),cauta3(),cautap(),cautfnc(),analizor1()
idnr(),cauta3(),cautap(),cautfnc(),analizor1() */ */
int
int main()
main()
{double
{double x;x; char
char buf[100]="3+5*pow(0.5*4,6.5-4.5)-20*pow(2,2-1.5-1/2)";
buf[100]="3+5*pow(0.5*4,6.5-4.5)-20*pow(2,2-1.5-1/2)";
//scanf("%s",buf);
//scanf("%s",buf);
if((x=analizor1(buf))!=123456789L)
if((x=analizor1(buf))!=123456789L) printf("ŞIR:%s
printf("ŞIR:%s ---
--- D:%f\n",buf,x);
D:%f\n",buf,x);
else printf("Expresie ERONATA!");
else printf("Expresie ERONATA!");
return
return 0;}
0;}
Notă: Analizorul poate fi îmbunătăţit prin eliminarea altor excep-
ţii sau prin adăugarea de noi funcţii sau operatori.

108
V.Programarea orientată pe obiecte Limbajul C++

V. Programarea orientată pe obiecte


Limbajul C++
1. Clase şi moşteniri

1.1. Noţiunea de clasă şi obiect


Ideea de bază de la care pleacă programarea orientată pe
obiecte este de a grupa structurile de date cu operaţiile care
prelucrează respectivele date. Un asemenea ansamblu poartă
denumirea de clasă. Clasa reprezintă tipul datei, iar obiectul
reprezintă variabila după ce a fost alocată în memorie. Obiectul
reprezintă instanţierea unei clase. Proiectarea de programe
utilizând clase se numeşte programare orientată pe obiecte (OOP-
Object Oriented Programming).
O clasă poate să conţină date (membri/atribute) şi funcţii
(membre/metode/operatori). Datele sunt reprezentate prin decla-
rațiile variabilelor din cadrul clasei. În cadrul unei clase pot fi
declarate inclusiv variabile de tip structură sau de tip clasă.
Date + Metode = Clasă (tipul datei)
Principiul încapsulării (ascunderea informaţiei ce stă la
baza realizării clasei): accesul la datele membre se poate face
numai prin intermediul setului de metode asociat. Obiectul este
caracterizat complet prin metodele asociate. O parte din metode vor
fi utilizate de cel ce utilizează clasa în cadrul aplicațiilor, iar
altă parte va fi invizibilă pentru utlizatorul final (atât codul,
cât și apelul acestora). Conform principiului încapsulării,
utilizatorul nu are acces la date, ci numai la metodele pe care
dezvoltatorul clasei le-a lăsat pentru a putea fi apelate.
1.2. Operatori specifici C++
Operator Simbol Forma Operaţia realizată
• accesează variabilele globale
rezoluţie chiar dacă au acelaşi nume cu
/ ::a
:: cele locale;
indicator ::f() • accesează membrii/membrele din
acces cadrul claselor.
valoare .* OB.*a întoarce valoarea (OB valoare)
valoare ->* OB->*a întoarce valoarea (OB pointer)
alocă memorie; ex ptr. un vector
de 10 întregi: new int[10]
new new new tip întoarce NULL în caz de eşec,
altfel întoarce adresa alocată

109
V.Programarea orientată pe obiecte Limbajul C++

Operator Simbol Forma Operaţia realizată


delete aeliberează zona de memorie;

delete delete delete Operatorul
• [] se utilizează
a[] pentru tablouri.
arată adresa obiectului curent (în
this/ care ne aflăm), iar *this este
this this *this valoarea obiectului curent
(obiectul).
Adresa obiectului curent este dată de operatorul this.
1.3. Noţiunea de moştenire
Procedeul de derivare permite definirea unei clase noi,
numită clasă derivată, care moşteneşte proprietăţile (membri +
membre) unei clase deja definite, numită clasă de bază, pe care le
completează cu proprietăţi (membri + membre) noi.
Prin procedeul de derivare/moştenire, clasa de bază nu este
afectată şi nu trebuie recompilată, declaraţia (fişierul header) şi
codul obiect ale clasei de bază (librăria) sunt suficiente pentru
crearea unei clase derivate.
Dintr-o clasă (numită clasă de bază) pot fi derivate mai
multe clase, iar fiecare clasă derivată poate servi ca bază pentru
alte clase derivate. Astfel se realizează o ierarhie de clase.
Pornind de la clase simple şi generale, fiecare nivel al ierarhiei
acumulează caracteristicile (membri + membre) claselor părinte
(bază) şi le adaugă un anumit grad de specializare. În cazul în
care o clasă moşteneşte proprietăţile mai multor clase, avem
moştenire multiplă, aceasta fiind specifică POO doar în câteva
limbaje. Sintaxa declaraţia clasei derivate în cazul unei singure
clase de bază:
class nume_clasa_derivata : modificator_acces nume_clasă_baza
iar în cazul mai multor clase de bază (moştenire multiplă):
class nume_clasa_derivata : modificator_acces nume_clasă_baza_1
[<, modificator_acces nume_clasă_baza_n >]

2. Constructori şi destructori

2.1. Constructori
 Constructorul este o funcţie care are acelaşi nume cu al clasei
(inclusiv case sensitive) – specific C++;
 Constructorul se apelează automat la crearea fiecărui obiect al
clasei – creare de tip static, automatic sau dinamic;
 O clasă poate avea mai mulţi constructuri, ei se deosebesc prin
numărul şi tipul argumentelor;

110
2.Constructori şi destructori

 În cazul în care nu se scrie niciun constructor, se va apela


automat un constructor, fără argumente şi de tip public. Dacă
utilizatorul declară cel puţin un constructor, atunci
compilatorul nu mai generează acest constructor implicit;
 Există şi un constructor implicit de copiere ce iniţializează
obiectul curent cu datele (membrii) din alt obiect de acelaşi
tip (aparţinând aceleiaşi clase). Acesta este de forma:
nume_clasă(nume_clasa &) şi poate fi redefinit;
 Constructorii nu întorc valori;
2.2. Destructori
 Destructorul are acelaşi nume cu al clasei şi se declară cu
operatorul ~ (tilda) înainte – specific C++;
 Destructorul este apelat automat atunci când obiectul
respectiv îşi încetează existenţa în memorie;
 Utilizatorul nu va putea apela explicit destructorul unei
clase;
 Destructorul nu preia şi nu întoarce valori;
 O clasă are un singur destructor;
 În cazul în care nu este declarat niciun destructor se va
apela unul implicit;
Explicitarea funcţiilor membre (inclusiv a constructorilor
şi destructorilor) se poate face fie în interiorul declaraţiei
clasei, fie în exteriorul clasei prin utilizarea operatorului
rezoluţie " :: ".
Funcţiile din cadrul unei clase se pot clasifica în:
✗ constructori;
✗ destructori;
✗ funcţii normale de acces;
✗ funcţii de tip prieten (friend);
✗ funcţii virtuale;
✗ funcţii statice;
✗ funcţii de tipul operator.
2.3. Exemple
În continuare sunt prezentate câteva exemple în care apar
noţiunile de constructor, destructor şi moştenire.
2.3.1. Exemplu privind moştenirea
/*Ex.
/*Ex. 2.3(1.1):definire
2.3(1.1):definire clasa
clasa A*/
A*/ class
class A{
A{ public:
public: short
short a1,a2;
a1,a2;
A(short
A(short a1_=5,
a1_=5, short
short a2_=7)
a2_=7) {{ a1=a1_;
a1=a1_; a2=a2_;}
a2=a2_;}
void
void afis()
afis() {std::cout<<"\n
{std::cout<<"\n a1="<<a1<<"
a1="<<a1<<" a2="<<a2;}
a2="<<a2;} };};
Constructorul A(short a1_=5, short a2_=7) { a1=a1_;a2=a2_;}

111
V.Programarea orientată pe obiecte Limbajul C++

înlocuieşte mai mulţi constructori, în acest caz trei constructori


şi anume:
☑ A(short a1_, short a2_);
☑ A(short a1_,) echivalent cu A(short a1_, 7);
☑ A() echivalent cu A(5, 7).
/*Ex.
/*Ex. 2.3(1.2):definirea
2.3(1.2):definirea claseiclasei B*/
B*/
class
class BB :: public
public A{
A{ public:
public: short
short b1,b2;
b1,b2;
B():A()
B():A() {b1
{b1 == -1;
-1; b2
b2 == -1;}
-1;} B(short
B(short b1_):A(b1_)
b1_):A(b1_) {b1 {b1 == b1_;
b1_; b2
b2 == 20;}
20;}
B(short
B(short b1_,
b1_, short
short b2_)
b2_) :: A(b2_)
A(b2_) {{ b1
b1 == b1_;
b1_; b2
b2 == b2_;}
b2_;}
void
void afis(){std::cout<<"a1="<<a1<<"
afis(){std::cout<<"a1="<<a1<<" a2="<<a2<<"
a2="<<a2<<" b1="<<b1<<"
b1="<<b1<<" b2="<<b2;}
b2="<<b2;}
};
};
Parametrii din cadrul constructorului B pot fi pasaţi
constructorului A. În acest sens este necesar ca denumirea
parametrilor din declaraţia constructorului B să fie aceeaşi cu
denumirea parametrilor constructorului A din momentul explicitării
constructorului B. Chiar dacă în “B(short b1_,short b2_):A(b2_)”
parametrul b2_ este al doilea parametru, acesta va fi preluat
corect.
/*Ex.
/*Ex. 2.3(1.3):*/
2.3(1.3):*/ #include<iostream>
#include<iostream>
/*Se
/*Se copiază
copiază Ex2.3(1.1),(1.2)(1.3)*/
Ex2.3(1.1),(1.2)(1.3)*/
int
int main()
main() {{ BB x,y(11),
x,y(11), z(100,200);
z(100,200); x.afis();
x.afis(); y.afis();
y.afis(); z.afis();
z.afis();
return
return 0;}
0;}
2.3.2. Exemplu - apel constructori şi destructori într-o ierarhie de clase
Este prezentat un exemplu cu două clase având mai mulţi
constructori, cu scopul de a pune în evidenţă ordinea de apel a
constructorilor şi destructorilor. Clasa A este clasa de bază, iar
B este clasa derivată.
/*
/* Ex
Ex 2.3(2.1):definire
2.3(2.1):definire clasa clasa AA */*/ class
class AA {int
{int a1,a2;
a1,a2;
A(){
A(){ a1a1 == 0;
0; a2
a2 == 0;
0; cout<<"\n
cout<<"\n in in constr.
constr. A()
A() ";}
";}
A(int
A(int a1_)
a1_) {{ a1a1 == a1_;
a1_; a2
a2 == 0;
0; cout<<"\n
cout<<"\n inin constr.
constr. A(int)
A(int) ";
"; }}
A(int
A(int a1_,int
a1_,int a2_)
a2_) {a1
{a1 == a1_;
a1_; a2
a2 == a2_;cout<<"\n
a2_;cout<<"\n in in A(int,int)
A(int,int) ";}";}
~A()
~A() {cout<<"\n
{cout<<"\n in in DESTRUCTOR
DESTRUCTOR ~A()
~A() ";}
";} };
};
Cuvântul cheie public este detaliat în capitolele următoare
şi semnifică posibilitatea de acces a datelor şi funcţiilor atât
din cadrul clasei, cât şi din exteriorul acesteia.
/*Ex
/*Ex 2.3(2.2):definirea
2.3(2.2):definirea clasă
clasă B*/
B*/
class
class B:public
B:public A{
A{ public:
public: int
int b1,
b1, b2;
b2;
B():A()
B():A() {{ b1=0;b2=0;cout<<"\n
b1=0;b2=0;cout<<"\n in in constr.
constr. B()B() ";}
";}
B(int
B(int b1_):A(b1_-5)
b1_):A(b1_-5) {b1
{b1 == b1_;
b1_; b2
b2 == 0;
0; cout<<"\n
cout<<"\n inin constr.B(int)";}
constr.B(int)";}
B(int
B(int b1_,int
b1_,int b2_)
b2_) :: A(b1_+b2_,
A(b1_+b2_, 15)
15) {b1
{b1 == b1_;b2
b1_;b2 == b2_;
b2_; cout<<"\n
cout<<"\n în
în
constructorul
constructorul B(int,int)
B(int,int) ";}
";} ~B()~B() {cout<<"\n
{cout<<"\n inin DESTRUCTOR
DESTRUCTOR ~B()
~B() ";}
";}
};
};
Constructorul clasei derivate va avea specificat construc-

112
2.Constructori şi destructori

torul clasei de bază pe care îl apelează deoarece o clasă poate


avea mai mulţi constructori (funcţii de tip constructor). Mai
întâi, se apelează în mod automat constructorul clasei de bază, iar
apoi se apelează în mod automat constructorul clasei derivate.
Apelul destructorilor se realizează automat în momentul distru-
gerii obiectului şi se realizează în ordine inversă, mai întâi se
apelează destructorul clasei derivate, iar apoi se apelează
destructorul clasei de bază.
/*Ex
/*Ex 2.3(2.3):*/
2.3(2.3):*/ #include
#include <iostream>
<iostream>
using
using namespace std; /*Se
namespace std; /*Se copiază
copiază ExEx 2.3(2.1),(2.2),(2.3)*/
2.3(2.1),(2.2),(2.3)*/
int
int main()
main() {cout<<"\n
{cout<<"\n X"<<endl;
X"<<endl; BB x;
x; cout<<"\n
cout<<"\n S[3]"<<endl;
S[3]"<<endl; BB s[3];
s[3];
cout<<"\n
cout<<"\n Y"<<endl; B y(4,5); return 0;
Y"<<endl; B y(4,5); return 0; }}
În momentul declaraţiei variabilei (de tip automatic), ce
reprezintă obiectul, este apelat automat un constructor al clasei,
iar în momentul închiderii blocului în care este declarat obiectul
(cu excepţia obiectelor alocate dinamic), este apelat automat
destructorul. Dacă obiectul este alocat dinamic, atunci
constructorul va fi apelat în momentul alocării dinamice a memoriei
pentru acel obiect, iar destructorul va fi apelat automat în
momentul eliberării zonei de memorie ce conţine obiectul.
În acest exemplu, x este obiectul (variabila), iar B este
clasa (tipul de dată). Secvenţa de declarare “B x;” este
echivalentă cu “B x()” unde x() este constructorul fără parametri.
Notă:
➢ Pot exista cazuri în care nu avem constructori, situaţie în care
este apelat automat un constructor fără argumente şi fără
conţinut, de ex.: A() {} denumit şi constructor implicit;
➢ Se apelează în primul rând constructorul clasei de bază, iar
apoi constructorul clasei derivate;
➢ Pentru fiecare obiect din tablou se apelează constructorul fără
argumente sau după caz constructorul implicit;
➢ La încetarea vizibilităţii unui tablou cu obiecte, se apelează
destructorul pentru fiecare obiect din tablou.
2.4. Exemple privind ordinea de apel a constructorilor şi
destructorilor
2.4.1. Exemplul nr.1
Ce se afişează în urma execuţiei funcţiei int main() ?
#include <iostream>
#include <string.h>
using namespace std;
class A {
protected: char nob[30]; //nob=nume obiect
public:

113
V.Programarea orientată pe obiecte Limbajul C++

int a;
A() { a = 15; strcpy(nob,"N/A"); afis("A-Constr."); }
A(int a1) { a = a1; strcpy(nob,"N/A"); afis("A-Constr."); }
~A() { afis("A-Destr."); }
virtual void print() = 0;
virtual void afis(char* clasa)
{ cout<<"CLASA:"<<clasa<<" Variabila:"<<nob<<endl; }
};
class B : protected virtual A
{protected:
int b;
public:
B(int a1) : A(a1) { b = 0; afis("B-Constr_0"); }
B(char *nob1,int b1):A()
{ b = b1; strcpy(nob,nob1); afis("B-Constr_2");}
~B() { afis("B-Destructor"); }
void print() {cout<<"B::a:"<<a<<endl;}
};
class C : public virtual A
{ int c;
public:
C() : A() { c = 0; afis("C-Constr_0"); }
C(char *nob1, int c1) : A()
{ strcpy(nob, nob1); afis("C-Constr_2"); }
~C() { afis("C-Destructor"); }
};
class D : public B, private C
{ int d; public:
D() : B(1), C() { afis("D-Constr_0"); }
D(char *nob1 ,int d1) : B(d1-4), C()
{ strcpy(A::nob,nob1); afis("D-Constr_2"); }
~D() { afis("D-Destructor"); }
};
int main()
{ D y("y",5);/* verificare posibilitate asignari*/ return 0;}

2.4.2. Exemplul nr.2


Ce se afişează în urma execuţiei codului următor?
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
class A1
{ int x, ta; char nvar[30];
protected:
int ya;
public:
A1() { strcpy(nvar,"N/A"); x = 0; ya = 0; afis("CONSTR_0"); }
A1(char *nvar1)
{ strcpy(nvar,nvar1); x = 0; ya=0; afis("CONSTR_1"); }

114
2.Constructori şi destructori

A1(char *nvar1,int x1, int ya1)


{ strcpy(nvar,nvar1); x = x1; ya = ya1; afis("CONSTR_2"); }
~A1() { afis("DESTRUCTOR"); }
virtual void afis(char *sir);
};

void A1::afis(char *sir) { cout<<"A1#numevar:"<<nvar<<"-"<<sir<<endl; }

class B2 : protected A1
{int x; char nvar[30]; protected:int yb; public:int t2;
B2() : A1() { strcpy(nvar,"N/A"); x = 0; yb = 0; afis("CONSTR_0"); }
B2(char *nvar1) : A1(nvar1,2,5)
{ strcpy(nvar,nvar1); x = 0; yb = 0; afis("CONSTR_1"); }
B2(char *nvar1,int x1,int yb1):A1(nvar1)
{ strcpy(nvar,nvar1); x = x1; yb = yb1; afis("CONSTR_2"); }
~B2() {afis("DESTRUCTOR");}
void afis(char *sir);
};

void B2::afis(char *sir)


{ cout<<"B2#numevar:"<<nvar<<"-"<<sir<<endl; }
int main()
{ A1 *ax[2]; B2 bx;
{ A1 az("az");
{ B2 by("by",0,0);
ax[0] = new A1("ax",1,1);
//asignări ptr. pct. 2
}
ax[1] = ax[0];
delete ax[0];
}
return 0;
}

3. Modificatori şi specificatori de acces. Exemple

3.1. Modificatori şi specificatori de acces


Specificatorii de acces sunt cuvinte cheie care specifică
zona de vizibilitate pentru datele şi funcţiile ce urmează
cuvântului cheie.
Modificatorii de acces permit modificarea drepturilor de
acces asupra unor date sau funcţii în cazul derivării în sensul
restrângerii acestora.
Datele (membrii) şi membrele (funcţiile) din interiorul unei
clase pot avea următorii specificatori de acces:
➢ public: datele/membrele pot fi accesate de oriunde, chiar şi
din afara clasei;

115
V.Programarea orientată pe obiecte Limbajul C++

➢ protected: datele/membrele dintr-o clasă de bază pot fi văzute


în interiorul claselor derivate, dar nu şi în afara acestora;
➢ private: datele/membrele dintr-o clasă nu sunt văzute decât în
interiorul clasei.
Implicit toate datele şi metodele din cadrul unei clase sunt
de tip private.
Dacă constructorii nu ar fi declaraţi cu specificatorul de
acces public, nu am putea avea obiecte de tipul clasei respective
în afara clasei.
Modificatorii de acces
Specificatorul de Modificator Accesul Accesul
acces din clasa de de acces moştenit de din
bază clasa derivată exterior
private private inaccesibil inaccesibil
protected private private inaccesibil
public private private inaccesibil
private protected inaccesibil inaccesibil
protected protected protected inaccesibil
public protected protected inaccesibil
private public inaccesibil inaccesibil
protected public protected inaccesibil
public public public accesibil

3.2. Exemplu privind accesul


În continuare este prezentat un exemplu de utilizare a
modificatorilor şi specificatorilor de acces. Este definită o
ierarhie de clase pe trei nivele: 1-clasa de baza A, 2- clasele
B1, B2, B3 cu modificatorii de acces public, protected şi private,
3- clasele C1, C2, C3 cu modificatorii de acces de tip public. În
vederea testării se va elimina treptat câte un comentariu
corespunzător intrucţiunilor de atribuire din funcţia int main ().
/*Ex.
/*Ex. 3.2(1.1):definire
3.2(1.1):definire clasă
clasă A*/
A*/
class
class A{
A{ short
short a1;
a1; protected:
protected: short
short a2;
a2; public:
public: short
short a3;
a3;
A(short
A(short a1_=0,short
a1_=0,short a2_=0,short
a2_=0,short a3_=
a3_= 0)
0) {a1
{a1 == a1_;
a1_; a2=a2_;
a2=a2_; a3=a3_;}
a3=a3_;}
~A()
~A() {}
{} };
};
Într-o clasă, dacă nu este scris un specificator de acces
înainte de declaraţia unei date (variabile/membru) sau a unei
funcţii (metode/membre); acesta va fi private, altfel spus aceasta
va putea fi utilizată doar în cadrul clasei respective, cu
excepţia funcţiilor de tip friend, funcţii detaliate în alt
subcapitol (de exemplu, variabila a1 din cadrul clasei A).

116
3.Modificatori şi specificatori de acces. Exemple

/*Ex.
/*Ex. 3.2(1.2):definirea
3.2(1.2):definirea clasei
clasei B1*/
B1*/ class
class B1
B1 :: public
public AA
{{ short
short bx1;
bx1; protected:
protected: short
short by1;
by1; public:
public: short
short bz1;
bz1;
B1(
B1( short
short bx1_,
bx1_, short
short by1_,
by1_, short
short bz1_,
bz1_, short
short a1_,
a1_, short
short a2_,
a2_, short
short
a3_
a3_ )) :: A(
A( a1_,
a1_, a2_,
a2_, a3_)
a3_) {{ bx1
bx1 == bx1_;
bx1_; by1
by1 == by1_;
by1_; bz1
bz1 == bz1_;
bz1_; }}
short
short sum()
sum() {{ return
return a2a2 ++ a3;
a3; }} };
};
Prin modificatorul de acces public, tot ce este public sau
protected în clasa A va rămâne la fel şi în clasele derivate, iar
datele de tip private din A nu sunt accesibile şi în clasele
derivate.
/*Ex.
/*Ex. 3.2(1.3):definire
3.2(1.3):definire clasăclasă B2*/
B2*/
class
class B2:
B2: protected
protected AA {short
{short bx1;protected:short
bx1;protected:short by1;public:short
by1;public:short bz1;bz1;
B2(
B2( short
short bx1_,
bx1_, short
short by1_,
by1_, short
short bz1_,
bz1_, short
short a1_,
a1_, short
short a2_,
a2_, short
short
a3_
a3_ )) :: A(
A( a1_,
a1_, a2_,
a2_, a3_)
a3_) {{ bx1
bx1 == bx1_;
bx1_; by1
by1 == by1_;
by1_; bz1
bz1 == bz1_;
bz1_; }}
short
short sum()
sum() {{ return
return a2a2 ++ a3;
a3; }} };
};
Prin utilizarea modificatorului de acces protected, tot ce
este de tip public sau protected în clasa A devine de tip
protected în clasa derivată B2.
/*Ex.
/*Ex. 3.2(1.4):definire
3.2(1.4):definire clasăclasă B3*/
B3*/
class
class B3:
B3: private
private AA {short
{short bx1;protected:short
bx1;protected:short by1;public:short
by1;public:short bz1;
bz1;
B3(
B3( short
short bx1_,
bx1_, short
short by1_,
by1_, short
short bz1_,
bz1_, short
short a1_,
a1_, short
short a2_,
a2_, short
short
a3_
a3_ )) :: A(
A( a1_,
a1_, a2_,
a2_, a3_
a3_ )) {{ bx1
bx1 == bx1_;
bx1_; by1
by1 == by1_;
by1_; bz1
bz1 == bz1_;
bz1_; }}
short
short sum()
sum() {{ return
return a2a2 ++ a3;
a3; }} };
};
În acest caz, când modificatorul de acces este de tip
private, tot ce este de tip public sau protected în clasa A devine
de tip private în clasa derivată B3.
/*Ex.
/*Ex. 3.2(1.5):definire
3.2(1.5):definire clasăclasă C1*/
C1*/
class
class C1C1 :: public
public B1
B1 {{
C1(
C1( short
short bx1_,short
bx1_,short by1_,short
by1_,short bz1_,short
bz1_,short a1_,short
a1_,short a2_,
a2_, short
short
a3_
a3_ )) :: B1(
B1( bx1_,
bx1_, by1_,
by1_, bz1_,
bz1_, a1_,
a1_, a2_,
a2_, a3_
a3_ )) {{ }}
short
short sum()
sum() {{ return
return a2 a2 ++ a3;
a3; }} };
};
În acest caz, variabilele a2 şi a3 sunt accesibile în cadrul
clasei C1.
/*Ex.
/*Ex. 3.2(1.6):definirea
3.2(1.6):definirea clasei
clasei C2*/
C2*/
class C2: public B2
class C2: public B2 { {
C2(short
C2(short bx1_,short
bx1_,short by1_,short
by1_,short bz1_,
bz1_, short
short a1_,short
a1_,short a2_,short
a2_,short a3_)
a3_) ::
B2(
B2( bx1_, by1_, bz1_, a1_, a2_, a3_) {}
bx1_, by1_, bz1_, a1_, a2_, a3_) {}
short
short sum()
sum() {{ return
return a2
a2 ++ a3;
a3; }} };
};
De asemenea variabilele a2 şi a3 sunt accesibile în cadrul
clasei C2.
/*Ex.
/*Ex. 3.2(1.7):definire
3.2(1.7):definire clasă
clasă C3*/
C3*/
class
class C3:
C3: public
public B3
B3 {{
C3(
C3( short
short bx1_,short
bx1_,short by1_,short
by1_,short bz1_,short
bz1_,short a1_,short
a1_,short a2_,short
a2_,short a3_)
a3_) ::
B3(
B3( bx1_,
bx1_, by1_,
by1_, bz1_,
bz1_, a1_,
a1_, a2_,
a2_, a3_
a3_ )) {}
{}
/*short
/*short sum(){return
sum(){return a2+a3;}//a2
a2+a3;}//a2 si
si a3
a3 sunt
sunt INACCESIBILE
INACCESIBILE inin C3
C3 !*/
!*/ };
};
Datorită modificatorului de acces al clasei B3 (private),

117
V.Programarea orientată pe obiecte Limbajul C++

variabilele a2 şi a3 rămân inaccesbile şi pentru clasa derivată C3.


/*Ex
/*Ex 3.2(1.8):implementare
3.2(1.8):implementare
Se
Se copiază
copiază Ex
Ex 3.2(1.1),(1.2),(1.3),(1.4),(1.5),(1.6),(1.7)*/
3.2(1.1),(1.2),(1.3),(1.4),(1.5),(1.6),(1.7)*/
int
int main(){
main(){ B1
B1 x(3,7,8,1,2,3);
x(3,7,8,1,2,3); B2
B2 y(3,7,8,1,2,3);
y(3,7,8,1,2,3); B3 B3 z(3,7,8,1,2,3);
z(3,7,8,1,2,3);
short
short q;//
q;// Notă:Pentru
Notă:Pentru test
test se
se vor
vor şterge,
şterge, pe
pe rând,
rând, comentariile
comentariile
/*q=x.a1;*/
/*q=x.a1;*/ /*q=x.a2;*/
/*q=x.a2;*/ /*q=x.a3;*/
/*q=x.a3;*/ /*q=y.a1;*/
/*q=y.a1;*/ /*q=y.a2;*/
/*q=y.a2;*/
/*q=y.a3;*/
/*q=y.a3;*/ /*q=z.a1;*/
/*q=z.a1;*/ /*q=z.a2;*/
/*q=z.a2;*/ /*q=z.a3;*/
/*q=z.a3;*/ return
return 0;}
0;}
Rezultatele testării sunt date în tabelul următor:
Atribuirea Acce- SA - A MA - B SA - B MA - C SA - C
sibilă
q = x.a1; NU private public inaccesibil public inaccesibil
q = x.a2; NU protected public protected public protected
q = x.a3 DA public public public public public
q = y.a1; NU private protected inaccesibil public inaccesibil
q = y.a2; NU protected protected protected public protected
q = y.a3; NU public protected protected public protected
q = z.a1; NU private private inaccesibil public inaccesibil
q = z.a2; NU protected private private public private
q = z.a3; NU public private private public private
În acest tabel “MA -” reprezintă modificatorul de acces
pentru clasa derivată ce are în denumire prima literă ce apare după
“MA -”, iar “SA -” reprezintă specificatorul de acces în cadul
clasei ce are în denumire prima literă ce apare după “SA -”

4. Funcţii de tip friend şi de tip static

4.1. Funcţii de tipul friend


O funcţie de tip friend (prieten) este o funcţie care nu
este membră a clasei şi are acces la toţi membrii clasei. În
interiorul clasei, ea se declară prin scrierea cuvântului cheie
friend înaintea declaraţiei propiu-zise a funcţiei. O clasă ale
cărei funcţii sunt, în totalitate, friend pentru altă clasă se
numeşte clasă prietenă a acelei clase.
4.2. Exemplu cu funcţie de tip friend
Scopul exemplului este de a arăta când este necesară
definirea unei funcţii de tip friend. În acest sens, se defineşte o
clasă ce conţine o funcţie friend pentru clasa PCT. Dacă

118
4.Funcţii de tip friend şi de tip static

coordonatele x, y ar fi fost de tip public, nu ar fi fost necesară


declarea funcţiei double distanta(PCT &a, PCT &b) de tip friend
pentru clasa PCT.
/*Ex.
/*Ex. 4.2(1.1):definirea
4.2(1.1):definirea claseiclasei A*/
A*/
class
class PCT{
PCT{ double
double x,y;
x,y; public:
public: friend
friend double
double distanta(
distanta( PCT PCT &a,
&a, PCTPCT
&b
&b );
);
PCT()
PCT() {x{x == 0;
0; yy == 0;}
0;} PCT(
PCT( double
double x1,
x1, double
double y1
y1 )) {{ xx == x1;
x1; yy ==
y1;}
y1;} };};
Funcţia double distanta(PCT &a, PCT &b) este definită ca una
obişnuită, externă clasei PCT, preia referinţele a două obiecte de
tip PCT şi întoarce valoarea distanţei dintre cele două puncte.
/*Ex.
/*Ex. 4.2(1.2):definirea
4.2(1.2):definirea funcţiei
funcţiei externe
externe clasei
clasei A*/
A*/
double
double distanta(
distanta( PCT
PCT &a,
&a, PCT
PCT &b
&b ))
{return
{return sqrt((
sqrt(( a.x
a.x -- b.x
b.x )*(a.x
)*(a.x -- b.x)+(a.y
b.x)+(a.y -- b.y)*(a.y
b.y)*(a.y -- b.y)
b.y) );}
);}
În acest exemplu funcţia accesează date de tip private ale
clasei PCT, din acest motiv este declarată de tip friend, astfel
poate accesa datele de tip private şi protected.
/*Ex.
/*Ex. 4.2(1.3):implementare
4.2(1.3):implementare apelapel funcţie
funcţie frend
frend */#include
*/#include <iostream>
<iostream>
#include
#include <math.h>
<math.h> // // pentru
pentru funcţia
funcţia sqrt
sqrt
using
using namespace
namespace std;
std; /*Se
/*Se copiază
copiază pe
pe linie
linie nouă
nouă Ex
Ex 4.2(1.1),(1.2)*/
4.2(1.1),(1.2)*/
int
int main()
main() {{ PCT
PCT p1(5,8),
p1(5,8), p2(7,10);
p2(7,10); double
double dist
dist == distanta(p1,p2);
distanta(p1,p2);
cout<<"distanta=
cout<<"distanta= "<<dist;
"<<dist; return
return 0;
0; }}
Dacă declarăm coordonatele x şi y de tip public, nu mai este
necesară declaraţia de tip friend pentru funcţie. Nu se recomandă
utilizarea funcţiilor de tip friend deoarece reduc avantajul con-
ceptului de programare orientată pe obiecte, de exemplu această
funcţie nu apare în clasa curentă şi implicit nu apare nici în
clasele derivate.
4.3. Membrii statici
Un tip aparte de membri ai unei clase sunt membrii statici.
Atât funcţiile membru, cât şi datele membru (atributele) unei clase
pot fi declarate static.
Dacă declaraţia unei variabile membru este precedată de
cuvantul-cheie static, atunci va exista o copie unică a acelei
variabile, care va fi folosită în comun de către toate obiectele
instanţiate din clasa respectivă. Spre deosebire de variabilele
membru obişnuite, pentru variabilele statice nu sunt create copii
individuale ale acestora pentru fiecare obiect în parte; practic,
toate obiectele vor accesa aceeaşi variabilă. Accesarea unei
variabile statice se face folosind numele clasei şi operatorul de
specificare a domeniului ("::").
Când se declară o dată membru ca fiind de tip static într-o
clasă, ea nu este încă alocată. Prin declarare nu se alocă memorie,

119
V.Programarea orientată pe obiecte Limbajul C++

acest lucru se realizează în exteriorul clasei. Pentru a defini o


variabilă membru statică, aceasta trebuie prevazută cu o definire
globală, undeva în afara clasei.
Variabilele statice (ca şi funcţiile membru statice de
altfel) pot fi utilizate indiferent dacă există sau nu instanţe ale
clasei respective. De aceea, iniţializarea unei variabile statice
NU poate cădea în sarcina constructorilor clasei (constructorii, se
execută doar la momentul generării unei instanţe). Constructorii
pot, însă, să modifice valorile variabilelor statice (de exemplu,
pentru a contoriza numărul instanţelor unei clase, create la
execuţia unui program).
Variabilele membru statice sunt folosite cel mai adesea
pentru a asigura controlul accesului la o resursă comună. Acest tip
de variabile se mai folosesc şi pentru a stoca informaţii comune
unei întregi clase de obiecte.
Funcţiile membru pot fi declarate statice. În C++ o funcţie
membru statică se comportă asemănător cu o funcţie globală al cărei
domeniu este delimitat de clasa în care este definită.
4.4. Exemplu cu date de tip static şi funcţii de tip static
Scopul exemplului este de a arăta utilitatea datelor de tip
static şi a funcţiilor de tip static.
/*Ex.
/*Ex. 4.4(1.1):definirea
4.4(1.1):definirea clasei
clasei A*/
A*/ class
class AA {{ public:
public: static
static int
int s;
s;
int
int x;
x; A()
A() {x
{x == 0;
0; s++;
s++; }} A(int
A(int x1)
x1) {x
{x == x1;
x1; s++;}
s++;} ~A()
~A() {s--;}
{s--;}
static
static void
void afis()
afis() {{ cout<<"s="<<s<<endl;
cout<<"s="<<s<<endl; }} }; };
Clasa A conţine o variabilă de tip static s, variabilă ce
este incrementată în cadrul constructorului şi decrementată în
cadrul destructorului. De asemenea, este definită o funcţie, membră
a clasei A, ce permite afişarea valorii variabilei de tip static s.
Funcţia trebuie să fie de tip static deoarece iniţial nu avem
niciun fel de obiect şi nu ar putea fi apelată.
/*Ex.
/*Ex. 4.4(1.2):implementare
4.4(1.2):implementare */ */ #include
#include <iostream>
<iostream>
using
using namespace
namespace std;/*Se
std;/*Se copiază
copiază pepe linie
linie nouă
nouă 4.4(1.1)*/int
4.4(1.1)*/int A::s
A::s == 0;
0;
int main() { cout<<"\n 1: Nr: obiecte: "; A::afis();
int main() { cout<<"\n 1: Nr: obiecte: "; A::afis();
{A
{A v[6];
v[6]; cout<<"\n
cout<<"\n 2:2: Nr:
Nr: obiecte:
obiecte: ";
"; A::afis();
A::afis();
{A
{A m(3),u; cout<<"\n 3: Nr: obiecte: ";
m(3),u; cout<<"\n 3: Nr: obiecte: "; A::afis();
A::afis();
}} cout<<"\n
cout<<"\n 4:4: Nr:
Nr: obiecte:
obiecte: ";
"; A::afis();
A::afis();
}} cout<<"\n
cout<<"\n 5:5: Nr:
Nr: obiecte:
obiecte: ";
"; A::afis();
A::afis();
return
return 0;}
0;}
Pentru linia 1: s = 0 deoarece nu există niciun obiect,
adică nu s-a apelat niciun constructor. Pentru linia 2: s = 6 ca
urmare a apelului de tip automatic pentru vectorul ce conţine 6
obiecte în urma instanţierii clasei A. Pentru linia 3: s = 8
datorită apariţiei a încă două obiecte de tipul A. După închiderea

120
4.Funcţii de tip friend şi de tip static

ultimului bloc de instrucţiuni, este apelat automat destructorul


pentru ultimele două obiecte adăugate, astfel pentru linia 4: avem
s = 6. După închiderea blocului următor este apelat destructorul
pentru obiectele din cadrul vectorului, astfel pentru linia 5: avem
s = 0.
Variabila s este comună tuturor obiector de tipul clasei A,
aceasta este independentă de existenţa obiectelor de tipul clasei
respective, variabila s fiind memorată separat de celelalte
obiecte.

5. Polimorfism, funcţii virtuale


Funcţiile virtuale sunt utilizate pentru implementarea
obiectelor polimorfice.
5.1. Obiecte polimorfice
Obiectele polimorfice se careacterizează prin faptul că
folosind aceeaşi formă de apel pentru funcţiile membre realizăm
operaţii diferite. Funcţiile virtuale implementează filozofia "o
singură interfaţă, mai multe metode", care pune în evidenţă
polimorfism-ul.
5.2. Variabile de tip pointer care punctează la clasele derivate
Presupunem două clase, denumite BAZA şi DERIVATA, unde clasa
DERIVATA moşteneşte clasa BAZA. În aceste condiţii, următoarele
instrucţiuni sunt valabile:
☑ BAZA *p;//pointer-ul care punctează la clasa BAZA
☑ BAZA OB_TIP_BAZA;//un obiect de tip BAZA
☑ DERIVATA OB_TIP_DER;//un obiect de tip DERIVATA, pointer-ul p
poate puncta(prelua adresa) la obiecte de tip BAZA
☑ p=&OB_TIP_BAZA;//p preia adresa lui OB_TIP_BAZA, p poate puncta la
obiecte derivate
☑ p=&OB_TIP_DER;//p preia adresa lui OB_TIP_DER

Un pointer al clasei de bază poate puncta la un obiect din


clasa derivată, fără a genera vreo eroare, invers nu este valabil.
Prin acest pointer putem avea acces doar la membrii clasei derivate
care au fost moşteniţi de la clasa de bază.
5.3. Funcţii virtuale
O funcţie virtuală este o funcţie membră a unei clase, care
se declară în interiorul unei clase de bază şi se redefineşte
(modifică) în clasele derivate.
Pentru a crea o funcţie virtuală, trebuie să utilizăm

121
V.Programarea orientată pe obiecte Limbajul C++

cuvântul cheie virtual, înainte de declaraţia funcţiei. La nivelul


fiecărei clase, funcţiile virtuale implementează cod specific
clasei respective, însă ele au acelaşi nume, aceeaşi listă de
parametrii şi întorc aceelaşi tip de dată. Când un pointer al
clasei de bază punctează la o funcţie virtuală din clasa derivată,
şi aceasta este apelată prin intermediul acestui pointer,
compilatorul determină care versiune (care din codurile/ care
explicitare) a funcţiei trebuie apelată, ţinând cont de tipul
obiectului (identifică clasa din ierarhie) la care punctează acel
pointer. Altfel spus, tipul obiectului (clasa) la care punctează
determină versiunea funcţiei virtuale care va fi executată.
O funcţie virtuală poate fi declarată şi numai formal, ea
nerealizând nimic concret, aceasta fiind declarată doar pentru ca
în clasele derivate să fie declarată şi explicitată. O astfel de
funcţie virtuală, fără cod/explicitare se numeşte funcţie virtuală
pură. Sintaxa pentru o funcţie virtuală pură este:
virtual tip nume_funcţie(listă_de_parametri) = 0 ;
O clasă ce conţine o funcţie virtuală pură se numeşte clasă
abstractă. Clasele abstracte sunt utilizate doar pentru a fi
moştenite. Nu se pot exista instanţe ale claselor abstracte.
5.4. Exemple – polimorfism şi clase abstracte
5.4.1. Implementare polimorfism pentru afişare simplă
Primul exemplu conţine trei clase A, B şi C, ce conţin
funcţia void afis(), declarată de tip virtual. Această funcţie este
apelată utilizând polimorfismul.
/*Ex.
/*Ex. 5.4(1.1):definirea
5.4(1.1):definirea clasei
clasei AA */
*/
class
class AA {protected:
{protected: int
int x;
x; public:
public: A(){
A(){ xx == 0;}
0;} A(int
A(int x1){
x1){ xx == x1;}
x1;}
virtual void afis() { cout<<"A: x="<<x<<endl;
virtual void afis() { cout<<"A: x="<<x<<endl; } }; } };

/*Ex.
/*Ex. 5.4(1.2):definirea
5.4(1.2):definirea clasei
clasei BB */*/
class
class B:
B: public
public AA {{ protected:
protected: int
int y;
y; public:
public: B()
B() :: A(){
A(){ yy == 0;
0; }}
B(int
B(int x1,int
x1,int y1)
y1) :: A(x1){
A(x1){ yy == y1;}
y1;}
void
void afis()
afis() {{ cout<<"B:
cout<<"B: x="<<x<<"
x="<<x<<" y="<<y<<endl;
y="<<y<<endl; }} };};
/*Ex.
/*Ex. 5.4(1.3):definirea
5.4(1.3):definirea clasei
clasei CC */
*/
class
class C: public B { protected: int
C: public B { protected: int z;z; public:
public: C()
C() :: B(){
B(){ zz == 0;
0; }}
C(int
C(int x1,
x1, int
int y1,int
y1,int z1):B(x1,y1)
z1):B(x1,y1) {z=z1;}
{z=z1;}
void
void afis()
afis() {{ cout<<"C:
cout<<"C: x="<<x<<"
x="<<x<<" y="<<y<<"
y="<<y<<" z="<<z<<endl;
z="<<z<<endl; }} }; };
Dacă nu se specifică constructorul pentru clasa de bază, se
preia automat constructorul implicit.
În funcţia int main() se declară un vector cu adrese de
tipul clasei de bază.

122
5.Polimorfism, funcţii virtuale

/*Ex.
/*Ex. 5.4(1.4):implementare
5.4(1.4):implementare polimorfism
polimorfism */ */ #include
#include <iostream>
<iostream>
using
using namespace std;/*Se copiază pe linie nouă 5.4(1.1),(1.2),(1.3)*/
namespace std;/*Se copiază pe linie nouă 5.4(1.1),(1.2),(1.3)*/
int
int main()
main() {{ cout<<"Din
cout<<"Din A"<<endl;
A"<<endl; AA t(1);
t(1); t.afis();
t.afis();
cout<<"Din B"<<endl; B m(5,7); m.afis();
cout<<"Din B"<<endl; B m(5,7); m.afis();
cout<<"Din
cout<<"Din C"<<endl;
C"<<endl; CC s(1,2,3);
s(1,2,3); s.afis();
s.afis();
cout<<"Vector
cout<<"Vector cu 5 adrese de
cu 5 adrese de tipul
tipul A"<<endl;
A"<<endl;
A*
A* w[5];
w[5]; cout<<"B()";
cout<<"B()"; w[0]
w[0] == new
new B();
B(); //(idem
//(idem w[1],w[3],w[4])
w[1],w[3],w[4])
cout<<"B(10,11)
cout<<"B(10,11) ";w[1]=new B(10,11); cout<<"A(30) ";w[2]
";w[1]=new B(10,11); cout<<"A(30) ";w[2] == new
new A(30);
A(30);
cout<<"C()
cout<<"C() ";w[3]=new
";w[3]=new C();cout<<"C(21,22,23)\n";w[4]=new
C();cout<<"C(21,22,23)\n";w[4]=new C(21,22,23);
C(21,22,23);
for(int
for(int i=0;
i=0; i<5;
i<5; i++)
i++) {{ cout<<"
cout<<" i:"<<i<<"--";
i:"<<i<<"--"; w[i]->afis();
w[i]->afis(); }}
return
return 0;
0; }}
La adresa unui obiect de tipul clasei de bază a fost pusă o
adresă a unui obiect de tipul unei clase derivate. Pentru w[0] şi
w[1] apelează funcţia void afis() din cadrul clasei B, pentru w[2]
apelează funcţia void afis() din cadrul clasei A, iar pentru w[3]
şi w[4] apelează funcţia void afis() din cadrul clasei C.
Pentru controalele dintr-o interfaţă grafică, considerăm o
clasă de bază generică TControl, din care sunt derivate clase
generice precum: TEDit, TComboBox, TRadioButtons, TLabel etc.
Fiecare din aceste clase are o metodă/funcţie specifică pentru
desenarea fundalului. Pentru a schimba rapid culoarea de fond, se
defineşte un vector w[] cu adrese la tip TControl. Adresa fiecărui
control se copiază în acest vector, apoi se apelează într-o buclă o
funcţie generică de forma void w[i]―>afisare_fundal(), ce va avea
ca efect apelul fiecărei funcţii specifice (ex.: pentru un obiect
de tip TRadioButtons, se apelează funcţia de afişare fundal speci-
fică clasei TRadioButtons).
5.4.2. Implementare polimorfism pentru afişarea datei calendaristice
Un alt exemplu, permite afişarea datei calendaristice în
funcţie de setările pentru ţară. Clasa de bază permite afişarea
datei calendaristice în formatul general, specific bazelor de date
an-lună-zi.
/*
/* Ex.
Ex. 5.4(2.1):definirea
5.4(2.1):definirea clasei
clasei de
de bază
bază DataC
DataC */
*/
class
class DataC { protected: unsigned short zi, luna,
DataC { protected: unsigned short zi, luna, an;
an; public:
public:
DataC(
DataC( unsigned
unsigned short
short an1,
an1, unsigned
unsigned short
short luna1,
luna1, unsigned
unsigned short
short zi1)
zi1)
{{ an = an1; zi = zi1; luna = luna1; } virtual void
an = an1; zi = zi1; luna = luna1; } virtual void afis() afis()
{cout<<"Format
{cout<<"Format general:"<<an<<"-"<<luna<<"-"<<zi<<endl;}};
general:"<<an<<"-"<<luna<<"-"<<zi<<endl;}};
Clasa derivată pentru reprezentarea datei calendaristice în
format românesc:
/*Ex.
/*Ex. 5.4(2.2):definirea
5.4(2.2):definirea clasei
clasei DataCro*/
DataCro*/
class
class DataCro:
DataCro: public
public DataC
DataC {{ public:
public:
DataCro(
DataCro( unsigned
unsigned short
short an1,
an1, unsigned
unsigned short
short luna1,
luna1, unsigned
unsigned short
short
zi1
zi1 )) :: DataC(
DataC( an1,
an1, luna1,
luna1, zi1
zi1 )) {}
{}
void
void afis()
afis() {{ cout<<"Format
cout<<"Format ro:"<<zi<<"."<<luna<<"."<<an<<endl;
ro:"<<zi<<"."<<luna<<"."<<an<<endl; }} }; };
Clasa derivată pentru reprezentarea datei calendaristice în

123
V.Programarea orientată pe obiecte Limbajul C++

format englez:
/*Ex.
/*Ex. 5.4(2.3):definirea
5.4(2.3):definirea clasei
clasei DataCen*/
DataCen*/
class
class DataCen:
DataCen: public
public DataC
DataC {{ public:
public:
DataCen(
DataCen( unsigned
unsigned short
short an1,
an1, unsigned
unsigned short
short luna1,
luna1, unsigned
unsigned short
short
zi1
zi1 )) :: DataC(
DataC( an1,
an1, luna1,
luna1, zi1
zi1 )) {{ }}
void
void afis()
afis() {{ cout<<"Format
cout<<"Format en:"<<luna<<"/"<<zi<<"/"<<an<<endl;
en:"<<luna<<"/"<<zi<<"/"<<an<<endl; }} }; };
Funcţia int main() ce implementează polimorfismul:
/*Ex.
/*Ex. 5.4(2.4):implementare
5.4(2.4):implementare polimorfism*/
polimorfism*/ #include
#include <iostream>
<iostream>
using
using namespace
namespace std;/*Se
std;/*Se copiază
copiază pepe linie
linie nouă
nouă 5.4(2.1),(2.2),(2.3)*/
5.4(2.1),(2.2),(2.3)*/
int
int main()
main() {{ DataC
DataC *x[3],
*x[3], *c;
*c; DataCro
DataCro *a;*a; DataCen
DataCen *b;
*b;
aa == new
new DataCro(2013,03,11);
DataCro(2013,03,11); bb == new
new DataCen(2011,5,25);
DataCen(2011,5,25);
cc == new
new DataC(2012,1,21);
DataC(2012,1,21); x[0]
x[0] == a;
a; x[1]
x[1] == b;
b; x[2]
x[2] == c;
c;
for(int
for(int i=0;i<3;i++)
i=0;i<3;i++) x[i]->afis();
x[i]->afis(); deletedelete a;delete
a;delete b;delete
b;delete c;
c;
return
return 0;0; }}
5.4.3. Implementare clase abstracte
Clasă abstractă:
/*Ex.
/*Ex. 5.4(3.1):implementare
5.4(3.1):implementare clasă
clasă abstractă*/
abstractă*/ class
class A{
A{ public:
public: int
int i;
i;
A(int
A(int i1){
i1){ ii == i1;
i1; }} A(){i=0;}
A(){i=0;} //sau
//sau unul
unul singur:A(int
singur:A(int i1=0)
i1=0) {i=i1;}
{i=i1;}
virtual
virtual void
void afis1()
afis1() == 0;
0; //functie
//functie virtuala
virtuala pura
pura ---
--- nu
nu are
are corp
corp
virtual
virtual void
void afis2()
afis2() {{ cout<<"A---i2:
cout<<"A---i2: "<<i<<endl;
"<<i<<endl; }} };
};
Clasă derivată:
/*
/* Ex.
Ex. 5.4(3.2):implementare
5.4(3.2):implementare clasă
clasă derivată
derivată */
*/
class
class B: public A { public: B(int i1) :: A(i1){
B: public A { public: B(int i1) A(i1){ }} B()
B() :: A(){
A(){ }}
void
void afis1()
afis1() {{ cout<<"B--i1:
cout<<"B--i1: "<<i<<endl;
"<<i<<endl; }} };
};
Funcţia int main() ce implementează clasa abstractă:
/*Ex.
/*Ex. 5.4(3.3):implementare
5.4(3.3):implementare clasăclasă abstractă*/
abstractă*/ #include
#include <iostream>
<iostream>
using
using namespace
namespace std;
std; /*Se
/*Se copiază
copiază pe
pe linie
linie nouă
nouă Ex
Ex 5.4(3.1),(3.2)*/
5.4(3.1),(3.2)*/
int
int main()
main() {{ /*A
/*A x(5);
x(5); dă
dă eroare
eroare */
*/ BB x(5),y;
x(5),y; x.afis1();
x.afis1(); x.afis2();
x.afis2();
y.afis1();
y.afis1(); y.afis2();
y.afis2(); return
return 0;}
0;}
Se observă faptul că este posibil apelul funcţiei void
afis2() (funcţie ce aparţine clasei A) din cadrul obiectelor x şi
y: x.afis2() şi y.afis2() .

6. Moştenirea multiplă
În cazul în care o clasă moşteneşte proprietăţile mai multor
clase, avem moştenire multiplă, o clasă derivă din mai multe clase
de bază. Sintaxa:
/*Sintaxă
/*Sintaxă moştenire
moştenire multiplă*/
multiplă*/
class
class nume_clasa_derivata :: modificator_acces
nume_clasa_derivata modificator_acces nume_clasă_baza_1
nume_clasă_baza_1
[<,
[<, modificator_acces
modificator_acces nume_clasă_baza_n
nume_clasă_baza_n >]
>]
Constructorul clasei derivate apelează constructorii ambelor
clase de bază în ordinea dată.

124
6.Moştenirea multiplă

Moştenirea de tip diamant apare atunci când o clasă


moşteneşte două sau mai multe clase, iar cel puţin două din acestea
provin prin moştenire de la aceeaşi clasă.
6.1. Exemplu pentru moştenire multiplă simplă
Exemplul pune în evidenţă ordinea de apel a construc-
torilor. Sunt date două clase de bază A şi B din care se
derivează clasa C.
/*Ex.
/*Ex. 6.1.(1.1):implementare
6.1.(1.1):implementare clasă
clasă bază
bază AA */
*/
class
class A{
A{ protected:
protected: int
int x;
x; public:
public: A(){x
A(){x == 0;0; cout<<"\n
cout<<"\n in
in A()
A() ";}
";}
A(int x1) { x = x1; cout<<"\n in A(int)
A(int x1) { x = x1; cout<<"\n in A(int) "; } "; }
virtual
virtual void
void afis(){cout<<"x="<<x<<endl;}
afis(){cout<<"x="<<x<<endl;} ~A(){cout<<"\n
~A(){cout<<"\n ~A()
~A() ";}
";} };
};
/*Ex.
/*Ex. 6.1.(1.2):implementare
6.1.(1.2):implementare clasă clasă de
de bază
bază BB */
*/
class
class BB {{ protected:
protected: intint y;
y; public:
public: B()
B() {{ yy == 0;cout<<"\n
0;cout<<"\n in
in B()
B() ";}
";}
B(int
B(int y1)
y1) {{ yy == y1;
y1; cout<<"\n
cout<<"\n inin B(int)
B(int) ";}
";}
virtual
virtual void
void afis(){cout<<"\n
afis(){cout<<"\n y="<<y<<endl;}
y="<<y<<endl;} ~B(){cout<<"\n
~B(){cout<<"\n ~B()";}
~B()";} };};
/*Ex.
/*Ex. 6.1.(1.3):implementare
6.1.(1.3):implementare clasă clasă derivată
derivată CC */ */
class
class C:C: public
public A,
A, public
public BB {int
{int z;z; public:
public:
C()
C() :: A(),
A(), B(){
B(){ zz == 0;
0; cout<<"\n
cout<<"\n inin C()
C() ";
"; }}
C(int
C(int x1,int
x1,int y1,int
y1,int z1):A(y1),B(y1)
z1):A(y1),B(y1) {z=z1;
{z=z1; cout<<"\n
cout<<"\n C(int,int,int)";}
C(int,int,int)";}
virtual
virtual void
void afis(){
afis(){ cout<<"\n
cout<<"\n X="<<x<<"
X="<<x<<" Y="<<y<<"
Y="<<y<<" Z="<<z<<endl;}
Z="<<z<<endl;}
~C()
~C() {cout<<"\n
{cout<<"\n inin ~C()
~C() ";}
";} };
};
Este apelat mai întâi constructorul A(), apoi este apelat
constructorul B() şi apoi se execută codul specific constructorului
C(). Apelul constructorului C(int x1,int y1,int z1) duce la apelul
constructorului A(y1), apoi la apelul constructorului B(y1), urmat
de execuţia codului din constructorul C(int x1,int y1,int z1).
/*Ex.
/*Ex. 6.1(1.4):Creare
6.1(1.4):Creare obiecte*/
obiecte*/ #include
#include <iostream>
<iostream>
using
using namespace
namespace std;/*Se
std;/*Se copiază
copiază pe
pe linie
linie nouă
nouă 6.1(1.1),(1.2),(1.3)*/
6.1(1.1),(1.2),(1.3)*/
int
int main()
main() {{ CC w1,
w1, r1(2,3,4);
r1(2,3,4); w1.afis();
w1.afis(); r1.afis();
r1.afis(); return
return 0;}
0;}

6.2. Exemplu privind moştenirea de tip diamant


În acest exemplu este prezentat un exemplu de clasă (D) ce
moşteneşte două clase (B şi C), care la rândul lor au moştenit o
clasă comună (A).
Exemplul pune în evidenţă forţarea apelului constructorului
împlicit pentru clasa D, în care apare moştenirea de tip diamant.
/*Ex.
/*Ex. 6.2.(1.1):implementare
6.2.(1.1):implementare clasă
clasă bază
bază A*/
A*/
class
class A{
A{ protected:
protected: int
int x;
x; public:
public: A(){
A(){ xx == -100;
-100; }} A(int
A(int x1)
x1) {x=x1;}
{x=x1;}
virtual void afis() { cout<<"A: x="<<x<<endl;
virtual void afis() { cout<<"A: x="<<x<<endl; } }; } };

125
V.Programarea orientată pe obiecte Limbajul C++

/*Ex.
/*Ex. 6.2.(1.2):implementare
6.2.(1.2):implementare clasăclasă derivată
derivată B*/
B*/
class
class B: public virtual A {protected: int
B: public virtual A {protected: int y;
y; public:
public: B():A(){
B():A(){ yy == 0;}
0;}
B(int
B(int x1,
x1, int
int y1)
y1) :: A(x1)
A(x1) {{ yy == y1;}
y1;}
virtual
virtual void
void afis()
afis() {{ cout<<"B:
cout<<"B: x="<<x<<"
x="<<x<<" y="<<y<<endl;}
y="<<y<<endl;} };};
Prin utilizarea în momentul derivării a opţiunii virtual A,
ne asigurăm că vom avea o singură copie a clasei A pentru obiectele
ce sunt rezultatul instanţierii unei clase ce utilizează moştenirea
multiplă.
/*Ex.
/*Ex. 6.2.(1.3):implementare
6.2.(1.3):implementare clasă clasă derivată
derivată CC */
*/
class
class C:
C: public
public virtual
virtual AA {protected:int
{protected:int z; z; public:C():
public:C(): A(){
A(){ zz == 0;
0; }}
C(int
C(int x1,
x1, int
int z1)
z1) :: A(x1)
A(x1) {{ zz == z1;}
z1;}
virtual
virtual void
void afis()
afis() {{ cout<<"C:
cout<<"C: x="<<x<<"
x="<<x<<" z="<<z<<endl;
z="<<z<<endl; }} };
};
/*Ex.
/*Ex. 6.2.(1.4):implementare
6.2.(1.4):implementare clasăclasă moştenire
moştenire multiplă
multiplă DD */
*/
class
class D: public B, public C {int t; public: D() :: B(),
D: public B, public C {int t; public: D() B(), C()
C() {{ tt == 0;
0; }}
D(int
D(int x1,
x1, int
int y1,
y1, int
int z1,
z1, int
int t1)
t1) :: B(x1,y1),
B(x1,y1), C(x1,z1)
C(x1,z1) {{ t=t1;
t=t1; }}
virtual
virtual void
void afis()
afis()
{cout<<"D:x="<<x<<"
{cout<<"D:x="<<x<<" y="<<y<<"
y="<<y<<" z="<<z<<"
z="<<z<<" t="<<t<<endl;}};
t="<<t<<endl;}};
Apelul clasei D:
/*Ex.
/*Ex. 6.1(1.5):Creare
6.1(1.5):Creare obiecte*/
obiecte*/ #include
#include <iostream>
<iostream>
using
using namespace std;/*Se copiază \n
namespace std;/*Se copiază \n Ex
Ex 6.1(1.1),(1.2),(1.3),(1.4)*/
6.1(1.1),(1.2),(1.3),(1.4)*/
int
int main()
main() {{ DD w1(2,3,4,5);
w1(2,3,4,5); w1.afis();
w1.afis(); return
return 0;}
0;}
Se observă faptul că valoarea 2 transmisă ca şi parametru
pentru membrul x din cadrul clasei A nu se afişează, în schimb se
afişează valoarea -100, corespunzătoare iniţializării membrului x
prin constructorul fără parametrii A().

7. Supraîncărcarea operatorilor
Supraîncărcarea operatorilor (overloading-ul) presupune re-
definirea operatorilor cu ajutorul funcţiei de tipul operator.
7.1. Restricţii privind supraîncărcarea operatorilor
➢ nu pot fi supraîncărcaţi decât operatorii existenţi;
➢ operatorii: " . "," .* "," :: " , " ?: " şi " sizeof " nu pot fi
supraîncărcaţi;
➢ operatorii binari vor fi supraîncărcaţi doar ca operatori binari;
➢ operatorii unari vor fi supraîncărcaţi doar ca operatori unari;
➢ se păstrează precedenţa operatorilor operator (nu există posibi-
litatea de a determina, de exemplu, ca "+" să fie prioritar faţă de
"/");
➢ nu este posibilă definirea unui operator care să ia ca parametri
exclusiv pointeri (exceptând operatorii: = & ,);
➢ nu se poate modifica numărul operanzilor preluaţi de un operator

126
7.Supraîncărcarea operatorilor

(însă se poate ignora unul din parametri);


➢ un operator trebuie să fie ori membru al unei clase, ori să aibă
cel puţin un parametru de tip clasă. De la această regulă fac
excepţie doar operatorii new şi delete;
➢ pentru operatorii : " = "," [] ", " () "," -> " funcţia operator
trebuie să fie membră nestatică a clasei.
7.2. Sintaxa formei de apel
7.2.1. Operatori binari
Pentru un operator binar "op" avem: (op poate fi unul din
operatorii din lista cu operatori C++):
Funcţia Sintaxa formei de Sintaxa formei interne
apel externe (canonice) de apel
membră a clasei obiect1 op obiect2 obiect1.operator op (obiect 2)
nemembră obiect1 op obiect2 operator op (obiect1, obiect2)
7.2.2. Operatori unari
Pentru operatorul unar "op" avem:
Funcţia Sintaxa formei de Sintaxa formei interne
apel externe (canonice) de apel
op obiect1 obiect1.operator op ()
membră a clasei
obiect1 op obiect1.operator op ()
op obiect1 operator op (obiect1)
nemembră
obiect1 op operator op (obiect1)

7.3. Exemple privind supraîncărcarea operatorilor


7.3.1. Clasă ce conţine coordonatele carteziene ale unui punct
Este prezentat un exemplu de supraîncărcare a operatorilor
pentru o clasă ce conţine coordonatele carteziene ale unui punct.
/*Ex.7.3.1(1.1):definirea
/*Ex.7.3.1(1.1):definirea clasei clasei Loc2D
Loc2D cece utilizează
utilizează supraînc.
supraînc. op*/
op*/
class Loc2D{ int x,y; public:
class Loc2D{ int x,y; public:
Loc2D()
Loc2D() {{ xx == 0;
0; yy == 0;
0; }}
Loc2D(int
Loc2D(int x1, int y1) {{ xx == x1;
x1, int y1) x1; yy == y1;
y1; }}
friend
friend Loc2D
Loc2D operator
operator +( +( Loc2D
Loc2D &a,
&a, Loc2D
Loc2D &b);
&b);
Loc2D
Loc2D operator
operator ^(Loc2D
^(Loc2D d); d);
Loc2D
Loc2D operator
operator -(Loc2D
-(Loc2D d) d) {Loc2D
{Loc2D s;
s; s.x=x+d.x;
s.x=x+d.x; s.y=y+d.y;
s.y=y+d.y; return
return s;}
s;}
Loc2D operator %(Loc2D
Loc2D operator %(Loc2D d) d)
{{ Loc2D
Loc2D s;
s; s.x
s.x == xx ++ (d.x-x)/2;
(d.x-x)/2; s.ys.y == this->y
this->y ++ (d.y-y)/2;return
(d.y-y)/2;return s;}
s;}
void
void afis(char* str) {cout<<"x="<<x<<" y="<<y<<" ;"<<str<<endl;} };
afis(char* str) {cout<<"x="<<x<<" y="<<y<<" ;"<<str<<endl;} };
Supraîncărcarea operatorului ^ se realizează utilizând o
funcţie membră a clasei iar a operatorului + prin utilizarea unei

127
V.Programarea orientată pe obiecte Limbajul C++

funcţii externă clasei (funcţie de tip friend). Cei doi operatori


execută aceleaşi operaţii. Funcţia ce supraîncarcă operatorul %
întoarce un punct situat la jumătatea distanţei (dintre
coordonatele date de obiectul curent-this şi cele din obiectul din
argument).
/*Ex.7.3.1(1.2):definirea
/*Ex.7.3.1(1.2):definirea operatorilor
operatorilor ^^ ++ */ */
Loc2D
Loc2D Loc2D::operator
Loc2D::operator ^(Loc2D
^(Loc2D d)d)
{{ Loc2D
Loc2D s;
s; s.x
s.x == xx ++ d.x;
d.x; s.y
s.y == yy ++ d.y;
d.y; return
return s;}
s;}
Loc2D
Loc2D operator
operator +(Loc2D
+(Loc2D &a,
&a, Loc2D
Loc2D &b)
&b)
{{ Loc2D
Loc2D c(a.x+b.x,
c(a.x+b.x, a.y+b.y);
a.y+b.y); return
return c;}c;}
Funcţia operator^ se apelează din cadrul unui obiect, fiind
în obiectul din stânga se preia ca argument obiectul din dreapta şi
este întors un nou obiect cu rezultatul execuţiei.
/*Ex.
/*Ex. 7.3.1(1.3):definirea
7.3.1(1.3):definirea operatorilor
operatorilor ^^ ++ */ */
#include
#include <iostream>
<iostream>
using
using namespace
namespace std;/*se
std;/*se copiază
copiază :Ex6.3.1(1.1),(1.2)*/
:Ex6.3.1(1.1),(1.2)*/
int
int main(int
main(int argc,
argc, char
char **argv)
**argv)
{{ Loc2D
Loc2D f(3,4),
f(3,4), g(5,6),
g(5,6), m, m, h,
h, k;
k; mm == f+g;
f+g; m.afis("adunare
m.afis("adunare m=f+g;");
m=f+g;");
mm == operator+(f,g);
operator+(f,g); m.afis("apel
m.afis("apel înîn clar
clar :: m=operator+(f,g);");
m=operator+(f,g);");
mm == f-g;
f-g; m.afis("apel
m.afis("apel cu cu fct.
fct. membra
membra aa clasei
clasei m=f-g;");
m=f-g;");
mm == f.operator-(g);
f.operator-(g); m.afis("apel
m.afis("apel înîn clar
clar m=f.operator-(g);");
m=f.operator-(g);");
hh == f^g;
f^g; h.afis("adunare
h.afis("adunare cu cu functie
functie membra
membra aa clasei
clasei h=f^g;");
h=f^g;");
hh == f.operator
f.operator ^(g);
^(g); h.afis("apel
h.afis("apel functie
functie inin clar
clar h=f.operator
h=f.operator
^(g);");
^(g);");
kk == f%g;
f%g; k.afis("jumatatea
k.afis("jumatatea distantei
distantei intre
intre 22 pcte
pcte k=f%g
k=f%g ");
");
kk == f.operator%(g);
f.operator%(g); k.afis("apel
k.afis("apel functie
functie în în clar
clar h=f.operator
h=f.operator
^(g);");
^(g);"); returnreturn 0;
0; }}
Sunt prezentate ambele forme de utilizare a funcţiilor
operator: prin operatori sau apel “în clar” a funcţiei operator.
Alţi operatori supraîncărcaţi sunt prezentaţi în exemplul următor:
/*Ex.7.3.1(2.1):definirea
/*Ex.7.3.1(2.1):definirea operatorilor
operatorilor ^^ ++ */ */
class
class Loc2D{int
Loc2D{int x,y;public:
x,y;public: Loc2D()
Loc2D() {x=0;y=0;}
{x=0;y=0;}
Loc2D(int
Loc2D(int x1,
x1, int
int y1)
y1) {x=x1;y=y1;}
{x=x1;y=y1;}
void
void afis(char*
afis(char* str1,
str1, char
char *str2)
*str2)
{cout<<str1<<".x="<<x<<"
{cout<<str1<<".x="<<x<<" ;; "<<str1<<".y="<<y<<"
"<<str1<<".y="<<y<<" ;; "<<str2<<endl;}
"<<str2<<endl;}
friend
friend Loc2D
Loc2D operator
operator +=(Loc2D
+=(Loc2D &c,
&c, Loc2D
Loc2D &b);
&b);
Loc2D
Loc2D operator
operator /=(Loc2D
/=(Loc2D &b){x+=b.x;
&b){x+=b.x; y+=b.y;
y+=b.y; return
return *this;}};
*this;}};//end_class
//end_class
Loc2D
Loc2D operator
operator +=(Loc2D
+=(Loc2D &c,
&c, Loc2D
Loc2D &b)
&b) {{ c.x+=b.x;
c.x+=b.x; c.y+=b.y;
c.y+=b.y; return
return c;}
c;}
Funcţia operator+= este externă clasei. Funcţia operator/=
întoarce obiectul în care ne aflăm, ca şi valoare.

128
7.Supraîncărcarea operatorilor

/*Ex.7.3.1(2.2):apelul
/*Ex.7.3.1(2.2):apelul operatorilor
operatorilor */ */ #include
#include <iostream>
<iostream>
using namespace std; /*copiere Ex7.3.1(2.1)*/
using namespace std; /*copiere Ex7.3.1(2.1)*/
int
int main()
main() {{ Loc2D
Loc2D s(5,6),
s(5,6), t(7,8);
t(7,8); ss +=+= t;
t;
t.afis("t","t:apel
t.afis("t","t:apel normal"); s.afis("s","s:apel normal");
normal"); s.afis("s","s:apel normal");
Loc2D
Loc2D m(5,6),
m(5,6), n(7,8);
n(7,8); mm == operator
operator +=(m,n);
+=(m,n);
n.afis("n",":apel
n.afis("n",":apel explicit");
explicit"); m.afis("m",":apel
m.afis("m",":apel explicit");
explicit");
Loc2D
Loc2D a(1,2),
a(1,2), b(3,4);
b(3,4); bb /=/= a;
a;
a.afis("a",":a-apel
a.afis("a",":a-apel normal");
normal"); b.afis("b",":b-apel
b.afis("b",":b-apel normal");
normal");
Loc2D
Loc2D c(1,2),
c(1,2), d(3,4);
d(3,4); dd == d.operator
d.operator /=(c);
/=(c);
c.afis("c",":c-apel
c.afis("c",":c-apel explicit");
explicit"); d.afis("d",":d-apel
d.afis("d",":d-apel explicit");
explicit");
return
return 0;}
0;}
În continuare este prezentat un exemplu cu operatorul de
asignare/atribuire.
/*Ex.7.3.1(3.1):apelul
/*Ex.7.3.1(3.1):apelul operatorilor
operatorilor */ */
class
class Loc2D{
Loc2D{ int
int x,y;
x,y; public:
public: Loc2D()
Loc2D() {{ x=0;
x=0; y=0;
y=0; }}
Loc2D(int
Loc2D(int x1,
x1, int
int y1)
y1) {{ x=x1;
x=x1; y=y1;
y=y1; }}
void
void afis(char*
afis(char* str1,
str1, char
char *str2)
*str2)
{{ cout<<str1<<".x="<<x<<"
cout<<str1<<".x="<<x<<" ;; "<<str1<<".y="<<y<<"
"<<str1<<".y="<<y<<" ;; "<<str2<<endl;
"<<str2<<endl; }}
Loc2D
Loc2D operator=(Loc2D
operator=(Loc2D t) t) {{ xx == t.x+10;
t.x+10; yy == t.y+10;
t.y+10; return
return *this;
*this; }} };
};
Nu este o simplă copiere a obiectelor, operatorul = este
supraîncărcat pentru a efectua alte operaţii. În lipsa
supraîncărcării operatorul de asignare va copia obiectul din
dreapta în partea stângă.
/*Ex.7.3.1(3.2):apelul
/*Ex.7.3.1(3.2):apelul operatorilor
operatorilor */*/ #include
#include <iostream>
<iostream>
using
using namespace
namespace std;/*copiere
std;/*copiere Ex7.3.1(3.1)*/
Ex7.3.1(3.1)*/ int
int main()
main() {Loc2D
{Loc2D a(4,5),
a(4,5),
b;
b; a.afis("a","initial");
a.afis("a","initial"); b.afis("b","initial");
b.afis("b","initial"); bb == a; a;
b.afis("b","dupa
b.afis("b","dupa atribuire
atribuire -- functie
functie membra
membra aa clasei");
clasei"); return
return 0;
0; }}
În continuare, este prezentat un alt exemplu ce supraîncarcă
operatori de egalitate şi diferit - fiecare întoarce o valoare de
adevăr:
/*Ex.7.3.1(4.1):declararea
/*Ex.7.3.1(4.1):declararea operatorilor
operatorilor == == şi
şi !=
!= */
*/
class
class Loc2D{
Loc2D{ int
int x,y;
x,y; public:Loc2D()
public:Loc2D() {x=0;y=0;}
{x=0;y=0;}
Loc2D(int
Loc2D(int x1,
x1, int
int y1)
y1) {x=x1;y=y1;}
{x=x1;y=y1;} void
void afis(char*
afis(char* str1,
str1, char
char *str2)
*str2)
{cout<<str1<<".x="<<x<<"
{cout<<str1<<".x="<<x<<" ;; "<<str1<<".y="<<y<<"
"<<str1<<".y="<<y<<" ;; "<<str2<<endl;}
"<<str2<<endl;}
bool
bool friend
friend operator
operator !=(Loc2D
!=(Loc2D a,Loc2D
a,Loc2D b);
b); bool
bool operator
operator ==(Loc2D
==(Loc2D b);};
b);};
bool
bool operator
operator !=(Loc2D
!=(Loc2D a,Loc2D
a,Loc2D b)
b) {return
{return ((a.x==b.x)&&(a.y==b.y));}
((a.x==b.x)&&(a.y==b.y));}
bool
bool Loc2D::operator
Loc2D::operator ==(Loc2D
==(Loc2D b)
b) {return
{return ((x==b.x)
((x==b.x) && && (y==b.y));}
(y==b.y));}
Operatorul == întoarce valoarea TRUE dacă cele două obiecte sunt
egale.

129
V.Programarea orientată pe obiecte Limbajul C++

/*Ex.7.3.1(4.2):apelul
/*Ex.7.3.1(4.2):apelul operatorilor
operatorilor */*/ #include
#include <iostream>
<iostream>
using namespace std;/*copiere Ex7.3.1(4.1)*/
using namespace std;/*copiere Ex7.3.1(4.1)*/
int
int main()
main() {Loc2D
{Loc2D s(6,8),t(6,8),m(6,9);
s(6,8),t(6,8),m(6,9);
if(s==t)
if(s==t) cout<<"1. ss ==
cout<<"1. == t"<<endl;
t"<<endl; else
else cout<<"1.
cout<<"1. ss !=
!= t"<<endl;
t"<<endl;
if(s==m)
if(s==m) cout<<"2.
cout<<"2. ss ==
== m"<<endl;
m"<<endl; else
else cout<<"2.
cout<<"2. ss !=
!= m"<<endl;
m"<<endl;
if(s!=t)
if(s!=t) cout<<"3.
cout<<"3. ss !=
!= t"<<endl;
t"<<endl; else
else cout<<"3.
cout<<"3. ss ==
== t"<<endl;
t"<<endl;
if(s!=m)
if(s!=m) cout<<"4.
cout<<"4. ss !=
!= m"<<endl;
m"<<endl; else
else cout<<"4.
cout<<"4. ss ==
== m"<<endl;
m"<<endl;
return
return 0;
0; }}
De asemenea, se poate supraîncărca operatorul de indexare.
Pentru aceasta considerăm o clasă VECT ce conţine un vector cu 5
elemente.
/*Ex.7.3.1(5.1):
/*Ex.7.3.1(5.1): operatorul
operatorul dede indexare
indexare */
*/
class
class VECT
VECT {double
{double a[5];public:VECT()
a[5];public:VECT() {{ for(int
for(int i=0;i<5;i++)
i=0;i<5;i++) a[i]=0;}
a[i]=0;}
VECT(double
VECT(double *a1)
*a1) {{ for(int
for(int i=0;i<5;i++)
i=0;i<5;i++) a[i]=a1[i];
a[i]=a1[i]; }}
~VECT(){
~VECT(){ }} double
double operator[](int
operator[](int i)i)
{if(i==3)
{if(i==3) return
return a[i]+1000;
a[i]+1000; if(i<2)
if(i<2) return
return a[i]-1000;
a[i]-1000; return
return a[i];}};
a[i];}};
Accesarea indexului 3 va întoarce un rezultat mai mare decât
valoarea iniţială cu 1000, iar pentru indecşii strict mai mici
decât 2 va întoarce un rezultat mai mic cu 1000:
/*Ex.7.3.1(5.2):apelul
/*Ex.7.3.1(5.2):apelul operatorului
operatorului de
de indexare
indexare */
*/ #include
#include <iostream>
<iostream>
using namespace std;/*copiere Ex7.3.1(5.1)*/
using namespace std;/*copiere Ex7.3.1(5.1)*/
int
int main(){double
main(){double q[5]={10,20,30,40,50};
q[5]={10,20,30,40,50}; VECT
VECT s(q),e[7];
s(q),e[7];
double s1,s2,s3; s1=s[3]; s2=s[4]; s3=s[1];
double s1,s2,s3; s1=s[3]; s2=s[4]; s3=s[1];
cout<<"s1="<<s1<<"
cout<<"s1="<<s1<<" ;; s2="<<s2<<";
s2="<<s2<<"; s3="<<s3<<endl;
s3="<<s3<<endl; e[4]=s;
e[4]=s;
cout<<"Din
cout<<"Din vector cu obiecte e[4][1]: "<<e[4][1];
vector cu obiecte e[4][1]: "<<e[4][1]; return
return 0;}
0;}
Instrucţiunea s3=s[1]; este echivalentă cu instrucţiunea
s3=s.operator[](1); Accesarea e[4][1] este echivalentă cu apelul
e[4].operator[](1) ( indexul 1 – al doilea index din cadrul celui
de-al cincilea obiect).
7.3.2. Clasă ce conţine o matrice
În continuare este prezentat un exemplu de supraîncărcare a
operatorilor pentru operaţiile cu matrice. În acest sens, declarăm
o clasă Matrice:
/*Ex.7(1.1):definirea
/*Ex.7(1.1):definirea clasei
clasei Matrice
Matrice ce
ce foloseşte
foloseşte supraîncărcarea
supraîncărcarea op*/
op*/
class
class Matrice{
Matrice{ double
double **a;
**a;
int
int m,n;
m,n; //
// dimensiunea
dimensiunea matricei:
matricei: nr.linii
nr.linii xx nr.coloane
nr.coloane
double**
double** aloca(int
aloca(int m2,
m2, int
int n2);
n2);
public:
public: Matrice(int
Matrice(int m1, m1, int
int n1);
n1); //preluare
//preluare dimensiune
dimensiune
Matrice(int
Matrice(int m1,m1, int
int n1,
n1, double
double **a1);
**a1); //preluare
//preluare dim.
dim. matrice+date
matrice+date
Matrice
Matrice operator%(Matrice
operator%(Matrice B); B);
friend
friend Matrice
Matrice operator^(Matrice
operator^(Matrice &A,Matrice
&A,Matrice &B);
&B);
void
void set(int
set(int i,int
i,int j,
j, double
double val);
val);
double
double get(int
get(int i,int
i,int j);
j); //preia
//preia valoarea
valoarea de
de la
la poziţia
poziţia (i,j);
(i,j);
void
void afis();
afis(); };};

130
7.Supraîncărcarea operatorilor

Metoda operator^ este funcţie externă clasei ce are acces la


date de tip private (int m,n; double *a) prin declarea acesteia
friend.
Funcţiile set şi get asigură implementarea mecanismului de
încapsulare a datelor. Datele din cadrul clasei Matrice vor fi
accesate şi modificate numai prin aceste funcţii.
Funcţia afis asigură afişarea unei matrice pe ecran.
/*Ex.7(1.2):definirea
/*Ex.7(1.2):definirea clasei
clasei Matrice
Matrice ce
ce foloseşte
foloseşte supraîncărcarea
supraîncărcarea op*/
op*/
void
void Matrice::afis()
Matrice::afis() {for(int
{for(int i=0;i<m;i++)
i=0;i<m;i++)
{{ for(int
for(int j=0;
j=0; j<n;
j<n; j++)
j++)
cout<<"
cout<<" "<<a[i][j];
"<<a[i][j]; cout<<"\n";}
cout<<"\n";}
}}
Funcţia aloca asigură alocarea dinamică a memoriei pentru o
matrice cu dimensiunea dată prin lista de parametri ai funcţiei
aloca (varianta simplificată fără testele privind alocarea
dinamică).
/*Ex.7(1.3):alocare
/*Ex.7(1.3):alocare dinamică
dinamică memorie
memorie matrice
matrice -- fără
fără teste
teste de
de alocare*/
alocare*/
double**
double** Matrice::aloca(int
Matrice::aloca(int m2,
m2, int
int n2)
n2)
{double
{double **a;
**a;
aa == (double**)new
(double**)new double[m2];
double[m2];
for(
for( int
int i=0;i<m2;i++)
i=0;i<m2;i++) a[i]
a[i] == new
new double[n2];
double[n2];
return
return a;a; }}
Sau varianta completă:
/*Ex7(1.3):alocare
/*Ex7(1.3):alocare dinamică
dinamică memorie
memorie matrice
matrice -- cu
cu teste
teste de
de alocare*/
alocare*/
double**
double** Matrice::aloca(int
Matrice::aloca(int m2,
m2, int
int n2)
n2)
{double
{double **a;
**a;
if(!(a
if(!(a == (double**)new
(double**)new double[m2]))
double[m2]))
{cout<<”alocare
{cout<<”alocare esuată”;return
esuată”;return NULL;}
NULL;}
for(int
for(int i=0;i<m2;i++)
i=0;i<m2;i++)
if(!(a[i]=new
if(!(a[i]=new double[n2]))
double[n2]))
{{ for(int
for(int k=0;k<i;k++)
k=0;k<i;k++) delete[]
delete[] a[k];
a[k];
delete[]
delete[] a;cout<<”alocare esuată”;return
a;cout<<”alocare esuată”;return NULL;
NULL;
}}
return
return a;}
a;}
Clasa Matrice are un constructor cu doi parametri ce alocă
memorie pentru o matrice şi iniţializează elementele matricii cu
valoarea zero
/*Ex.7(1.4):constructor
/*Ex.7(1.4):constructor cu cu 22 parametri*/
parametri*/
Matrice::Matrice(int
Matrice::Matrice(int m1, m1, int
int n1)
n1)
{{ mm == m1;
m1; nn == n1;
n1; aa == aloca(m,n);
aloca(m,n);
for(
for( int
int i=0;
i=0; i<m;
i<m; i++)
i++)
for(
for( int
int j=0;
j=0; j<n;
j<n; j++)
j++) a[i][j]
a[i][j] == 0;
0; }}
şi un constructor cu trei parametri ce preia matricea prin
argument:

131
V.Programarea orientată pe obiecte Limbajul C++

/*Ex.7(1.5):constructor
/*Ex.7(1.5):constructor cu cu 33 parametri*/
parametri*/
Matrice::Matrice(int
Matrice::Matrice(int m1, int
m1, int n1,
n1, double
double **a1)
**a1)
{{ mm == m1;
m1; nn == n1;
n1; aa == aloca(m,n);
aloca(m,n);
for(
for( int
int i=0;
i=0; i<m;
i<m; i++)
i++)
for(int
for(int j=0;j<n;j++)
j=0;j<n;j++) a[i][j]=a1[i][j];
a[i][j]=a1[i][j];
}}
Sunt definite funcţiile ce asigură încapsularea datelor:
/*Ex.7(1.6):funcţii
/*Ex.7(1.6):funcţii ptr.încapsularea
ptr.încapsularea datelor*/
datelor*/
void
void Matrice::set(int
Matrice::set(int i,
i, int
int j,
j, double
double val)
val)
{{ a[i][j]
a[i][j] == val;
val; }}
double
double Matrice::get(int
Matrice::get(int i,i, int
int j)
j)
{{ return
return a[i][j];
a[i][j]; }}
Este definită funcţia operator% ce asigură supraîncărcarea
operaţiei de adunare a două matrice:
/*Ex.7(1.7):supraîncărcarea
/*Ex.7(1.7):supraîncărcarea operatorului
operatorului %% pentru
pentru adunare*/
adunare*/
Matrice
Matrice Matrice::operator%(Matrice
Matrice::operator%(Matrice B) B)
{{ Matrice
Matrice C(m,n);
C(m,n);
for(int
for(int i=0;i<m;i++)
i=0;i<m;i++)
for(int
for(int j=0;j<n;j++)
j=0;j<n;j++) C.a[i][j]=this->a[i][j]+B.a[i][j]
C.a[i][j]=this->a[i][j]+B.a[i][j]
//sau
//sau C.set(i,j,this->get(i,j)+B.get(i,j));
C.set(i,j,this->get(i,j)+B.get(i,j));
return
return C;
C; }}
Se presupune că matricea B este de dimensiune m x n.
/*Ex.7(1.8):supraîncărcarea
/*Ex.7(1.8):supraîncărcarea ptr. ptr. adunare
adunare –– funcţie
funcţie externă*/
externă*/
Matrice
Matrice operator^(Matrice
operator^(Matrice &A,&A, Matrice
Matrice &B)
&B)
{{ Matrice
Matrice C(
C( A.m,
A.m, A.n
A.n );
);
for(
for( int
int i=0;
i=0; i<A.m;
i<A.m; i++
i++ ))
for(
for( int
int j=0;
j=0; j<A.n;
j<A.n; j++
j++ )) C.set(i,
C.set(i, j,j, A.get(i,j)+B.get(i,j)
A.get(i,j)+B.get(i,j) );
);
return
return C;C; }}
Exemplul complet este:
/*Ex.7(1.9):supraîncărcarea
/*Ex.7(1.9):supraîncărcarea ptr. ptr. adunare
adunare –– funcţie
funcţie externă*/
externă*/
#include
#include <iostream>
<iostream>
using
using namespace
namespace std;
std;
/*Se
/*Se copiază:Ex7(1.1),(1.2),(1.3),(1.4),(1.5),(1.6),(1.7),(1.8)*/
copiază:Ex7(1.1),(1.2),(1.3),(1.4),(1.5),(1.6),(1.7),(1.8)*/
int
int main()
main() //ex.
//ex. de
de utilizare
utilizare ptr.
ptr. oo matrice
matrice 3x2
3x2
{{ double
double **c;
**c; cc == (double**)new
(double**)new double[3];
double[3];
for(int
for(int i=0;
i=0; i<3;
i<3; i++)
i++) c[i]
c[i] == new
new double[2];
double[2];
c[0][0]=5;c[0][1]=4;c[1][0]=25;c[1][1]=24;
c[0][0]=5;c[0][1]=4;c[1][0]=25;c[1][1]=24; c[2][0]=15;
c[2][0]=15; c[2][1]=14;
c[2][1]=14;
Matrice
Matrice X(3,2,c),
X(3,2,c), Y(3,2,c),
Y(3,2,c), Z(3,2);
Z(3,2); //Z=X+Y;
//Z=X+Y;
Z=X%Y;
Z=X%Y; cout<<"X"<<endl;
cout<<"X"<<endl; X.afis();
X.afis(); cout<<"Y"<<endl;
cout<<"Y"<<endl; Y.afis();
Y.afis();
cout<<"Z=X%Y"<<endl;
cout<<"Z=X%Y"<<endl; Z.afis();
Z.afis();
return
return 0;
0; }}

132
8.Fluxuri de intrare/ieşire

8. Fluxuri de intrare/ieşire

8.1. Fluxuri de intrare/ieşire şi obiecte standard


Stream-urile au în principal rolul de a abstractiza
operaţiile de intrare-ieşire. Acestea oferă metode de scriere şi
citire a datelor, independente de dispozitivul I/O şi chiar
independente de platformă. Aceste stream-uri tratează într-un mod
unitar lucrul cu diverse periferice atât standard (tastatură,
display), cât şi nestandard (plotere, controllere etc.).
Stream-urile încapsulează (ascund) problemele specifice dispo-
zitivului cu care se lucrează, sub librăria standard iostream. În
C++ stream-urile au fost implementate utilizând clase, după cum
urmează:
➢ clasa streambuf gestionează buffer-ele;
➢ clasa ios este clasa de bază pentru clasele de stream-uri de
intrare şi de ieşire. Clasa ios are ca variabilă membru un
obiect de tip streambuf;
➢ clasele istream şi ostream sunt derivate din ios;
➢ clasa iostream este derivată din istream şi ostream (moştenire
multiplă) şi oferă metode pentru lucrul cu dispozitivul standard
de intrare/ieşire;
➢ clasa fstream oferă metode pentru operaţii cu fişiere.
Un exemplu de formatare a datelor:
/*Ex.
/*Ex. 8.1(1):
8.1(1): operaţii
operaţii de
de formatare
formatare aa datelor
datelor */ */ #include
#include <iostream>
<iostream>
#include
#include <iomanip>
<iomanip>
using
using namespace
namespace std;
std;
class
class AA {{ int
int m,n;
m,n; double
double d;
d; public:
public:
A(
A( int
int m1,
m1, int
int n1,
n1, double
double d1
d1 )) {{ mm == m1;
m1; nn == n1;
n1; dd == d1;}
d1;}
void
void afis()
afis() {cout<<setfill('*')<<"1.m="<<setw(6)<<m<<endl;
{cout<<setfill('*')<<"1.m="<<setw(6)<<m<<endl;
cout<<setfill('*')<<"2.m="<<m<<setw(6)<<endl;
cout<<setfill('*')<<"2.m="<<m<<setw(6)<<endl; // // nu
nu are
are efect
efect
cout<<"3.m="<<setfill('*')<<setw(6)<<m<<endl;
cout<<"3.m="<<setfill('*')<<setw(6)<<m<<endl; //rămâne //rămâne tottot width
width 66
cout<<"4.m="<<setfill('
cout<<"4.m="<<setfill(' ')<<setw(6)<<m<<endl;
')<<setw(6)<<m<<endl; //width //width este
este tot
tot 66
cout<<"5.m="<<setw(6)<<m<<endl;
cout<<"5.m="<<setw(6)<<m<<endl; cout<<"6.d="<<setw(15)<<d<<endl;
cout<<"6.d="<<setw(15)<<d<<endl;
cout<<"7.d="<<setprecision(6)<<d<<endl;
cout<<"7.d="<<setprecision(6)<<d<<endl; // // rotunjire
rotunjire
cout<<"8.d="<<setprecision(9)<<d<<endl;
cout<<"8.d="<<setprecision(9)<<d<<endl; // // rotunjire
rotunjire
cout<<"9.n="<<n<<hex<<"
cout<<"9.n="<<n<<hex<<" n="<<n<<endl;}
n="<<n<<endl;} }; };
int
int main()
main() {{ AA x(3,28,7.123456789);
x(3,28,7.123456789); x.afis();
x.afis();
int
int k;
k; cin>>k;
cin>>k; cout<<dec<<"k="<<k<<endl;return
cout<<dec<<"k="<<k<<endl;return 0; 0; }}
Aceste clase, prin instanţiere, oferă câteva obiecte stan-
dard. Astfel, când un program C++, care include biblioteca
iostream, este lansat în execuţie, sunt create şi iniţializate
automat următoarele obiecte:
✗ cin - gestionează intrarea de la dispozitivul standard de
intrare (tastatura);

133
V.Programarea orientată pe obiecte Limbajul C++

✗cout - gestionează ieşirea către dispozitivul standard de


ieşire (ecranul);
✗ cerr - gestionează ieşirea către dispozitivul standard de
eroare.
Altfel spus:
☑ INPUT STREAM (fluxuri de intrare): spre memoria RAM (IN RAM).
☑ OUTPUT STREAM (fluxuri de ieşire): tot ce pleacă din memoria
RAM (OUT RAM).
8.2. Operaţii de intrare/ieşire cu fişiere
Lucrul cu fişiere se realizează prin intermediul clasei
ifstream pentru citire, respectiv ofstream pentru scriere. Pentru a
le utiliza, aplicaţiile trebuie să includă biblioteca definită în
fstream.h. Clasele ofstream şi ifstream sunt derivate din clasa
iostream, ca urmare toţi operatorii şi toate funcţiile descrise mai
sus sunt moştenite şi de această clasă.
Este prezentat un exemplu de scriere şi citire în fişier:
/*Ex.
/*Ex. 8.2(1.1):declarare
8.2(1.1):declarare clasă
clasă AA pentru
pentru scrierea
scrierea şişi citirea
citirea fişier
fişier */*/
class
class A{A{ public:
public: void
void scrie_in_fisier(char*
scrie_in_fisier(char* numefis)
numefis)
{{ ofstream
ofstream fis1(numefis);
fis1(numefis);
if(!fis1)
if(!fis1) {cout<<"fisierul
{cout<<"fisierul "<<numefis<<"nu
"<<numefis<<"nu aa putut
putut fi
fi deschis
deschis pentru
pentru
scriere"<<endl;
scriere"<<endl; return;}
return;}
fis1<<"ABC"<<"
fis1<<"ABC"<<" 12 12 34"<<endl;
34"<<endl; //au
//au fost
fost scrise
scrise două
două secvenţe
secvenţe
fis1<<"a=";
fis1<<"a="; int
int a;
a; cout<<"a=";
cout<<"a="; cin>>a;
cin>>a;
fis1<<a<<endl;
fis1<<a<<endl; }//prel.tastatură
}//prel.tastatură
void
void citeste_din_fisier(char*
citeste_din_fisier(char* numefis)
numefis)
{ifstream
{ifstream fis2(numefis);//instanţiem
fis2(numefis);//instanţiem clasaclasa pentru
pentru citirea
citirea dindin fişier
fişier
char
char m[100];
m[100]; if(!fis2)
if(!fis2) {cout<<"fişierul
{cout<<"fişierul "<<numefis<<"
"<<numefis<<" nu nu aa putut
putut fi
fi
deschis
deschis pentru
pentru citire"<<endl;
citire"<<endl; return;}
return;}
while(!fis2.eof())//cat
while(!fis2.eof())//cat timptimp NU
NU s-a
s-a ajuns
ajuns la
la sfârşitul
sfârşitul fişierului
fişierului
{{ fis2>>m;
fis2>>m; if(fis2.eof())
if(fis2.eof()) break;
break; cout<<m<<endl;}
cout<<m<<endl;} }} }; };
Instanţiem clasa ofstream pentru scriere în fişier. Obiectul
este fis1, iar constructorul preia numele şi calea acestuia. O altă
variantă ar fi ofstream fis1(numefis,ios::app); unde ios::app este
utilizat pentru adăugarea la sfârşitul fişierului (fără
trunchiere). De asemenea, se putea scrie şi ofstream
fis1(numefis,ios::app|ios::out); caz în care adaugă la sfârşitul
fişierului, dar permite şi operaţia de citire din acesta. Dacă
fişierul nu a putut fi deschis, fis1 va avea valoarea NULL.
#include
#include <iostream>/*Ex.8.2(1.2):
<iostream>/*Ex.8.2(1.2): instanţiere
instanţiere clasă
clasă pentru
pentru scrierea
scrierea şi
şi
citirea din fişier */ #include <iomanip>
citirea din fişier */ #include <iomanip>
#include
#include <fstream>
<fstream>
using
using namespace
namespace std;
std; /*se
/*se copiază
copiază Ex
Ex 8.2(1.1)*/
8.2(1.1)*/
int
int main()
main() {A
{A s;
s; s.scrie_in_fisier("C:/a01.txt");
s.scrie_in_fisier("C:/a01.txt");
s.citeste_din_fisier("C:/a01.txt");
s.citeste_din_fisier("C:/a01.txt"); return
return 0;
0; }}

134
8.Fluxuri de intrare/ieşire

În exemplul de mai sus ultimele secvenţe conţin şi carac-


terul spaţiu. Separatorul pentru date în cadrul fişierelor text
este caracterul spaţiu (cod ASCII:32). În bucla while se realizează
citirea datelor din fişier atâta timp cât nu s-a ajuns la sfârşitul
fişierului.

9. Introducere în funcţii şi clase template


O funcţie template este o funcţie şablon, având unul sau mai
mulţi parametri formali de un tip generic. Funcţiile template sunt
utilizate atunci când dorim să utilizăm aceeaşi funcţie pentru
tipuri de date diferite, altfel spus acelaşi cod să poată prelucra
tipuri diferite de date. Mecanismul template permite definirea o
singură dată a şablonului de funcţii, după care se generează
automat funcţiile propriu-zise în concordanţă cu necesităţile de
utilizare
O definiţie a unei funcţii de tip template poate avea forma:
template
template <class
<class T>
T> unde T este tipul generic de dată. În
void
void f1(T
f1(T t1,
t1, TT t2)
t2) {{ ...
... }}
cadrul funcţiilor template nu putem avea conversii de dată
(operaţii de tip cast).
Un cod template este compilat în două faze. În prima fază
sunt verificate erorile de sintaxă, iar în faza următoare este
generat codul corespunzător fiecărui tip de instanţe (pentru
fiecare tip de dată din cadrul apelurilor funcţiei).
În continuare este prezentat un exemplu de funcţie template.
#include <iostream> // ex. 8.1
using namespace std;
template <class T> // T - tip generic de data (denumit, de exemplu: T)
T DeterminaMaximul (T a, T b)
{T rezultat;
rezultat = (a>b)? a : b; //dacă a>b valoarea este a altfel este b
return rezultat;
}
template <class T>
T DeterminaSuma (T a, T b)
{ return a + b;
}
int main () {
int i = -7, j = 6, max_int, suma_int;
long l = 10, m = 5, max_long, suma_long;
double s = 15.4, t = 7.2, max_double, suma_double;
max_int = DeterminaMaximul<int>(i,j);
max_long = DeterminaMaximul<long>(l,m);
max_double = DeterminaMaximul<double>(s,t);
suma_int = DeterminaSuma<int>(i,j);
suma_long = DeterminaSuma<long>(l,m);

135
V.Programarea orientată pe obiecte Limbajul C++

suma_double = DeterminaSuma<double>(s,t);
cout <<"max_int: "<< max_int <<" suma_int: "<<suma_int<< endl;
cout <<"max_long: "<< max_long <<" suma_long: "<<suma_long<< endl;
cout <<"max_double: "<< max_double <<" suma_double: "<<suma_double<<
endl;
return 0;}

O clasă template defineşte un şablon pe baza căruia se pot


genera clase propriu-zise. O clasă template se mai numeşte şi
clasă generică sau metaclasă (o clasă de clase), reprezentând un
nivel de abstractizare ridicat.
Exemplul de mai sus este pus într-o structură de clase:
#include <iostream>
using namespace std;
template <class T> class CALCULEAZA
{ public:
T maximum(T x, T y);
T adunare(T x, T y);
};
template <class T> T CALCULEAZA<T>::maximum(T x, T y)
{ return (x > y) ? x : y;
}
template <class T> T CALCULEAZA<T>::adunare(T x, T y)
{ return x + y;
}
int main () {
CALCULEAZA<int> cx;
CALCULEAZA<double> cy;
int i = -7, j = 6, max_int, suma_int;
long l = 10, m = 5, max_long, suma_long;
double s = 15.4, t = 7.2, max_double, suma_double;
max_int = c x.maximum(i,j); max_long = cx.maximum(l,m);
max_double = cx.maximum(s,t);
suma_int = cx.adunare(i,j); suma_long = cx.adunare(l,m);
suma_double = cx.adunare(s,t);
cout <<"cx -- max_int: "<< max_int <<" suma_int: "<<suma_int<< endl;
cout <<"cx -- max_long: "<< max_long <<" suma_long: "<<suma_long<<
endl;
cout <<"cx:max_double: "<< max_double <<" suma_double:
"<<suma_double<<endl;
max_int = cy.maximum(i,j); max_long = cy.maximum(l,m);
max_double = cy.maximum(s,t);
suma_int = cy.adunare(i,j); suma_long = cy.adunare(l,m);
suma_double = cy.adunare(s,t);
cout <<"cy -- max_int: "<< max_int <<" suma_int: "<<suma_int<< endl;
cout <<"cy -- max_long: "<< max_long <<" suma_long: "<<suma_long<<
endl;
cout<<"cy:max_double: "<< max_double <<" suma_double:
"<<suma_double<<endl;
return 0; }

136
10.Tratarea excepţiilor

10. Tratarea excepţiilor


În timpul executării unui program pot apărea situaţii în
care sunt generate erori, datorate de multe ori depăşirii puterii
de reprezentare a numerelor, de operaţii nepermise, de depăşiri ale
limitelor alocate etc. Ca urmare, este necesară anticiparea unor
posibile excepţii, de multe ori provenite de la preluarea incorectă
a datelor. Limbajul C++ oferă trei cuvinte cheie pentru a gestiona
o excepţie: try, catch şi throw. Cuvântul cheie try este urmat de
blocul ce se execută şi care poate genera o eroare în execuţie:
✗ try { bloc ce se execută în mod normal}
Acest cuvânt cheie permite aplicaţiei să anticipeze un
comportament anormal şi va încerca să trateze posibilele
erori;
✗ catch { bloc ce se execută în cazul în care apare o
excepţie/eroare }
Acest cuvânt cheie este urmat, între acolade, de blocul ce
se execută în cazul unei excepţii/erori;
✗ throw transmite excepţia, în vederea tratării acesteia,
către sistemul de operare. La întâlnirea lui throw, codul
următor nu se mai execută:
/*Ex.
/*Ex. 9(1):
9(1): utilizarea
utilizarea excepţiilor
excepţiilor */ */
using
using namespace
namespace std; std;
#include<iostream>
#include<iostream>
int
int main()
main() {char
{char *a;*a; int
int b=1,c=5,d=0;//ptr.test
b=1,c=5,d=0;//ptr.test se se va
va alege
alege val.ptr.d
val.ptr.d
try{
try{ bb == 7;
7; if(!d)
if(!d) throwthrow (char*)"Operatie
(char*)"Operatie esuata!";
esuata!";
bb == 6;
6; cc == b/d;
b/d; //lăsat
//lăsat singur
singur ==>
==> excepţie
excepţie primită
primită dede la
la S.O.
S.O.
if
if (d>-5
(d>-5 &&&& d<0)
d<0) throw
throw -1;
-1; b=5;
b=5; if(d<=-5)
if(d<=-5) throw
throw 2.14;
2.14; b=4;
b=4; }}
catch(char
catch(char ** strstr ){cout
){cout <<
<< "Exceptia
"Exceptia 11 primita
primita este:
este: "<<
"<< str
str <<
<< '\n';}
'\n';}
catch(int
catch(int a) a) /*daca
/*daca arg.
arg. este
este de
de tip
tip int.*/{cout<<"Exceptia
int.*/{cout<<"Exceptia 2"<<endl;}
2"<<endl;}
catch(...)
catch(...) {{ cout<<"Toate
cout<<"Toate exceptiile
exceptiile netratate"<<endl;}
netratate"<<endl;}
cout<<"b="<<b;
cout<<"b="<<b; return
return 0;}
0;}
În secvenţa dată de catch se intră ca urmare a lui throw din
blocul curent sau dintr-o bibliotecă ce corespunde unei funcţii
utilizate în acest bloc şi al cărui throw întoarce un tip de dată
în concordanţă cu tipul din argumentul lui catch.

137
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

VI. Dezvoltarea interfeţei client cu ajutorul


bibliotecilor
1. Biblioteca wxWidgets şi dezvoltarea interfeţelor grafice
în C++
Biblioteca wxWidgets este o bibliotecă cu clase C++ ce
permite dezvoltarea interfeţelor grafice. Această bibliotecă este
cross-platform şi open sources. În acest capitol vor fi exem-
plificate câteva din clasele frecvent utilizate.
1.1. Structura unei aplicaţii
Pentru a observa structura unei aplicaţii ce utilizează
clase wxWidgets putem alege următorul template pentru aplicaţie:
“Workspace> NewProject> GUI> Executable> Executable (wxWidgets
enabled)” (GUI: Graphics User Interface).
La aplicaţiile de tip consolă, punctul de intrare în program
era dat de intrarea în funcţia main(), iar aici nu se observă o
astfel de funcţie. Observăm o clasă wxApp şi o metodă OnInit().
Lansarea aplicaţiei corespunde cu execuţia metodei OnInit().
Ieşirea din metoda OnInit() cu valoarea de adevăr FALSE (return
false) corespunde ieşirii din aplicaţie. Dacă ieşirea din metoda
OnInit() se realizează cu
valoarea de adevăr TRUE, /*Ex.1(1):intrarea
/*Ex.1(1):intrarea în în aplicaţie
aplicaţie */
*/
aplicaţia intră într-o buclă #include
#include <wx/wx.h>
<wx/wx.h>
class O_aplicatie
de citire a eve-nimentelor {class O_aplicatie :: public
public wxApp
wxApp
{ public:
public:
(majoritatea eveni-mentelor virtual
virtual bool
bool OnInit(){return
OnInit(){return true;}
true;}
sunt de la dispo-zitivele }; };
standard, acestea sunt de IMPLEMENT_APP(O_aplicatie);
IMPLEMENT_APP(O_aplicatie);
tipul: apăsarea unei taste,
ridicarea unei taste, mişcarea mouse-ului, click sau dublu click pe
unul din butoanele mouse-ului etc). Exemplul 1(1) nu afişează
nimic, dar constituie structura minimală a unei aplicaţii ce
utilizează clase wxWidgets.
În continuare este prezentat un exemplu 2(1-1) ce conţine o
fereastră cu două butoane. Prin realizarea unui click cu mouse-ul
pe unul din butoane se afişează un mesaj, iar celălat buton
deschide o fereastră ce conţine un text.
Clasa primaAplicatie este derivată din clasa wxApp şi
conţine metoda OnInit(), cele două funcţii void apasaButon_1
(wxCommandEvent& event) şi void apasaButon_2 (wxCommandEvent&
event), ce corespund evenimentului clik pe buton cu mouse-ul. În
cadrul primei funcţii este apelată o metodă pentru afişarea unui

138
1.Biblioteca wxWidgets şi dezvoltarea interfeţelor grafice în C++

mesaj – wxMessageBox.
/*Ex.2(1-1):*/
/*Ex.2(1-1):*/ class
class primaAplicatie
primaAplicatie :: public
public wxApp
wxApp
{public:virtual
{public:virtual bool
bool OnInit();
OnInit();
void
void apasaButon_1(wxCommandEvent&
apasaButon_1(wxCommandEvent& event){wxMessageBox("
event){wxMessageBox("Mesaj BUTON!");}
Mesaj BUTON! ");}
void
void apasaButon_2(wxCommandEvent&
apasaButon_2(wxCommandEvent& event)
event)
{{ wxFrame
wxFrame *fereastra_2
*fereastra_2 == new
new wxFrame(
wxFrame( NULL,ID_ANY,wxT("Titlu-2"),
NULL,ID_ANY,wxT("Titlu-2"),
wxDefaultPosition,
wxDefaultPosition, wxSize(150,200));
wxSize(150,200));
fereastra_2->SetBackgroundColour(
fereastra_2->SetBackgroundColour( wxColour(
wxColour( 118,
118, 235,
235, 158
158 )) );
);
wxStaticText
wxStaticText *L1
*L1 == new
new wxStaticText(
wxStaticText( fereastra_2,
fereastra_2, wxID_ANY,
wxID_ANY,
_("TESTE"));
_("TESTE")); fereastra_2->Show();}
fereastra_2->Show();} }; };
În cadrul funcţiei void apasaButon_2 (wxCommandEvent& event)
este instanţiată clasa wxFrame, ce realizează construcţia unei
ferestre. Primul patrametru pentru constructorul clasei wxFrame
este NULL, ceea ce semnifică că această fereastră nu este ataşată
unei alte ferestre. Parametrul ID_ANY semnifică faptul că această
fereastră nu are ataşat un identificator (raportat la dezvoltator).
Şirul “Titlu-2" reprezintă titlul ce va fi afişat în bara feres-
trei. Funcţia wxT(“...”) este utilizată pentru conversia şirului de
simboluri unicode într-un obiect de tip wxString (clasa wxString
gestionează şirurile de simboluri/caractere). Nu a fost stabilită o
anumită poziţie a ferestrei şi a fost pusă constanta
wxDefaultPosition. Dimensiunea ferestrei a fost stabilită la 150
pixeli pe orizontală şi 200 de pixeli pe verticală. În acest sens
dimensiunea ferestrei a fost preluată printr-un constructor al
clasei wxSize – wxSize(150,200).
Culoarea pentru fundalul ferestrei a fost stabilită prin
utilizarea celor trei canale RGB (RedGreenBlue), fiecare având
valori cuprinse între 0 şi 255, 0 semnificând lipsa culorii pe
canalul respectiv. Preluarea culorii pentru cele trei canale se
realizează prin constructorul wxColour(118, 235, 158). Funcţia
SetBackgroundColour(wxColour(118, 235, 158)) setează fundalul
ferestrei.
În cadrul metodei void apasaButon_2 (wxCommandEvent& event)
a fost instanţiată o clasă wxStaticText, ce construieşte un control
de tip text – etichetă. Primul parametru din constructorul clasei
identifică ferestra/obiectul căruia îi aparţine, în acest caz
fereastra_2. Al doilea parametru, având valoarea wxID_ANY, arată
faptul că nu există identificator atribuit acestui control.
Scrierea _("TESTE ANSI") este pentru caractere din setul
ASCII, iar wxT("TESTE diacritice:ĂÎŞŢÂ") este pentru codificarea
UNICODE.
Metoda OnInit() este explicitată în 2(2-2). Este instanţiată
clasa wxFrame pentru prima fereastră din cadrul aplicaţiei, apoi
este setată culoarea de fundal pentru fereastră.
În continuare sunt realizate două instanţe ale clasei
wxButton. Pentru fiecare din cei doi constructori primul parametru

139
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

este reprezentat de adresa ferestrei în care se construieşte


controlul ce implementeză un buton. Al doilea parametru (aici
numerele 10, 11) este reprezentat de identificatorul pentru fiecare
control. Al treilea parametru din constructor este reprezentat de
textul afişat pe buton. Următorii parametrii sunt reprezentaţi de
poziţia controlului şi de dimensiunea acestuia.
/*Ex-2(2-1):*/
/*Ex-2(2-1):*/ bool
bool primaAplicatie::OnInit()
primaAplicatie::OnInit()
{wxFrame
{wxFrame *fereastra_1=new
*fereastra_1=new wxFrame(
wxFrame( NULL,
NULL, -1,
-1, wxT("Un
wxT("Un titlu-1"),
titlu-1"),
wxDefaultPosition,
wxDefaultPosition, wxSize(
wxSize( 250,
250, 200)
200) );
);
fereastra_1->SetBackgroundColour(
fereastra_1->SetBackgroundColour( wxColour(
wxColour( 218,
218, 235,
235, 158
158 )) );
);
wxButton
wxButton *b1,*b2;
*b1,*b2; b1
b1 == new
new wxButton(
wxButton( fereastra_1,11,
fereastra_1,11,
wxT("Mesaj
wxT("Mesaj nou!"),
nou!"), wxPoint(90,20),
wxPoint(90,20), wxSize(80,50));
wxSize(80,50));
b2=new
b2=new wxButton(
wxButton( fereastra_1,
fereastra_1, 10,10, wxT("Fereastră
wxT("Fereastră nouă"),
nouă"),
wxPoint(10,20),
wxPoint(10,20), wxSize(100,100)
wxSize(100,100) );
);
Connect(11,
Connect(11, wxEVT_COMMAND_BUTTON_CLICKED,
wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler
wxCommandEventHandler (primaAplicatie::apasaButon_1)
(primaAplicatie::apasaButon_1) ); );
Connect(10,
Connect(10, wxEVT_COMMAND_BUTTON_CLICKED,
wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler
wxCommandEventHandler (primaAplicatie::apasaButon_2)
(primaAplicatie::apasaButon_2) ); );
fereastra_1->Show();
fereastra_1->Show(); return
return true;}
true;}

Legătura între un control şi


funcţia ce răspunde la un anumit /*Ex-2(3-1):*/
/*Ex-2(3-1):*/
eveniment este realizată de către #include
#include <wx/wx.h>
<wx/wx.h>
funcţiile Connect detaliate /*Se
/*Se copiază
copiază Ex-2(1-1):*/
Ex-2(1-1):*/
în IMPLEMENT_APP(primaAplicatie);
IMPLEMENT_APP(primaAplicatie);
capitolul următor. Aplicaţia comple- /*Se
tă este dată de exemplul 2(3). În /*Se copiază
copiază Ex-2(2-1):*/
Ex-2(2-1):*/
acest exemplu poziţionarea controa-lelor este absolută, poziţia
fiecărui control este transmisă ferestrei părinte (fereastra pe
care sunt ataşate controalele grafice). Pentru a avea o
poziţionare relativă pentru controale, se poate utiliza clasa
wxBoxSizer.
/*Ex.2(1-2):*/
/*Ex.2(1-2):*/ class
class primaAplicatie
primaAplicatie :: public
public wxApp
wxApp
{public:
{public: virtual
virtual bool
bool OnInit();
OnInit();
void
void apasaButon_1(wxCommandEvent&
apasaButon_1(wxCommandEvent& event){wxMessageBox("
event){wxMessageBox("Mesaj BUTON!");}
Mesaj BUTON! ");}
void
void apasaButon_2(wxCommandEvent&
apasaButon_2(wxCommandEvent& event) event) {wxFrame
{wxFrame *fereastra_2
*fereastra_2 == newnew
wxFrame( NULL,,wxID_ANY,wxT
wxFrame(NULL wxID_ANY,wxT(("titlu-2"
"titlu-2"), wxDefaultPosition,,wxSize
),wxDefaultPosition wxSize(150,200));
(150,200));
fereastra_2->SetBackgroundColour(
fereastra_2->SetBackgroundColour( wxColour(wxColour( 118,
118, 235,
235, 158
158 ));
));
wxStaticText
wxStaticText *L1*L1 == new
new wxStaticText( fereastra_2,, wxID_ANY
wxStaticText( fereastra_2 wxID_ANY,, _( "TESTE"));
_("TESTE" ));
wxBoxSizer
wxBoxSizer *bSizer_2
*bSizer_2 == newnew wxBoxSizer(
wxBoxSizer( wxVERTICAL
wxVERTICAL ); );
fereastra_2->SetSizer(
fereastra_2->SetSizer( bSizer_2
bSizer_2 ););
bSizer_2->Add(
bSizer_2->Add( L1, L1, 0, wxALIGN_CENTER|wxALL,, 55 );fereastra_2->Show();}};
0, wxALIGN_CENTER|wxALL );fereastra_2->Show();}};
Prin instanţierea clasei wxBoxSizer, se definesc “layout”-
uri (moduri de afişare) pentru controalele din cadrul ferestrei. În
acest sens, fiecare control trebuie adăugat în acel ”container” ce
defineşte modul de aranjare a controalelor.
Exemplul 2(2-1) devine:

140
1.Biblioteca wxWidgets şi dezvoltarea interfeţelor grafice în C++

/*Ex.2(2-2):*/bool
/*Ex.2(2-2):*/bool primaAplicatie::OnInit()
primaAplicatie::OnInit() {{ wxFrame
wxFrame *fereastra_1;
*fereastra_1;
fereastra_1 = new wxFrame( NULL, -1, wxT("Un titlu-1"),
fereastra_1 = new wxFrame( NULL, -1, wxT("Un titlu-1"),
wxDefaultPosition,
wxDefaultPosition, wxSize(
wxSize( 250,
250, 200)
200) );
);
fereastra_1->SetBackgroundColour(
fereastra_1->SetBackgroundColour( wxColour(
wxColour( 218,
218, 235,
235, 158
158 )) );
);
wxButton
wxButton *b1,*b2;
*b1,*b2; b1=new
b1=new wxButton(
wxButton( fereastra_1,11,wxT("Mesaj
fereastra_1,11,wxT("Mesaj nou!"));
nou!"));
b2
b2 == new
new wxButton(
wxButton( fereastra_1,
fereastra_1, 10,10, wxT("Fereastră
wxT("Fereastră nouă")
nouă") ););
wxBoxSizer
wxBoxSizer *bSizer_1
*bSizer_1 == new
new wxBoxSizer(
wxBoxSizer( wxHORIZONTAL
wxHORIZONTAL );
);
bSizer_1->Add(
bSizer_1->Add( b1, 0, wxALIGN_CENTER|wxALL, 55 );
b1, 0, wxALIGN_CENTER|wxALL, );
bSizer_1->Add(
bSizer_1->Add( b2,b2, 0,
0, wxALIGN_CENTER|wxALL,
wxALIGN_CENTER|wxALL, 55 ); );
fereastra_1->SetSizer(
fereastra_1->SetSizer( bSizer_1
bSizer_1 );); fereastra_1->Layout();
fereastra_1->Layout();
Connect(11,
Connect(11, wxEVT_COMMAND_BUTTON_CLICKED,
wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(primaAplicatie::apasaButon_1)
wxCommandEventHandler(primaAplicatie::apasaButon_1) ); );
Connect(10,
Connect(10, wxEVT_COMMAND_BUTTON_CLICKED,
wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(primaAplicatie::apasaButon_2)
wxCommandEventHandler(primaAplicatie::apasaButon_2) ); );
fereastra_1->Show();
fereastra_1->Show();return
return true;}
true;}
iar exemplul 2(3-1) devine:
/*Ex.2(3-2):*/
/*Ex.2(3-2):*/ #include
#include <wx/wx.h>
<wx/wx.h>
/*Se
/*Se copiază
copiază Ex-2(1-1):*/
Ex-2(1-1):*/ IMPLEMENT_APP(primaAplicatie);
IMPLEMENT_APP(primaAplicatie);
/*Se
/*Se copiază
copiază Ex-2(2-1):*/
Ex-2(2-1):*/
Ultima implementare prezintă avantajul posibilităţii de afi-
şare completă a controalelor pe o varietate mare de rezoluţii de
afişare.
1.2. Gestiunea evenimentelor
În exemplul anterior evenimentul de click al mouse-ului pe
un buton era ataşat unei funcţii. Această legătură se poate realiza
prin funcţia Connect din cadrul metodei OnInit(). Primul parametru
al funcţiei este reprezentat de identificatorul (ales de către noi)
pentru controlul căruia îi aparţin evenimentele. Al doilea
parametru este reprezentat de o constantă ce reprezintă “codul
numeric” al evenimentului, în acest caz wxEVT_COMMAND_BUTTON_CLICKED.
Funcţia wxCommandEventHandler preia ca parametru metoda ce
prelucrează evenimentul (metoda ce trebuie să fie executată la
apariţia evenimentului). Este necesar ca funcţiile ce tratează
evenimentul să preia ca parametru o referinţă la o clasă de tip
eveniment (wxCommandEvent&). Din aceeaşi categoria fac parte şi
clasele wxKeyEvent şi wxMouseEvent, ce sunt specializate pe eveni-
mentele de la tastatură, respectiv de la mouse.
Exemplul anterior, în care evenimentele sunt la nivel de
aplicaţie, îl vom modifica astfel încât evenimentele să fie la
nivel de fereastră. În acest sens avem două variante, una cu
funcţii de conectare între evenimente şi metode la nivel de
constructor de fereastră şi de deconectare la nivelul
destructorului ferestrei şi o altă variantă (nu este singulară) cu
tabele de evenimente.

141
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

În continuare este prezentat exemplul în care avem funcţii


de conectare în cadrul constructorului ferestrei şi de deconectare
în cadrul destructorului ferestrei.
/*Ex.2(1-3):*/
/*Ex.2(1-3):*/ class
class adouaFereastra
adouaFereastra :: public
public wxFrame
wxFrame
{{ wxStaticText
wxStaticText *L1;
*L1; public:
public:
adouaFereastra()
adouaFereastra() :: wxFrame(
wxFrame( NULL,
NULL, wxID_ANY,
wxID_ANY, wxT("Un
wxT("Un titlu-2"),
titlu-2"),
wxDefaultPosition,
wxDefaultPosition, wxSize(
wxSize( 150,
150, 200)
200) )) {{
this->SetBackgroundColour(
this->SetBackgroundColour( wxColour(
wxColour( 118,
118, 235,
235, 158
158 )) );
);
L1
L1 == new
new wxStaticText(
wxStaticText( this,
this, wxID_ANY,
wxID_ANY, _("TESTE"));
_("TESTE"));
wxBoxSizer *bSizer_2=new
wxBoxSizer wxBoxSizer((wxVERTICAL
*bSizer_2=new wxBoxSizer wxVERTICAL);this-> SetSizer(bSizer_2);
);this->SetSizer (bSizer_2);
bSizer_2->Add(
bSizer_2->Add( L1,0, wxALIGN_CENTER|wxALL,5);}
L1,0,wxALIGN_CENTER|wxALL ,5);} };};
Iar declaraţia clasei ce conţine fereastra cu evenimente:
/*Ex.2(2-3):*/
/*Ex.2(2-3):*/ class
class primaFereastra
primaFereastra :: public
public wxFrame
wxFrame
{{ public:
public: wxButton
wxButton *b1,*b2;
*b1,*b2;
void
void apasaButon_1(wxCommandEvent&
apasaButon_1(wxCommandEvent& event);
event);
void
void apasaButon_2(wxCommandEvent&
apasaButon_2(wxCommandEvent& event);
event);
primaFereastra();
primaFereastra(); ~primaFereastra();
~primaFereastra(); };};
În continuare explicităm metodele din cadrul clasei:
/*Ex.2(3-3):*/
/*Ex.2(3-3):*/ primaFereastra::primaFereastra():wxFrame(
primaFereastra::primaFereastra():wxFrame( NULL, NULL, -1,
-1,
wxT("Un
wxT("Un titlu-1"),
titlu-1"), wxDefaultPosition,
wxDefaultPosition, wxSize(
wxSize( 250,
250, 200)
200) ))
{b1
{b1 == new
new wxButton(
wxButton( this,
this, 11,
11, wxT("Mesaj
wxT("Mesaj nou!")
nou!") );
);
b2
b2 == new
new wxButton(
wxButton( this,
this, 10,
10, wxT("Fereastră
wxT("Fereastră nouă")
nouă") ););
wxBoxSizer
wxBoxSizer *bSizer_1
*bSizer_1 == new
new wxBoxSizer(
wxBoxSizer( wxHORIZONTAL
wxHORIZONTAL ); );
bSizer_1->Add(b1,0,wxALIGN_CENTER|wxALL,5);
bSizer_1->Add(b1,0,wxALIGN_CENTER|wxALL,5); bSizer_1->Add(b2,0,
bSizer_1->Add(b2,0,
wxALIGN_CENTER|wxALL,5);
wxALIGN_CENTER|wxALL,5); this->SetSizer(bSizer_1);
this->SetSizer(bSizer_1); this->Layout();
this->Layout();
this->SetBackgroundColour(wxColour(
this->SetBackgroundColour(wxColour( 218, 218, 235,
235, 158
158 )) );
);
b1->Connect(
b1->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(
wxCommandEventHandler( primaFereastra::apasaButon_1
primaFereastra::apasaButon_1 ), ), NULL,
NULL, this
this );
);
b2->Connect(
b2->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(
wxCommandEventHandler( primaFereastra::apasaButon_2
primaFereastra::apasaButon_2 ),NULL,
),NULL, this
this );}
);}
/*Ex.2(4-3):*/
/*Ex.2(4-3):*/ void
void primaFereastra::apasaButon_1(wxCommandEvent&
primaFereastra::apasaButon_1(wxCommandEvent&
event) { wxMessageBox("
event) { wxMessageBox("UnUn mesaj:
mesaj: BUTON APASAT!");}
BUTON APASAT! ");}
void
void primaFereastra::apasaButon_2(wxCommandEvent&
primaFereastra::apasaButon_2(wxCommandEvent& event) event) {{
adouaFereastra *f = new adouaFereastra(); f->Show();}
adouaFereastra *f = new adouaFereastra(); f->Show();}
//explicitare
//explicitare DESTRUCTOR
DESTRUCTOR
primaFereastra::~primaFereastra()
primaFereastra::~primaFereastra() {{
b1->Disconnect(
b1->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED,
wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(
wxCommandEventHandler(
primaFereastra::apasaButon_1
primaFereastra::apasaButon_1 ), ), NULL,
NULL, this
this );
);
b2->Disconnect(
b2->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED,
wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(
wxCommandEventHandler(
primaFereastra::apasaButon_2
primaFereastra::apasaButon_2 ), ), NULL,
NULL, this
this );}
);}
Funcţiile de conectare şi deconectare au aceeaşi listă de
argumente. Fişierul va avea următoarea structură:

142
1.Biblioteca wxWidgets şi dezvoltarea interfeţelor grafice în C++

/*Ex.2(3):*/
/*Ex.2(3):*/ #include
#include <wx/wx.h>
<wx/wx.h>
class
class primaAplicatie
primaAplicatie :: public
public wxApp
wxApp {public:
{public: virtual
virtual bool
bool OnInit();};
OnInit();};
/*Se
/*Se copiază
copiază Ex-2(1-3),
Ex-2(1-3), Ex-2(2-3),
Ex-2(2-3), Ex-2(3-3),
Ex-2(3-3), Ex-2(4-3),:*/
Ex-2(4-3),:*/
IMPLEMENT_APP(primaAplicatie);
IMPLEMENT_APP(primaAplicatie);
bool
bool primaAplicatie::OnInit()
primaAplicatie::OnInit() {{ primaFereastra
primaFereastra *fereastra_1
*fereastra_1 == new
new
primaFereastra();
primaFereastra(); fereastra_1->Show();
fereastra_1->Show(); return
return true;
true; }}
/*Ex.2(2-4):*/
/*Ex.2(2-4):*/ classclass primaFereastra
primaFereastra :: public
public wxFrame
wxFrame
{{ public:wxButton
public:wxButton *b1,*b2;
*b1,*b2;
primaFereastra()
primaFereastra() :: wxFrame(
wxFrame( NULL,
NULL, -1,
-1, wxT("Un
wxT("Un titlu-1"),
titlu-1"),
wxDefaultPosition,
wxDefaultPosition, wxSize(
wxSize( 250,
250, 200)
200) ))
{b1 = new wxButton( this, 11, wxT("Mesaj
{b1 = new wxButton( this, 11, wxT("Mesaj nou!") );nou!") );
b2
b2 == new
new wxButton(
wxButton( this,
this, 10,
10, wxT("Fereastră
wxT("Fereastră nouă")
nouă") );
);
wxBoxSizer
wxBoxSizer *bSizer_1 = new wxBoxSizer( wxHORIZONTAL );
*bSizer_1 = new wxBoxSizer( wxHORIZONTAL );
bSizer_1->Add(
bSizer_1->Add( b1, b1, 0,
0, wxALIGN_CENTER|wxALL,5);
wxALIGN_CENTER|wxALL,5);
bSizer_1->Add(
bSizer_1->Add( b2, b2, 0,
0, wxALIGN_CENTER|wxALL,5);
wxALIGN_CENTER|wxALL,5);
this->SetSizer(
this->SetSizer( bSizer_1
bSizer_1 );this->Layout();
);this->Layout();
this->SetBackgroundColour
this->SetBackgroundColour (wxColour(218,235,158));}
(wxColour(218,235,158));}
void
void apasaButon_1(wxCommandEvent&
apasaButon_1(wxCommandEvent& event)event) {wxMessageBox("Apăsat!");}
{wxMessageBox("Apăsat!");}
void apasaButon_2(wxCommandEvent&
void apasaButon_2(wxCommandEvent& event) event)
{{ adouaFereastra
adouaFereastra *f*f == new
new adouaFereastra();
adouaFereastra(); f->Show();}
f->Show();}
wxDECLARE_EVENT_TABLE();
wxDECLARE_EVENT_TABLE(); };
};
Acelaşi exemplu este refăcut pentru a demonstra utilizarea
tabelelor de evenimente. Exemplul 2(1-4) este identic cu exemplul
2(1-3).
Tabela de evenimente:
/*Ex.2(3-4):*/
/*Ex.2(3-4):*/
wxBEGIN_EVENT_TABLE(primaFereastra,wxFrame)
wxBEGIN_EVENT_TABLE(primaFereastra,wxFrame)
EVT_BUTTON(11,
EVT_BUTTON(11, primaFereastra::apasaButon_1)
primaFereastra::apasaButon_1)
EVT_BUTTON(10,
EVT_BUTTON(10, primaFereastra::apasaButon_2)
primaFereastra::apasaButon_2)
wxEND_EVENT_TABLE()
wxEND_EVENT_TABLE()

/*Ex.2(4):*/
/*Ex.2(4):*/ #include
#include <wx/wx.h>
<wx/wx.h>
class
class primaAplicatie
primaAplicatie :: public
public wxApp
wxApp {public:
{public: virtual
virtual bool
bool OnInit();};
OnInit();};
/*Se copiază Ex-2(1-4), Ex-2(2-4), Ex-2(3-4):*/
/*Se copiază Ex-2(1-4), Ex-2(2-4), Ex-2(3-4):*/
IMPLEMENT_APP(primaAplicatie);
IMPLEMENT_APP(primaAplicatie);
bool
bool primaAplicatie::OnInit()
primaAplicatie::OnInit() {{ primaFereastra
primaFereastra *fereastra_1
*fereastra_1 == new
new
primaFereastra();
primaFereastra(); fereastra_1->Show();
fereastra_1->Show(); return
return true;}
true;}
Există tehnici de gestiune mai eficientă a evenimentelor
(ex. metoda Bind()), însă tehnicile expuse acoperă necesităţile
pentru construcţia unei interfeţe grafice simple.
1.3. Utilizarea claselor generate de template-uri
În zona open source generatoarele pentru interfeţe grafice
sunt limitate, însă consider că fără a utiliza generatoare pentru
interfaţă se pot realiza aplicaţii mult mai performante. Singurul

143
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

avantaj al generatoarelor pentru interfaţă constă în reducerea


timpului iniţial de dezvoltare pentru aplicaţie. Dacă se doreşte
personalizarea interfeţei grafice este mai avantajos să lucrăm fără
generatoare de interfaţă, inclusiv din punct de vedere al timpilor
de dezvoltare.
Pentru clasele wxWidgets există utilitarele wxCraft şi
wxFormsBuilder, ce permit construcţia unei interfeţe grafice
vizual. Practic, toate proprietăţile adăugate într-o interfaţă sunt
stocate într-un fişier de tip XML. Aceste date pot fi transpuse
într-o clasă din limbajul C++.
În continuare este prezentat un
exemplu de utilizare al unei clase
obţinute cu utilitarul wxFormsBuilder
(http://sourceforge.net/projects/wxformb
uilder/), iar după aceasta este prezen-
tat acelaşi modul software (componentă a
unei aplicaţii software) fără utilizarea Figura VI-1: Exemplu
unui constructor pentru interfeţe sincronizare controale
grafice.
Considerăm că dorim construcţia unui modul software ce
afişează aceeaşi informaţie în trei moduri diferite: cutie de
editare, bargraf orizonatal, cursor şi de asemenea valoarea
acestuia poate fi schimbată prin cutia de editare sau cursor. În
acest sens deschidem un proiect nou: Workspace> NewProject>
Executable (wxWidgets + wxFB frame). Deschidem “resources> gui.fbp”
şi este lansat automat utilitarul wxFormBuilder. Obiectul ale cărui
proprietăţi le modificăm îl selectăm în fereastra din stânga
“Object Tree”. Denumirile funcţiilor ce vor trata evenimentele vor
fi scrise direct în căsuţele corespunzătoare. În momentul
finalizării setărilor se va selecta Generate code sau se va apăsa
tasta F8.
Utilitarul wxFormBuilder va genera un fişier cu declaraţii
gui.h şi unul cu explicitarea metodelor gui.cpp. Structura
fişierului gui.h este următoarea:
/*
/* Ex.3(1-1)
Ex.3(1-1) gui.h
gui.h */*/ //adăugare
//adăugare biblioteci
biblioteci wxWidgets
wxWidgets ……
class
class FereastraGUI
FereastraGUI :: public
public wxFrame
wxFrame {{ protected:
protected: wxStaticText*
wxStaticText* L1; L1;
wxStaticText*
wxStaticText* L2; wxTextCtrl* E1; wxSlider* Cursor1; wxGauge*
L2; wxTextCtrl* E1; wxSlider* Cursor1; wxGauge* Bar1;
Bar1;
virtual
virtual void
void val_modif(
val_modif( wxCommandEvent&
wxCommandEvent& event
event )) {{ event.Skip();
event.Skip(); }}
virtual
virtual void
void deplaseaza(
deplaseaza( wxScrollEvent&
wxScrollEvent& event
event )) {{ event.Skip();
event.Skip(); }}
public:FereastraGUI(
public:FereastraGUI( wxWindow*
wxWindow* parent,
parent, wxWindowID
wxWindowID id id == wxID_ANY,
wxID_ANY,
const
const wxString& title = _("Prima fereastra"), const wxPoint&
wxString& title = _("Prima fereastra"), const wxPoint& pos
pos ==
wxDefaultPosition,
wxDefaultPosition, const
const wxSize&
wxSize& size
size == wxSize(
wxSize( 334,178
334,178 ), ), long
long style
style
== wxCLOSE_BOX|wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL
wxCLOSE_BOX|wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); );
~FereastraGUI();
~FereastraGUI(); }; };
Clasa FereastraGUI cuprinde declaraţiile celor trei con-
troale utilizate pentru afişare: wxTextCtrl (cutia de editare),

144
1.Biblioteca wxWidgets şi dezvoltarea interfeţelor grafice în C++

wxSlider (cursor) şi wxGauge (bargraf). Pe lângă aceste clase, mai


sunt declarate şi clase ce implementează controale pentru afişare
text (wxStaticText). În clasa de bază FereastraGUI metodele ce
răspund la evenimente nu execută nimic (pasează evenimentul mai
departe – event.Skip()).
În clasa gui.cpp sunt explicitate metodele declarate în
fişierul header. În exemplul 3(2-1) este prezentat codul din
constructorul clasei FereastraGUI, responsabil pentru aşezarea
controalelor grafice pe suprafaţa ferestrei.
/*Ex.3(2-1):*/
/*Ex.3(2-1):*/ this->SetBackgroundColour(
this->SetBackgroundColour( wxColour( wxColour( 218, 218, 235,
235, 158
158 )) ); );
wxBoxSizer*
wxBoxSizer* bSizer_1;
bSizer_1; bSizer_1
bSizer_1 == new new wxBoxSizer(
wxBoxSizer( wxVERTICAL
wxVERTICAL ); );
L1
L1 == new
new wxStaticText(
wxStaticText( this,this, wxID_ANY,
wxID_ANY, _("TESTE"),
_("TESTE"), wxDefaultPosition,
wxDefaultPosition,
wxDefaultSize, 00 );
wxDefaultSize, ); L1->Wrap(
L1->Wrap( -1 -1 );
);
L1->SetFont(
L1->SetFont( wxFont(
wxFont( 14,14, 74,74, 90,
90, 92,
92, false,
false, wxT("Courier
wxT("Courier New") New") )) ); );
bSizer_1->Add(
bSizer_1->Add( L1, L1, 0,
0, wxALIGN_CENTER|wxALL,
wxALIGN_CENTER|wxALL, 55 ); );
wxBoxSizer*
wxBoxSizer* bSizer_2;
bSizer_2; bSizer_2
bSizer_2 == new new wxBoxSizer(
wxBoxSizer( wxHORIZONTAL
wxHORIZONTAL ); );
L2
L2 == new wxStaticText(this,
new wxStaticText( this,wxID_ANY,
wxID_ANY,_("Valoare"),
_("Valoare"),wxDefaultPosition,
wxDefaultPosition,wxDefaultSize,
wxDefaultSize,00);
);
L2->Wrap(
L2->Wrap( -1 -1 );
); bSizer_2->Add(
bSizer_2->Add( L2, L2, 0, 0, wxALL,
wxALL, 55 ); );
E1
E1 == new wxTextCtrl( (this,
new wxTextCtrl this,wxID_ANY,
wxID_ANY,wxEmptyString,
wxEmptyString,wxDefaultPosition,
wxDefaultPosition,wxDefaultSize,
wxDefaultSize,
wxTE_PROCESS_ENTER
wxTE_PROCESS_ENTER););
bSizer_2->Add(E1,0,wxALL,5);
bSizer_2->Add(E1,0,wxALL,5); bSizer_1->Add(bSizer_2,0,wxEXPAND,5);
bSizer_1->Add(bSizer_2,0,wxEXPAND,5);
Cursor1
Cursor1 == new
new wxSlider(
wxSlider( this,this, wxID_ANY,
wxID_ANY, 0, 0, 0,
0, 100,
100, wxDefaultPosition,
wxDefaultPosition,
wxDefaultSize, wxSL_HORIZONTAL
wxDefaultSize, wxSL_HORIZONTAL); );
bSizer_1->Add(Cursor1,
bSizer_1->Add(Cursor1, 0, 0, wxALL|wxEXPAND,
wxALL|wxEXPAND, 5); 5);
Bar1 = new wxGauge(
Bar1 = new wxGauge( this, wxID_ANY, 100,
this, wxID_ANY, 100, wxDefaultPosition,
wxDefaultPosition,
wxDefaultSize, wxGA_HORIZONTAL|wxGA_SMOOTH );
wxDefaultSize, wxGA_HORIZONTAL|wxGA_SMOOTH );
bSizer_1->Add(
bSizer_1->Add( Bar1,0,
Bar1,0, wxALL|wxEXPAND,
wxALL|wxEXPAND, 55 ); );
this->SetSizer(
this->SetSizer( bSizer_1
bSizer_1 );this->Layout();this->Centre(
);this->Layout();this->Centre( wxBOTH wxBOTH ); );
Se observă două instanţieri ale clasei wxBoxSizer ce au ca
scop aşezarea tuturor controalelor în acestea. Sunt instanţiate
clasele corespunzătoare controalelor şi de asemenea se stabilesc
proprietăţile pentru fiecare obiect, ce corespunde câte unui
control. După cum se observă toate poziţiile controalelor sunt
relative. Clasa FereastraGUI conţine de asemenea legătura între
evenimente şi metodele de tratare a acestora.
/*Ex-3(3-1):*/
/*Ex-3(3-1):*/ E1->Connect(
E1->Connect( wxEVT_COMMAND_TEXT_ENTER,
wxEVT_COMMAND_TEXT_ENTER,
wxCommandEventHandler(
wxCommandEventHandler( FereastraGUI::val_modif
FereastraGUI::val_modif ), ), NULL,
NULL, this
this );
);
Cursor1->Connect( wxEVT_SCROLL_TOP, wxScrollEventHandler
Cursor1->Connect( wxEVT_SCROLL_TOP, wxScrollEventHandler
(( FereastraGUI::deplaseaza
FereastraGUI::deplaseaza ),
), NULL,
NULL, this
this );
);
Cursor1->Connect(
Cursor1->Connect( wxEVT_SCROLL_BOTTOM, wxScrollEventHandler
wxEVT_SCROLL_BOTTOM, wxScrollEventHandler
(( FereastraGUI::deplaseaza
FereastraGUI::deplaseaza ),
), NULL,
NULL, this
this );
);
Cursor1->Connect(
Cursor1->Connect( wxEVT_SCROLL_PAGEDOWN,
wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler
wxScrollEventHandler
(( FereastraGUI::deplaseaza
FereastraGUI::deplaseaza ),
), NULL,
NULL, this
this );
);
//.............................................................
//.............................................................
Cursor1->Connect(
Cursor1->Connect( wxEVT_SCROLL_CHANGED,
wxEVT_SCROLL_CHANGED, wxScrollEventHandler
wxScrollEventHandler
(( FereastraGUI::deplaseaza
FereastraGUI::deplaseaza ),
), NULL,
NULL, this
this );
);
Funcţiile pentru tratarea evenimentelor corespund

145
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

modificării valorii din cutia de editare (FereastraGUI ::


val_modif) şi a deplasării cursorului (FereastraGUI :: deplaseaza).
Fişierul generat gui.cpp va avea forma:
/*Ex.3(4-1):
/*Ex.3(4-1): gui.cpp
gui.cpp */
*/ #include
#include "gui.h"
"gui.h"
FereastraGUI::FereastraGUI(
FereastraGUI::FereastraGUI( wxWindow*
wxWindow* parent,
parent, wxWindowID
wxWindowID id,
id, const
const
wxString&
wxString& title,
title, const
const wxPoint&
wxPoint& pos,
pos, const
const wxSize&
wxSize& size,
size, long
long
style
style )) :: wxFrame(
wxFrame( parent,
parent, id,
id, title,
title, pos,
pos, size,
size, style
style ))
{{ /*
/* Ex-3(2-1)
Ex-3(2-1) ++ 3(3-1)
3(3-1) */*/ }}
FereastraGUI::~FereastraGUI()
FereastraGUI::~FereastraGUI() {{ /* /* DECONECTARE
DECONECTARE EVENIMENTE*/}
EVENIMENTE*/}
Pentru a utiliza această clasă se construieşte o nouă clasă
derivată din aceasta şi se rescriu metodele definite în clasa de
bază ca funcţii virtuale.
/*Ex.3(5-1):
/*Ex.3(5-1): main.h
main.h */
*/ #include
#include "gui.h"
"gui.h"
class
class PrimaAplicatie
PrimaAplicatie :: public
public wxApp
wxApp
{{ public:
public: virtual
virtual bool
bool OnInit();};
OnInit();};
DECLARE_APP(PrimaAplicatie)
DECLARE_APP(PrimaAplicatie)
class
class FereastraPricipala
FereastraPricipala :: public
public FereastraGUI
FereastraGUI
{public:
{public: FereastraPricipala(
FereastraPricipala( wxWindow
wxWindow *parent
*parent );
);
virtual
virtual ~FereastraPricipala();
~FereastraPricipala();
void
void deplaseaza(wxScrollEvent&
deplaseaza(wxScrollEvent& event);
event);
void
void val_modif(wxCommandEvent&
val_modif(wxCommandEvent& event
event );
); };
};
Fişierul main.cpp devine:
/*Ex.2(2-4):
/*Ex.2(2-4): gui.cpp
gui.cpp */*/ #include
#include "main.h"
"main.h"
IMPLEMENT_APP(PrimaAplicatie);
IMPLEMENT_APP(PrimaAplicatie); bool bool PrimaAplicatie::OnInit()
PrimaAplicatie::OnInit()
{{ SetTopWindow(new
SetTopWindow(new FereastraPricipala(NULL));
FereastraPricipala(NULL)); GetTopWindow()->Show();
GetTopWindow()->Show();
return
return true;}
true;}
FereastraPricipala::FereastraPricipala(wxWindow
FereastraPricipala::FereastraPricipala(wxWindow *parent)*parent) ::
FereastraGUI(
FereastraGUI( parent
parent ){}
){}
FereastraPricipala::~FereastraPricipala(){}
FereastraPricipala::~FereastraPricipala(){}
void
void FereastraPricipala::deplaseaza(
FereastraPricipala::deplaseaza( wxScrollEvent&
wxScrollEvent& event
event ))
{{ int
int x=Cursor1->GetValue();
x=Cursor1->GetValue(); Bar1->SetValue(x);
Bar1->SetValue(x);
wxString
wxString a;
a; aa == a.FromDouble(x);
a.FromDouble(x); E1->SetValue(a);
E1->SetValue(a);
}} //sau
//sau Bar1->SetValue(Cursor1->GetValue());
Bar1->SetValue(Cursor1->GetValue());
void
void FereastraPricipala::val_modif(
FereastraPricipala::val_modif( wxCommandEvent&
wxCommandEvent& event
event ))
{{ wxString
wxString a;
a; aa == E1->GetValue();
E1->GetValue(); double
double x;
x; a.ToDouble(&x);
a.ToDouble(&x);
Bar1->SetValue(x);
Bar1->SetValue(x); Cursor1->SetValue(x);
Cursor1->SetValue(x); }}
Metoda FereastraPricipala::deplaseaza preia valoarea
cursorului prin utilizarea funcţiei GetValue(), membră a clasei
wxSlider şi o afişează în cutia de editare (wxTextCtrl) şi setează
bargraf-ul (wxGauge) cu valoarea respectivă. Valoarea întoarsă este
de tip întreg şi este transmisă prin intermediul funcţiei
SetValue(int) membră a clasei wxGaudge. Pentru afişarea valorii în
controlul de editiare este necesară transformarea valorii
cursorului din tipul întreg într-un tip clasă wxString. Preluarea
valorii în clasa wxString este asigurată de metoda
FromDouble(double). Afişarea în cadrul cutiei de editare este

146
1.Biblioteca wxWidgets şi dezvoltarea interfeţelor grafice în C++

asigurată de metoda SetValue(wxString).


Metoda FereastraPricipala::val_modif() preia valoarea din
cutia de editare şi setează atât bargraf-ul (wxGauge), cât şi
cursorul (wxSlider) cu valoarea respectivă. Valoarea obţinută prin
funcţia GetValue din cutia de editare este de tip wxString. Această
valoare este convertită într-o valoare compatibilă cu tipul întreg
prin funcţia ToDouble(). Cu ajutorul funcţiilor corespunzătoare
SetValue (din fiecare clasă) este transmisă valoarea celor două
controale.
Exemplul anterior, refăcut în varianta fără generator de
interfaţă este prezentat în cele ce urmează. Declaraţia clasei
cuprinde şi datele din vechea clasă:
/*Ex.2(1-5):declaraţia
/*Ex.2(1-5):declaraţia clasei*/
clasei*/ class
class FerPr
FerPr :: public
public wxFrame
wxFrame
{{ wxStaticText*
wxStaticText* L1; wxStaticText* L2;wxTextCtrl* E1;
L1; wxStaticText* L2;wxTextCtrl* E1;
wxSlider*
wxSlider* Cursor1;
Cursor1; wxGauge*
wxGauge* Bar1;
Bar1;
public:
public: FerPr(wxWindow* parent,
FerPr(wxWindow* parent, wxWindowID
wxWindowID id,
id, const
const wxString&
wxString&
title,
title, const
const wxPoint&
wxPoint& pos,
pos, const
const wxSize&
wxSize& size,
size, long
long style
style );
);
void
void deplaseaza(
deplaseaza( wxScrollEvent&
wxScrollEvent& event
event );
);
void
void val_modif(
val_modif( wxCommandEvent&
wxCommandEvent& event
event );
);
~FerPr();
~FerPr(); };
};
Constructorul preia şi instrucţiunile din vechiul
constructor al clasei de bază:
/*Ex.2(2-5):*/
/*Ex.2(2-5):*/ FerPr::FerPr(
FerPr::FerPr( wxWindow*
wxWindow* parent,
parent, wxWindowID
wxWindowID id,
id, const
const
wxString&
wxString& title,
title, const
const wxPoint&
wxPoint& pos,
pos, const
const wxSize&
wxSize& size,
size, long
long
style)
style) :wxFrame(
:wxFrame( parent,
parent, id,
id, title,
title, pos,
pos, size,
size, style
style ))
{{this->SetBackgroundColour(wxColour(
this->SetBackgroundColour(wxColour( 218,
218, 235,
235, 158
158 ));
)); wxBoxSizer*
wxBoxSizer* bSizer_1;
bSizer_1;
bSizer_1
bSizer_1 == new
new wxBoxSizer(wxVERTICAL);
wxBoxSizer(wxVERTICAL); L1 L1 == new
new wxStaticText(this,
wxStaticText(this, wxID_ANY,
wxID_ANY,
_("TESTE"),
_("TESTE"), wxDefaultPosition,
wxDefaultPosition, wxDefaultSize,
wxDefaultSize, 00 ); ); L1->Wrap(-1);
L1->Wrap(-1);
L1->SetFont(wxFont(
L1->SetFont(wxFont( 14, 14, 74,
74, 90,
90, 92,
92, false,
false, wxT("Courier
wxT("Courier New")
New") )) );
);
bSizer_1->Add(L1,
bSizer_1->Add(L1, 0, 0, wxALIGN_CENTER|wxALL,
wxALIGN_CENTER|wxALL, 55 ); ); wxBoxSizer*
wxBoxSizer* bSizer_2;
bSizer_2;
bSizer_2 = new wxBoxSizer(wxHORIZONTAL); L2 = new wxStaticText(this,
bSizer_2 = new wxBoxSizer(wxHORIZONTAL); L2 = new wxStaticText(this,
wxID_ANY,
wxID_ANY, _("Valoare"),
_("Valoare"), wxDefaultPosition,
wxDefaultPosition, wxDefaultSize,
wxDefaultSize, 00 );L2->Wrap(-1);
);L2->Wrap(-1);
bSizer_2->Add(L2,0,wxALL,5);
bSizer_2->Add(L2,0,wxALL,5); E1 E1 == new
new wxTextCtrl(
wxTextCtrl( this,
this, wxID_ANY,
wxID_ANY,
wxEmptyString,
wxEmptyString, wxDefaultPosition,
wxDefaultPosition, wxDefaultSize,
wxDefaultSize, wxTE_PROCESS_ENTER
wxTE_PROCESS_ENTER ); );
bSizer_2->Add(E1,0,wxALL,5);
bSizer_2->Add(E1,0,wxALL,5); bSizer_1->Add(bSizer_2,0,wxEXPAND,5);
bSizer_1->Add(bSizer_2,0,wxEXPAND,5);
Cursor1=new
Cursor1=new wxSlider(this,wxID_ANY,0,0,100,
wxSlider(this,wxID_ANY,0,0,100, wxDefaultPosition,
wxDefaultPosition,
wxDefaultSize,wxSL_HORIZONTAL
wxDefaultSize,wxSL_HORIZONTAL ); ); bSizer_1->Add(Cursor1,0,wxALL|wxEXPAND,5);
bSizer_1->Add(Cursor1,0,wxALL|wxEXPAND,5);
Bar1
Bar1 == new
new wxGauge(
wxGauge( this,
this, wxID_ANY,
wxID_ANY, 100,
100, wxDefaultPosition,
wxDefaultPosition, wxDefaultSize,
wxDefaultSize,
wxGA_HORIZONTAL|wxGA_SMOOTH
wxGA_HORIZONTAL|wxGA_SMOOTH ); ); bSizer_1->Add(
bSizer_1->Add( Bar1,
Bar1, 0,
0, wxALL|wxEXPAND,
wxALL|wxEXPAND, 55 ););
this->SetSizer(
this->SetSizer( bSizer_1
bSizer_1 );
); this->Layout();
this->Layout(); this->Centre(
this->Centre( wxBOTH
wxBOTH ); );
E1->Connect(wxEVT_COMMAND_TEXT_ENTER,
E1->Connect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler
wxCommandEventHandler (FerPr::
(FerPr::
val_modif),NULL,this);
val_modif),NULL,this); Cursor1->Connect(wxEVT_SCROLL_CHANGED,
Cursor1->Connect(wxEVT_SCROLL_CHANGED,
wxScrollEventHandler(FerPr::deplaseaza),NULL,this);}}
wxScrollEventHandler(FerPr::deplaseaza),NULL,this);
iar destructorul execută deconectarea evenimentelor:
/*Ex.2(3-5):*/
/*Ex.2(3-5):*/ FerPr::~FerPr()
FerPr::~FerPr() {E1->Disconnect( wxEVT_COMMAND_TEXT_ENTER,,
{E1->Disconnect(wxEVT_COMMAND_TEXT_ENTER
wxCommandEventHandler(
wxCommandEventHandler( FerPr::val_modif
FerPr::val_modif ), ), NULL,
NULL, this
this );
);
Cursor1->Disconnect( wxEVT_SCROLL_CHANGED,, wxScrollEventHandler
Cursor1->Disconnect(wxEVT_SCROLL_CHANGED wxScrollEventHandler
(FerPr::deplaseaza
(FerPr::deplaseaza ),
), NULL,
NULL, this
this );
); }}

147
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

Cele două funcţii pentru tratarea evenimentelor au rămas


nemodificate:
/*Ex.2(4-5):*/
/*Ex.2(4-5):*/ voidvoid FerPr::deplaseaza(
FerPr::deplaseaza( wxScrollEvent&
wxScrollEvent& event
event ))
{{ int
int xx == Cursor1->GetValue();
Cursor1->GetValue(); Bar1->SetValue(x);
Bar1->SetValue(x);
wxString
wxString a; a; aa == a.FromDouble(x);
a.FromDouble(x); E1->SetValue(a);
E1->SetValue(a); }}
void
void FerPr::val_modif(wxCommandEvent&event)
FerPr::val_modif(wxCommandEvent&event)
{{ wxString
wxString a; a; a=E1->GetValue();
a=E1->GetValue(); double
double x;
x; a.ToDouble(&x);
a.ToDouble(&x);
Bar1->SetValue(x);
Bar1->SetValue(x); Cursor1->SetValue(x);
Cursor1->SetValue(x); }}
Aplicaţia completă va avea structura:
/*Ex.2(5-5):*/
/*Ex.2(5-5):*/ #include
#include <wx/wx.h>
<wx/wx.h>
class
class wxMiniApp
wxMiniApp :: public
public wxApp
wxApp {public:virtual
{public:virtual bool
bool OnInit();};
OnInit();};
/*Se
/*Se copiază
copiază exemplele:
exemplele: 2(1-5),2(2-5),2(3-5),2(4-5),
2(1-5),2(2-5),2(3-5),2(4-5), */ */
IMPLEMENT_APP(wxMiniApp);
IMPLEMENT_APP(wxMiniApp);
bool
bool wxMiniApp::OnInit()
wxMiniApp::OnInit() {{ FerPr
FerPr *f=new
*f=new FerPr(NULL,wxID_ANY,"Titlu
FerPr(NULL,wxID_ANY,"Titlu
fereastra",
fereastra", wxDefaultPosition,
wxDefaultPosition, wxSize(
wxSize( 334,178
334,178 ),
), wxCLOSE_BOX|
wxCLOSE_BOX|
wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL);
wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL); f->Show();
f->Show(); return
return true;
true; }}
Se observă că nu există niciun fel de diferenţă între
rezultatele celor două variante ale aplicaţiei.
1.4. Exemplu cu primitive grafice şi evenimente
În continuare este prezentat un exemplu de aplicaţie ce
permite deplasarea unui obiect grafic (în acest caz, un cerc) pe
suprafaţa unei ferestre. Deplasarea se poate realiza atât prin
tragerea cu mouse-ul, cât şi
cu ajutorul tastelor
(Alt+săgeţi). De asemnea,
este afişată în fereastră
poziţia mouse-ului şi pozi-
ţia centrului cercului. Prin
rotirea rozetei pentru scrol
dimensiunea cercului creşte
sau scade. Atât cercul, cât
şi un dreptunghi, desenat pe Figura VI-2: Captură ecran
conturul interior al ferestrei, îşi schimbă culoarea (roşu, verde
sau albastru) la un interval prestabilit de timp. Sursa aplicaţiei
conţine două fişiere, unul cu declaraţii (main.h) şi celălalt cu
explicitările metodelor (main.cpp)
class PanouDesen : public wxPanel
{wxCoord x,y;//pozitie cerc
wxCoord x_speed, y_speed; //avans pe cele două direcţii
//poziţie actuală mouse şi în momentul reţinerii cercului
wxCoord xm,ym,xm1,ym1;
int R; //raza cercului
bool retine; wxStaticText* L1;

148
1.Biblioteca wxWidgets şi dezvoltarea interfeţelor grafice în C++

public: char culoare_cerc;


PanouDesen(wxFrame* fereastra_parinte);
void evenimentDesenare(wxPaintEvent& evt);
void evi_tasta(wxKeyEvent& evt);//apasă o tastă
void evi_guzgan_apasaBSt(wxMouseEvent& evt); //apasă buton stâng mouse
void evi_guzgan_ridicaBSt(wxMouseEvent& evt);//ridică buton stâng mouse
void evi_guzgan_misca(wxMouseEvent& evt); //mişcă mouse
void evi_guzgan_scroll(wxMouseEvent& evt);// rotire rozetă scol mouse
DECLARE_EVENT_TABLE()
};
class GestiuneEvenimenteTimp : public wxTimer
{ PanouDesen* panou;
public: GestiuneEvenimenteTimp(PanouDesen* panou);
void Notify(); /*redeclarăm funcţia Notify , aceasta este o funcţie
predefinită ce este apelată la un interval de timp [ms] dat de variabila
timer din cadrul clasei wxTimer*/
void seteazaContor();
};
class FereastraPrinc : public wxFrame
{ PanouDesen* panou_desenare_1;
GestiuneEvenimenteTimp* timp;
// clasa ce gestionează evenimentele de timp
public: FereastraPrinc();
~FereastraPrinc();
void inchideFereastra(wxCloseEvent& evt);
DECLARE_EVENT_TABLE()
};
class AplicatiaTEST1: public wxApp
{ bool OnInit();
FereastraPrinc* fereastra_1;
};
Pentru gestiunea evenimentelor de timp este utilizată o
clasă derivată din wxTime. În această clasă este redefinită funcţia
Notify() din cadrul clasei wxTime. Această funcţie este executată
periodic la un interval de timp prestabilit prin funcţia
wmTimer::Start(int).
#include <wx/wx.h>
#include <wx/timer.h>
#include "main.h"
IMPLEMENT_APP(AplicatiaTEST1)
//---------------------------------------------------------------------
FereastraPrinc::FereastraPrinc() : wxFrame((wxFrame *)NULL, -1,
wxT("Deplasare obiect"), wxPoint(50,50), wxSize(400,200))
{ wxBoxSizer* sizer_1 = new wxBoxSizer(wxHORIZONTAL);
panou_desenare_1 = new PanouDesen(this);
sizer_1->Add(panou_desenare_1, 1, wxEXPAND);SetSizer(sizer_1);
timp = new GestiuneEvenimenteTimp(panou_desenare_1);
Show();timp->seteazaContor(); }
//---------------------------------------------------------------------

149
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

FereastraPrinc::~FereastraPrinc() {}
//---------------------------------------------------------------------
void FereastraPrinc::inchideFereastra(wxCloseEvent& evt)
{ timp->Stop(); evt.Skip(); }
BEGIN_EVENT_TABLE(FereastraPrinc, wxFrame)
EVT_CLOSE(FereastraPrinc::inchideFereastra)
END_EVENT_TABLE()
//---------------------------------------------------------------------
bool AplicatiaTEST1::OnInit()
{ fereastra_1=new FereastraPrinc(); fereastra_1->Show(); return true; }
//---------------------------------------------------------------------
BEGIN_EVENT_TABLE(PanouDesen, wxPanel)
EVT_PAINT(PanouDesen::evenimentDesenare)
EVT_KEY_DOWN(PanouDesen::evi_tasta)
EVT_LEFT_DOWN(PanouDesen::evi_guzgan_apasaBSt)
EVT_LEFT_UP(PanouDesen::evi_guzgan_ridicaBSt)
EVT_MOTION(PanouDesen::evi_guzgan_misca)
EVT_MOUSEWHEEL(PanouDesen::evi_guzgan_scroll)
END_EVENT_TABLE()
//---------------------------------------------------------------------
PanouDesen::PanouDesen(wxFrame* fereastra_parinte) :
wxPanel(fereastra_parinte)
{ x=20; y=40; y_speed=2; x_speed=2; R=20; retine=false; xm1=-1; ym1=-1;
culoare_cerc='r';
L1=new wxStaticText(this,wxID_ANY,_("TESTE"),wxDefaultPosition,
wxDefaultSize, 0); this->SetCursor(wxCURSOR_CROSS);}
//---------------------------------------------------------------------
void PanouDesen::evenimentDesenare(wxPaintEvent& evt)
{wxPaintDC dc(this); wxString sir_culoare;
dc.SetBackground(*wxWHITE_BRUSH); dc.Clear();//sterge fundal
if(culoare_cerc=='r') {dc.SetPen(wxPen(wxColour(255,0,0)));
sir_culoare="rosu";}
if(culoare_cerc=='g')
{dc.SetPen(wxPen(wxColour(0,255,0)));sir_culoare="verde";}
if(culoare_cerc=='b')
{dc.SetPen(wxPen(wxColour(0,0,255)));sir_culoare="albastru";}
dc.DrawRectangle(1,1,dc.GetSize().GetWidth()-1,dc.GetSize().GetHeight()-
1);
dc.DrawText(wxT("Un cerc ")+sir_culoare,x-5, y+R);
dc.DrawCircle(wxPoint(x,y),R);}
//---------------------------------------------------------------------
void PanouDesen::evi_tasta(wxKeyEvent& evt)
{ wxString ach;
switch (evt.m_keyCode)
{case WXK_RIGHT:x+=x_speed;this->Refresh();break;//ALT +SAGEATA
case WXK_LEFT: x -= x_speed; this->Refresh();break;
case WXK_DOWN: y += y_speed; this->Refresh();break;
case WXK_UP: y -= y_speed; this->Refresh();break;
} }
//---------------------------------------------------------------------
void PanouDesen::evi_guzgan_apasaBSt(wxMouseEvent& evt)

150
1.Biblioteca wxWidgets şi dezvoltarea interfeţelor grafice în C++

{ wxString ach; xm=evt.GetX(); ym=evt.GetY();


if((!retine)&&(xm>=(x-R))&&(xm<=(x+R))&&(ym>=(y-R)) &&(ym<=(y+R)))
{xm1=xm; ym1=ym; retine=true; } }
//---------------------------------------------------------------------
void PanouDesen::evi_guzgan_ridicaBSt(wxMouseEvent& evt)
{retine=false; xm1=-1;ym1=-1; }
//---------------------------------------------------------------------
void PanouDesen::evi_guzgan_misca(wxMouseEvent& evt)
{ xm=evt.GetX();ym=evt.GetY();
if((xm>=(x-R))&&(xm<=(x+R))&&(ym>=(y-R))&&(ym<=(y+R)))
this->SetCursor(wxCURSOR_HAND);else this-
>SetCursor(wxCURSOR_CROSS);
wxString sir="x: "+sir.FromDouble(xm)+" --- Y: "+sir.FromDouble(ym);
wxString sir1 = " x1: " + sir1.FromDouble(xm1) + " --- Y1: " +
sir1.FromDouble(ym1);
wxString sir2=" xc: "+sir2.FromDouble(x)+" --- Yc:
"+sir2.FromDouble(y);
wxString sir3;
if(retine) sir3=" MEMO";
else sir3=" Liber"; L1->SetLabel(sir+sir1+sir2+sir3);
if(retine) {x+=xm-xm1;y+=ym-ym1;xm1=xm;ym1=ym;this->Refresh();}
}
//---------------------------------------------------------------------
void PanouDesen::evi_guzgan_scroll(wxMouseEvent& evt)
{ R+=evt.GetWheelRotation()/120.0; this->Refresh();}
//---------------------------------------------------------------------
GestiuneEvenimenteTimp::GestiuneEvenimenteTimp(PanouDesen*
panou):wxTimer()
{ GestiuneEvenimenteTimp::panou = panou;}
//---------------------------------------------------------------------
void GestiuneEvenimenteTimp::Notify()
{ panou->Refresh();// redesenare+afisare
if(panou->culoare_cerc=='r') panou->culoare_cerc='g'; else
if(panou->culoare_cerc=='g') panou->culoare_cerc='b'; else
if(panou->culoare_cerc=='b') panou->culoare_cerc='r'; }
//---------------------------------------------------------------------
void GestiuneEvenimenteTimp::seteazaContor()
{ wxTimer::Start(1000);//1000 ms interval apel rutina Notify()
}
În aceste exemple avem mai multe categorii de evenimente şi
anume:
➢ eveniment pentru (re)desenare (clasa wxPaintEvent);
➢ evenimente generate de tastatură (clasa wxKeyEvent);
➢ evenimente generate de către mouse (clasa wxMouseEvent);
➢ evenimente generate de către controalele grafice (clasa
wxCommandEvent);
➢ eveniment închidere fereastră (clasa wxCloseEvent).
Evenimenul de tip “TIMP” este tratat într-o manieră
diferită, explicată mai sus.

151
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

Fiecare funcţie ce tratează evenimente preia o referinţă la


o clasă de evenimente, iar în blocul de instrucţiuni corespunzător
funcţiei sunt tratate evenimentele cu ajutorul obiectului a cărui
referinţă este transmisă prin lista de argumente a funcţiei. De
exemplu, pentru un obiect de tip wxMouseEvent, avem funcţia
evt.GetX() ce întoarce poziţia pe orizonală a mouse-ului pe ecran,
exprimată în număr de pixeli de la originea sistemului de coordo-
nate cartezian (originea va fi în colţul din stânga sus) pe axa Ox.

2. Interfaţarea client-server PostgreSQL în limbajul C/C++

2.1. Descrierea bibliotecii libpq


Biblioteca libpq din cadrul PostgreSQL permite interfaţarea
server-ului PostgreSQL de către clienţi în limbajul C. În
directorul dat de calea “.../PostgreSQL/.../include” se află fişie-
rul libpq-fe.h, ce conţine declaraţiile funcţiilor şi structurilor
utilizate de biblioteca libpq.
Principalele funcţii din biblioteca libpq permit:
✗ conectarea la un server PostgreSQL;
✗ obţinerea de informaţii privind starea conexiunii;
✗ transmiterea execuţiei pentru comenzile SQL – mod sincron;
✗ extragerea rezultatelor în urma unei comenzi SQL.
2.1.1. Conectarea la server
Conectarea la un server PostgreSQL se realizează prin funcţia:
PGconn
PGconn *PQconnectdb(const
*PQconnectdb(const char
char *conninfo);
*conninfo);
Şirul pentru conexiune transmis ca şi parametru acestei
funcţii cuprinde: ip-ul serverului cu care se conectează (implicit
localhost), portul pe care se realizează conexiunea (implicit
5432), numele bazei de date (implicit postgres), numele utilizator
(implicit postgres), parola (în mod implicit o preia din
fişierul ...\Roaming\postgresql.conf). De asemenea, şirul de
conectare poate cuprinde şi opţiuni de conectare. Rezultatul
apelului funcţiei de conectare este stocat într-o structură PGconn.
Putem să deschidem mai multe conexiuni simultan, fiecare conexiune
va corespunde unei variabile de tip PGconn. Fiecare conexiune va
putea fi închisă separat prin funcţia void
void PQfinish(PGconn
PQfinish(PGconn *conn);
*conn);
ce preia identificatorul conexiunii.
Statusul unei conexiuni îl obţinem cu funcţia:
ConnStatusType
ConnStatusType PQstatus(const
PQstatus(const PGconn
PGconn *conn);
*conn);
Acesta poate fi CONNECTION_BAD sau CONNECTION_OK pentru conexiunile
sincrone. De asemenea, există o serie de funcţii ce permit obţine-
rea de alte informaţii precum: portul, utilizatorul, informaţii

152
2.Interfaţarea client-server PostgreSQL în limbajul C/C++

despre SSL etc.


În continuare este prezentat un exemplu de conectare
/*Ex.1(1):*/
/*Ex.1(1):*/ const
const char
char *sir_conexiune;
*sir_conexiune; PGconn
PGconn *conexiune1;
*conexiune1;
sir_conexiune="dbname=curs_info
sir_conexiune="dbname=curs_info password=abcd
password=abcd user=postgres
user=postgres
port=5435";
port=5435"; conexiune1
conexiune1 == PQconnectdb(sir_conexiune);
PQconnectdb(sir_conexiune);
if
if (PQstatus(conexiune1)
(PQstatus(conexiune1) != != CONNECTION_OK)
CONNECTION_OK)
{{ PQfinish(conexiune1);
PQfinish(conexiune1); printf("
printf("EROARE
EROARE LA CONECTARE!");}
LA CONECTARE! ");}
else
else printf(" CONECTAT!!!\n");
printf("CONECTAT!!!\n ");

2.1.2. Transmiterea comenzilor SQL către server


Transmiterea către server a unei comenzi SQL în vederea
execuţiei este îndeplinită prin funcţia:
PGresult
PGresult *PQexec(PGconn
*PQexec(PGconn *conexiune,
*conexiune, const
const char
char *commandă);
*commandă);
Această funcţie preia identificatorul conexiunii şi şirul ce
caractere ce conţine comanda SQL. Rezultatul obţinut, transmis de
la server-ul PostgreSQL către client, după execuţia comenzii, este
încapsulat într-o structură de date PGresult.
Testarea rezultatului execuţiei se realizează cu funcţia:
ExecStatusType
ExecStatusType PQresultStatus(const
PQresultStatus(const PGresult
PGresult *rezultat);
*rezultat);
Rezultatul poate fi:
✗ PGRES_EMPTY_QUERY – comandă vidă (fără conţinut), transmisă
către server;
✗ PGRES_COMMAND_OK – comanda a fost corectă, dar clientul nu a
primit date de la server;
✗ PGRES_TUPLES_OK – comandă corectă, iar server-ul a răspuns,
este specifică comenzii SQL “SELECT”, dar poate fi utilizată
şi la comenzile pentru editare;
✗ PGRES_COPY_OUT – datele sunt în curs de transfer de la
server către client;
✗ PGRES_COPY_IN – datele sunt în curs de transfer de la client
către server;
✗ PGRES_BAD_RESPONSE – răspunsul de la server nu poate fi
interpretat;
✗ PGRES_NONFATAL_ERROR – server-ul a transmis mesaj de
avertizare;
✗ PGRES_FATAL_ERROR – server-ul a transmis mesaj de eroare;
✗ PGRES_SINGLE_TUPLE – server-ul a transmis către client o
singură înregistrare.
Un exemplu de apel a funcţiei de execuţie este redat în
continuare:

153
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

/*Ex.2(1):*/
/*Ex.2(1):*/ PGresult
PGresult *rezultat1;
*rezultat1;
rezultat1
rezultat1 = PQexec(conexiune1,"SELECT cota,titlu,an_ap
= PQexec(conexiune1,"SELECT cota,titlu,an_ap FROM
FROM c8.inv;");
c8.inv;");
if(PQresultStatus(rezultat1)
if(PQresultStatus(rezultat1) !=!= PGRES_TUPLES_OK)
PGRES_TUPLES_OK)
{PQclear(rezultat1);PQfinish(conexiune1);printf("
{PQclear(rezultat1);PQfinish(conexiune1);printf("EROARE INTEROGARE!");}
EROARE INTEROGARE! ");}
În caz de eroare este necesară eliberarea memoriei de even-
tualele alocări de resurse efectuate până în acel punct. În acest
sens este utilizată funcţia PQclear(rezultat1) înainte de
închiderea conexiunii.
Se poate trimite o singură comandă SQL sau un bloc cu
comenzi SQL.
2.1.3. Extragerea datelor obţinute în urma interogării serverului
În cazul în care rezultatul constă într-un set de înre-
gistrări este necesar să ştim numărul de înregistrări returnate. În
acest sens utilizăm funcţia:
int
int PQntuples(const
PQntuples(const PGresult
PGresult *rezultat);
*rezultat);
Aceasta preia adresa variabilei cu rezultatul interogării şi
întoarce numărul de înregistrări rezultate în urma interogării şi
preluate în variabila de tip PGresult.
Notă: Pentru sistemele pe 32 de biţi, numărul de înregistrări nu
poate fi mai mare de 232 înregistrări, aceasta se datorează
reprezentării tipului “int” pe sistemele pe 32 biţi.
Pentru obţinerea numărului de câmpuri rezultate în urma
interogării avem o funcţie asemănătoare:
int
int PQntuples(const
PQntuples(const PGresult
PGresult *rezultat);
*rezultat);
Este posibilă obţinerea numelui pentru fiecare câmp în
parte. Funcţia preia adresa resursei şi numărul de ordine al
coloanei şi întoarce numele câmpului.
char
char *PQfname(const
*PQfname(const PGresult
PGresult *rezultat,
*rezultat, int
int număr_coloană);
număr_coloană);
De asemenea, se poate obţine tipul datei din cadrul fiecărui
câmp. Funcţia întoarce OID-ul corespondent tipului datei (acesta
este un număr unic şi există în sistemul de cataloage din cadrul
PostgreSQL – tabelul pg_type).
Oid
Oid PQftype(const
PQftype(const PGresult
PGresult *rezultat,
*rezultat, int
int număr_coloană);
număr_coloană);
Dimensiunea câmpurilor, în octeţi, poate fi obţinută prin
funcţia:
int
int PQfsize(const
PQfsize(const PGresult
PGresult *rezultat,
*rezultat, int
int număr_coloană);
număr_coloană);
Preluarea înregistrărilor se realizează prin preluare celulă
cu celulă din tabelul întors. Funcţia prin care se poate realiza
preluarea este: char
char *PQgetvalue(const
*PQgetvalue(const PGresult
PGresult *rezultat,
*rezultat,
int
int număr_rând,
număr_rând, int
int număr_coloană);
număr_coloană);

154
2.Interfaţarea client-server PostgreSQL în limbajul C/C++

Indiferent de tipul de dată al câmpului/coloanei funcţia


întoarce ca valori şiruri de caractere. Secvenţa următoare
ilustrează obţinerea acestora.
/*Ex.3(1):*/
/*Ex.3(1):*/ int
int nrCampuri=PQnfields(rezultat1);
nrCampuri=PQnfields(rezultat1);
int
int nrInregistrari=
nrInregistrari= PQntuples(rezultat1);
PQntuples(rezultat1);
printf("Nr.Inregistr:%d
printf("Nr.Inregistr:%d Nr.Campuri:%d
Nr.Campuri:%d \n",nrInregistrari,nrCampuri);
\n",nrInregistrari,nrCampuri);
for(int
for(int i=0;i<nrInregistrari;i++)
i=0;i<nrInregistrari;i++) {{
for(int
for(int j=0;j<nrCampuri;j++)
j=0;j<nrCampuri;j++) printf("%s
printf("%s ",PQgetvalue(rezultat1,i,j));
",PQgetvalue(rezultat1,i,j));
printf("\n");
printf("\n"); }}
Întreruperea unei comenzi aflate în execuţie de către server
se realizează prin comenzile:
PGcancel
PGcancel *PQgetCancel(PGconn
*PQgetCancel(PGconn *conn);void
*conn);void PQfreeCancel(PGcancel*cancel);
PQfreeCancel(PGcancel*cancel);

2.2. Exemplu privind apelul funcţiilor libpq în cadrul unui cod C


standard
Pentru a putea utiliza biblioteca statică libpq, sunt nece-
sare următoarele setări la nivelul unui proiect C/C++:
☑ în setările pentru compilare, pentru “Include Path”, se va
adăuga calea pentru fişierele cu declaraţii din cadrul
bibliotecii (este localizată: “..... /PostgreSQL /.....
/include”);
☑ în setările pentru link-editare, pentru “Libraries Search
Path” se va adăuga calea pentru fişierele cu biblioteca
libpq: (are forma: “...../PostgreSQL/...../lib”);
☑ în setările pentru link-editare, pentru Linker Options se va
adăuga opţiunea “-lpq”;
☑ în fişierele ce apelează funcţii din biblioteca libpq se vor
adăuga: “#include <libpq-fe.h>”
Pentru testare, în baza de date picdb construim schema c5 şi
un tabel inv în care adăugăm câteva înregistrări:
-- Ex. 1-1
CREATE SCHEMA pic; DROP TABLE IF EXISTS pic.inv;
CREATE TABLE pic.inv (cota character varying(50) NOT NULL, titlu
character varying(100), editura character varying(50), an_ap integer,
nr_pag integer, autori character varying(200), nr_vol integer, limba
character varying(30), imprumutata boolean DEFAULT false);
INSERT INTO pic.inv VALUES ('001', 'T1', 'ALL', 1977, 150, NULL, NULL, NULL,
false), ('002', 'T21', 'Nemira', 1977, 190, NULL, NULL, NULL, false), ('003',
'T1', 'Tehnica', 1977, 250, NULL, NULL, NULL, false), ('004', 'T54', 'Albatros',
1977, 220, NULL, NULL, NULL, false), ('005', 'T97', 'Nemira', 1977, 80, NULL,
NULL, NULL, false), ('006', 'T1', 'ALL', 1977, 50, NULL, NULL, NULL, false),
('007', 'T77', 'Tehnica', 1977, 120, NULL, NULL, NULL, false), ('008', 'T21',
'Albatros', 1987, 100, NULL, NULL, NULL, false);

155
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

Fişierul main.cpp ce permite citirea tabelului va avea structura:


//Ex. 1-2
#include <stdio.h>
#include <libpq-fe.h>
int main()
{const char *sir_conexiune;
PGconn *conexiune1;//PGconn structura din libpq ce ţine datele conexiunii
sir_conexiune =
"dbname=picdb password=abcd user=postgres port=5435 host=127.0.0.1";
//conectarea la baza de date -- conexiune persistentă
conexiune1=PQconnectdb(sir_conexiune);
//funcţie ce deschide o conexiune pentru pidb
if(PQstatus(conexiune1) != CONNECTION_OK)
{ PQfinish(conexiune1); printf("EROARE LA CONECTARE!"); }
else printf("CONECTAT!!!\n");
//PGresult - structura din libpq ce gestionează rezultatul interogării
PGresult *rezultat1;
//apelează funcţia ce lansează o interogare SQL pentru server
rezultat1 = PQexec(conexiune1, "SELECT cota,titlu,an_ap FROM pic.inv;");
//testează rezultatul interogării
if (PQresultStatus(rezultat1) != PGRES_TUPLES_OK)
{PQclear(rezultat1);PQfinish(conexiune1);printf("EROARE LA INTEROGARE!");}
//citim nr. de câmpuri şi înregistrări din rezultatul transmis
int nrCampuri=PQnfields(rezultat1);//citeşte nr. de câmpuri din rezultat
int nrInregistrari=PQntuples(rezultat1); //citeşte nr. de înregistrări
printf("Nr.Inregistrari:%d Nr.Campuri:%d \n", nrInregistrari, nrCampuri);
//citeşte şi afişează rezultatul --
//PQgetvalue obţine rezultatul dintr-o celulă sub forma unui şir de caractere
for(int i = 0; i < nrInregistrari; i++)
{ for(int j = 0; j < nrCampuri; j++)
printf("%s ",PQgetvalue(rezultat1, i, j));
printf("\n");
} return 0; }

2.3. Exemplu privind utilizarea bibliotecii libpq în cadrul


wxWidgets
Rezultatele interogării bazelor de date sunt reflectate în
afişarea controalelor din cadrul ferestrelor. De asemenea, datele
din cadrul controalelor se pot reflecta în valorile din cadrul
bazelor de date (operaţiile de editare din cadrul aplicaţiilor de
baze de date).
Cel mai uzitat control pentru afişarea rezultatelor unei
interogări este controlul wxGrid. Considerând rezultatul inte-
rogării un tabel, avem o corespondenţă biunivocă între tabelul din
cadrul resursei de tip PGresult şi tabelul asociat controlului
wxGrid.

156
2.Interfaţarea client-server PostgreSQL în limbajul C/C++

În tabelul dat de controlul wxGrid datele din celule sunt de


tipul wxString, în timp ce datele din cadrul tabelului asociat
resursei de tip PGresult sunt de tip şir de caractere (char*).
Prin urmare, pentru afişarea datelor în controlul de tip wxGrid,
este necesară conversia datelor din şir de caractere în wxString.
Această conversie este asigurată prin metoda FromUTF8, funcţie
membră a clasei wxString.
Numărul de linii şi coloane al tabelului asociat resursei va
fi acelaşi cu cel al tabelului asociat controlului wxGrid.
Înaintea execuţiei unei interogări este posibil ca să se cunoască
coloanele rezultate (implicit numărul şi ordinea acestora), însă
nu se cunoşte numărul de înregistrări. În cazul în care numărul
înregistrărilor rezultate în urma interogării este mare (de
exemplu de ordinul zecilor de mii), este recomandat ca interogarea
să se facă astfel încât să poată fi posibilă paginarea
rezultatului prin utilizarea în comanda SQL a instrucţiunilor
OFFSET şi LIMIT.
Pentru adăugarea unui set nou de înregistrări vor fi şterse
în prealabil toate liniile din grid cu execepţia capului de tabel.
În continuare este prezentat un exemplu de extragere a
datelor din tabelul pic.inv (din exemplul anterior) într-un
control de tip wxGrid. Numărul de coloane şi înregistrări va fi
stabilit dinamic, în funcţie de rezultatul întors. De asemenea se
adaugă o înregistrare ce va conţine numărul OID specific fiecărui
tip de dată, acesta fiind util în procesarea datelor din tabel.

Figura VI-3: Afişare date


Aplicaţia conţine două fişiere de tip header şi două fişiere
sursă cpp : igpg.h, main.h, igpg.cpp, main.cpp. Fişierul igpg.h
conţine declaraţia clasei IGPG ce realizează efectiv interfaţarea.
// igpg.h - clasă de interfatare wxwidgests C++ -- PostgreSQL
#include <wx/string.h>
#include <libpq-fe.h>
#include <wx/grid.h>
class IGPG { PGconn *conexiuneRx;
public: IGPG(wxString dbname, wxString user, wxString pw, wxString

157
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

host,wxString port);
~IGPG();
//afişare în grid cu structură variabilă de coloane
void afisQ(wxGrid *GDx, wxString cdaSQL);
};

Un obiect obţinut prin instanţierea clasei IGPG corespunde


unei conexiuni. Constructorul clasei IGPG preia parametrii de
conectare (numele bazei de date, numele utilizatorului sau
aliasul, parola, host-ul reprezentat prin IP-ul calculatorului pe
care se află server-ul PostgreSQL - implicit: localhost şi portul
pe care rulează server-ul PostgreSQL - implicit: 5432).
igpg.cpp
#include <wx/wx.h>
#include <wx/grid.h>
#include "igpg.h"
IGPG::IGPG(wxString dbname, wxString user, wxString pw, wxString
host,wxString port)
{wxString ach;
char sir_conexiune[500];
ach = wxT(" dbname=") + dbname + wxT(" user=") + user + wxT("
password=") + pw + wxT(" host=") + host + wxT(" port=") + port;
strcpy(sir_conexiune, ach.c_str());
conexiuneRx = PQconnectdb(sir_conexiune);
if (PQstatus(conexiuneRx) != CONNECTION_OK)
{ PQfinish(conexiuneRx); wxMessageBox("EROARE LA CONECTARE!"); }
else wxMessageBox("CONECTAT!!!\n");
}
//----------------------------------------------------------------
IGPG::~IGPG() { PQfinish(conexiuneRx);}
//----------------------------------------------------------------
void IGPG::afisQ(wxGrid *GDx, wxString cdaSQL)
{ PGresult *rezultatRx;
rezultatRx = PQexec(conexiuneRx, cdaSQL);
if(PQresultStatus(rezultatRx) != PGRES_TUPLES_OK) return;
int nrCampuri = PQnfields(rezultatRx);
int nrInregistrari = PQntuples (rezultatRx);
GDx->DeleteRows(0, GDx->GetNumberRows());
GDx->DeleteCols(0, GDx->GetNumberCols());
GDx->AppendCols(nrCampuri); GDx->AppendRows(nrInregistrari);
wxString ach; int i, j; //preia datele în fiecare celulă
for(i = 0; i < nrInregistrari; i++)
for(j = 0; j < nrCampuri; j++)
{ ach = ach.FromUTF8(PQgetvalue(rezultatRx, i, j));
GDx->SetCellValue(i, j, ach); }
//afişează denumiri câmpuri
for(j = 0; j < nrCampuri; j++)
{ ach = ach.FromUTF8(PQfname(rezultatRx, j));
GDx->SetColLabelValue(j, ach); }

158
2.Interfaţarea client-server PostgreSQL în limbajul C/C++

// pe ultima linie afişează OID corespunzător tipulului de câmp


int z; GDx->AppendRows(1);
GDx->SetRowLabelValue(nrInregistrari, _("OID tip"));
for(j = 0; j < nrCampuri; j++)
{ z = (int)PQftype(rezultatRx, j); ach = ach.FromDouble(z);
GDx->SetCellValue(nrInregistrari, j, ach);
}
if(rezultatRx) PQclear(rezultatRx); //ştergere date buffer
}
În cadrul constructorului clasei IGPG este format şirul de
conectare, se încearcă realizarea conexiunii şi se testează
rezultatul.
Metoda “void IGPG::afisQ(wxGrid *GDx, wxString cdaSQL)”
preia comanda ce se transmite server-ului PostgreSQL şi
adresa grid-ului în care va fi scris rezultatul. În cadrul
acestei metode se execută comanda SQL şi se testează
rezultatul. Dacă au fost obţinute rezultate, se citeşte
numărul de câmpuri şi numărul de înregistrări obţinute. Se
setează controlul de tip wxGrid cu dimensiunea obţinută.
Datele sunt copiate celulă cu celulă în controlul wxGrid.
Fiecare dată este convertită din şir de caractere în obiect
wxString cu ajutorul funcţiei wxString.FromUTF8(char*).
Pentru afişarea valorilor de tip OID a fost necesară conversia din
tipul integer în obiect wxString cu ajutorul funcţiei
wxString.FromDouble(double). După utilizare, zona de memorie
ocupată de variabila de tip PGresult este eliberată prin funcţia
PQclear.

//main.h
#include <wx/wx.h>
class MainApp : public wxApp { public: virtual bool OnInit(); };
DECLARE_APP(MainApp)
class MainFrame : public wxFrame
{ IGPG *x; protected: wxGrid* GD1; wxButton* B_AFIS; public:
MainFrame(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString&
titlu = _(" Afişare în grid"), const wxPoint& poz = wxDefaultPosition,
const wxSize& dim = wxSize(500, 193), long stilfer = wxCLOSE_BOX|
wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL);
~MainFrame();
protected: virtual void afiseaza(wxCommandEvent& eveniment);
};

În acest fişier, de tip header, sunt declarate clasa ce


gestionează aplicaţia (MainApp) şi clasa ce gestionează fereastra
principală (unica fereastră). Constructorul ferestrei este setat cu
o serie de parametri impliciţi. Stilul ferestrei este construit
printr-o operaţie de SAU logic la nivel de bit (operatorul “|”)

159
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

între constantele specificate.


//main.cpp
#include "igpg.h"
#include "main.h"
IMPLEMENT_APP(MainApp);
bool MainApp::OnInit()
{ SetTopWindow(new MainFrame(NULL)); GetTopWindow()->Show(); return
true;}
MainFrame::MainFrame(wxWindow* parent, wxWindowID id, const wxString&
titlu, const wxPoint& poz, const wxSize& dim, long stilfer) :
wxFrame(parent, id, titlu, poz, dim, stilfer)
{wxBoxSizer* mainSizer; mainSizer = new wxBoxSizer(wxVERTICAL);
GD1 = new wxGrid(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0);
GD1->CreateGrid(5,5);GD1->EnableEditing(true);GD1->EnableGridLines(true);
GD1->EnableDragGridSize(false);GD1->SetMargins(0, 0);
// Coloane
GD1->EnableDragColMove(false); GD1->EnableDragColSize(true);
GD1->SetColLabelSize(30);
GD1->SetColLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
// Rânduri
GD1->EnableDragRowSize(true); GD1->SetRowLabelSize(80);
GD1->SetRowLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
GD1->SetDefaultCellAlignment(wxALIGN_LEFT, wxALIGN_TOP);
mainSizer->Add(GD1, 0, wxALL, 5);
wxBoxSizer* bSizer2; bSizer2 = new wxBoxSizer(wxHORIZONTAL);
B_AFIS=new wxButton(this, wxID_ANY, _("AFISEAZA"), wxDefaultPosition,
wxDefaultSize, 0); bSizer2->Add(B_AFIS, 0, wxALL, 5);
mainSizer->Add(bSizer2, 0, wxEXPAND, 5);
this->SetSizer(mainSizer); this->Layout(); this->Centre(wxBOTH);
B_AFIS->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(MainFrame::afiseaza), NULL, this);
x=new IGPG("picdb","postgres","abcd","127.0.0.1","5435");
}
MainFrame::~MainFrame()
{ B_AFIS->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(MainFrame::afiseaza), NULL, this);
}
void MainFrame::afiseaza(wxCommandEvent& eveniment)
{x->afisQ(GD1, "select * from pic.inv"); }

Metodele privind poziţionarea controalelor sunt apelate în


constructorul ferestrei. Aceste metode sunt detaliate în docu-
mentaţia de pe www.widgets.org, dar pentru acest exemplu nu sunt
relevante.
În ultimul capitol este explicat şi modul de legare al altor
controale cu server-ul PostgreSQL.

160
3.Generarea în limbajul C a rapoartelor în fişiere format PDF

3. Generarea în limbajul C a rapoartelor în fişiere format


PDF

3.1. Introducere în utilizarea bibliotecii libHARU


Biblioteca libHARU “http://libharu.org/” este o colecţie de
funcţii C, open source, ce permite crearea fişierelor PDF,
indiferent de platformă. Biblioteca se descarcă sub formă de cod
sursă. Pentru a putea fi utilizată cu un anumit compilator, este
necesară compilarea acesteia cu acelaşi compilator cu care se va
lucra. În funcţie de compilator sunt activate sau nu anumite
resurse. Din acest motiv este necesar un program de tip makefile
pentru compilarea bibliotecii libHARU. În acest sens, pentru un
sistem MS Windows şi cu un mediu de dezvoltare CodeLite este
necesară parcurgerea următoarelor etape:
☑ În variabila sistem PATH se verifică existenţa directorului
care conţine mingw32-make.exe (de ex.: ;D:\MinGW-4.7.1\bin),
dacă nu se adaugă în "Local Variable" şi se restartează
sistemul de operare;
☑ În MakeFile.mingw se înlocuieşte -mno-cygwin cu NIMIC;
☑ Din directorul libharu-versiunea se execută comanda:
mingw32-make -f script/MakeFile.mingw;
☑ Pentru testare se deschide un proiect în CodeLite şi se
adaugă opţiunile la nivel de proiect (click dreapta pe
proiect) – exemplu:
>>Common_settings>>Compiler>>Include_Path:>>
D:/LibHaru/libharu-2.0.8/include;
>>Common settings >>Linker>>Library Path:>>
D:/LibHaru/libharu-2.0.8/win32/mingw;
D:/LibHaru/libharu-2.0.8>>Common settings
>>Linker>>Options:>> -lhpdf -lpng -lz -lm
Fişierele construite cu ajutorul bibliotecii libHARU pot
conţine text, linii, imagini, adnotări etc. În module este folosit
tipul de dată HPDF_REAL pentru stocarea poziţiei pe una din
coordonate.
3.2. Funcţii uzuale oferite de bibliotecă
Modulul HPDF_Doc conţine funcţiile ce gestionează obiectul
document (conţine funcţii la nivel de fişier PDF). Dintre aceste
funcţii menţionez:
✗ HPDF_Doc HPDF_New (HPDF_Error_Handler user_error_fn, void
*user_data) – funcţia instanţiază un obiect PDF;
✗ HPDF_Doc HPDF_Free (HPDF_Doc pdf) – eliberează memoria
alocată pentru documentul pdf;

161
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

✗HPDF_STATUS HPDF_Info_SetInfoDateAttr(HPDF_Dict info,


HPDF_InfoType type, HPDF_Date value) – funcţie ce setează
atributele documentului PDF (autor, titlu, subiect, cuvinte
cheie etc.).
Modulul HPDF_Page gestionează informaţii specifice fiecărei
pagini din document. Câteva funcţii relevante din acest modul:
☑ HPDF_STATUS HPDF_Page_SetWidth (HPDF_Page page, HPDF_REAL
value) – setează lăţimea paginii;
☑ HPDF_STATUS HPDF_Page_SetHeight(HPDF_Page page, HPDF_REAL
value); – setează înălţimea paginii;
☑ HPDF_STATUS HPDF_Page_SetSize(HPDF_Page page, HPDF_PageSizes
size, HPDF_PageDirection direction); – setare pagină (ex.:
HPDF_Page_SetSize(pag1, HPDF_PAGE_SIZE_A4, HPDF_PAGE_PORTRAIT);
☑ HPDF_REAL HPDF_Page_TextWidth (HPDF_Page page, const char
*text) – obţine lungimea, în unităţi grafice pentru textul
transmis prin argument.
Modulul HPDF_Font gestionează informaţiile specifice
fonturilor utilizate. Câteva funcţii relevante pentru acest modul:
✗ const char * HPDF_Font_GetFontName (HPDF_Font font) – obţine
numele fontului din cadrul unei resurse de tip font;
✗ const char * HPDF_Font_GetEncodingName(HPDF_Font font) –
obţine numele encoding-ul ataşat unei resurse font.
Pot fi utilizate mai multe tipuri de fonturi:
☑ Base 14 Font – sunt fonturi create în cadrul fişierelor PDF,
nu necesită o încărcare a acestora din fişiere.
☑ TrueType Font – cele mai răspândite, fonturi bazate pe
funcţiile de desenare ale conturului (TrueType este un
format vectorial care folosește curbe B-spline pătratice);
☑ CID Font – fonturi multicaracter dezvoltate de Adobe.
Punctul de origine pentru desenarea unui font este
considerat a fi stâga jos.
Modulul HPDF_Image permite încărcarea imaginilor format JPG
sau PNG. Dintre funcţiile acestui modul fac parte:
✗ HPDF_LoadPngImageFromFile() – încarcă un fişier format PNG;
✗ HPDF_LoadJpgImageFromFile() – încarcă un fişier format JPG;
✗ HPDF_LoadRawImageFromMem() – încarcă direct din memorie o
imagine binară;
✗ HPDF_Point HPDF_Image_GetSize (HPDF_Image image);
✗ HPDF_UINT HPDF_Image_GetWidth (HPDF_Image image);
✗ HPDF_UINT HPDF_Image_GetHeight (HPDF_Image image).
Modulul HPDF_Annotations permite adăugarea de adnotări şi
conţine funcţii precum:
☑ HPDF_STATUS HPDF_TextAnnot_SetOpened(HPDF_Annotation annot,
HPDF_BOOL opened) – setează adnotarea pentru a se afişa
automat la deschiderea fişierului PDF;
☑ HPDF_TextAnnot_SetIcon(HPDF_Annotation annot, HPDF_AnnotIcon

162
3.Generarea în limbajul C a rapoartelor în fişiere format PDF

icon) – setează imaginea ataşată adnotării


(HPDF_ANNOT_ICON_COMMENT, HPDF_ANNOT_ICON_NOTE,
HPDF_ANNOT_ICON_HELP, HPDF_ANNOT_ICON_INSERT etc.).

Centrul coordonatelor paginii (0,0) este în punctul stânga-


jos. Rezoluţia implicită este de 72 dpi, deci pentru o pagină
format A4 vom avea 210 × 297 (mm) sau 8.27 x 11.69 inches, adică
aproximativ 595 x 841 pixeli. Reprezentarea pentru numărul de
pixeli este dată de tipul HPDF_REAL.
O resursă pagină (HPDF_Page) se poate afla în următoarele
moduri: HPDF_GMODE_PAGE_DESCRIPTION, HPDF_GMODE_PATH_OBJECT şi
HPDF_GMODE_TEXT_OBJECT. Trecerea dintr-un mod în altul se realizează
în funcţie de metodele apelate şi este detaliată în diagrama
următoare:
HPDF_GMODE_TEXT_OBJECT
Operaţii permise;
Afişare text
Setări pagină, font, culoare etc.

HPDF_Page_BeginText() HPDF_Page_BeginText()

HPDF_GMODE_PAGE_DESCRIPTION
(implicit)
Operaţii permise: Setări
pagină, font, culoare etc.

HPDF_Page_MoveToNextLine() Funcţii de validare a


HPDF_Page_MoveTo() etc. operaţiilor de desenare:
sau primitive grafice: HPDF_Page_Stroke()
HPDF_Page_Rectangle() HPDF_Page_FillStroke
HPDF_Page_Circle() etc. HPDF_Page_EndPath etc.

HPDF_GMODE_PATH_OBJECT
Operaţii permise; Funcţiile de mai
sus. Poziţionarea va fi relativă,
se raportează la ultima poziţie.
Figura VI-4: Diagrama de trecere dintr-un mod grafic în altul
Pentru a putea scrie, este necesară trecerea în modul
HPDF_GMODE_TEXT_OBJECT. În acest mod este asigurat avansul pe
orizontală conform dimensiunii fontului. Pentru a continua pe linia
următoare, se poate utiliza funcţia HPDF_Page_MoveToNextLine(). La
încheierea sesiunii de scriere se va ieşi din acest mod grafic.

163
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

3.3. Exemplu de construcţie a unui fişier PDF


În continuare este prezentat un exemplu comentat pentru
crearea a unui fişier format PDF.

Figura VI-5: Exemplu afişare raport


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <exception>
#include "hpdf.h"

#ifdef HPDF_DLL
void __stdcall
#else
void
#endif
//definirea funcţiei pentru afişarea erorilor
error_handler (HPDF_STATUS error_no, HPDF_STATUS detail_no,
void *user_data)
{ printf ("ERROR: error_no=%04X, detail_no=%u\n",
(HPDF_UINT)error_no, (HPDF_UINT)detail_no);
// A SE VERIFICA http://libharu.sourceforge.net/error_handling.html
throw std::exception ();
}

164
3.Generarea în limbajul C a rapoartelor în fişiere format PDF

int main (int argc, char **argv)


{ const char *page_titlu = "TITLUL RAPORTULUI";
HPDF_Doc pdf; //Documentul PDF
HPDF_Page page1, page2; // două pagini din cadrul documentului
HPDF_Font def_font;
HPDF_REAL tw, height, width;

pdf = HPDF_New (error_handler, NULL);


if(!pdf) {printf("eroare: Nu poate fi creat obiectul PdfDoc !\n");
return 1; }
//setare informatii fişier PDF
HPDF_SetInfoAttr (pdf,HPDF_INFO_AUTHOR,"Autor Nume");
HPDF_SetInfoAttr (pdf,HPDF_INFO_TITLE,"TEST");
try {
page1 = HPDF_AddPage (pdf);
// adăugarea primei pagini din cadrul documentului PDF
HPDF_Page_SetSize (page1, HPDF_PAGE_SIZE_A4, HPDF_PAGE_PORTRAIT);
// setarea paginii A4 portret
height=HPDF_Page_GetHeight(page1); width=HPDF_Page_GetWidth(page1);
//preluarea dimensiuni paginii
HPDF_Page_SetRGBFill(page1, 1, 0, 0);
//setare culoare de scriere rosu
HPDF_Page_SetLineWidth(page1, 1); //grosime limie de scriere
HPDF_Page_Rectangle(page1,50,50,width-100,height-110);
//desenare dreptunghi
HPDF_Page_Stroke(page1); // definitivare imagini desenare
def_font = HPDF_GetFont(pdf, "Helvetica", NULL);
HPDF_Page_SetFontAndSize(page1, def_font, 24);
// setare font scriere pentru prima pagină
tw = HPDF_Page_TextWidth(page1, page_titlu);
// dimensiunea pe orizontală pentru textul din page_titlu
HPDF_Page_BeginText(page1); //începe secvenţa de scriere
HPDF_Page_MoveTextPos(page1,(width-tw)/2,height-50);
//poziţionare cursor pentru începerea scrierii
HPDF_Page_ShowText(page1, page_titlu); //afişare text
HPDF_Page_EndText(page1);//sfârşitul secţiunii de scriere

HPDF_Page_BeginText(page1);//o nouă secţiune de scriere


HPDF_Page_MoveTextPos(page1,60,height-80);
//o noua repoziţionare absolută
HPDF_Page_SetFontAndSize(page1, def_font, 16);
//setare font scriere
HPDF_Page_SetRGBFill(page1, 0.8, 0.521, 0.2);
//setare culoare de scriere
HPDF_Page_ShowText(page1, "UN TEST CU FONTURI"); //afişare text
HPDF_Page_EndText(page1); //sfârşit secţiune de scriere
//desenarea unui dreptunghi linie cu linie
const HPDF_UINT16 DASH_MODE2[] = {3, 9};
HPDF_Page_SetLineWidth(page1, 2.0);
HPDF_Page_SetDash(page1,DASH_MODE2,2,2);
HPDF_Page_MoveTo(page1, 210, 370);

165
VI.Dezvoltarea interfeţei client cu ajutorul bibliotecilor

HPDF_Page_LineTo(page1,390,370); HPDF_Page_LineTo(page1,390,410);
HPDF_Page_LineTo(page1,210,410); HPDF_Page_LineTo(page1,210,370);
HPDF_Page_Stroke(page1);

//un alt text în prima pagină


HPDF_Page_BeginText(page1);
HPDF_Font font=HPDF_GetFont(pdf,"Courier-BoldOblique","CP1250");
HPDF_Page_SetFontAndSize (page1, font, 29);
HPDF_Page_SetRGBFill (page1, 0.3, 0.121, 0.7);
HPDF_Page_MoveTextPos (page1, 220, 380);
HPDF_Page_ShowText (page1, "UN CHENAR");
HPDF_Page_EndText (page1);

page2 = HPDF_AddPage(pdf); //adăugarea celei de-a doua pagini


HPDF_Image image;
image = HPDF_LoadPngImageFromFile(pdf, "stoky.png");
double iw, ih;
iw=HPDF_Image_GetWidth(image);ih=HPDF_Image_GetHeight(image);
//preluarea dimensiunii imaginii
HPDF_Page_DrawImage(page1,image,10,height-ih-10,iw,ih);
HPDF_Page_DrawImage(page2,image,10,height-ih-10,iw,ih);
//ataşarea imaginii în a doua pagină
HPDF_Rect rect1 = {50, 350, 70, 330};
HPDF_Rect rect2 = {210, 350, 230, 330};
HPDF_Rect rect3 = {50, 250, 70, 230};
HPDF_Annotation adnotare;
//adăugarea celor trei adnotări

adnotare=HPDF_Page_CreateTextAnnot(page1,rect1,"COMENTARIU...",NULL);
HPDF_TextAnnot_SetIcon(adnotare, HPDF_ANNOT_ICON_COMMENT);
HPDF_TextAnnot_SetOpened(adnotare, HPDF_TRUE);
adnotare=HPDF_Page_CreateTextAnnot(page1,rect2,"Adnotare ...",
NULL); HPDF_TextAnnot_SetIcon(adnotare, HPDF_ANNOT_ICON_NOTE);
adnotare=HPDF_Page_CreateTextAnnot(page1, rect3,"Help ....",
NULL); HPDF_TextAnnot_SetIcon(adnotare, HPDF_ANNOT_ICON_HELP);

//scriere text în a doua pagină


HPDF_Page_BeginText(page2);
def_font = HPDF_GetFont(pdf, "Times-BoldItalic", NULL);
HPDF_Page_SetFontAndSize(page2, def_font, 36);
HPDF_Page_MoveTextPos(page2, (width - tw) / 2, height - 50);
HPDF_Page_ShowText(page2, "TITLUL DIN PAGINA 2");
def_font = HPDF_GetFont (pdf, "Times-Roman", NULL);
HPDF_Page_SetFontAndSize (page2, def_font, 24);
HPDF_Page_MoveTextPos (page2, 0, 0 - 136);
// deplasare relativă cu 136 de unităţi(pixeli din doc) mai jos
HPDF_Page_ShowText (page2, "abcd ABCD");
def_font = HPDF_GetFont (pdf, "Symbol",NULL);
//afişare utilizând fonturile “simbol”
HPDF_Page_SetFontAndSize (page2, def_font, 24);
HPDF_Page_MoveTextPos (page2, 0, 0-24);

166
3.Generarea în limbajul C a rapoartelor în fişiere format PDF

HPDF_Page_ShowText (page2, "abcd ABCD");


def_font = HPDF_GetFont (pdf, "Times-Roman","CP1250");
//font Times-Roman cu encoding CP1250
HPDF_Page_SetFontAndSize (page2, def_font, 24);
HPDF_Page_EndText (page2);

//desenarea unor dreptunghiuri în prima pagină


HPDF_Page_SetLineWidth(page1,1);
HPDF_Page_SetDash(page1,NULL,0,0); //linie plină
HPDF_Page_Rectangle (page1, rect1.left, rect1.bottom, rect1.right-
rect1.left, rect1.top-rect1.bottom);
HPDF_Page_Rectangle (page1, rect2.left, rect2.bottom, rect2.right-
rect2.left, rect2.top-rect2.bottom);
HPDF_Page_Rectangle (page1, rect3.left, rect3.bottom, rect3.right-
rect3.left, rect3.top-rect3.bottom);
HPDF_Page_Stroke (page1);

HPDF_Page_SetFontAndSize (page2, def_font,16);


unsigned char buf[2]; buf[1]=0;
HPDF_Page_BeginText (page2);
HPDF_Page_MoveTextPos (page2, 50, 600);
for(int i=0;i<255;i++)
{ buf[0]=i;if(i % 20) HPDF_Page_ShowText(page2, (char*)buf);
else {HPDF_Page_MoveTextPos (page2, 0, 0-20);
HPDF_Page_ShowTextNextLine(page2, (char*)buf);
//scrie pe linia următoare
}//end else
}//end for
HPDF_Page_MoveTextPos2(page2, 0, 0-20);
HPDF_Page_ShowText(page2,"DIACRITICE: ");
char diacritice[10]={0xAA,0xBA,0xC2,0xE2,0xC3,0xE3,0xCE,0xEE,0xDE,0xFE};
for(int i=0;i<10;i++)
{ buf[0]=diacritice[i]; HPDF_Page_ShowText(page2, (char*)buf);}
HPDF_Page_EndText (page2);

HPDF_SaveToFile (pdf,"pic.pdf"); //crearea fişierului PDF

} catch (...) {//în cazul în care nu a putut fi creat documentul


HPDF_Free (pdf);//eliberarea resurselor alocate
return 1;
}
HPDF_Free (pdf); //eliberarea resurselor alocate
return 0;
}
Înainte de lansarea executabilului, ce duce la crearea
fişierului pic.pdf, se va verifica dacă nu există deschis un fişier
cu acelaşi nume, existenţa unui astfel de fişier deschis generează
eroare în momentul execuţiei. Erorile vor fi identificate conform:
https://github.com/libharu/libharu/wiki/Error-handling.

167
VII.Dezvoltarea unor module pentru logistica stocurilor

VII. Dezvoltarea unor module pentru logistica


stocurilor
Pentru exemplificarea dezvoltării unor module din cadrul
unei aplicaţii de baze de date cu interfaţă în C++ am ales o
aplicaţie de logistica stocurilor (de gestiune) – LogX. Această
aplicaţie este doar pentru gestiunile ce operează cu produse ce nu
presupun existenţa unor termene de garanţie, altfel aplicaţia ar fi
fost centrată pe loturile de produse.
LogX este o aplicaţie ce implementează doar o parte din
operaţiile de bază aferente aplicaţiilor de tip warehouse
management. Din cadrul acestei aplicaţii am implementat doar
modulele: recepţie marfă (intrări), livrări şi consultare stocuri.
Această implementare este demonstrativă, nu a fost testată
în condiţii de lucru şi nu utilizează clase ce implementează modele
de date, aceasta se traduce într-o viteză mai mică de lucru şi o
dependenţă mai mare de performanţele calculatorului client
(limitările sunt doar la nivel de interfaţă, SGBD-ul PostgreSQL are
performanţe comparabile cu SGBD-ul Oracle).
Stabilirea cerinţelor
Să se proiecteze o aplicaţie pentru gestiunea stocurilor
având următoarele specificaţii:

➢ gestiunea este formată din mai multe depozite(dep), fiecare


având amplasamente specifice (ampl);
➢ marfurile se pot transfera dintr-un depozit în altul sau de pe
un amplasament pe altul în cadrul aceluiaşi depozit;
➢ fiecare item (obiect din cadrul mărfii) este caracterizat prin:
referinţă comercială (ref_com), referinţă tehnică(ref_teh) şi o
descriere (descr) corespunzătoare acestora;
➢ unele item-uri pot avea serie (serial_no);
➢ opţional, se urmăreşte numărul comenzii (po – process order);
➢ opţional, se urmăreşte furnizorul (supplier);
➢ fiecare produs/item se poate afla într-o anumită stare (status);
➢ operaţiile efectuate sunt: recepţie (intrare marfă), livrare
(ieşire marfă) şi transfer între depozite sau amplasamente.

1. Arhitectura aplicaţiei – LogX

1.1. Structura de bază a aplicaţiei


Proiectarea aplicaţiei se va realiza etapizat într-o manieră
top-down.

168
1.Arhitectura aplicaţiei – LogX

P1: Pentru început putem considera un singur bloc intrare-ieşire.


RAPOARTE:
OPERAŢII DE EDITARE: Intrări
Receţie Aplicaţie Transfer
Operaţii transfer LogX Ieşiri
Livrări Stoc (curent)
Figura VII-1: Modelul intrare-ieşire, nivel aplicaţie
Datele din cadrul bazei de date se modifică în urma opera-
ţiilor de editare. Aplicaţia LogX oferă: operaţii de editare şi
generare rapoarte.
P2: Aplicaţia este de tip desktop şi cuprinde interfaţa cu
server-ul PostreSQL (dezvoltată în C++) şi funcţii pe partea de
server ce implementează atât operaţiile de editare, cât şi
construcţia rapoartelor.

EDITARE
(SQL/plpgSQL)
INTERFAŢĂ
UTILIZATOR DATE SERVER
(dezvoltată PostgreSQL
în C++)
RAPOARTE
(SQL/plpgSQL)
SERVER PostgreSQL

Figura VII-2: Structura aplicaţiei

P2: Aplicaţia LogX o descompunem în modulele: intrări, transfer,


ieşiri şi stoc. Operaţiile de editare a ieşirilor şi a trans-
ferurilor utilizează modulul rapoarte stoc (selecţie stoc în
vederea livrării sau a unui transfer).
P3: Operaţiile de recepţie şi livrare presupun mai mulţi paşi,
aceasta va duce la aparaţia de noi submodule. Punerea în evidenţă a
unor resurse comune conduce, de asemenea, la noi submodule.
Astfel putem avea următoarea structură:

169
VII.Dezvoltarea unor module pentru logistica stocurilor

EDITARE INTRĂRI INTRĂRI RAPOARTE INTRĂRI

EDITARE IEŞIRI IEŞIRI RAPOARTE IEŞIRI

STOC RAPOARTE STOC

EDITARE OPERAŢII RAPOARTE TRANSFER


TRANSFER TRANSFERURI
LogX
Figura VII-3: Principalele module

1.2. Fluxul informaţional


Astfel, pentru a evita introducerea mai multor denumiri
pentru descrierea aceluiaşi item, se va utiliza un nomenclator de
produse (nom).
Recepţia se realizează, iniţial, în cadrul unui borderou de
recepţie (BR), care apoi se validează. Borderourile de recepţie în
curs de editare (ce nu sunt definitive/nevalidate) vor fi stocate
într-un tabel PostgreSQL, pe măsură ce sunt validate, acestea vor
fi eliminate din tabel.
Înainte de livrare se realizează rezervarea mărfii prin
constituirea borderoului de preparare (BP), borderou ce se
transformă în borderou de livrare (BL) în momentul validării
borderoului de preparare (BP). Validarea borderoului de preparare
este echivalentă cu constituirea borderoului de livrare (BL va
păstra numărul de la BP).
Fiecare borderou (de preparare/livrare (BP/BL), de recepţie
(BR), de transfer (BT) va avea câte un număr unic asociat. Fiecare
borderou cuprinde unul sau mai multe item-uri. De asemenea, avem şi
informaţii la nivel de borderou, în afară de numărul acestuia,
precum: data constituirii borderoului, sursa, destinaţia etc.
Fluxul informaţional este dat de diagrama de mai jos.

170
1.Arhitectura aplicaţiei – LogX

A B
OPERATOR
nom

SELECŢIE
1 2
FIŞIER CSV
br inx
OPERATOR VALIDARE

D 3 OPERATOR
C OPERATOR
REZERVARE 4
5 bp_
loc SELECŢIE stx
7 _bp
E G ACTUALIZARE STOC
6
ACTUALIZARE VALIDARE
STOC
trx outx
SELECŢIE
OPERATOR
F
Figura VII-4: Fluxul informaţional
Operaţiile de editare a stocurilor sunt descrise prin:
1 - introducerea de date manual sau prin import fişiere CSV;
2 - validarea BR, adăugarea înregistrării în inx şi ştergerea
înregistrărilor din tabelul br;
3 - adăugare automată a înregistrărilor şi în stoc (ID-ul din inx
se va copia şi în stx);
4 - operatorul selectează din stoc liniile ce vor fi livrate;
5 - rezervarea cantităţii (se scade cantitatea disponibilă);
6 - validare BP, inserare înregistrări în outx;
7 - actualizarea cantităţilor fizice în urma livrării;
A - introducerea de coduri noi direct de la operator;
B - actualizarea nomenclatorului pe baza intrărilor, modificări în
intrări pe baza acestuia;
C - introducerea de noi coduri de depozite şi amplasamente direct
de operator;
D - preluarea prin selecţie de noi coduri de depozite şi ampla-
samente;
E - selecţia depozitelor şi amplasamentelor din cadrul transfe-
rurilor;
F - selecţie din stoc în vederea transferurilor;

171
VII.Dezvoltarea unor module pentru logistica stocurilor

G - actualizarea stocurilor în urma transferurilor.


Livrarea mărfurilor se realizează în două etape:
1. Se realizează rezervarea mărfurilor prin constiutuirea BP;
2. Se realizează validarea BP şi transformarea în BL.
În acest sens vom utiliza următoarele noţiuni de cantitate:
☑ cantitate fizică (qty_fiz) – fizic, existent în stoc;
☑ cantitate disponibilă (qty_disp) – poate deveni rezervată;
☑ cantitate rezervată (qty_rez) – rezervată în cadrul BP;
☑ cantitate confirmată (qty_conf) – livrată efectiv.
Iniţial, qty_fiz=qty_disp, apoi prin crearea unui nou BP se
realizează o rezervare de cantitate prin scăderea acesteia din
cantitatea disponibilă. În momentul validării BP se verifică dacă
întreaga cantitate care se livrează (este posibil ca unele produse
să fie refuzate şi atunci cantitatea confirmată devine mai mică
decât cea rezervată. Operaţiile asupra cantităţii sunt:
☑ Rezervare: qty_disp = qty_disp - qty_rez;
☑ Livrare (validare BP):
qty_disp = qty_disp + (qty_disp - qty_conf);
qty_fiz = qty_fiz - qty_conf.
unde:
✗ qty_fiz ≥ qty_disp
✗ qty_rez ≤ qty_disp
✗ qty_conf ≤ qty_rez
1.3. Structura tabelelor PostgreSQL
Tabelele persistente vor fi create în cadrul schemei pic.
Tabele ce vor fi create sunt:
Tabel Descriere Cheia primară Temp.
nom Nomenclatorul de produse idcode
inx Intrările în gestiune id
stx Stocul curent dep, ampl, id
outx Ieşirile din gestiune nrbl, id, dep, ampl
loc Depozite şi amplasamente dep, ampl
bp_ Tabel master pentru BP nrbp
_bp Tabel slave pentru BP nrbp,id,dep,ampl
itmp_bp Tabel pentru interfaţare BP nrbp,id,dep,ampl X
itmp_br Tabel pentru interfaţare BR nrbr, idx X
trx Transferuri EFECTUATE nrbt, nrlin
tr_ Tabel master pentru BT (nevalidate) nrbt

172
1.Arhitectura aplicaţiei – LogX

Tabel Descriere Cheia primară Temp.


_tr Tabel slave pentru BT (nevalidate) nrbt, id, dep_srs,
ampl_srs
itmp_bt Tabel pentru interfaţare BT nrbt, id, dep_srs, X
ampl_srs
tmpselst Selecţie stoc ptr. BP sau TR - X
acces Conţine alias-urile, parolele (iniţiale, alias (codul
la nivel de aplicaţie) şi lista cu codurile privind numele
depozitelor accesibile pentru fiecare utilizatorului)
utilizator
tmpacc Conţine depozitele unde are acces - X
utilizatorul curent (logat)
În acest caz, tabelele temporare PostgreSQL sunt utilizate
numai în vederea interfaţării cu utilizatorul. De asemenea tabelele
master-slave tr_ şi _tr ce conţin transferurile în curs de editare
nu sunt trecute în diagramă deoarece, împreună, conţin aceeaşi
informaţie ca şi tabelul trx cu transferuri deja validate.
Tabelele PostgreSQL de interfaţare au structură asemănătoare
cu cele pe care le reprezintă, sunt utilizate doar pentru a secu-
riza transferul datelor din grid în tabelul PostgreSQL.
Structura tabelelor de bază este dată de diagrama din
fig. 5. Tabelul cu drepturi de acces, denumit acces este utilizat
doar pentru acces şi pentru crearea unui tabel temporar cu
depozitele la care are acces utilizatorul curent (logat). Acest
tabel intră în interogările în care apar şi depozite, astfel vor fi
vizibile doar înregistrările pentru care codul depozitului se
regăseşte în tabelul temporar de acces tmpacc.
Cheile externe pentru depozit şi amplasament (de tip 1:N, 1
fiind corespondent tabelului loc) nu permit existenţa altor
depozite sau amplasamente în tabelele aplicaţiei care nu se
regăsesc în tabelul loc. De asemenea cheile externe pentru câmpul
id nu permite existenţa în alte tabele a unor valori ce nu se
regăsesc în câmpul id din tabelul inx.

173
VII.Dezvoltarea unor module pentru logistica stocurilor

loc
#dep
#ampl
trx
#nrbt
#nrlin
dep_srs
br inx ampl_srs
#nrbr #id dep_dest
#idx nrbr ampl_dest bt_
datain datain id #nrbt
idcode idcode qtytr databt
po po dep_dest
status status ampl_dest
supplier supplier stx
serialno serialno #dep
depint depint _bt
#ampl #nrbt
amplinit amplinit
qty #id #id
qty
val val qtyfiz #dep_srs
qtydisp #ampl_srs
qtytr
nom
#idcode outx
refteh bp_ _bp #nrbl
refcom #nrbp #nrbp #id
descr databp #id #dep
utiliz destin #dep #ampl
data #ampl destin
ora qtyrez qtyliv
qtyconf dataout
Figura VII-5: Structura tabelelor
În tabelul următor sunt enumerate cheile externe:
Tabel Câmp tabelN câmp1:N Tabel Câmp tabelN câmp1:N
inx id stx id br depinit
trx id _bp dep
outx id trx dep_srs
_bt id trx dep_dest
_bp id tr_ dep_dest
nom idcode inx idcode _tr dep_srs
loc dep stx dep loc ampl stx ampl
inx depinit inx amplinit

174
1.Arhitectura aplicaţiei – LogX

Tabel Câmp tabelN câmp1:N Tabel Câmp tabelN câmp1:N


br amplinit tr_ ampl_des
t
_bp ampl
_tr ampl_srs
trx ampl_srs
bt_ nrbt _bt nrbt
trx ampl_des
t bp_ nrbp _bp nrbp

Codul pentru crearea acestor tabele se regăseşte în anexe.

2. Dezvoltarea modulelor SQL/plpgSQL

2.1. Dezvoltarea modulelor SQL/plpgSQL pentru editare


Pentru separarea interfeţei utilizator, dezvoltată în C++,
de comenzile SQL sau funcţiile plpgSQL s-au utilizat doar proceduri
plpgSQL. Acestea sunt detaliate în cele ce urmează:
NOMENCLATOR
● nom_add(_idcode_ text,_refteh_ text,_refcom_ text,_descr_ text)
Adaugă în nomenclator un produs/item. În cazul în care valoarea
dată de idcode deja există în nomenclator întoarce un mesaj text
de eroare altfel întoarce 'OK';
INTRARI/RECEPŢIE
● br_nou()
Adaugă o nouă linie în tabelul logx.br. Funcţia preia data
curentă şi nu întoarce nimic;
● id_nou(_dep_ text)
Funcţia preia depozitul curent şi întoarce un identificator id
unic în sistem (de tip text);
● valid_br(_nrbr_ int)
Validează borderoul de recepţie (BR) al cărui număr este transmis
prin argument. Întoarce un text cu un mesaj de eroare dacă nu
sunt verificate anumite restricţii, altfel întoarce 'OK';
IEŞIRI/LIVRĂRI
● bp_nou(_destin_ text,_databp_ date DEFAULT CURRENT_DATE)
Adaugă o nouă linie în tabelul bp_ şi nu întoarce nimic;
● prelSelST2(_nrbp_ int)
Preia datele din stocul selectat în BP-ul al cărui număr este
transmis prin argument şi nu întoarce nimic;
● valid_bp(_nrbp_ int)
Validează BP-ul al cărui număr este transmis prin argument şi
întoarce un mesaj de eroare sau 'OK'.
TRANSFER
● bt_nou(_dep_dest_ text, _ampl_dest_ text, _databt_ date DEFAULT

175
VII.Dezvoltarea unor module pentru logistica stocurilor

CURRENT_DATE);
Adaugă o nouă linie în tabelul bt_ şi nu întoarce nimic;
● prelSelST_Tr(_nrbt_ int)
Preia datele din stocul selectat în BT-ul al cărui număr este
transmis prin argument. Nu întoarce nimic.
● tr_valid(_nrbt_ int)
Validează BT-ul al cărui număr este transmis prin argument.
Întoarce un mesaj de eroare sau 'OK'.
2.2. Dezvoltarea modulelor plpgSQL pentru rapoarte
Sunt patru tipuri de rapoarte: stoc curent, intrări, ieşiri,
transfer. Interogările sunt concepute într-o manieră flexibilă,
astfel pentru fiecare câmp există două bife, una în stânga şi una
în partea dreaptă. Prin bifa din stânga se stabileşte dacă acel
câmp va apare ca şi coloană în tabel. De exemplu, dacă nu este
bifat decât câmpul supplier, va aparea un număr de linii egal cu
numărul de furnizori, iar în coloana cantitate va fi trecută suma
cantităţilor grupate după furnizor. Bifa din dreapta semnifică
activarea sau dezactivarea filtrului pentru parametrul din stânga
acesteia. Rezultatul interogării va fi pus în tabelul TEMPORAR dat
de tipul interogării (q_tmp_in, q_tmp_st, q_tmp_out sau q_tmp_tr).
Toate cele patru interogări vor utiliza o singură funcţie ce
se bazează pe construcţia dinamică a interogărilor şi are forma:
q_x(_tipQ_ text,_nr_ smallint,_nume_ text[], _tip_ text[], _a_
boolean[], _s_ boolean[], _val1_ text[], _val2_ text[],_ord_ text)
unde:
● _tipQ_: tipul interogarii: IN-intrare, ST-stoc, STL-stoc selectat
în vederea livrării, OUT-iesire, TR-transfer;
● _nr_: numărul de câmpuri ce sunt în fereastra de selecţie;
● _nume_[]: vectorul cu numele câmpurilor/coloanelor;
● _tabel_[]: vector cu tabelele corespondente pentru fiecare
câmp/colană;
● _tip_[]: vectorul ce conţine tipul fiecărui câmp din fereastra de
selecţie. Acesta poate fi:
☑ C – şir de caractere – cautare exactă – case sensitive (ţine
cont de literă mare sau mică);
☑ A – şir de caractere – cautare aproximativa – case
insensitive (nu ţine cont de litera mare sau mică);
☑ D1 – data calendaristică: valoare exactă;
☑ D2 – data calendaristică: data va fi preluată ca interval;
☑ N1 – data de tip numeric: valoare exactă;
☑ N2 – data de tip numeric: interval.
● _a_[]: vectorul cu bifele pentru afişarea câmpurilor;
● _s_[]: vectorul cu bifele pentru selecţia/filtrarea câmpurilor;
● _ord_: câmpul după care se face ordonarea (un singur câmp).

176
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul

3. Dezvoltarea în C++ a modulelor de interfaţare grafică cu


utilizatorul
Aplicaţia va avea următoarele ferestre (cele scrise cu
“italic” nu sunt implementate):
● iFACC - ferestră de acces;
● ferestre pentru editare:
➢ iFPB - fereastră ce conţine panoul cu butoane;
➢ iFEI - fereastră editare intrări/recepţii;
➢ iFEBP - fereastră editare ieşiri/livrări;
➢ FETR - fereastră editare transferurile;
➢ iFNOM - fereastră editare/selecţie nomenclator;
● ferestre pentru rapoarte/selecţie:
➢ FRIN - fereastră rapoarte intrări;
➢ iFSTSEL - fereastră rapoarte stoc şi fereastră
selecţie stoc pentru BP sau BT;
➢ FROUT - fereastră raport ieşiri;
➢ FRTR - fereastră raport transferuri.
3.1. Fereastra de acces iFACC
Această fereastră este lansată înainte de fereastra
principală şi este o fereastră modală
(derivată din wxDialog, aplicaţia nu
rulează mai departe decât după închiderea
acestei ferestre). În cadrul acestei
ferestre sunt implementate şi funcţiile de
acces la nivel de aplicaţie – aplicaţia
intră cu un utilizator (logx_sw) prede-
finit şi în funcţie de alias generează un
nou utilizator cu o parolă generată
aleator, iar după crearea noului
utilizator este cedat controlul acestuia
(este schimbat utilizatorul curent).
Această fereastră asigură următoarele
funcţii: Figura VII-6
☑ preluare nume utililizator(alias) şi
a parolei în vederea accesului la baza de date;
☑ posibilitatea schimbării adresei server-ului PostgreSQL şi a
portului asociat;
☑ posibilitatea schimbării codului de acces (parolei);
☑ generarea utlizatorului la nivel de aplicaţie şi cedarea
controlului către acesta.
Ierarhia obiectelor din cadrul ferestrei este redată în

177
VII.Dezvoltarea unor module pentru logistica stocurilor

digrama următoare:
OBIECTELE GRAFICE DIN LEGENDĂ:
FEREASTRA iFACC wxDialog/wxFrame
wxBoxSizer(wxVERTICAL)
iFACC s1
wxBoxSizer(wxHORIZONTAL)
wxFlexGridSizer
wxPanel
OK wxButton
s2 s3 CB wxChoice/wxComboBox
OK b42 OK bAcces wxChoicebook
Edit wxTextCtrl
Text wxStaticText
31.12.00 wxDatePickerCtrl

Text t2 Text t3 Text t4 wxRadioBox


x
Text t1 Edit e_pw wxCheckListBox
Edit e_port
wxGrid
Edit e_alias Edit e_host
Figura VII-8: Legendă
Figura VII-7: obiecte
Semnificaţia controalelor este dată în legenda din partea
dreaptă.
Fişierul header cu declaraţia clasei:
#ifndef __facc__ //fişierul facc.h
#define __facc__
#include <wx/wx.h>
#include "lx.h"
class iFACC : public wxDialog
{wxString rezX; public: static bool accesX;
iFACC(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title
= wxT("Acces"), const wxPoint& pos = wxDefaultPosition, const wxSize&
size = wxSize(201,234), long style = wxDEFAULT_DIALOG_STYLE);
~iFACC();
protected: wxStaticText* t1; wxTextCtrl* e_alias; wxStaticText* t2;
wxTextCtrl* e_pw; wxButton* b42; wxStaticText* t3; wxTextCtrl* e_host;
wxStaticText* t4; wxTextCtrl* e_port; wxButton* bAcces;
virtual void schimbPW(wxCommandEvent& event);
virtual void acces(wxCommandEvent& event);
};
#endif

Constructorul preia adresa ferestrei în care se deschide, în


acest caz NULL deoarece este apelat înainte de crearea ferestrei
principale (înainte de instanţierea clasei iFPB). Fereastra nu va
avea identificator propriu, deci este preluat wxID_ANY, titlul
ferestrei este “Acces”.

178
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul

Notă: Pentru conversia din şir de caractere în obiect wxString se


utilizează _(“sir”) când şirul este ASCII sau wxT(“şir”) când
şirul este UNICODE (dacă şirul are diacritice se va utiliza wxT)
sau _(L“şir”).
Răspunsul la evenimente este dat de următorul tabel:
<Controlul> Evenimentul Tratare eveniment
funcţie ataşată
<b42> wxEVT_COMMAN ● verifică corectitudinea parolei
D_BUTTON_CLICK
iFACC ::schimbPW introduse;
ED
(apăsare buton) ● preia de două ori noua parolă şi veri-
fică dacă sunt sunt identice;
● actualizează în tabelul logx.acces
noua parolă.
<bAcces> wxEVT_COMMAN ● verifică corectitudinea parolei intro-
D_BUTTON_CLICK
iFACC ::acces duse şi setează corespunzător
ED
(apăsare buton) variabila de acces ;
● şterge aliasul precedent (dacă există)
creat pe baza actualului alias introdus;
● creează un nou utilizator şi cu o
parolă generată aleator;
● conferă drepturi noului utilizator
pentru grupul “LOGX”;
● cedează controlul noului utilizator.

3.2. Fereastra ce conţine panoul cu butoane iFPB


Este ferastra principală ce
rămâne deschisă până la sfârşitul
aplicaţiei. Această fereastră conţine
un panou cu butoane. Constructorii
obiectelor de tip fereastră (în acest
caz, derivaţi din clasa wxDialog), ce
vor fi executaţi în cadrul acestei
ferestre (în momentul instanţierii
claselor corespunzătoare celorlalte
ferestre), vor prelua în lista de
argumente (primul argument) adresa
acestei ferestre (pentru ferestrele
noi, aceasta va reprezenta fereastra
părinte). Datorită acestei legături, Figura VII-9
fereastra principală nu va putea fi
închisă decât după ce sunt închise ferestrele copil sau închiderea
acesteia va duce la închiderea ferestrelor copil (în funcţie de
atributele ferestrelor copil şi ale ferestrei principale –

179
VII.Dezvoltarea unor module pentru logistica stocurilor

părinte).
Ierarhia obiectelor din cadrul ferestrei este redată în
diagrama următoare:

OBIECTELE GRAFICE DIN FEREASTRA iFPB

iFPB s1

Text t1 s2

s3 s3

Text t3
OK br2
br1
OK br4
Text t2 OK be2 OK be4 OK
OK br3
OK be1 OK be3
Figura VII-10: Obiecze grafice iFPB

Fişierul header cu declaraţia clasei:


#ifndef __main__
#define __main__
#include <wx/wx.h>
class LogXApp : public wxApp //clasa ce implementează buclele de
evenimente
{public: virtual bool OnInit(); /* ieşirea din metoda OnInit echivalează
cu ieşirea din aplicaţie */
};
DECLARE_APP(LogXApp)
class iFPB : public wxFrame
{ public:
iFPB(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString&
title = wxT("GESTIUNE STOCURI"), const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxSize(230,251), long style = wxCAPTION|wxCLOSE_BOX|
wxMINIMIZE_BOX|wxSTAY_ON_TOP|wxSYSTEM_MENU|wxTAB_TRAVERSAL);
virtual ~iFPB();
protected: wxStaticText* t1; wxStaticText* t2; wxButton* be1; wxButton*
be2; wxButton* be3; wxButton* be4; wxStaticText* t3; wxButton* br1;
wxButton* br2; wxButton* br3; wxButton* br4;
virtual void apasa_be1(wxCommandEvent& event);

180
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul

virtual void apasa_be2(wxCommandEvent& event);


virtual void apasa_be4(wxCommandEvent& event);
virtual void apasa_br4(wxCommandEvent& event);
};
#endif //__main__

Răspunsul la evenimente este dat de următorul tabel:


<Controlul> Evenimentul Tratare eveniment
funcţie ataşată
< be1 > wxEVT_COMMAND_ ● lansează fereastra editare intrări
BUTTON_CLICKED
apasa_be1 (iFEI);
(apăsare buton)
<be2 > wxEVT_COMMAND_ ● lansează fereastra editare livrări
BUTTON_CLICKED
apasa_be2 (iFEBP);
<be4 > wxEVT_COMMAND_ ● lansează fereastra editare
BUTTON_CLICKED
apasa_be4 nomenclator (iFNOM);
<br4 > wxEVT_COMMAND_ ● lansează fereastra interogare stoc
BUTTON_CLICKED
apasa_br4 ● (iFSELST).

3.3. Fereastra de editare intrări (recepţii) iFEI


În cadrul acestei ferestre se realizează recepţiile în stoc.
Mai multe linii de intrare având în comun un furnizor şi o dată
calendaristică de intrare pot forma un Borderou de Recepţie (BR).

Figura VII-11: Fereastra pentru editarea intrărilor


Aceste borderouri de recepţie (BR) primesc numere unice la
intrare. Fiecare linie recepţionată are un identificator ID unic la
nivelul aplicaţiei. Acest identificator este construit automat pe
baza codului depozitului iniţial în care intră marfa şi un contor
unic la nivel de aplicaţie. Astfel fiecare linie de intrare este

181
VII.Dezvoltarea unor module pentru logistica stocurilor

identificată unic în sistem.


În cadrul controlului de tip grid se pot realiza o serie de
operaţii, precum cea de selecţie a status-ului (a stării) sau
selecţia depozitelor şi amplasamentelor. Numai depozitele unde
există drepturi de acces vor fi afişate. Aceasta se realizează prin
construcţia unui tabel temporar cu depozitele unde este permis
accesul pentru utilizatorul respectiv. Orice interogare ce
utilizează depozite se va lega şi de acest tabel (tmpacces).

Figura VII-12: Selecţia amplasamentului (citire din baza de date)


Fişierul header cu declaraţia clasei:
#ifndef __fei__ //fişierul fei.h
#define __fei__
#include <wx/wx.h>
#include "lx.h"
class iFEI : public wxDialog
{ lxComboBox *cx_nrbr; lxEditT *cx_supplier; lxDPC *cx_datain;
lxGrid *cx_grid;
public: iFEI(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString&
title = wxT("EDITARE INTRARI"), const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxSize(700,549), long style = wxCAPTION|wxCLOSE_BOX|
wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER|
wxSYSTEM_MENU|wxALWAYS_SHOW_SB);
virtual ~iFEI();
protected: wxStaticText* t4; wxComboBox* cbBR;
wxDatePickerCtrl* eDataBR; wxStaticText* t5; wxTextCtrl* eSupplierBR;
wxButton* bAddBR; wxPanel* pBR; wxButton* bDelBR; wxButton* bSavBR;
wxGrid* GD1; wxButton* bValidBR; wxButton* bAddLin; wxButton* bDelLin;
wxButton* B_selNom;
virtual void afis_BR(wxCommandEvent& event);
virtual void sav_BR(wxCommandEvent& event);
virtual void add_BR(wxCommandEvent& event);
virtual void add_Lin(wxCommandEvent& event);
virtual void del_Lin(wxCommandEvent& event);
virtual void sel_nom(wxCommandEvent& event);
virtual void del_BR(wxCommandEvent& event);

182
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul

virtual void valid_BR(wxCommandEvent& event);


virtual void GD1OnGridEditorShown(wxGridEvent& event);
virtual void GD1OnGridCellChange(wxGridEvent& event);
};
#endif //__fei__

Ierarhia obiectelor din cadrul ferestrei este redată în


diagrama următoare:
OBIECTELE GRAFICE DIN FEREASTRA iFEI

iFEI s1

s2 pBR

s3
Text t4

CB cbBR
Text t5 GD1
s4 s5
31.12.00 eDataBR
OK bAddBR

Edit eSupplierBR

OK bDelBR OK bValidBR
OK bDelLin
OK bSavBR
bAddLin OK
OK bSelNom
Figura VII-13: Obiectele grafice din fereastra iFEI
Această fereastră asigură următoarele funcţii:
☑ constituirea unui nou BR;
☑ modificarea unui BR existent;
☑ ştergerea parţială sau completă a unui BR;
☑ validarea unui BR.
Răspunsul la evenimente este dat de următorul tabel:

183
VII.Dezvoltarea unor module pentru logistica stocurilor

<Controlul> Evenimentul Tratare eveniment


funcţie
ataşată
<cbBR> wxEVT_COMMAND_ ● actualizează afişarea pentru furnizor din
iFEI::afis_BR COMBOBOX_SELECT
baza de date;
ED
● actualizează afişarea datei calendaristice
(selecţie
combobox) de intrare;
● creează tabelul temporar temp_br ce
conţine datele din tabelul br
corespunzătoare numărului BR selectat;
● apelează funcţia de afişare a datelor în
grid (din clasa lxGrid)
<bAddBR> wxEVT_COMMAND_ ● execută funcţia plpgSQL br_nou();
iFEI::add_BR BUTTON_CLICKED
● reîncarcă lista din combobox-ul cu
(apasă buton)
numerele BR afişate (apelează o metodă
din clasa lxComboBox)
<bDelBR> wxEVT_COMMAND_ ● şterge din tabelul br toate liniile afişate în
iFEI::del_BR BUTTON_CLICKED
grid
(apasă buton)

<bSavBR> wxEVT_COMMAND_ ● .apelează metoda din clasa lxGrid pentru


iFEI::sav_BR BUTTON_CLICKED
salvarea datelor din grid în tabelul
(apasă buton)
temporar ataşat (temp_br);
● actualizează tabelul br pe baza datelor
din temp_br
<GD1> wxEVT_GRID_CELL_C ● preia poziţia celulei modificate şi schimbă
iFEI:: HANGE (modificare culoarea de scriere în roşu
GD1OnGridCell conţinut celulă)
Change
<GD1> wxEVT_GRID_EDITO ● preia poziţia din tabel (rând + coloană) şi
iFEI:: R_SHOWN (afişare pe baza valorii celulei ce conţine
GD1OnGridEdit editor depozitul se construieşte lista de selecţie
orShown corespunzător
celulei)
a amplasamentelor (corespunzătoare
depozitului selectat în acel rând)
<bValidBR> wxEVT_COMMAND_ ● apelează metoda pentru salvarea BR
iFEI::valid_BR BUTTON_CLICKED
● apelează funcţia plpgSQL valid_BR
(apasă buton)
pentru BR selectat
<bAddLin> wxEVT_COMMAND_ ● adaugă o linie în grid
iFEI::add_Lin BUTTON_CLICKED
(apasă buton)

<bDelLin> wxEVT_COMMAND_ ● şterge din tabelul br linia selectată;


iFEI::del_Lin BUTTON_CLICKED
● apelează funcţia afis_BR

184
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul

<Controlul> Evenimentul Tratare eveniment


funcţie
ataşată
(apasă buton)

<B_selNom> wxEVT_COMMAND_ ● deschide fereastra cu nomenclatorul;


iFEI::sel_nom BUTTON_CLICKED
● preia datele selectate
(apasă buton)

3.4. Fereastra de editare ieşiri/livrări iFEBP


Livrările de marfă se realizează în două etape:
➢ Rezervarea din stoc în vederea livrării. În această fază se
scade din stoc doar cantitatea rezervată din cantitatea
disponibilă. Această etapă se concretizează prin constituirea
borderoului de preparare (BP);
➢ Livrarea efectivă. Cantitatea confirmată (în momentul livrării)
se scade din cantitatea fizică. De asemenea, în cazul în care
cantitatea confirmată diferă de cantitatea rezervată, se
realizează corecţia asupra cantităţii disponibile din stoc.
Livrarea efectivă corespunde validării BP. Liniile validate vor
fi şterse din BP şi vor fi regăsite în tabelul cu ieşirile outx.
Rezervarea mărfurilor se realizează prin intermediul ferestrei
de interogare a stocurilor.

Figura VII-14: Afişarea BP-ului selectat


Această fereastră asigură următoarele funcţii:
☑ constituirea unui nou BP;
☑ ştergerea parţială sau completă a unui BP;
☑ validarea unui BP.

185
VII.Dezvoltarea unor module pentru logistica stocurilor

Figura VII-15:Selecţie în vederea constituirii unui BP


Fişierul header cu declaraţia clasei:
#ifndef __febp__ //fişier febp.h
#define __febp__
#include <wx/wx.h>
#include "lx.h"
class iFEBP : public wxDialog
{public:iFEBP(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString&
title = wxT("LIVRĂRI"), const wxPoint& pos = wxDefaultPosition, const
wxSize& size = wxSize(598,499), long style = wxCLOSE_BOX|
wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER);
virtual ~iFEBP();
protected: wxStaticText* t1; wxComboBox* cbBP; wxButton* bAddBP;
wxDatePickerCtrl* eDataBP; wxStaticText* t2; wxTextCtrl* eDestinBP;
wxButton* bDelBP; wxPanel* pBP; wxGrid* GD1; wxButton* bDelLin; wxButton*
bSelST; wxButton* bValidBP;
virtual void afis_BP(wxCommandEvent& event);
virtual void addBP(wxCommandEvent& event);
virtual void delBP(wxCommandEvent& event);
virtual void delLin(wxCommandEvent& event);
virtual void selST(wxCommandEvent& event);
virtual void validBP(wxCommandEvent& event);

186
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul

wxString achBP; lxEditT *cx_destin;lxComboBox *cx_nrbp; lxDPC


*cx_databp; lxGrid *cx_grid;
};
#endif //__febp__

Ierarhia obiectelor din cadrul ferestrei este redată în


diagrama următoare:
OBIECTELE GRAFICE DIN FEREASTRA iFBP

iFBP s1

s2 pBP

s4

Text t1
GD1 s3

Text t2
OK bDelBP
OK bAddBP Edit eDestinBP
CB cbBP 31.12.00 eDataBP OK bSelST
OK bDelLin OK bValidBP
Figura VII-16: Obiectele grafice din fereastra iFBP
Răspunsul la evenimente este dat de următorul tabel:
<Controlul>
funcţie Evenimentul Tratare eveniment
ataşată
<cbBP> wxEVT_COMMAND ● actualizează afişarea pentru Destinatar din
iFEBP::afis_BP _COMBOBOX_SELE
baza de date;
CTED
● actualizează afişarea datei calendaristice
(selecţie
combobox) pentru BP;
● creează tabelul temporar temp_bp, ce
conţine datele din tabelul _bp,
corespunzătoare numărului BP selectat şi

187
VII.Dezvoltarea unor module pentru logistica stocurilor

<Controlul>
funcţie Evenimentul Tratare eveniment
ataşată
implicit a liniei corespondente din tabelul
părinte bp_. Pe lângă datele din tabelul _bp,
tabelul temporar conţine şi date din
nomenclatorul nom sau din tabelul cu intrări
inx;
● apelează funcţia de afişare a datelor în grid
(din clasa lxGrid)
● execută funcţia plpgSQL bp_nou;
wxEVT_COMMAND
<bAddBP> _BUTTON_CLICKED ● reîncarcă lista din combobox-ul cu numerele
iFEBP::addBP (apasă buton) BP afişate (apelează metodă din clasa
lxComboBox)
wxEVT_COMMAND ● şterge din tabelele bp_ şi _bp toate liniile
<bDelBP> _BUTTON_CLICKED
iFEBP::delBP corespondente cu cele afişate în grid
(apasă buton)
wxEVT_COMMAND ● anulează din tabelul _bp linia selectată;
<bDelLin> _BUTTON_CLICKED
iFEBP::delLin ● apelează funcţia afis_BP
(apasă buton)

● deschide fereastra de interogare a stocului


wxEVT_COMMAND
<bSelST> _BUTTON_CLICKED în vederea operaţiei de rezervare;
iFEBP::selST (apasă buton) ● apelează funcţia afis_BP în vederea
actualizării datelor
● apelează metoda pentru validarea BP-ului.
● actualizează cantitatea confirmată din grid
în tabelul temporar temp_bp;
wxEVT_COMMAND
<bValidBP> _BUTTON_CLICKED ● apelează funcţia plpgSQL valid_bp pentru
iFEBP::validBP (apasă buton) BP selectat;
● reîncarcă lista din combobox-ul cu numerele
BP afişate;
● apelează funcţia afis_BP

3.5. Fereastra editare/selecţie nomenclator iFNOM


Descrierea completă a unui produs/item se regăseşte în
nomenclator. În momentul editării sau căutării este mai simplă
selecţia tuturor caracteristicilor unui produs din nomenclator
decât preluarea acestora de la tastatură. Această fereastră este
utilizată atât în modulul de editare intrări, cât şi în modulele de
construcţie a rapoartelor.

188
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul

Figura VII-17: Apelul ferestrei cu nomenclatorul


De asemenea această
fereastră permite editarea
nomenclatorului. Ordinea de
afişare este stabilită cu
ajutorul unui control de
tip radiobuton. În cadrul
nomenclatorului coloana
IDCode este unică. În
operaţiile de modificare a
nomenclatorului nu se va
modifica IDCode deoarece
aceasta ar echivala cu
introducerea unei noi
linii. Interfaţarea între
tabelul nom şi controlul de Figura VII-18: Editarea nomenclatorului
tip grid este realizată
prin intermediul unui tabel temporar itmp_nom.
Această fereastră asigură următoarele funcţii:
☑ selecţia unei înregistrări din nomenclator în vederea
completării câmpurilor corespunzătoare din cadrul
ferestrelor de editare sau de raportare;
☑ ştergerea unor item-uri din nomenclator;
☑ adăugarea de noi item-uri în nomenclator;
☑ modificarea item-urilor deja existente în nomenclator.
Ierarhia obiectelor din cadrul ferestrei este redată în

189
VII.Dezvoltarea unor module pentru logistica stocurilor

diagrama următoare:
OBIECTELE GRAFICE DIN FEREASTRA iFNOM

iFNOM s1

rOrd GD1 s2
pEdit

s2Nom OK bEdit
OK bSel
OK bRenNom
OK bAdd
OK bRenEdit
OK bDel
OK bSav
Figura VII-19: Obiectele din fereastra iFNOM
Fişierul header cu declaraţia clasei:
#define __fnom__
#include <wx/wx.h>
#include "lx.h"
class iFNOM : public wxDialog
{public:iFNOM(wxWindow* parent, wxArrayString* rez1, wxWindowID id =
wxID_ANY, const wxString& title = wxT("Nomenclator produse"), const
wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
long style = wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|
wxMINIMIZE_BOX|wxRESIZE_BORDER);
virtual ~iFNOM();
protected:wxRadioBox* rOrd; wxGrid* GD1; wxPanel* pEdit; wxButton* bAdd;
wxButton* bDel; wxButton* bSav; wxButton* bRenEdit; wxButton* bEdit;
wxButton* bSel; wxButton* bRenNom;
void afis_NOM();
virtual void ordoneaza(wxCommandEvent& event);
virtual void add_Lin(wxCommandEvent& event);
virtual void del_Lin(wxCommandEvent& event);
virtual void salveaza(wxCommandEvent& event);
virtual void ren_Edit(wxCommandEvent& event);
virtual void editare(wxCommandEvent& event);
virtual void selecteaza(wxCommandEvent& event);
virtual void ren_Nom(wxCommandEvent& event);

190
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul

wxArrayString *rez, ord; int nr_ord; lxGrid *cx_grid;


};
#endif

Răspunsul la evenimente este dat de următorul tabel:


<Controlul> Tratare eveniment
Evenimentul
funcţie ataşată
wxEVT_COMMAND_ ● apelează funcţia afis_NOM pentru
<rOrd> RADIOBOX_SELECTE reactualizarea afişării din grid
iFNOM::ordoneaza D conform noului criteriu de ordonare
(selecţie radiobuton)
wxEVT_COMMAND_ ● adaugă o linie în grid şi redi-
<bAdd>
BUTTON_CLICKED mensionează controlul
iFNOM::add_Lin (apăsare buton)
<bDel> wxEVT_COMMAND_ ● şterge linia selectată din grid
iFNOM::del_Lin BUTTON_CLICKED

● actualizează în tabelul nom liniile


<bSav> wxEVT_COMMAND_ care au fost modificate;
iFNOM::salveaza BUTTON_CLICKED ● adaugă liniile ce nu se regăsesc în
tabelul nom
<bRenEdit> wxEVT_COMMAND_ ● închide fereastra de editare
iFNOM::ren_Edit BUTTON_CLICKED

<bEdit> wxEVT_COMMAND_ ● afişează panoul de editare


iFNOM::editare BUTTON_CLICKED

● copiază datele liniei selectate


<bSel> wxEVT_COMMAND_ într-o variabilă de tip
iFNOM::selecteaza BUTTON_CLICKED (wxArrayString*);
● închide fereastra
<bRenNom> wxEVT_COMMAND_ ● renunţă la selecţie (închide
iFNOM::ren_Nom BUTTON_CLICKED fereastra)

3.6. Fereastra pentru rapoarte / selecţie stoc iFSTSEL


Această fereastră este utilizată atât pentru rapoartele
stoc, cât şi pentru rezervarea din stoc în vederea livrării sau
pentru constituirea borderourilor de transfer. Acest lucru se
realizează cu ajutorul unui panou (un obiect al clasei wxPanel), ce
este ascuns sau vizibil în funcţie de rolul ferestrei.
Câmpurile selectate pentru afişare (în lista cu căsuţe de
selecţie) se constituie în coloane ale grid-ului, iar câmpurile
selectate pentru selecţie/filtrare se constituie în filtru pentru
interogare. Afişarea în grid va cuprinde întodeauna cantităţile
fizice qtyfizic şi cantităţile disponibile qtydisp. Astfel, dacă nu
selectăm pentru afişare nimic, se vor afişa doar cantităţile totale

191
VII.Dezvoltarea unor module pentru logistica stocurilor

(valabil pentru modul raportare). Dacă fereastra este apelată din


cadrul borderoului de preparare sau cel de transfer, indiferent de
selecţia de afişare se vor afişa şi câmpurile: id, dep şi ampl.

Figura VII-20: Fereastra "raport stoc"


Datele rezultate ce vor fi afişate în grid vor putea fi
ordonate după un câmp selectat printr-un control de tip combobox
(lista din combobox este identică cu lista câmpurilor din cadrul
afişării).
Dacă fereastra este apelată din cadrul borderoului de
preparare sau cel de transfer, va mai exista un grid destinaţie. În
cazul apelului din cadrul BP, liniile sunt constituite pe baza
structurii tabelului copil _bp, având în plus câmpurile idcode şi
descr.
Primul grid (GD1) preia datele din tabelul temporar
q_tmp_st, tabel creat de funcţia plpgSQL pentru interogare q_x. Al
doilea grid (GD2) utilizează pe post de tabel intermediar tabelul
temporar tmpselst. Ambele grid-uri doar afişează date (sunt de tip
read-only).
Această fereastră asigură următoarele funcţii:
☑ construcţie raport cu posibilitatea grupării informaţiei şi
a alegerii filtrelor;
☑ rezervarea item-urilor din cadrul stocului în vederea reali-
zării borderoului de preparare;
☑ preluarea item-urilor în borderourile de transfer (neimple-
mentat).

192
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul

Figura VII-21: Apel fereastră din iFEBP

Ierarhia obiectelor din cadrul ferestrei este redată în


fig.VII-22. Fişierul header cu declaraţia clasei:
#ifndef __fselst__ //fişier selst.h
#define __fselst__
#include <wx/wx.h>
#include <wx/choicebk.h>
#include "lx.h"
class iFSELST : public wxDialog
{ wxArrayString listaN,listaT,val1,val2;
lxGrid *cx_grid1, *cx_grid2;
lxComboBox *cx_dep, *cx_ampl;
wxArrayString aL,sL,valL,aListaInit;
//lista cu câmpuri ptr. afişare şi selecţie
wxString achBP; //numărul BP pentru care se face selecţia
public:iFSELST(wxWindow* parent, wxString achBP1, wxWindowID id =
wxID_ANY, const wxString& title = wxT("SELECŢIE STOC"), const wxPoint&
pos = wxDefaultPosition, const wxSize& size = wxSize(800,650),
long style = wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|
wxRESIZE_BORDER|wxFULL_REPAINT_ON_RESIZE);
virtual ~iFSELST();

193
194
LEGENDĂ OBIECTELE GRAFICE DIN
:
wxDialog/wxFrame
FEREASTRA iFSELST
wxBoxSizer(wxVERTICAL) iFSTSEL s1

wxBoxSizer(wxHORIZONTAL)
wxFlexGridSizer s2 pSelBP
wxPanel GD1
OK wxButton
CB wxChoice/wComboBox s4 s17
s3 s5
wxChoicebook
x
Edit wxTextCtrl cblS PQtyPrep
Text t9 s35 p9
Text wxStaticText OK bQ GD2
31.12.00wxDatePickerCtrl s14
x
cblA s15
wxRadioBox
x CBP CB cbOrd
wxCheckListBox Text t12
Text t13 OK Rezerva
Text t8
OK bCopiaza
wxGrid Edit OK bBP
eQtyPrep
OK bDelLin
VII.Dezvoltarea unor module pentru logistica stocurilor

p1 p2 p3 p4 p6
p5 p7 p10

s8 s10 s11 s12


s13 s16
s6 s7
OK bRetData Text t5 Text t10 OK bRetID
s9 Text t6 Text t7
Edit eID
Text t1 Text t3 CB cbStatus
OK bRetFurnizor Edit eSerialNo
CB cbDep Edit
ePO OK bRetStatus
OK bNom Edit eSupplier Text t2 31.12.00 eData2 Text t4 OK bRetDepAmpl OK

Figura VII-22: Ierarhia obiectelor din cadrul ferestrei iFSELST


bRetSerialNo
31.12.00 OK bRetPO
eData1 CB cbAmpl
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul

protected:
wxStaticText* t8; wxCheckListBox* cblA; wxStaticText* t9;
wxCheckListBox* cblS; wxChoicebook* CBP; wxPanel* p1; wxButton*
bNom; wxPanel* p2; wxStaticText* t1; wxTextCtrl* eSupplier; wxButton*
bRetFurnizor; wxPanel* p3;
wxDatePickerCtrl* eData1; wxStaticText* t2; wxDatePickerCtrl* eData2;
wxButton* bRetData; wxPanel* p4; wxStaticText* t3; wxComboBox* cbDep;
wxStaticText* t4; wxComboBox* cbAmpl; wxButton* bRetDepAmp; wxPanel*
p5; wxStaticText* t5; wxTextCtrl* ePO; wxButton* bRetPO; wxPanel* p6;
wxStaticText* t6; wxChoice* cbStatus; wxButton* bRetStatus; wxPanel* p7;
wxStaticText* t7; wxTextCtrl* eSerialNo; wxButton* bRetSerialNo;
wxPanel* p10; wxStaticText* t10; wxTextCtrl* eID; wxButton*
bRetID; wxStaticText* t13; wxChoice* cbOrd; wxButton* bCopiaza;
wxButton* bQ; wxGrid* GD1; wxPanel* pSelBP; wxPanel* P_QtyPrep;
wxStaticText* t12; wxTextCtrl* eQtyPrep; wxButton* bRezerva;
wxGrid* GD2; wxPanel* p9; wxButton* bDelLin; wxButton* bBP;
virtual void selNom(wxCommandEvent& event);//Selecţie NOMENCLATOR
virtual void retineFurnizor(wxCommandEvent& event);//selectează furnizor
virtual void retineData(wxCommandEvent& event);//selectează filtru data
calendaristică de intrare
virtual void selDep(wxCommandEvent& event);//Schimbă lista cu amplasamentele
în funcţie de depozitul selectat
virtual void retineDepAmp(wxCommandEvent& event);//selectează depozitul şi
amplasamentul curent
virtual void retinePO(wxCommandEvent& event);//selectează PO
virtual void retineID(wxCommandEvent& event);//selectează ID
virtual void retineSTATUS(wxCommandEvent& event);//selectează STATUS
virtual void retineSerialNo(wxCommandEvent& event);//selectează SerialNo
virtual void qST(wxCommandEvent& event);//FUNCŢIA DE INTEROGARE A STOCULUI
virtual void rezervaQtySelST(wxCommandEvent& event);//Rezervă item-uri
virtual void delLinSelST(wxCommandEvent& event);//Şterge linia selectată
virtual void transferBP(wxCommandEvent& event);//Închide fereastra de sel.
virtual void copiaza(wxCommandEvent& event);//Copiază din GRID în CLIPBOARD
virtual void afis_sel(); // AFIŞARE PANOU SELECŢIE --FILTRE
};
#endif //__fselst__

Răspunsul la evenimente este dat de următorul tabel:


<Controlul>
funcţie ataşată
Evenimentul Tratare eveniment

wxEVT_COMMAND ● preia în lista de selecţie valorile


<bNom> _BUTTON_CLICKED
iFSELST::selNom (apăsare buton)
câmpurilor selectate în fereastra iFNOM;
● apelează funcţia de afişare selecţie
<bRetFurnizor> ● preia valoarea pentru câmpul supplier în
wxEVT_COMMAND
iFSELST::retineFu _BUTTON_CLICKED lista de selecţie;
rnizor ● apelează funcţia de afişare selecţie
<bRetData> wxEVT_COMMAND ● preia valoarile pentru intervalul dată de

195
VII.Dezvoltarea unor module pentru logistica stocurilor

<Controlul>
funcţie ataşată
Evenimentul Tratare eveniment

iFSELST::retine intrare în lista de selecţie;


_BUTTON_CLICKED
Data ● apelează funcţia de afişare selecţie
● actualizează lista de selecţie pentru
wxEVT_COMMAND
<cbDep> controlul cbAmpl prin interogare asupra
_COMBOBOX_SELE
iFSELST::selDep CTED bazei de date cu filtrul dat de selecţia
cbDep
● preia valoarile pentru câmpurile dep şi
ampl în lista de selecţie (acestea sunt
<bRetDepAmp> separate în lista pentru constituirea
wxEVT_COMMAND
iFSELST::retine _BUTTON_CLICKED
DepAmp filtrului – putem avea filtru numai pentru
depozit);
● apelează funcţia de afişare selecţie
● preia valoarea pentru câmpul po în lista de
<bRetPO> wxEVT_COMMAND
iFSELST::retinePO _BUTTON_CLICKED
selecţie;
● apelează funcţia de afişare selecţie
<bRetStatus> ● preia valoarea pentru câmpul status în
wxEVT_COMMAND
iFSELST::retine _BUTTON_CLICKED lista de selecţie;
STATUS ● apelează funcţia de afişare selecţie
<bRetSerialNo> ● preia valoarea pentru câmpul serialno în
wxEVT_COMMAND
iFSELST::retineSe _BUTTON_CLICKED lista de selecţie;
rialNo ● apelează funcţia de afişare selecţie
● preia valoarea pentru câmpul id în lista de
<bRetID> wxEVT_COMMAND
iFSELST::retineID _BUTTON_CLICKED
selecţie;
● apelează funcţia de afişare selecţie
<bCopiaza> wxEVT_COMMAND ● citeşte datele din grid şi le transmite în
iFSELST::copiaza _BUTTON_CLICKED memoria CLIPBOARD
● construieşte comanda SQL pentru
interogare pornind de la listele de afişare
şi cele de selecţie;
● apelează funcţia plpgSQL q_st, ce
<bQ> wxEVT_COMMAND implementează interogarea pentru
iFSELST::qST _BUTTON_CLICKED constituirea raportului;
● în cazul în care este utilizată pentru
rezervarea item-urilor, creează tabelul
temporar de selecţie tmpselst pe baza
datelor din tabelul copil _bp
<bRezerva> wxEVT_COMMAND ● preia din controlul de editare cantitatea
_BUTTON_CLICKED

196
3.Dezvoltarea în C++ a modulelor de interfaţare grafică cu utilizatorul

<Controlul>
funcţie ataşată
Evenimentul Tratare eveniment

rezervată şi apelează funcţia plpgSQL


add_bp
iFSELST::rezerva _
QtySelST ● reactualizează tabelul tmpselst;
● reactualizează afişarea în cele două grid-
uri
● pentru linia selectată, apelează funcţia
plpgSQL
<bDelLin>
wxEVT_COMMAND del_bp
iFSELST::delLinSe _BUTTON_CLICKED
lST ● reactualizează tabelul tmpselst;
● reactualizează afişarea în cele două grid-
uri
<bBP>
wxEVT_COMMAND
● şterge datele din tabelul tmpselst;
iFSELST::transfer _BUTTON_CLICKED ● închide fereastra
BP

4. Dezvoltarea interfeţei SQL/plpgSQL – controale grafice


Pentru simplificarea dezvoltării aplicaţiei, au fost
dezvoltate câteva clase care să ofere suportul interfaţării între
specificul controlului şi popularea sau extragerea de date din
acesta. Codurile sursă pentru fişierele de tip header sunt
prezentate în acest subcapitol, iar codul sursă pentru fişierele cu
extensia cpp sunt prezentate în anexă.
Pentru fiecare control grafic a fost definită câte o clasă
de interfaţare, toate acestea având clasa de bază lxCtrl, clasă ce
gestionează conexiunea cu baza de date.
Astfel au fost dezvoltate clasele:
☑ lxCtrl - clasă de bază ce gestiunează conexiunea;
☑ lxComboBox - clasă ce permite o legătură între un control de
tip combobox şi o bază de date;
☑ lxDPC - clasă ce permite setarea şi preluarea datei calen-
daristice (legătura cu un control wxDatePickerCtrl);
☑ lxEditN - clasă ce permite legătura dintre un control de tip
editare şi baza de date pentru date de tip numeric;
☑ lxEditT - clasă ce permite legătura dintre un control de tip
editare şi baza de date pentru date de tip text;
☑ lxGrid - clasă ce permite legătura dintre un control de tip
grid şi un control wxGrid(tabel).

197
VII.Dezvoltarea unor module pentru logistica stocurilor

4.1. Clasa lxCtrl


Asigură conectarea şi deconectarea cu baza de date. Metodele
din cadrul clasei sunt:
✗ Constructorul clasei lxCtrl(), nu realizează nimic;
✗ Funcţia void lxCtrl::conectare(wxString dbname, wxString
user, wxString pw, wxString host, wxString port)
construieşte şirul de conectare, apelează funcţia pentru
conectare din biblioteca libpq şi testează realizarea
conexiunii;
✗ Funcţia wxString lxCtrl::cda(wxString achQ) permite execuţia
unei comenzi dată de şirul achQ şi întoarce rezultatul
execuţiei comenzii transmis de către serverul PostgreSQL.
Rezultatul întors poate fi o singură valoare (un câmp pe un
rând) sau un mesaj transmis de către server către client;
✗ Funcţia wxString lxCtrl::lista(wxString achQ, wxArrayString&
a) permite execuţia comenzii dată de şirul achQ şi întoarce
rezultatul într-un vector cu obiecte wxString, rezultatul
interogării trebuie să conţină un singur câmp. Această
funcţie este utilă pentru controalele specializate pentru
selecţia de tip listă.

Fişierul cu declaraţiile corespunzătoare clasei este:


//fişierul lx_ctrl.h
#ifndef _lxCtrl_ //previne includerea multipla : dacă nu EXISTA _lxCtrl_
#define _lxCtrl_ // defineste _lxCtrl_
#include <libpq-fe.h>
#include <wx/wx.h>
class lxCtrl
{public:
static PGconn *conexiune0;
lxCtrl();
static void conectare(wxString dbname, wxString user, wxString pw,
wxString host, wxString port);
static void deconectare();
static wxString cda(wxString achSQL);
static wxString lista(wxString achQ, wxArrayString& a);/*comandă ce are
ca rezultat un singur câmp, valorile rezultate vor fi transmise prin
referinţă sub forma unei liste cu elemente de tip wxString */
virtual int refreshlx();
};
#endif //închide condiţia

Metodele sunt de tip static, acestea sunt apelate indiferent


de instanţierile clasei lxCtrl.

198
4.Dezvoltarea interfeţei SQL/plpgSQL – controale grafice

4.2. Clasa lxComboBox


Această clasă permite o legătură între un control de tip
combobox şi o bază de date. Este necesar ca atât conexiunea cu baza
de date, cât şi obiectul de tip wxComboBox să existe.
Metodele din cadrul clasei sunt:
✗ Constructorul clasei lxComboBox::lxComboBox(wxComboBox *cb1,
wxString achQ) preia adresa controlului şi şirul ce conţine
comanda SQL, ce asigură, prin execuţia sa de către server-ul
PostgreSQL, lista din cadrul controlului;
✗ Funcţia int lxComboBox::rdLista(wxString achQ) citeşte în
control un nou şir rezultat în urma execuţiei comenzii SQL
(înlocuieşte vechea listă de selecţie);
✗ Funcţia wxString lxComboBox::getSel() întoarce şirul
selectat din cadrul controlului;
✗ Funcţia int lxComboBox::refreshlx() reface lista de selecţie
din control conform comenzii SQL transmise prin constructor.

Fişierul cu declaraţiile corespunzătoare clasei este:


#ifndef _lxComboBox_ //fişierul lx_combobox.h
#define _lxComboBox_
#include <wx/wx.h>
#include "lx_ctrl.h"
//clasa asigură o legătură între un contrl ComboBox şi o bază de date
// se presupune că ambele există înainte de instanţierea clasei
class lxComboBox:public lxCtrl
{wxComboBox *cb;
int tipdata;
wxString achQRD;
public:
lxComboBox(wxComboBox *cb1, wxString achQ1);
int rdLista(wxString achQ1);//reactualizeaza lista în urma actualizării
bazei de date
int refreshlx();
wxString getSel();
};
#endif

4.3. Clasa lxDPC


Realizează legătura cu baza de date pentru un control
wxDatePickerCtrl. Este necesar ca atât conexiunea cu baza de date
cât şi obiectul de tip wxDatePickerCtrl să existe.
Metodele din cadrul clasei sunt:
✗ Constructorul clasei lxDPC::lxDPC(wxDatePickerCtrl *dpc1)
preia adresa controlului;
✗ Funcţia int lxDPC::rdSQL(wxString rdachQ) citeşte din baza

199
VII.Dezvoltarea unor module pentru logistica stocurilor

de date în control, funcţia preia comanda SQL pentru citire;


✗Funcţia int lxDPC::wrSQL(wxString wrachQ) scrie valoarea din
control în baza de date, preia comanda de scriere (valoarea
din control este transmisă prin şirul format - wrachQ);
✗ Funcţia wxString lxDPC::getValFormatStr() returnează valoa-
rea controlului în formatul 'yyyy-mm-dd' pentru a putea fi
utilizată cu uşurinţă în comenzile SQL.
Fişierul cu declaraţiile corespunzătoare clasei este:
#ifndef _lxDPC_ //fişierul lx_dpc.h
#define _lxDPC_
#include <wx/wx.h>
#include <wx/datectrl.h>
#include "lx_ctrl.h"
class lxDPC:public lxCtrl
{ wxDatePickerCtrl *dpc;
public: lxDPC(wxDatePickerCtrl *dpc1);
int rdSQL(wxString rdachQ1);
int wrSQL(wxString wrachQ1);
wxString getValFormatStr();
};
#endif

4.4. Clasa lxEditN


Realizează legătura cu baza de date, pentru un control
wxTextCtrl ce preia şi afişează valori de tip numeric. Este necesar
ca atât conexiunea cu baza de date cât şi obiectul de tip
wxTextCtrl să existe.
Metodele din cadrul clasei sunt:
✗ Constructorul clasei lxEditN::lxEditN(wxTextCtrl *ed1) preia
adresa controlului;
✗ Funcţia int lxEditN::rdSQL(wxString rdachQ) citeşte din baza
de date în control, funcţia preia comanda SQL pentru citire;
✗ Funcţia int lxEditN::wrSQL(wxString wrachQ) scrie valoarea
din control în baza de date, preia comanda de scriere
(valoarea din control este transmisă prin şirul format –
wrachQ);
✗ Funcţia wxString lxEditN::getValFormatStr() returnează va-
loarea controlului.
Fişierul cu declaraţiile corespunzătoare clasei este:
#ifndef _lxEditN_ //fişierul lx_edit_n.h
#define _lxEditN_
#include <wx/wx.h>
#include "lx_ctrl.h"
/*clasa asigură o legătură între un contrl wxTextControl şi o valoare
dintr-o bază de date de tip numeric, se presupune că ambele există
înainte de instanţierea clasei */

200
4.Dezvoltarea interfeţei SQL/plpgSQL – controale grafice

class lxEditN:public lxCtrl


{wxTextCtrl *ed; public:
lxEditN(wxTextCtrl *ed1);
int rdSQL(wxString rdachQ1);
int wrSQL(wxString wrachQ1);
wxString getValFormatStr();
};
#endif

4.5. Clasa lxEditT


Realizează legătura cu baza de date pentru un control
wxTextCtrl. Este necesar ca atât conexiunea cu baza de date, cât şi
obiectul de tip wxTextCtrl să existe.
Metodele din cadrul clasei sunt:
✗ Constructorul clasei lxEditT::lxEditT(wxTextCtrl *ed1) preia
adresa controlului;
✗ Funcţia int lxEditT::rdSQL(wxString rdachQ) citeşte din baza
de date în control, funcţia preia comanda SQL pentru citire;
✗ Funcţia int lxEditT::wrSQL(wxString wrachQ) scrie valoarea
din control în baza de date, preia comanda de scriere
(valoarea din control este transmisă prin şirul format);
✗ Funcţia wxString lxEditT::getValFormatStr() returnează va-
loarea controlului pentru a putea fi utilizată cu uşurinţă
în comenzile SQL.
Fişierul cu declaraţiile corespunzătoare clasei este:
#ifndef _lxEditT_ //fişierul lx_edit_t.h
#define _lxEditT_
#include <wx/wx.h>
#include "lx_ctrl.h"
/*clasa asigură o legătură între un contrl wxTextControl şi o valoare
dintr-o bază de date de tip text, se presupune că ambele există înainte
de instanţierea clasei */
class lxEditT: public lxCtrl
{wxTextCtrl *ed; public: lxEditT(wxTextCtrl *ed1);
int rdSQL(wxString rdachQ1);
int wrSQL(wxString wrachQ1);
wxString getValFormatStr(); };
#endif

4.6. Clasa lxGrid


Realizează legătura cu baza de date pentru un control wxGrid
(tip tabel). Este necesar ca atât conexiunea cu baza de date, cât
şi obiectul de tip wxGrid să existe.
Metodele din cadrul clasei sunt:
✗ Constructorul clasei lxGrid::lxGrid(wxGrid *gridX1, wxString
tabelBD1, wxArrayString dencol1) preia adresa controlului,

201
VII.Dezvoltarea unor module pentru logistica stocurilor

numele tabelului PostgreSQL asupra căruia efectuează


operaţii de citire şi scriere. Opţional, poate prelua şi
lista cu denumirile coloanelor ce vor fi afişate în capul de
tabel reprezentat prin wxGrid (altfel vor fi preluate
denumirile câmpurilor – caz în care numărul de elemente din
dencol1 va fi zero);
✗ Destructorul clasei lxGrid::~lxGrid() eliberează memoria în
urma alocărilor dinamice efectuate (nu este necesară
eliberarea memoriei pentru controalele grafice ataşate
ferestrei deoarece în momentul eliberării memoriei pentru o
fereastră wxFrame/wxDialog se vor dealoca toate obiectele
grafice ataşate ferestrei);
✗ Funcţia void lxGrid::salvare() realizează copierea datelor
din grid în tabelul PostgreSQL (de obicei temporar) atşat.
Copierea se realizează pentru toate câmpurile şi pentru
toate înregistrările. În momentul copierii se ţine cont de
tipul de dată (de exemplu, pentru data calendaristică
trebuie utilizată funcţia de conversie din PostgreSQL
to_date);
✗ Funcţia void lxGrid::preluareVarColRow() – această funcţie
preia datele dintr-un tabel PostgreSQL într-un control grid.
Grid-ul va fi redimensionat conform numărului de coloane şi
numărului de înregistrări din tabelul PostgreSQL ataşat.
Dacă există o listă cu denumirile coloanelor aceasta va fi
preluată altfel vor fi preluate denumirile câmpurilor din
tabelul PostgreSQL. Liniile afişate vor avea culori
alternante.
Fişierul cu declaraţiile corespunzătoare clasei este:
#ifndef _lxGrid_ //fişierul lx_grid.h
#define _lxGrid_
#include <wx/wx.h>
#include <wx/grid.h>
#include "lx_ctrl.h"
//clasa asigură o legătura între un control wxGrid şi un tabel
PostgreSQL, se presupune că ambele există înainte de instanţierea clasei
class lxGrid:public lxCtrl
{protected:
wxGrid *gridX;
int *tipCol;// tipul fiecărei coloane (nr. OID din tabelul
pg_catalog.pg_type)
PGresult *rezultatRx;
wxString tabelBD;//tabel temporar
wxArrayString dencol; //denumiri coloane
wxString sirRcampuri;//şirul cu denumirile câmpurilor, mai puţin tcu
public:
lxGrid(wxGrid *gridX1, wxString tabelBD1, wxArrayString dencol1);
~lxGrid();
void preluareVarColRow(); // din tabel BD în GRID

202
4.Dezvoltarea interfeţei SQL/plpgSQL – controale grafice

void salvare(); //din grid în tabel


};
#endif

Aceste fişiere cu declaraţii sunt reunite în fişierul lx.h.


#include "RES_LX/lx_ctrl.h" //fişier lx.h
#include "RES_LX/lx_combobox.h"
#include "RES_LX/lx_edit_n.h"
#include "RES_LX/lx_edit_t.h"
#include "RES_LX/lx_grid.h"
#include "RES_LX/lx_dpc.h"
//fişierele sunt puse într-un subdirector-RES_LX

5. Integrarea modulelor aplicaţiei LogX


Funcţiile plpgSQL pot fi testate plecând de la datele
iniţiale de test şi apoi apelând funcţiile cu argumentele stabilite
în funcţie de context. Dacă baza de date picdb nu există, se va
crea manual. După deschiderea bazei de date picdb, se va rula din
pgAdminIII blocul SQL din capitolul ANEXE. În urma execuţiei
secvenţei SQL, se vor crea utilizatorul logx_sw, grupul de
utilizatori LOGX şi schema logx cu funcţiile, secvenţele şi
tabelele asociate.
Se contituie un proiect într-un mediu de dezvoltare (ex.
CodeLite, Code Blocks etc.) cu opţiunea wxWidgets activată (setarea
pentru compilare şi linkeditare pentru utilizarea bibliotecilor
wxWidgets. În cadrul proiectului se vor adăuga opţiunile pentru
încluderea bibliotecii libpq, specificate în capitolul VI.2.
În funcţia bool LogXApp::OnInit() este realizată conexiunea
cu server-ul de baze de date a utilizatorului logx_sw. Dacă
conexiunea reuşeşte să se realizeze, este instanţiată clasa iFACC
ce permite gestiunea drepturilor de acces. După introducerea alias-
ului pentru utilizator şi a codului de acces corespunzător este
verificată existenţa acestora în tabelul de acces şi este închisă
fereastra. În cazul în care nu există perechea alias-parolă în
tabelul de acces (iFACC::accesX == false), se iese din funcţia
LogXApp::OnInit() cu valoarea de adevăr false, ceea ce duce la
închiderea aplicaţiei LogX. În cazul în care este găsită
corespondenţa alias-parolă, este instanţiată clasa cu panelul cu
butoane iFPB.

lxCtrl::conectare new iFACC(NULL) new iFPB(NULL)


Figura VII-23: Intrarea în aplicaţie
Ferestrele de lucru sunt apelate din cadrul ferestrei iFPB.
Constructorul clasei iFSELST este apelat cu un parametru

203
VII.Dezvoltarea unor module pentru logistica stocurilor

suplimentar faţă ce celelalte ferestre similare; prin acest


parametru se transmite numărul borderoului curent pentru care se
face selecţia sau “#” dacă este realizat un raport privind situaţia
stocurilor.
Datele sunt preluate în controale prin intermediul claselor
din ierarhia lxCtrl.

Clasele de interfaţare LX
iFEI
schema: logx
tabele,
secvenţe
iFNOM

UTILIZATOR
Clasa
iFPB
schema: logx
funcţii plpgSQL iSELST
GRUPURI şi
UTILIZATORI
SERVER POSTGRESQL iFEBP
Figura VII-24: Instanţierea unor clase
În continuare este prezentat un tabel cu instanţierea clase-
lor.
clasa clase instanţiate Fişier
LogXApp iFACC; iFPB main.cpp
iFPB iFEI; iFNOM; iFSELST; iFEBP main.cpp
iFEI lxCtrl; lxComboBox; lxEditT; lxDPC; lxGrid; iFNOM fei.cpp
iFNOM lxCtrl; lxGrid fnom.cpp
iFEBP lxCtrl; lxEditT; lxComboBox; lxDPC; lxGrid; iFSELST febp.cpp
iFSELST lxCtrl; lxEditT; lxComboBox; lxDPC; lxGrid; iFNOM selst.cpp

Aplicaţia LogX cuprinde:


✗ schema logx din cadrul bazei de date picdb;
✗ grupul de acces LOGX şi utilizatorul logx_sw;
✗ fişierele sursă: main.h, main.cpp, fei.h, fei.cpp, fnom.h,
fnom.cpp, febp.h, febp.cpp, selst.h, selst.cpp, lx.h,
RES_LX/lx_combobox.h, RES_LX/lx_combobox.cpp,
RES_LX/lx_ctrl.h, RES_LX/lx_ctrl.cpp, RES_LX/lx_dpc.h,
RES_LX/lx_dpc.cpp, RES_LX/lx_edit_n.h, RES_LX/lx_edit_n.cpp,
RES_LX/lx_edit_t.h, RES_LX/lx_edit_t.cpp, RES_LX/lx_grid.h,

204
5.Integrarea modulelor aplicaţiei LogX

RES_LX/lx_grid.cpp.
Server-ul de baze utilizat a fost PostgreSQL 9.2, iar
versiunea de CodeLite utilizată a fost 6.0 cu wxWidgets 3.01.
Pentru simplificarea construcţiei interfeţei, se poate utiliza
wxFormBuilder sau echivalent, însă acestea pot fi utilile doar
pentru interfeţele simple.
Notă: Dacă se utilizează platforma Windows, recomand ca numele
directoarelor/subdirectoarelor în care se instalează ColdeLite şi
mai ales wxWidgets să nu conţină semne de punctuaţie sau alte
caractere de acest tip.
În capitolul ANEXE se regăsec fişierele cu extensia cpp din
cadrul aplicaţiei şi codul SQL pentru construcţia structurii de pe
server-ul PostgreSQL.

205
ANEXE

ANEXE
1. Secvenţe din sintaxa SQL/postgreSQL simplificată
Sunt prezentate pe scurt câteva comenzi SQL frecvent
utilizate. Opţiunile pentru aceste comenzi sunt mult mai numeroase,
am prezentat doar câteva opţiuni, frecvent utilizate.
Parantezele drepte “[opţional]” cuprind opţiuni ale comenzii
SQL (parantezele drepte nu vor fi scrise). Simbolul “|” este
utilizat cu semnificaţia de SAU. Acoladele “{ }” semnifică
repetiţie. Simbolurile “a[, …]“ semnifică o listă de cu elemente de
tipul a separate prin virgulă.
☑ Comanda ALTER ROLE
Modifică parametrii pentru un utilizator sau grup
ALTER
ALTER ROLE
ROLE nume_rol
nume_rol [[ [[ WITH
WITH ]] opţiuni
opţiuni [[ ...
... ]] ]]

Opţiune Descriere
SUPERUSER | NOSUPERUSER Cu drept de superuser sau nu
CREATEDB | NOCREATEDB Poate sau nu să creeze baze de date
CREATEROLE | NOCREATEROLE Poate sau nu să creeze alţi utilizatori sau grupuri
LOGIN | NOLOGIN Poate sau nu să se conecteze (este user sau grup)
PASSWORD 'password' Schimbare parolă
VALID UNTIL 'timestamp' Momentul până la care este valabilă parola

Sau modifică denumirea corespunzătoare unui utilizator sau grup


ALTER
ALTER ROLE
ROLE nume_vechi
nume_vechi RENAME
RENAME TO
TO nume_nou
nume_nou

☑ Comanda ALTER TABLE


Modifică structura unui tabel
ALTER
ALTER TABLE
TABLE [[ IF
IF EXISTS
EXISTS ]] nume
nume opţiune
opţiune [,
[, ...
... ]]

206
1.Secvenţe din sintaxa SQL/postgreSQL simplificată

Opţiune Descriere
ADD COLUMN nume_câmp tip_dată Adaugă un câmp/coloană
DROP COLUMN [IF EXISTS] nume_câmp Şterge un câmp
ALTER COLUMN nume TYPE tip_dată Schimbarea tipului de dată (un câmp)
ALTER nume_câmp SET DEFAULT valoare Setare valoare implicită câmp
ENABLE | DISABLE TRIGGER Activează/Dezactivează trigger

Redenumeşte un tabel
ALTER
ALTER TABLE
TABLE [[ IF
IF EXISTS
EXISTS ]] nume_vechi
nume_vechi RENAME
RENAME TO
TO nume_nou
nume_nou

☑ Comanda COPY
Copiază datele dintr-un tabel într-un fişier format CSV.
COPY
COPY {nume_tabel[(
{nume_tabel[( nume_coloană
nume_coloană [, [, ...]
...] )]
)] || (rezultat_interogare
(rezultat_interogare )} )}
TO
TO {'nume_fişier'
{'nume_fişier' || STDOUT
STDOUT }} [[ [[ WITH
WITH ]] (( opţiune
opţiune [,
[, ...]
...] )) ]]

Copiază datele dintr-un fişier de tip CSV într-un tabel.


COPY
COPY table_name[(
table_name[( nume_coloană
nume_coloană [,[, ...]
...] )]
)] FROM
FROM {'nume_fişier'|
{'nume_fişier'| STDIN
STDIN }}
[[ [[ WITH
WITH ]] (( opţiune
opţiune [,
[, ...]
...] )) ]]

Opţiune Descriere
FORMAT nume_format Formatul poate fi: CSV, text sau binary
DELIMITER 'caracter_delimitator' Pentru CSV este virgula “,” (implicit)
NULL În lipsa datelor scrie NULL
(nerecomandat)
HEADER Prima linie din fişier este antetul
ENCODING Implicit este cel al bazei de date

☑ Comanda CREATE TABLE


Creează un tabel, persistent sau temporar.
CREATE
CREATE [[ TEMPORARY
TEMPORARY ]] TABLE
TABLE nume_tabel
nume_tabel (( {{ nume_coloană
nume_coloană tip_dată
tip_dată
[[ restricţie_colană]
restricţie_colană] || restricţie_tabel
restricţie_tabel }) })

unde restricţie_coloană poate fi:

207
ANEXE

[CONSTRAINT
[CONSTRAINT nume_restricţie]
nume_restricţie] {{ NOT
NOT NULL
NULL || NULL
NULL || CHECK
CHECK (( expresie
expresie )) ||
DEFAULT valoare_implicită | PRIMARY KEY}
DEFAULT valoare_implicită | PRIMARY KEY}

iar restricţie_tabel poate fi:


[[ CONSTRAINT
CONSTRAINT nume_restricţie
nume_restricţie ]] {CHECK(expresie)
{CHECK(expresie) || UNIQUE
UNIQUE || PRIMARY
PRIMARY
KEY
KEY ( nume_câmp [, ... ] ) | FOREIGN KEY ( nume_câmp [,
( nume_câmp [, ... ] ) | FOREIGN KEY ( nume_câmp [, ...
... ]] ))
REFERENCES
REFERENCES nume_tabel_referinţă
nume_tabel_referinţă [[ (( nume_coloană_referinţă
nume_coloană_referinţă
[, ... ] ) ]}
[, ... ] ) ]}

☑ Comanda DELETE
Şterge înregistrările dintr-un tabel. Atenţie: a nu fi confundată
cu comanda DROP, care şterge întreaga structură a obiectului (în
acest caz, tabelul)!
DELETE
DELETE FROM
FROM nume_tabel
nume_tabel [USING
[USING nume_tabele_legătură]
nume_tabele_legătură] [WHERE
[WHERE expresie]
expresie]
Dacă nu se specifică o condiţie sau dacă expresie este întotdeauna
adevărată, se vor şterge toate înregistrările din cadrul tabelului
☑ Comanda INSERT
Comanda INSERT este utilizată pentru popularea bazei de date.
INSERT
INSERT INTO
INTO nume_tabel
nume_tabel [[ (( nume_câmp
nume_câmp [,
[, ...]
...] )) ]]
{{ DEFAULT
DEFAULT VALUES
VALUES ||
VALUES
VALUES (( {{ valoare_expresie
valoare_expresie || DEFAULT
DEFAULT }} [,
[, ...]
...] )) [,
[, ...]
...] ||
interogare
interogare }}
Se pot adăuga valorile implicite sau o listă de valori (eventual
obţinute din evaluarea unor expresii) sau se poate adăuga
rezultatul unei interogări (query).
☑ Comanda SELECT
Este cea mai complexă comandă din SQL.
SELECT
SELECT [[ ALL
ALL || DISTINCT
DISTINCT [[ ON ON (( expresie_1
expresie_1 [, [, ...]
...] )) ]] ]]
** || expressie_1
expressie_1 [ [ AS ] denumire_nouă_câmp_rezultat ]] [,
[ [ AS ] denumire_nouă_câmp_rezultat [, ...]
...]
[[ FROM
FROM tabel_sau_interogare
tabel_sau_interogare
[[ NATURAL
NATURAL ]] join_type
join_type tabel_sau_interogare
tabel_sau_interogare
[[ ON
ON join_condiţie
join_condiţie || USING USING (( join_column
join_column [, [, ...]
...] )) ]] ]]
[[ WHERE expresie_2
WHERE expresie_2 ] ]
[[ GROUP
GROUP BY BY expresie_3
expresie_3 [, [, ...]
...] ]]
[[ HAVING
HAVING expresie_4 [, ...] ]]
expresie_4 [, ...]
[[ {{ UNION
UNION || INTERSECT
INTERSECT || EXCEPT EXCEPT }} [[ ALL
ALL || DISTINCT
DISTINCT ]] select
select ]]
[[ ORDER BY expresie_5 [ ASC | DESC
ORDER BY expresie_5 [ ASC | DESC ] [, ...] ] ] [, ...] ]
[[ LIMIT
LIMIT {{ count
count || ALLALL }} ]]
[[ OFFSET
OFFSET start [ ROW || ROWS
start [ ROW ROWS ]] ]]
Unele instrucţiuni, precum [NATURAL], sunt redundante (NATURAL JOIN
= JOIN)

208
1.Secvenţe din sintaxa SQL/postgreSQL simplificată

Opţiune Descriere

expresie_1 Câmpul sau o expresie cu sau fără câmpuri


Sursa datelor poate să fie formată
tabel_sau_interogare din tabele sau să rezulte din alte interogări

Poate fi: INNER JOIN, LEFT OUTER JOIN,


join_type RIGHT JOIN, FULL OUTER JOIN
Ofsetul de la care începe
start
returnarea înregistrărilor
count Numărul maxim de înregistrări ce vor fi întoarse

select O nouă interogare executată separat

☑ Comanda UPDATE
Este comanda prin care sunt actualizate informaţiile existente în
tabele.
UPDATE
UPDATE nume_tabel
nume_tabel
SET
SET {{ nume_câmp
nume_câmp == {{ expresie_1
expresie_1 || DEFAULT
DEFAULT }} ||
(( nume_câmp
nume_câmp [, ...] ) = ( { expresie_1 || DEFAULT
[, ...] ) = ( { expresie_1 DEFAULT }}
[,
[, ...]
...] )) }} [,
[, ...]
...]
[[ FROM nume_tabele
FROM nume_tabele ] ] [ WHERE expresie_2
[ WHERE expresie_2 ] ]

Opţiune Descriere
nume_câmp Numele câmpurilor care se actualizează
expresie_1 Condiţia ce trebuie îndeplinită pentru actualizarea înregistrării
nume_tabele Dacă pentru actualizare sunt utilizate şi informaţii din alte tabele
expresie_2 Condiţie actualizare + Legătura între tabele

Atenţie: comanda UPDATE nume_tabel SET nume_câmp = NULL;


diferă de comanda:
UPDATE nume_tabel SET nume_câmp = '';
deoarece a doua comandă (în care sunt scrise două caractere
apostrof alăturate) presupune că acel câmp a fost „setat”, dar nu
au existat valori. Practic, este aceeaşi diferenţă ca între
noţiunile EMPTY şi NULL din cadrul claselor cu date de tip Variant.
În al doilea caz, putem avea un câmp cheie primară care să nu
conţină valori ceea ce încalcă regulile SQL.

209
ANEXE

2. Principalele tipuri de variabile în limbajul C

Tip Reprezentare Valori Descriere


variabilă *) posibile
caracter utilizat şi pentru codurile
char 8 biţi -27→ +27-1
ASCII (0-127)
unsigned 8 biţi caracter şi utilizat pentru codurile
0 → +216-1
char ASCII (0-255)
întreg cu semn (dependent de
int 8/16/32/64 -231→ +231-1
sistem 8/16/32/64 biţi)
unsigned 8/16/32/64 întreg fără semn (dependent de
0 → +232-1
int sistem 8/16/32/64 biţi)
short 16 biţi -215→ +215-1 întreg scurt cu semn
unsigned 16 biţi întreg scurt fără semn
0 → +216-1
short
long 32 biţi -215→ +215-1 întreg lung cu semn
long Ptr. compilatoare de tip g++
64 biţi -231→ +231-1
long
int64 64 biţi -231→ +231-1 Ptr. Visual C++
unsigned 32 biţi întreg lung fără semn
0 → +216-1
long
cca. 10−38 → Număr real reprezentat pe 32 de
float 32 biţi 1038, 6 cifre biţi
semnificative
cca. 10−308 → Număr real reprezentat pe 64 de
10308, 15 biţi (cel mai des utilizat)
double 64 biţi cifre
semnificative
long Număr real reprezentat pe 80 de
80 biţi
double biţi
bool 8 true / false boolean
void 8/16/32/64 Nimic / vid
*) Reprezentare în memorie (număr de biţi)

210
3.Operatori C/C++

3. Operatori C/C++
☑ A.Operatori unari
SIMBOLUL FORMA Operaţia realizată Exemplu
- -a negarea lui a -3
+ +a valoarea lui a 3
a++ Postincrementare: a=3++;
preia valoare, apoi creşte cu 1 a are val. 3
++
++a Preincrementare: mai întâi creşte a=++3;
cu 1, apoi preia valoarea a are val. 4
a-- Postdecrementare: a=3--;
preia valoarea, apoi scade cu 1 a are val. 3
--
--a Predecrementare: mai întâi scade a=--3;
cu 1, apoi preia valoarea a are val. 2

☑ B.Operatori binari aritmetici – aditivi şi multiplicativi


SIMBOLUL FORMA Operaţia realizată Exemplu
+ a+b a adunat cu b 3.7+5.3
- a-b b scăzut din a 3.1-5.0
* a*b a înmuţit cu b 1.2*4.1
/ a/b a împărţit la b 1.2/4.1
% a%b a modulo b (restul împărţirii) 5%4

☑ C.Operatori de atribuire aritmetici


SIMBOLUL FORMA Operaţia realizată
Asignare/atribuire:
= a=b
a preia valoarea lui b
+= a += b a=a(fostul conţinut)+b
-= a -= b a=a-b
/= a /= b a=a/b
%= a %= b a=a%b

☑ F.Operatori relaţionali (0 este false, diferit de 0 este


adevărat, ex.:-0.0000007 are valoare de adevăr TRUE)

SIMBOLUL FORMA Operaţia realizată


> a>b dacă a>b, atunci true, altfel false
>= a >= b dacă a>=b atunci true, altfel false
< a<b dacă a<b atunci true, altfel false
<= a <= b dacă a<=b atunci true, altfel false
== a == b dacă a identic cu b, atunci true, altfel false
!= a != b dacă a diferă de b, atunci true, altfel false

211
ANEXE

☑ G.Operatorii logici
SIMBOLUL FORMA Operaţia
&& a && b ŞI / AND
|| a || b SAU / OR
! !a NEGAT / NOT
(T – true, F – false)
x y x&&y x||y !x
F F F F T
F T F T T
T F F T F
T T T T

☑ H.Operatorul cast
SIMBOLUL FORMA Operaţia realizată
(tip) (tip)a a este convertit la tipul tip, operaţie de tip
cast

Precedenţa operatorilor:
1) ( … ), [ … ], , ::, .(referire membru);
2) operatori unari;
3) operatori multiplicativi;
4) acces la membri;
5) operator aditivi;
6) operatori de deplasare;
7) operatori relaţionali;
8) opertori logici la nivel de bit;
9) operatori logici;
10) operatorul condiţional;
11) operatori de asignare;
12) operatorul virgulă;
Citirea expresiilor se face de la dreapta spre stânga.

212
4.Codul sursă SQL pentru aplicaţia LogX

4. Codul sursă SQL pentru aplicaţia LogX


-- dacă nu există baza de date : CREATE DATABASE picdb;
DROP schema IF EXISTS logx CASCADE;
-- CASCADE STERGE ŞI LEGATURILE CU CELELALTE OBIECTE
create schema logx;

--##### CREARE TABELE ###################


-- creare tabel NOM
DROP TABLE IF EXISTS logx.nom;
CREATE TABLE logx.nom(idcode varchar(20) PRIMARY KEY, refteh
varchar(30),refcom varchar(20), descr varchar(100), utiliz varchar(30)
DEFAULT CURRENT_USER, data date DEFAULT CURRENT_DATE, ora time DEFAULT
CURRENT_TIME);
-- creare tabel LOC
DROP TABLE IF EXISTS logx.loc;
CREATE TABLE logx.loc(dep char(3), ampl varchar(10), CONSTRAINT loc_pk
PRIMARY KEY(dep,ampl));
-- creare tabel BR
DROP TABLE IF EXISTS logx.br;
CREATE TABLE logx.br(nrbr int,datain date DEFAULT CURRENT_DATE, idcode
varchar(20), po varchar(10), status varchar(15), supplier varchar(50),
serialno varchar(50), depinit char(3), amplinit varchar(10), qty int, val
numeric, idx serial PRIMARY KEY);
ALTER TABLE logx.br ADD CONSTRAINT br_f1 FOREIGN KEY (idcode) REFERENCES
logx.nom (idcode);
ALTER TABLE logx.br ADD CONSTRAINT br_f2 FOREIGN KEY (depinit,amplinit)
REFERENCES logx.loc (dep,ampl);
-- creare tabel INX
CREATE TABLE logx.inx(id varchar(11) PRIMARY KEY, nrbr int, datain date
DEFAULT CURRENT_DATE, idcode varchar(20), po varchar(10), status
varchar(15), supplier varchar(50), serialno varchar(50), depinit char(3),
amplinit varchar(10), qty int, val numeric);
ALTER TABLE logx.inx ADD CONSTRAINT in_f1 FOREIGN KEY (idcode) REFERENCES
logx.nom (idcode);
ALTER TABLE logx.inx ADD CONSTRAINT in_f2 FOREIGN KEY (depinit,amplinit)
REFERENCES logx.loc (dep,ampl);
-- creare tabel STX
CREATE TABLE logx.stx(dep char(3),ampl varchar(10), id varchar(11),
qtyfiz int, qtydisp int, CONSTRAINT st_pk PRIMARY KEY(dep,ampl,id));
ALTER TABLE logx.stx ADD CONSTRAINT st_f1 FOREIGN KEY (id) REFERENCES
logx.inx (id);
ALTER TABLE logx.stx ADD CONSTRAINT st_f2 FOREIGN KEY (dep,ampl)
REFERENCES logx.loc (dep,ampl);
-- creare tabele BT_ şi _BT
DROP TABLE IF EXISTS logx._bt; DROP TABLE IF EXISTS logx.bt_;
CREATE TABLE logx.bt_(nrbt int PRIMARY KEY, databt date, dep_dest
char(3), ampl_dest varchar(10), CONSTRAINT f2_bt_ FOREIGN KEY(dep_dest,

213
ANEXE

ampl_dest) REFERENCES logx.loc(dep,ampl));


CREATE TABLE logx._bt(nrbt int, id varchar(11), qtytr int DEFAULT 1,
dep_srs varchar(3), ampl_srs varchar(10),
CONSTRAINT k__bt PRIMARY KEY(nrbt,id,dep_srs,ampl_srs),
CONSTRAINT f1__bt FOREIGN KEY(id) REFERENCES logx.inx(id) ,
CONSTRAINT f2__bt FOREIGN KEY(dep_srs,ampl_srs) REFERENCES
logx.loc(dep,ampl),
CONSTRAINT f0__bt FOREIGN KEY(nrbt) REFERENCES logx.bt_(nrbt));
-- creare tabel TRX
CREATE TABLE logx.trx(nrbt int,datatr date,nrlin serial,dep_srs
char(3),ampl_srs varchar(10),dep_dest char(3),ampl_dest varchar(10), id
varchar(11), qtytr int, CONSTRAINT k_tr PRIMARY KEY(nrbt,nrlin));
ALTER TABLE logx.trx ADD CONSTRAINT tr_f1 FOREIGN KEY (id) REFERENCES
logx.inx (id);
ALTER TABLE logx.trx ADD CONSTRAINT tr_f21 FOREIGN KEY (dep_srs,ampl_srs)
REFERENCES logx.loc (dep,ampl);
ALTER TABLE logx.trx ADD CONSTRAINT tr_f22 FOREIGN KEY
(dep_dest,ampl_dest) REFERENCES logx.loc (dep,ampl);
-- creare tabele BP_ şi _BP
CREATE TABLE logx.bp_(nrbp int PRIMARY KEY,databp date,destin
varchar(30));
CREATE TABLE logx._bp(nrbp int,id varchar(11),qtyrez int DEFAULT
0,qtyconf int DEFAULT 0,dep varchar(3), ampl varchar(10),
CONSTRAINT k__bp PRIMARY KEY(nrbp,id,dep,ampl),
CONSTRAINT f1__bp FOREIGN KEY(id) REFERENCES logx.inx(id) ,
CONSTRAINT f2__bp FOREIGN KEY(dep,ampl) REFERENCES logx.loc(dep,ampl),
CONSTRAINT f0__bp FOREIGN KEY(nrbp) REFERENCES logx.bp_(nrbp));
-- creare tabel ieşiri (INX)
CREATE TABLE logx.outx(nrbl int,destin varchar(30),id varchar(11),qtyliv
int,dep varchar(3), ampl varchar(10),dataout date,
CONSTRAINT k_out PRIMARY KEY(nrbl,id,dep,ampl),
CONSTRAINT f1__out FOREIGN KEY(id) REFERENCES logx.inx(id) ,
CONSTRAINT f2__out FOREIGN KEY(dep,ampl) REFERENCES logx.loc(dep,ampl));
-- creare tabel acces (ACCES)
CREATE TABLE logx.acces(alias varchar(30) PRIMARY KEY, pw varchar(50),
nume_utilizator varchar(100), date_contact varchar(200), dep varchar(100)
[]);
--############## CREARE CONTOARE ##############
-- creare secvenţa parte numerica ID
DROP SEQUENCE IF EXISTS logx.id_seq; CREATE SEQUENCE logx.id_seq
INCREMENT 1 MINVALUE 1 START 1;
--verificare index: select nextval('logx.id_seq');
-- creare secvenţa NUMĂR BR
DROP SEQUENCE IF EXISTS logx.br_seq; CREATE SEQUENCE logx.br_seq
INCREMENT 1 MINVALUE 1 START 10;
--verificare index: select nextval('logx.br_seq');
-- creare secvenţa NUMĂR BP
DROP SEQUENCE IF EXISTS logx.bp_seq; CREATE SEQUENCE logx.bp_seq
INCREMENT 1 MINVALUE 1 START 10;
--verificare index: select nextval('logx.bp_seq');

214
4.Codul sursă SQL pentru aplicaţia LogX

--############# FUNCŢII "SERVER SIDE" ###################


--creare funcţie de adăugare în nomenclator
CREATE OR REPLACE FUNCTION logx.nom_add(_idcode_ text,_refteh_
text,_refcom_ text, _descr_ text)
RETURNS text AS
$$
DECLARE
f boolean;
ach text;
BEGIN
--testare existenţă idcode în nom
SELECT EXISTS(SELECT * FROM logx.nom WHERE idcode=_idcode_) INTO f;
IF f THEN
SELECT 'Există deja în nom.'||_idcode_||'-- refteh:'||refteh||' recom:'||
refcom||' Descriere:'||descr
||' User:'||utiliz||'-Data:'||data||'-Ora:'||ora
FROM logx.nom WHERE idcode=_idcode_ INTO ach;
return ach;
END IF;
INSERT INTO logx.nom(idcode, refteh, refcom, descr)
VALUES (_idcode_, _refteh_, _refcom_, _descr_);
RETURN 'OK';
END
$$
LANGUAGE 'plpgsql';

---## BR NOU ##-------------


create or replace function logx.br_nou(_datain_ date DEFAULT
CURRENT_DATE)
returns void as
$$
begin
insert into logx.br(nrbr,datain) VALUES ((select
nextval('logx.br_seq')),_datain_);
end
$$
language 'plpgsql';

-----------## CONSTITUIRE ID NOU ##----------


CREATE OR REPLACE FUNCTION logx.id_nou(_dep_ text)
RETURNS text AS
$$
DECLARE
i int;
BEGIN
SELECT nextval('logx.id_seq') INTO i;
return _dep_||i::text;
END;
$$
LANGUAGE 'plpgsql';

215
ANEXE

----------------## VALIDARE BR ##-----------


CREATE OR REPLACE FUNCTION logx.valid_br(_nrbr_ int)
RETURNS text AS
$$
DECLARE
f boolean;
BEGIN
--verificare introducere itemcode
SELECT EXISTS(SELECT * FROM logx.br WHERE idcode IS NULL) INTO f;
IF f THEN return 'Introduceti itemcode!'; END IF;
--verificare introducere cantităţi
SELECT EXISTS(SELECT * FROM logx.br WHERE qty<1) INTO f;
IF f THEN return 'Introduceti cantitatea!'; END IF;
--verificare introducere datain
SELECT EXISTS(SELECT * FROM logx.br WHERE datain IS NULL) INTO f;
IF f THEN return 'Introduceti DATA!'; END IF;
--NOTA: funcţia se execută intr-o singura tranzitie
--adăugare în logx.inx
INSERT INTO logx.inx(
id, nrbr, datain, idcode, po, status, supplier, serialno,
depinit, amplinit, qty, val)
SELECT logx.id_nou(depinit), nrbr, datain, idcode, po, status, supplier,
serialno, depinit, amplinit, qty, val FROM logx.br WHERE nrbr=_nrbr_;
--adaugă în stoc
INSERT INTO logx.stx(dep, ampl, id, qtyfiz, qtydisp)
SELECT depinit,amplinit,id,qty,qty FROM logx.inx WHERE inx.nrbr=_nrbr_;
--şterge din BR
DELETE FROM logx.br USING logx.inx WHERE inx.nrbr=br.nrbr;
RETURN 'OK';
END;
$$
LANGUAGE 'plpgsql';

-----------## BP NOU ##----------


create or replace function logx.bp_nou(_destin_ text,_databp_ date
DEFAULT CURRENT_DATE) returns void as
$$
begin
insert into logx.bp_(nrbp,databp,destin) VALUES ((select
nextval('logx.bp_seq')), _databp_,_destin_);
end
$$
language 'plpgsql';

---------------## ADAUGARE LINIE _BP ##-------


CREATE OR REPLACE FUNCTION logx.add_bp_id(_nrbp_ integer,_id_ text,_dep_
text,_ampl_ text,_qtyrez_ int)
RETURNS text AS
$BODY$
DECLARE

216
4.Codul sursă SQL pentru aplicaţia LogX

f boolean;
begin
--verificare existenţă linie deja adăugată
select exists (select * from logx._bp where _bp.nrbp=_nrbp_ AND
_bp.id=_id_ AND _bp.dep=_dep_ AND _bp.ampl=_ampl_) INTO f;
if f then return 'Exisă deja un item de acest tip introdus în BP-ul
curent!'; end if;
INSERT INTO logx._bp(nrbp,id,dep,ampl,qtyrez)
VALUES(_nrbp_,_id_,_dep_,_ampl_,_qtyrez_);
UPDATE logx.stx set qtydisp=qtydisp-_bp.qtyrez FROM logx._bp WHERE
_bp.id=stx.id AND _bp.dep=stx.dep AND _bp.ampl=stx.ampl AND
_bp.nrbp=_nrbp_ AND _bp.id=_id_ AND _bp.dep=_dep_ AND _bp.ampl=_ampl_;
return 'OK';
end
$BODY$
LANGUAGE plpgsql;

-------------## ŞTERGERE LINIE _BP ---------------


CREATE OR REPLACE FUNCTION logx.del_bp_id(_nrbp_ integer, _id_ text,
_dep_ text, _ampl_ text) RETURNS text AS
$BODY$
DECLARE
f boolean;
begin
UPDATE logx.stx set qtydisp=qtydisp+_bp.qtyrez FROM logx._bp WHERE
_bp.id=stx.id AND _bp.dep=stx.dep AND _bp.ampl=stx.ampl AND
_bp.nrbp=_nrbp_ AND _bp.id=_id_ AND _bp.dep=_dep_ AND _bp.ampl=_ampl_;
DELETE FROM logx._bp WHERE _bp.nrbp=_nrbp_ AND _bp.id=_id_ AND
_bp.dep=_dep_ AND _bp.ampl=_ampl_;
DELETE FROM tmpselst WHERE tmpselst.nrbp=_nrbp_ AND tmpselst.id=_id_ AND
tmpselst.dep=_dep_ AND tmpselst.ampl=_ampl_;
return 'OK';
end
$BODY$ LANGUAGE plpgsql;

-----------## VALIDARE BP ##-------


create or replace function logx.valid_bp(_nrbp_ int)
returns text as
$$
DECLARE
f boolean;
BEGIN
select EXISTS(select * from logx._bp where _bp.nrbp=_nrbp_ and
_bp.qtyconf>_bp.qtyrez) INTO f;
IF f THEN return 'Există cantitati confirmate mai mari decat cantitatile
rezervate'; END IF;
-- adaugă în iesiri (tabelul outx ce contine borderourile de livrare)
INSERT INTO logx.outx(nrbl, destin, id, qtyliv, dep, ampl,dataout)
SELECT _bp.nrbp,bp_.destin,_bp.id,_bp.qtyrez,_bp.dep,_bp.ampl,
bp_.databp
FROM logx.bp_,logx._bp WHERE bp_.nrbp=_bp.nrbp AND _bp.nrbp=_nrbp_;

217
ANEXE

--actualizare stoc -- identificarea se face pentru intreaga cheie


primara din tabelul logx.stx
UPDATE logx.stx SET qtyfiz=stx.qtyfiz-_bp.qtyconf,
qtydisp=stx.qtydisp+_bp.qtyrez-_bp.qtyconf
FROM logx._bp WHERE stx.dep=_bp.dep AND stx.ampl=_bp.ampl AND
stx.id=_bp.id AND _bp.nrbp=_nrbp_;
--sterge linii ramase cu qtyfiz=0 din stoc
DELETE FROM logx.stx WHERE qtyfiz<1;
--sterge din BP
DELETE FROM logx._bp WHERE _bp.nrbp=_nrbp_;
DELETE FROM logx.bp_ WHERE bp_.nrbp=_nrbp_;
RETURN 'OK';
END
$$
language 'plpgsql';

----## VALIDARE BT ##-------------


CREATE OR replace function logx.bt_valid(_nrbt_ int)
RETURNS text AS
$$
DECLARE
f boolean;
BEGIN
--verifică dacă toate cantităţile ce urmează a fi transferate sunt
disponibile
SELECT EXISTS(SELECT * FROM logx.stx,logx._bt
WHERE stx.dep=_bt.dep_srs AND stx.ampl=_bt.ampl_srs AND
stx.id=_bt.id AND stx.qtydisp<qtytr)INTO f;
IF f THEN RETURN 'Există cantităţi ce nu sunt disponibile ptr. tranfer!';
END IF;
--adaugă în tabelul cu transferurile efectuate
INSERT INTO logx.trx(nrbt, datatr, dep_srs, ampl_srs, dep_dest,
ampl_dest, id, qtytr)
SELECT bt_.nrbt, bt_.databt, _bt.dep_srs, _bt.ampl_srs, bt_.dep_dest,
bt_.ampl_dest, _bt.id, _bt.qtytr FROM logx.bt_,logx._bt
WHERE _bt.nrbt=bt_.nrbt AND _bt.nrbt=_nrbt_;
--dacă există înregistrări le actualizează cu noile cantităţi
UPDATE logx.stx SET qtyfiz=qtyfiz+_bt.qtytr, qtydisp=qtydisp+_bt.qtytr
FROM logx._bt,logx.bt_ WHERE bt_.dep_dest=stx.dep AND
bt_.ampl_dest=stx.ampl
AND _bt.id=stx.id AND _bt.nrbt=bt_.nrbt AND _bt.nrbt=_nrbt_;
--dacă nu există înregistrarile noi (dep,loc,id) le adaugă DOAR pe cele
care NU se regăsesc în stoc(dep,ampl,id)
INSERT INTO logx.stx(dep, ampl, id, qtyfiz, qtydisp)
SELECT par.dep,par.ampl,par.id, _bt.qtytr, _bt.qtytr FROM
(SELECT bt_.dep_dest as dep, bt_.ampl_dest as ampl, _bt.id
FROM logx._bt,logx.bt_ WHERE _bt.nrbt=bt_.nrbt AND _bt.nrbt=_nrbt_
EXCEPT
SELECT stx.dep,stx.ampl,stx.id FROM logx.stx,logx.bt_ WHERE
bt_.dep_dest=stx.dep AND bt_.ampl_dest=stx.ampl
-- se poate şi fără ultima condiţie dar poate creşte timpul de

218
4.Codul sursă SQL pentru aplicaţia LogX

interogare
)par,logx._bt,logx.bt_
WHERE _bt.id=par.id AND bt_.dep_dest=par.dep AND bt_.ampl_dest=par.ampl
AND _bt.nrbt=bt_.nrbt AND _bt.nrbt=_nrbt_;
--ATENŢIE: se va realiza mai intâi UPDATE şi apoi INSERT altfel
cantităţile se pot dubla

-- SE realizează scăderea din stoc (pentru SURSA)


UPDATE logx.stx SET qtyfiz=qtyfiz-_bt.qtytr, qtydisp=qtydisp-_bt.qtytr
FROM logx._bt WHERE _bt.dep_srs=stx.dep AND _bt.ampl_srs=stx.ampl
AND _bt.id=stx.id AND _bt.nrbt=_nrbt_;

-- dacă în urma scăderii qtyfiz =0 aceste linii se vor şterge


DELETE FROM logx.stx WHERE qtyfiz=0;
--şterge liniile din tabelele naster-slave (bt_ şi _bt) corespunzatoare
_nrbt_
DELETE FROM logx._bt WHERE nrbt=_nrbt_;
DELETE FROM logx.bt_ WHERE nrbt=_nrbt_;
RETURN 'OK';
END
$$ language plpgsql;

-------------## INTEROGARE GENERALĂ ##-----------------


-- Function: logx.q_x(text, smallint, text[], text[], boolean[],
boolean[], text[], text[], text)
CREATE OR REPLACE FUNCTION logx.q_x(_tipq_ text, _nr_ smallint, _nume_
text[], _tip_ text[], _a_ boolean[], _s_ boolean[], _val1_ text[], _val2_
text[], _ord_ text)
RETURNS text AS
$BODY$
DECLARE
vald1_ date[];
vald2_ date[];
valn1_ numeric[];
valn2_ numeric[];
valAC_ text[];
valb_ boolean[];
exeQ text; -- conţine şirul final pe baza căruia se realizează
interogarea (comanda SQL finala)
codR text; -- răspunsul funcţiei : OK - ieşire cu execuţie până la capat
a interogarii
achA text; -- şirul format pentru alcătuirea exeQ ce conţine partea de
afişare a coloanelor/câmpurilor din cda SQL
achS text; -- şirul format pentru alcătuirea exeQ ce conţine partea cu
contitiile de selecţie din cda SQL
achG text; -- şirul format pentru alcătuirea exeQ ce conţine partea cu
GRUPUL din cadrul czii SQL
achO text; -- şirul format pentru alcătuirea exeQ ce conţine partea cu
ORDER BY din cadrul czii SQL
achLegTab text;--inner join
achFROM text;

219
ANEXE

achTmpTable text;--nume tabel rezultat


i int; f boolean; r int; ach text;
BEGIN
-- REZULTATUL interogarii va fi pus în tabelul TEMPORAR dat de
achTmpTable
achA:='';achS:='';achG:='#';achO:='';codR:='OK';f:=false;
-- testare existenţă date pentru filtru selecţie
FOR i IN 1.._nr_
LOOP
IF _s_[i] AND (_val1_[i] IS NULL OR _val1_[i]='NULL') THEN
codR='Selectie fara valoare pentru '||_nume_[i]||'!'; RETURN codR; END
IF;
IF _s_[i] AND (_tip_[i]='N2' OR _tip_[i]='D2') AND (_val2_[i] IS NULL
OR _val2_[i]='NULL') THEN codR='Selectie fara valoare pentru '||
_nume_[i]||'!'; RETURN codR; END IF;
IF _a_[i] AND _nume_[i]=_ord_ THEN f:=true; END IF; IF _ord_='---'
THEN f:=true; END IF;
END LOOP;
IF NOT f THEN codR:='Câmpul dupa care se realizeaza ordonarea trebuie
să se regaseasca în optiunile de afisare pentru coloane !'; RETURN codR;
END IF;
--conversie date
FOR i IN 1.._nr_
LOOP
IF _val1_[i]='NULL' THEN CONTINUE; END IF;
IF _tip_[i]='D1' THEN vald1_[i]:=_val1_[i];END IF;
IF _tip_[i]='D2' THEN vald1_[i]:=_val1_[i];vald2_[i]:=_val2_[i];END IF;
IF _tip_[i]='N1' THEN valn1_[i]:=_val1_[i];END IF;
IF _tip_[i]='N2' THEN valn1_[i]:=_val1_[i];valn2_[i]:=_val2_[i];END IF;
IF _tip_[i]='B' THEN valb_[i]:=_val1_[i];END IF;
IF _tip_[i]='A' OR _tip_[i]='C' THEN valAC_[i]:=_val1_[i];END IF;
END LOOP;
-- constructie cda SQL
FOR i IN 1.._nr_
LOOP
IF _a_[i] THEN
achA := achA ||','|| _nume_[i]; --intai va fi sum(qty)
if achG='#' then achG:=''; ELSE achG := achG ||','; end if;
achG := achG || _nume_[i];
END IF;
IF _s_[i] AND _tip_[i]='A' THEN achS:=achS||' AND '||_nume_[i]||'
ILIKE ''%'||valAC_[i]||'%'''; END IF;
IF _s_[i] AND _tip_[i]='C' THEN achS:=achS||' AND '||_nume_[i]||' =
'||quote_literal(valAC_[i]); END IF;
IF _s_[i] AND _tip_[i]='B' THEN achS:=achS||' AND '||_nume_[i]||' IS
'||_valb_[i]; END IF;
IF _s_[i] AND _tip_[i]='N1' THEN achS:=achS||' AND '||_nume_[i]||' =
'||valn1_[i]; END IF;
IF _s_[i] AND _tip_[i]='N2' THEN achS:=achS||' AND '||_nume_[i]||' >=
'||valn1_[i]||' AND '||_nume_[i]||' <= '||valn2_[i]; END IF;
IF _s_[i] AND _tip_[i]='D1' THEN achS:=achS||' AND '||_nume_[i]||' =

220
4.Codul sursă SQL pentru aplicaţia LogX

to_date('||quote_literal(vald1_[i])||','||
quote_literal('dd.mm.yyyy')||')';END IF;
IF _s_[i] AND _tip_[i]='D2' THEN achS:=achS||' AND '||_nume_[i]||' >=
to_date('||quote_literal(vald1_[i])||','||quote_literal('dd.mm.yyyy')||')
AND '||_nume_[i]||' <= to_date('||quote_literal(vald2_[i])||','||
quote_literal('dd.mm.yyyy')||')';END IF;
END LOOP;
--creare tabele sursă şi tabele de legătură
-- utilizatorii pot vedea doar anumite magazii --- DOAR
MAGAZIILE/DEPOZITELE cu drepturi de acces valide vor fi luate în
rezultatul interogarii (tmpacc)!
DROP TABLE IF EXISTS tmpacc; CREATE TEMP TABLE tmpacc AS SELECT dep[s]
FROM (select acces.*, generate_subscripts(dep,1) as s from
logx.acces)par;
IF _tipQ_ ='IN' THEN achTmpTable:='q_tmp_in'; achFROM:=' FROM
logx.inx,logx.nom,tmpacc '; achLegTab:=' nom.idcode=inx.idcode AND
depinit=tmpacc.dep'; END IF;
IF _tipQ_ ='ST' OR _tipQ_ ='STL' THEN achTmpTable:='q_tmp_st';
achFROM:=' FROM logx.inx,logx.nom,logx.stx,tmpacc '; achLegTab:='
nom.idcode=inx.idcode AND inx.id=stx.id AND stx.dep=tmpacc.dep'; END IF;
IF _tipQ_ ='OUT' THEN achTmpTable:='q_tmp_out';achFROM:=' FROM
logx.inx,logx.nom,logx.outx,tmpacc '; achLegTab:=' nom.idcode=inx.idcode
AND inx.id=outx.id AND outx.dep=tmpacc.dep'; END IF;
IF _tipQ_ ='TR' THEN achTmpTable:='q_tmp_tr';achFROM:=' FROM
logx.inx,logx.nom,logx.trx,tmpacc '; achLegTab:=' nom.idcode=inx.idcode
AND inx.id=tr.id AND trx.dep_srs=tmpacc.dep AND trx.dep_dest=tmpacc.dep';
END IF;

IF achG !='#' THEN achG:=' GROUP BY '||achG; ELSE achG:=''; END IF;
achS:=' WHERE '||achLegTab||achS;
IF _tipQ_ ='STL' THEN achS:=achS||' AND stx.qtydisp>0 '; END IF;
IF _ord_!='---' THEN achO:=' ORDER BY '||_ord_; ELSE achO:=''; END IF;
IF _tipQ_ = 'ST' OR _tipQ_ ='STL' THEN ach:= ' AS SELECT SUM(qtyfiz) as
qtyfiz, SUM(qtydisp) as qtydisp '; ELSE ach:= ' AS SELECT SUM(qty) as qty
';END IF;
exeQ:='DROP TABLE IF EXISTS '||achTmpTable||' ;
CREATE TEMPORARY TABLE '||achTmpTable||ach||achA||
achFROM||achS||achG||achO||';';
RAISE NOTICE 'Interogarea: % ',exeQ;
EXECUTE exeQ;
GET DIAGNOSTICS r := ROW_COUNT; RAISE NOTICE 'Rez: %: % inregistrari',
achTmpTable, r;
RETURN codR;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION logx.q_x(text, smallint, text[], text[], boolean[],
boolean[], text[], text[], text)
OWNER TO postgres;
COMMENT ON FUNCTION logx.q_x(text, smallint, text[], text[], boolean[],

221
ANEXE

boolean[], text[], text[], text) IS '


© 2014, Liviu Serbanescu, Hyperion University Bucharest, v.001';

------------------------------------------------------------------------
--########### POPULAREA CU DATE DE TEST ###############
INSERT INTO logx.acces(alias, pw, dep)
VALUES('u1','0',ARRAY['DP1','DP2']),('u2','abc',ARRAY['DP2','DP3']);
INSERT INTO logx.nom(idcode,descr) VALUES
('A1','DescrA1'),('A2','DescrA2'),('B1','DescrB1'),('B2','DescrZ2'),
('C1','DescrC1'),('C2','DescrC2');
INSERT INTO logx.loc(dep, ampl) VALUES ('DP1','AA1'),('DP1','AA2'),
('DP2','AA1'),('DP2','AB1'),('DP2','AC1'),('DP2','AA2'),('DP3','AA1'),
('DP3','AA2');
INSERT INTO logx.br(nrbr, datain, idcode, status, supplier, serialno,
qty,depinit,amplinit) VALUES
(1,'2012-04-17','A1','functional','Furniz1','SS1',1,'DP1','AA1'),
(1,'2012-04-17','A2','rebut','Furniz1','SS2',1,'DP1','AA1'),
(1,'2012-04-17','B2','functional','Furniz1',NULL,10,'DP1','AA1'),
(2,'2012-04-18','B2','incert','Furniz1',NULL,15,'DP2','AA1'),
(2,'2012-04-18','C1','defect','Furniz1','SS3',1,'DP2','AA1'),
(3,'2012-04-18','C1','defect','Furniz1','SS3',1,'DP3','AA1'),
(3,'2012-04-18','B2','functional','Furniz2',NULL,25,'DP3','AA1');
-- select logx.valid_br(1) as rez;

--########## DREPTURI DE ACCES ########################


--pentru a putea şterge utilizatorii şi drepturile de acces trebuie să
ne asigurăm că nu mai există obiecte create de aceştia, obiectele sunt
trecute în posesia utilizatorului postgres
--DROP OWNED BY "LOGX";-- LA A DOUA RULARE A FIŞIERULUI SE VA ŞTERGE
COMENTARIUL !!!
DROP ROLE IF EXISTS logx_sw ; DROP ROLE IF EXISTS "LOGX";
CREATE ROLE logx_sw LOGIN PASSWORD 'gugu' SUPERUSER INHERIT NOCREATEDB
CREATEROLE; COMMENT ON ROLE logx_sw IS 'supervizor logx';
--se crează GRUPUL DE ACCES LOGX
CREATE ROLE "LOGX";
-- se oferă acces utlizatorului logx_sw la grupul LOGX
GRANT logx_sw TO "LOGX";
--asignări acces
GRANT CONNECT, TEMPORARY ON DATABASE picdb TO GROUP "LOGX";
GRANT ALL ON SCHEMA logx TO "LOGX";
GRANT EXECUTE ON FUNCTION logx.add_bp_id(integer, text, text, text,
integer) TO GROUP "LOGX";
GRANT EXECUTE ON FUNCTION logx.bp_nou(text, date) TO GROUP "LOGX";
GRANT EXECUTE ON FUNCTION logx.br_nou(date) TO GROUP "LOGX";
GRANT EXECUTE ON FUNCTION logx.bt_valid(integer) TO GROUP "LOGX";
GRANT EXECUTE ON FUNCTION logx.del_bp_id(integer, text, text, text) TO
GROUP "LOGX";
GRANT EXECUTE ON FUNCTION logx.id_nou(text) TO GROUP "LOGX";
GRANT EXECUTE ON FUNCTION logx.nom_add(text, text, text, text) TO GROUP
"LOGX";

222
4.Codul sursă SQL pentru aplicaţia LogX

GRANT EXECUTE ON FUNCTION logx.q_x(text, smallint, text[], text[],


boolean[], boolean[], text[], text[], text) TO GROUP "LOGX";
GRANT EXECUTE ON FUNCTION logx.valid_bp(integer) TO GROUP "LOGX";
GRANT EXECUTE ON FUNCTION logx.valid_br(integer) TO GROUP "LOGX";
GRANT ALL ON SEQUENCE logx.bp_seq TO GROUP "LOGX";
GRANT ALL ON SEQUENCE logx.br_idx_seq TO GROUP "LOGX";
GRANT ALL ON SEQUENCE logx.br_seq TO GROUP "LOGX";
GRANT ALL ON SEQUENCE logx.id_seq TO GROUP "LOGX";
GRANT ALL ON SEQUENCE logx.trx_nrlin_seq TO GROUP "LOGX";
GRANT ALL ON TABLE logx._bp TO GROUP "LOGX";
GRANT ALL ON TABLE logx._bt TO GROUP "LOGX";
GRANT ALL ON TABLE logx.acces TO GROUP "LOGX";
GRANT ALL ON TABLE logx.bp_ TO GROUP "LOGX";
GRANT ALL ON TABLE logx.br TO GROUP "LOGX";
GRANT ALL ON TABLE logx.bt_ TO GROUP "LOGX";
GRANT ALL ON TABLE logx.inx TO GROUP "LOGX";
GRANT ALL ON TABLE logx.loc TO GROUP "LOGX";
GRANT ALL ON TABLE logx.nom TO GROUP "LOGX";
GRANT ALL ON TABLE logx.outx TO GROUP "LOGX";
GRANT ALL ON TABLE logx.stx TO GROUP "LOGX";
GRANT ALL ON TABLE logx.trx TO GROUP "LOGX";
--drepturile de acces de tip public se pot şterge şi manual din pgAdinin
III
--#############################

5. Codul sursă pentru biblioteca LX


În continuare este prezentat codul sursă pentru fişierele
“.cpp”, pentru fişierele de tip header codul se regăseşte în
capitolul anterior.
☑ Fişierul pentru clasa lxComboBox
#include "lx_combobox.h" //lx_combobox.cpp
lxComboBox::lxComboBox(wxComboBox *cb1, wxString achQ):lxCtrl()
{ cb=cb1; tipdata=0; achQRD=achQ; rdLista(achQ); }
int lxComboBox::rdLista(wxString achQ)
{PGresult *rezultat;
int nrInregistrari;
wxString ach,ach_dmy;
rezultat = PQexec(lxCtrl::conexiune0,achQ);
if(PQresultStatus(rezultat) != PGRES_TUPLES_OK)
{ wxString ach; ach=ach.FromAscii(PQresultErrorMessage(rezultat));
wxMessageBox(_("EROARE LA CITIRE: ")+achQ+_(" ("+ach+")!")); return -1;
}
nrInregistrari=PQntuples(rezultat);
cb->Clear();
int tipData=PQftype(rezultat,0);
wxDateTime achT1;
for(int i = 0; i < nrInregistrari; i++) {
if(tipData==1082) { //1082 date

223
ANEXE

ach=ach.FromAscii(PQgetvalue(rezultat, i, 0));
achT1.ParseISODate(ach);
achT1.ParseFormat(ach,wxT("%d.%m.%Y"));
cb->AppendString(ach);}
else {ach=ach.FromAscii(PQgetvalue(rezultat, i, 0));
cb->AppendString(ach);} //end if
}//endfor i
return -1;
}
//-----------------------------------------------------------------
wxString lxComboBox::getSel()
{ return cb->GetStringSelection();
}
//-----------------------------------------------------------------
int lxComboBox::refreshlx()
{ rdLista(achQRD); return 0;
}

☑ Fişierul pentru clasa lxCtrl


#include "lx_ctrl.h" //fişierul lx_ctrl.cpp
lxCtrl::lxCtrl() { }
//-----------------------------------------------------------------
void lxCtrl::conectare(wxString dbname, wxString user, wxString pw,
wxString host, wxString port)
{wxString ach;
char sir_conexiune[500];
ach=wxT(" dbname=")+dbname+wxT(" user=")+user+wxT(" password=")+pw+wxT("
host=")+host+wxT(" port=")+port;
strcpy(sir_conexiune,ach.c_str());
lxCtrl::conexiune0 = PQconnectdb(sir_conexiune);
if (PQstatus(lxCtrl::conexiune0) != CONNECTION_OK)
{PQfinish(lxCtrl::conexiune0); wxMessageBox("EROARE LA CONECTARE!");}
}
void lxCtrl::deconectare()
{PQfinish(lxCtrl::conexiune0);
}
//-------------------------------------------------------------------
int lxCtrl::refreshlx() {return 0;}
//-----------------------------------------------------------------
wxString lxCtrl::cda(wxString achQ)
{PGresult *rezultat;
rezultat = PQexec(lxCtrl::conexiune0,achQ);
if((PQresultStatus(rezultat) != PGRES_COMMAND_OK) &&
(PQresultStatus(rezultat) != PGRES_TUPLES_OK))
{ wxString ach;
ach=ach.FromAscii(PQresultErrorMessage(rezultat));
wxMessageBox(_("EROARE CDA: ")+achQ+_(" ("+ach+")!")); return _(ach);}
if(PQresultStatus(rezultat) == PGRES_TUPLES_OK)
{ wxString achR=_("#");
int nrCampuri = PQnfields(rezultat);

224
5.Codul sursă pentru biblioteca LX

int nrInregistrari=PQntuples(rezultat);
if(nrCampuri==1 && nrInregistrari==1)
achR=achR.FromUTF8(PQgetvalue(rezultat, 0, 0));
return achR;
}
return _("OK");
}
//------------------------------------------------------------------
//comanda ce întoarce o lista
wxString lxCtrl::lista(wxString achQ, wxArrayString & a)
{PGresult *rezultat;
rezultat = PQexec(lxCtrl::conexiune0,achQ);
if((PQresultStatus(rezultat) != PGRES_COMMAND_OK) &&
(PQresultStatus(rezultat) != PGRES_TUPLES_OK))
{ wxString ach; ach=ach.FromAscii(PQresultErrorMessage(rezultat));
wxMessageBox(_("EROARE CDA: ")+achQ+_(" ("+ach+")!")); return _(ach);}
if(PQresultStatus(rezultat) == PGRES_TUPLES_OK)
{ wxString achR=_("#");
int nrCampuri = PQnfields(rezultat); int
nrInregistrari=PQntuples(rezultat);
if(nrCampuri!=1) return wxT("Comanda SQL întoarce mai mult de o coloană !");
a.Clear();
for(int i=0; i<nrInregistrari; i++)
a.Add(achR.FromUTF8(PQgetvalue(rezultat, i, 0)));
return achR;
}
return _("OK");
}

☑ Fişierul pentru clasa lxDPC


#include "lx_dpc.h" //fişirul lx_dpc.cpp
lxDPC::lxDPC(wxDatePickerCtrl *dpc1):lxCtrl()
{ dpc=dpc1;
}
//----------------------------------------------------------------
//citirea DIN baza de date (BD) în cutia de editare
int lxDPC::rdSQL(wxString rdachQ) //rdachSQKL1 - cda de citire DIN BD
{PGresult *rezultat;
wxString ach;
rezultat = PQexec(lxCtrl::conexiune0,rdachQ);
//comanda rdachQ trebuie să întoarcă o singură valoare
if(PQresultStatus(rezultat) != PGRES_TUPLES_OK)
{ wxMessageBox(_("Interogare ptr. preluare eronata: ") + rdachQ +
_(" !")); return -1;}
wxDateTime achT1;// wxMessageBox(rdachQ);
if(((int)PQftype(rezultat, 0))!=1082)
{wxMessageBox(_("Valoarea nu este de tip data calendaristica!"));return -1;}
ach=ach.FromAscii(PQgetvalue(rezultat, 0, 0)); achT1.ParseISODate(ach);
dpc->SetValue(achT1);
return 0;

225
ANEXE

}
//-----------------------------------------------------------------
int lxDPC::wrSQL(wxString wrachQ)
//wrachSQKL1 - cda de scrirere în baza de date
{ PGresult *rezultat;
rezultat=PQexec(lxCtrl::conexiune0,wrachQ);
if(PQresultStatus(rezultat) != PGRES_COMMAND_OK)
{ wxMessageBox(_("Comanda actualizare eronata: ") + wrachQ +
_(" !")); return -1;}
wxMessageBox("ACTUALIZAT !");
return 0;
}
//------------------------------------------------------------------
wxString lxDPC::getValFormatStr()
{ if(!dpc->GetValue().IsValid()) return _("NULL");
return _("'")+this->dpc->GetValue().FormatISODate()+_("'");
}

☑ Fişierul pentru clasa lxEditN


#include "lx_edit_n.h" // fişierul lx_edit_n.cpp
lxEditN::lxEditN(wxTextCtrl *ed1):lxCtrl()
{ ed=ed1;}
//-------------------------------------------------------------------
//citirea DIN baza de date în cutia de editare
int lxEditN::rdSQL(wxString rdachQ) // - cda de citire DIN baza de date
{PGresult *rezultat;
wxString ach;
rezultat = PQexec(lxCtrl::conexiune0,rdachQ);/*comanda din rdachQ
trebuie să întoarca o singură valoare (o înregistrare - un câmp)*/
if(PQresultStatus(rezultat) != PGRES_TUPLES_OK)
{wxString ach; ach=ach.FromAscii(PQresultErrorMessage(rezultat));
wxMessageBox(_("EROARE LA CITIRE: ")+rdachQ+_(" ("+ach+")!"));return -1;
}
ach=ach.FromAscii(PQgetvalue(rezultat, 0, 0));
ed->SetValue(ach);
return 0;
}
//--------------------------------------------------------------
int lxEditN::wrSQL(wxString wrachQ) // - cda de scrirere în baza de date
{PGresult *rezultat;
wxString ach,achQ;
ach=ed->GetValue();
rezultat=PQexec(lxCtrl::conexiune0,wrachQ);
if(PQresultStatus(rezultat) != PGRES_COMMAND_OK)
{wxString ach; ach=ach.FromAscii(PQresultErrorMessage(rezultat));
wxMessageBox(_("EROARE LA SCRIERE: ")+achQ+_(" ("+ach+")!"));return -1;
}
wxMessageBox("ACTUALIZAT !");
return 0;
}

226
5.Codul sursă pentru biblioteca LX

//------------------------------------------------------------------
wxString lxEditN::getValFormatStr()
{ if(ed->GetValue().IsEmpty()) return _("NULL");
return this->ed->GetValue();
}

☑ Fişierul pentru clasa lxEditT


#include "lx_edit_t.h" //fişierul lx_edit_t.cpp
lxEditT::lxEditT(wxTextCtrl *ed1):lxCtrl()
{ ed=ed1;}
//------------------------------------------------------------------
//citirea DIN baza de date(BD) în cutia de editare
int lxEditT::rdSQL(wxString rdachQ)//rdSQL1-cda de citire DIN BD
{PGresult *rezultat; wxString ach;
rezultat = PQexec(lxCtrl::conexiune0,rdachQ); /*comanda rdachQ trebuie
să întoarca o singură valoare (o înregistrare cu un singur câmp) */
if(PQresultStatus(rezultat) != PGRES_TUPLES_OK)
{wxString ach; ach=ach.FromAscii(PQresultErrorMessage(rezultat));
wxMessageBox(_("EROARE LA CITIRE: ")+rdachQ+_(" ("+ach+")!"));return -1;
}
ach=ach.FromAscii(PQgetvalue(rezultat, 0, 0));
if(ach.IsEmpty()||ach.IsNull()) ach="";
ed->SetValue(ach);
return 0;
}
//--------------------------------------------------------------------
int lxEditT::wrSQL(wxString wrachQ)//wrSQL1-cda scrirere în baza de date
{PGresult *rezultat;
wxString ach,achQ;
ach=ed->GetValue();
rezultat=PQexec(lxCtrl::conexiune0,wrachQ);
if(PQresultStatus(rezultat) != PGRES_COMMAND_OK)
{ wxString ach; ach=ach.FromAscii(PQresultErrorMessage(rezultat));
wxMessageBox(_("EROARE LA SCRIERE: ")+achQ+_(" ("+ach+")!"));return -1;
}
wxMessageBox("ACTUALIZAT !");
return 0;
}
//--------------------------------------------------------------------
wxString lxEditT::getValFormatStr()
{ if(ed->GetValue().IsEmpty()) return _("NULL");
return _("'")+this->ed->GetValue()+_("'");
}

☑ Fişierul pentru clasa lxEditT


#include "lx_grid.h" //fişierul lx_grid.cpp
lxGrid::lxGrid(wxGrid *gridX1, wxString tabelBD1, wxArrayString
dencol1):lxCtrl()
{ gridX=gridX1; tabelBD=tabelBD1; dencol=dencol1; tipCol=NULL;
/*se trece null ptr. a putea face verificarea în destructor - dacă a

227
ANEXE

fost sau nu alocată memorie*/


}
//-------------------------------------------------------------------
lxGrid::~lxGrid()
{ if(tipCol) delete[] tipCol;
}
/* 1043 varchar ; 1082 date; 1083 time; 11579 seq; 21 int2 ; 23 int4; 20
int8; 16 bool; 25 text ; 700 float4; 701 float8; 25 text
- tabelele temporare de interfaţă NU vor conţine date de tip serial,
acestea pot fi doar în tabelul sursă*/
//--------------------------------------------------------------------
void lxGrid::salvare()
{ wxString achQ,ach;
int nrCampuri=gridX->GetNumberCols();
int nrInregistrari=gridX->GetNumberRows();
//construim interogare pentru fiecare linie
achQ=" DELETE FROM "+tabelBD+"; INSERT INTO "+tabelBD+" VALUES ";
for(int i=0;i<nrInregistrari;i++)
{for(int j=0;j<nrCampuri;j++)
{if(!j) ach=wxT("("); else ach=ach+",";
if(gridX->GetCellValue(i,j).IsEmpty()) ach=ach+" NULL ";
else
switch(tipCol[j])
{case 1042: case 1043: case 1083: case 25: case 16:
ach=ach+_("'")+gridX->GetCellValue(i,j)+_("'"); break;
case 1082: ach=ach+_("to_date('")+gridX->GetCellValue(i,j)
+_("','dd.mm.yyyy'"); break;
case 11579: case 21: case 23: case 20: case 700: case 701:
ach=ach+gridX->GetCellValue(i,j); break;
default: wxMessageBox(_("Tip de date neidentificat !"));
}
}
ach=ach+")";
if(i) achQ=achQ+wxT(",");
achQ=achQ+ach;
}
rezultatRx = PQexec(conexiune0, achQ.ToUTF8());
if(PQresultStatus(rezultatRx) != PGRES_COMMAND_OK)
{wxMessageBox(_("Eroare salvare din grid în ")+tabelBD+_("!")); }
return;
}
//--------------------------------------------------------------------
void lxGrid::preluareVarColRow()
{ wxString achY; /*dacă nu există denumire a coloanelor transmisă şi
există coloane în grid se vor şterge celelalte coloane */
if(!dencol.GetCount() && gridX->GetNumberCols()>0)
gridX->DeleteCols(0,gridX->GetNumberCols());
//dacă există linii în grid acestea se vor şterge
if(gridX->GetNumberRows()>0)
gridX->DeleteRows(0,gridX->GetNumberRows());
//preia datele din tabelul "tabelBD" în structura rezultatRx

228
5.Codul sursă pentru biblioteca LX

wxString ach,achQ; achQ=_("SELECT * FROM ")+tabelBD+_(";");


rezultatRx = PQexec(conexiune0,achQ);
//testează operaţia de preluare a datelor
if(PQresultStatus(rezultatRx) != PGRES_TUPLES_OK)
wxMessageBox(_("Interogare ptr. preluare eronata: ")+achQ+_(" !"));
int nrCampuri = PQnfields(rezultatRx); int
nrInregistrari=PQntuples(rezultatRx);
if(((unsigned int)nrCampuri !=dencol.GetCount()) && dencol.GetCount()>0)
{wxMessageBox("Nr. de coloane din grid diferea de cele din tabel!");return;}
if(gridX->GetNumberCols()<1) gridX->AppendCols(nrCampuri);
gridX->AppendRows(nrInregistrari);
/*dacă există denumiri ptr. coloane, transmise, acestea vor fi setate cu
denumirile transmise, altfel preia den. câmpurilor*/
if(dencol.GetCount())
for(unsigned int j=0;j<dencol.Count();j++)
gridX->SetColLabelValue(j,dencol[j]);
else
for(int j = 0; j<nrCampuri; j++)
{ach=ach.FromUTF8(PQfname(rezultatRx,j));gridX->SetColLabelValue(j,ach);}
//determină tipul de dată pentru fiecare câmp şi îl stochează într-un vector
tipCol = new int[nrCampuri];
for(int j=0;j<nrCampuri;j++) tipCol[j]= (int)PQftype(rezultatRx, j);
sirRcampuri="";
for(unsigned int j=1;j<dencol.GetCount();j++)
{if(j>1) sirRcampuri=sirRcampuri+_(",");
ach=ach.FromUTF8(PQfname(rezultatRx,j)); sirRcampuri=sirRcampuri+ach;
}
for(int i = 0; i < nrInregistrari; i++)
{ for(int j = 0; j < nrCampuri; j++)
{ach=ach.FromUTF8(PQgetvalue(rezultatRx, i, j));
gridX->SetCellValue(i,j,ach);
}
}
//setarea gridului în modul "selecţie rând"
gridX->SetSelectionMode(wxGrid::wxGridSelectRows);
//setare culori alternate pentru randuri
//ATENŢIE ! Pentru fiecare atribuire este necesară o instanţiere !
for(int i=0; i<gridX->GetNumberRows(); i++)
{ if(i%2)
{ wxGridCellAttr *A_rand = new wxGridCellAttr();
A_rand->SetBackgroundColour(wxColor(220,250,150));
gridX->SetRowAttr(i,A_rand);
}
else { wxGridCellAttr *A_rand = new wxGridCellAttr();
A_rand->SetBackgroundColour(wxColor(248,253,217));
gridX->SetRowAttr(i,A_rand);
}
}//endfor
gridX->AutoSize(); gridX->Fit(); this->refresh();
}

229
ANEXE

6. Codul sursă pentru aplicaţia LogX


☑ Fişierul pentru clasa iFACC
#include "facc.h" //fişierul facc.cpp
iFACC::iFACC(wxWindow* parent, wxWindowID id, const wxString& title,
const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent,
id, title, pos, size, style)
{ this->SetSizeHints(wxDefaultSize, wxDefaultSize);
wxBoxSizer* s1; s1 = new wxBoxSizer(wxVERTICAL);
wxFlexGridSizer* s2; s2 = new wxFlexGridSizer(0, 2, 0, 0);
s2->SetFlexibleDirection(wxBOTH);
s2->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
t1 = new wxStaticText(this, wxID_ANY, wxT("Utilizator"), wxDefaultPosition, wxDefaultSize, 0) ;
t1->Wrap(-1); s2->Add(t1, 0, wxALL, 5);
e_alias = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0) ;
s2->Add(e_alias, 0, wxALL, 5);
t2 = new wxStaticText(this, wxID_ANY, wxT("Cod acces"), wxDefaultPosition, wxDefaultSize, 0 );
t2->Wrap(-1); s2->Add(t2, 0, wxALL, 5);
e_pw = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_PASSWORD|wxTE_PROCESS_ENTER); s2->Add(e_pw, 0, wxALL, 5);
s1->Add(s2, 0, wxEXPAND, 5);
b42 =new wxButton(this,wxID_ANY,wxT("SCHIMBARE PAROLĂ"),wxDefaultPosition,wxDefaultSize,0);
b42->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 94, 90, false, wxEmptyString ));
s1->Add(b42, 0, wxALIGN_CENTER|wxALL, 5);
wxFlexGridSizer* s3; s3 = new wxFlexGridSizer(0, 2, 0, 0);
s3->SetFlexibleDirection(wxBOTH);
s3->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
t3 = new wxStaticText(this, wxID_ANY, wxT("IP server PG"), wxDefaultPosition, wxDefaultSize, 0);
t3->Wrap(-1); s3->Add(t3, 0, wxALL, 5);
e_host = new wxTextCtrl(this, wxID_ANY, wxT("localhost"), wxDefaultPosition, wxDefaultSize, 0);
s3->Add(e_host, 0, wxALL, 5);
t4 = new wxStaticText(this, wxID_ANY, wxT("Port"), wxDefaultPosition, wxDefaultSize, 0);
t4->Wrap(-1); s3->Add(t4, 0, wxALL, 5);
e_port = new wxTextCtrl(this, wxID_ANY, wxT("5435"), wxDefaultPosition, wxDefaultSize, 0);
s3->Add(e_port, 0, wxALL, 5); s1->Add(s3, 0, wxEXPAND, 5);
bAcces = new wxButton(this, wxID_ANY, wxT("ACCES"), wxDefaultPosition, wxDefaultSize, 0);
bAcces->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString) );
s1->Add(bAcces, 0, wxALIGN_CENTER|wxALL, 5);
this->SetSizer(s1); this->Layout(); this->Centre(wxBOTH);
b42->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFACC::schimbPW), NULL, this);
bAcces->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFACC::acces), NULL, this);
iFACC::accesX=false;
}
//------------------------------------------------------------------
iFACC::~iFACC() {
b42->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFACC::schimbPW), NULL, this);
bAcces->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFACC::acces), NULL, this);
}
//----------------------------------------------------------------

230
6.Codul sursă pentru aplicaţia LogX

void iFACC::schimbPW(wxCommandEvent& event)


{ wxString pw1,pw2;
rezX=lxCtrl::cda(_("SELECT EXISTS(select * FROM logx.acces WHERE
alias='")+e_alias->GetValue()+_("' AND pw='")+e_pw->GetValue()+_("') AS
acces_permis;"));
if(rezX!=_("t"))
{ wxMessageBox(wxT("Alias nume sau parolă incorecte!")); return;}
pw1=wxGetPasswordFromUser(wxT("1.Introduceţi noul cod de acces!"));
pw2=wxGetPasswordFromUser(wxT("1.Introduceţi noul cod de acces!"));
if(pw1!=pw2) { wxMessageBox(wxT("Cele două parole nu coincid!"));
return;}
rezX=lxCtrl::cda(_("UPDATE logx.acces SET pw='")+pw1+_("' WHERE
alias='")+e_alias->GetValue()+_("';"));
wxMessageBox(rezX);
}
//---------------------------------------------------------------
void iFACC::acces(wxCommandEvent& event)
{ wxString pw1,pw2,pw, pwN,ach;
rezX=lxCtrl::cda(_("SELECT EXISTS(select * FROM logx.acces WHERE
alias='")+e_alias->GetValue()+_("' AND pw='")+e_pw->GetValue()+_("') AS
acces_permis;"));
if(rezX!=_("t"))
{ wxMessageBox(wxT("Alias nume sau parolă incorecte!"));
iFACC::accesX=false; return;
}
srand(time(NULL));//iniţializare generator numere aleatoare
pwN = _("123-")+ach.FromDouble(rand());
rezX=lxCtrl::cda(_("DROP ROLE IF EXISTS \"#")+e_alias->GetValue()+_("#\";
CREATE ROLE \"#")+e_alias->GetValue()+_("#\" LOGIN PASSWORD '")+pwN +
_("' CONNECTION LIMIT 1; GRANT \"LOGX\" TO \"#")+e_alias-
>GetValue()+_("#\"; SET ROLE \"#")+e_alias->GetValue()+_("#\";"));
if(rezX!=_("OK"))
wxGetTextFromUser(_("Mesaj de eroare"), _("Err 101"), rezX);
iFACC::accesX=((rezX==_("t"))||(rezX==_("OK")));
this->Destroy();//distruge fereara principală şi continuă codul din
OnInit()
}

☑ Fişierul pentru clasa iFEBP


#include <wx/clipbrd.h> //fişierul febp.cpp
#include "febp.h"
#include "selst.h"
iFEBP::iFEBP(wxWindow* parent, wxWindowID id, const wxString& title,
const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent,
id, title, pos, size, style)
{ this->SetSizeHints(wxDefaultSize, wxDefaultSize);
wxBoxSizer* s1; s1 = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* s2; s2 = new wxBoxSizer(wxHORIZONTAL);
t1 = new wxStaticText(this, wxID_ANY, wxT("BP nr:"), wxDefaultPosition, wxDefaultSize, 0);
t1->Wrap(-1); s2->Add(t1, 0, wxALL, 5);

231
ANEXE

cbBP = new wxComboBox(this, wxID_ANY, wxT("0000"), wxDefaultPosition,


wxSize(60,-1),0,NULL,0); s2->Add(cbBP, 0, wxALL, 5);
bAddBP = new wxButton(this, wxID_ANY, wxT("Adaugă BP"), wxDefaultPosition, wxSize(-1,25),0);
s2->Add(bAddBP, 0, wxALL, 5);
eDataBP = new wxDatePickerCtrl(this, wxID_ANY, wxDefaultDateTime, wxDefaultPosition,
wxDefaultSize, DEFAULT|wxDP_DROPDOWN); s2->Add(eDataBP, 0, wxALL, 5);
t2 = new wxStaticText(this,wxID_ANY,wxT("Destinaţie"), wxDefaultPosition,
wxDefaultSize,0); t2->Wrap(-1); s2->Add(t2, 0, wxALL, 5);
eDestinBP = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
wxDefaultSize,0); s2->Add(eDestinBP, 0, wxALL, 5);
bDelBP = new wxButton(this, wxID_ANY, wxT("Anulează BP"), wxDefaultPosition, wxDefaultSize, 0);
s2->Add(bDelBP, 0, wxALL, 5); s1->Add(s2, 0, wxEXPAND, 5);
pBP = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxTAB_TRAVERSAL); wxBoxSizer* s4; s4 = new wxBoxSizer(wxVERTICAL);
GD1 = new wxGrid(pBP, wxID_ANY, wxDefaultPosition,wxSize(-1,400), wxALWAYS_SHOW_SB);
GD1->CreateGrid(0, 0); GD1->EnableEditing(true);
GD1->EnableGridLines(true);
GD1->EnableDragGridSize(false); GD1->SetMargins(0, 0);
GD1->AutoSizeColumns();GD1->EnableDragColMove(false);
GD1->EnableDragColSize(true);GD1->SetColLabelSize(30);
GD1->SetColLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
GD1->AutoSizeRows(); GD1->EnableDragRowSize(true);
GD1->SetRowLabelSize(80);
GD1->SetRowLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
GD1->SetDefaultCellBackgroundColour(wxColour(237, 250, 237));
GD1->SetDefaultCellAlignment(wxALIGN_LEFT, wxALIGN_TOP);
GD1->SetBackgroundColour(wxColour(238, 251, 236));
s4->Add(GD1, 1, wxALL|wxEXPAND, 5);
wxBoxSizer* s3; s3 = new wxBoxSizer(wxHORIZONTAL);
bDelLin = new wxButton(pBP, wxID_ANY, wxT("Şterge linia"), wxDefaultPosition, wxDefaultSize,0);
s3->Add(bDelLin, 0, wxALL, 5); s3->Add(0, 0, 1, wxEXPAND, 5);
bSelST =new wxButton(pBP, wxID_ANY,wxT("Adaugă din
STOC"),wxDefaultPosition,wxDefaultSize,0); s3->Add(bSelST, 0, wxALL, 5); s3->Add(0,
0, 1, wxEXPAND, 5);
bValidBP=new wxButton(pBP, wxID_ANY, wxT("VALIDEAZĂ BP"),wxDefaultPosition,wxDefaultSize,0);
s3->Add(bValidBP, 0, wxALL, 5); s4->Add(s3,0,wxEXPAND,5);
pBP->SetSizer(s4); pBP->Layout(); s4->Fit(pBP);
s1->Add(pBP, 0, wxEXPAND | wxALL, 5);
this->SetSizer(s1); this->Layout(); this->Centre(wxBOTH);
cbBP->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(iFEBP::afis_BP), NULL, this);
bAddBP->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEBP::addBP), NULL, this);
bDelBP->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEBP::delBP), NULL, this);
bDelLin->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEBP::delLin), NULL, this);
bSelST->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEBP::selST), NULL, this);
bValidBP->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEBP::validBP), NULL, this);
pBP->Hide();
cx_nrbp = new lxComboBox(cbBP,_("SELECT DISTINCT nrbp::text FROM logx.bp_
ORDER BY nrbp;"));
cx_destin=new lxEditT(eDestinBP); cx_databp=new lxDPC(eDataBP);
wxArrayString a; cx_grid=new lxGrid(GD1,_("itmp_bp"),a);
GD1->SetColMinimalAcceptableWidth(0);

232
6.Codul sursă pentru aplicaţia LogX

}
//-------------------------------------------------------------
iFEBP::~iFEBP() {
cbBP->Disconnect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(iFEBP::afis_BP), NULL, this);
bAddBP->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEBP::addBP), NULL, this);
bDelBP->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEBP::delBP), NULL, this);
bDelLin->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEBP::delLin), NULL, this);
bSelST->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEBP::selST), NULL, this);
bValidBP->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEBP::validBP), NULL, this);
}
//-------------------------------------------------------------
void iFEBP::afis_BP(wxCommandEvent& event)
{if(cbBP->GetCount()<1) { pBP->Hide(); return;}
cx_destin->rdSQL(_("SELECT destin::text FROM logx.bp_ WHERE nrbp=")
+cx_nrbp->getSel()+_(" LIMIT 1;"));
cx_databp->rdSQL(_("SELECT databp::date FROM logx.bp_ WHERE nrbp=")
+cx_nrbp->getSel()+_(" LIMIT 1;"));
lxCtrl::cda(_("DROP TABLE IF EXISTS itmp_bp; CREATE TEMPORARY TABLE
itmp_bp AS SELECT (_bp.nrbp::text||'->'||_bp.id::text||'->'||
_bp.dep::text||'->'||_bp.ampl::text) AS tcu, _bp.nrbp, _bp.id,
nom.idcode, nom.refcom, nom.refteh, nom.descr, inx.po, inx.status,
inx.serialno, _bp.dep, _bp.ampl, _bp.qtyrez, _bp.qtyconf FROM
logx._bp,logx.inx,logx.nom WHERE nom.idcode=inx.idcode AND nrbp=")
+cx_nrbp->getSel()+_(" AND inx.id=_bp.id;"));
/*tcu contor unic pentru a realiza legătura între cele două tabele, în
grid va fi în prima coloana care va fi setată pe invizibil(dimensiune
zero)*/ cx_grid->preluareVarColRow();GD1->SetColSize(0,0);pBP->Show();
}
//-------------------------------------------------------------
void iFEBP::addBP(wxCommandEvent& event)
{lxCtrl::cda(_("select logx.bp_nou(")+cx_destin->getValFormatStr()+_(",")
+cx_databp->getValFormatStr()+_(");"));
cx_nrbp->rdLista(_("SELECT DISTINCT nrbp::text FROM logx.bp_ ORDER BY
nrbp;"));
cbBP->SetSelection(cbBP->GetCount()-1);
afis_BP(event);
}
//-------------------------------------------------------------
void iFEBP::delLin(wxCommandEvent& event)
{wxMessageDialog *x =
new wxMessageDialog(this,wxT("Doriţi ştergerea liniei curente?"), _(""),
wxYES_NO|wxICON_QUESTION); x->SetYesNoLabels(_("&Nu"),_("&Da"));
if(x->ShowModal() == wxID_NO)
{ lxCtrl::cda(_("DELETE FROM logx._bp USING itmp_bp WHERE itmp_bp.tcu =
(bp.nrbp::text||'->'||bp.id::text||'->'||_bp.dep::text||'->'||
_bp.ampl::text) AND tcu='") + GD1->GetCellValue(GD1->GetSelectedRows()
[0],0) +_("'; DELETE FROM itmp_bp WHERE itmp_bp.tcu='") +
GD1->GetCellValue(GD1->GetSelectedRows()[0],0) +_("';"));
GD1->DeleteRows(GD1->GetSelectedRows()[0],1);GD1->Fit();
}
afis_BP(event);

233
ANEXE

}
//-------------------------------------------------------------
void iFEBP::delBP(wxCommandEvent& event)
{ wxMessageDialog *x =
new wxMessageDialog(this,wxT("Doriţi ştergerea BPORDEROULUI curent?"),
_(""),wxYES_NO|wxICON_QUESTION); x->SetYesNoLabels(_("&Nu"),_("&Da"));
if(x->ShowModal() == wxID_NO) //&Nu corespunde lui wxID_YES
lxCtrl::cda(_("UPDATE logx.stx SET qtydisp=qtydisp+qtyrez FROM itmp_bp
WHERE stx.id=itmp_bp.id AND stx.dep=itmp_bp.dep AND stx.ampl=itmp_bp.ampl
AND itmp_bp.nrbp = ")+cx_nrbp->getSel()+_("; \
DELETE FROM logx._bp WHERE nrbp = ")+cx_nrbp->getSel()+_("; DELETE FROM
logx.bp_ WHERE nrbp = ")+cx_nrbp->getSel()+_(";"));
int s=cbBP->GetSelection(); cx_nrbp->refreshlx(); cbBP->Select(s-1);
afis_BP(event);
}
//-------------------------------------------------------------
void iFEBP::selST(wxCommandEvent& event)
{ iFSELST* x; x=new iFSELST(this, cx_nrbp->getSel()); x->ShowModal();
afis_BP(event);
}
//-------------------------------------------------------------
void iFEBP::validBP(wxCommandEvent& event)
{ wxString achQ,ach;
if(cbBP->GetSelection()<0) wxMessageBox(_("Nimic de selectat!"));
//actualizare în BP qtyconf
for(int i=0;i<GD1->GetNumberRows();i++)
lxCtrl::cda(_("UPDATE logx._bp SET qtyconf=") + GD1->GetCellValue(i,
GD1->GetNumberCols() - 1) + _(" FROM itmp_bp WHERE itmp_bp.tcu =
(_bp.nrbp::text || '->' || _bp.id::text || '->' || _bp.dep::text || '->'
|| _bp.ampl::text) AND itmp_bp.tcu='")+GD1->GetCellValue(i,0)+_("';"));

if((ach=lxCtrl::cda(_("SELECT logx.valid_bp(")+cx_nrbp->getSel()
+");"))==_("OK")) wxMessageBox(_("Validat!")); else
wxMessageBox(ach);
cx_nrbp->refreshlx(); afis_BP(event);
}

☑ Fişierul pentru clasa iFEI


#include "fei.h" //fişierul fei.cpp
#include "fnom.h"
#define _COLDEP_ 8
#define _COLAMPL_ 9
#define _COLSTATUS_ 6
iFEI::iFEI(wxWindow* parent, wxWindowID id, const wxString& title, const
wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id,
title, pos, size, style)
{ this->SetSizeHints(wxDefaultSize, wxDefaultSize);
wxBoxSizer* s1; s1 = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* s2; s2 = new wxBoxSizer(wxHORIZONTAL);
t4=new wxStaticText(this,wxID_ANY,wxT("BR nr:"),wxDefaultPosition,wxDefaultSize,0);

234
6.Codul sursă pentru aplicaţia LogX

t4->Wrap(-1);s2->Add(t4, 0, wxALL|wxEXPAND, 5);


cbBR =new wxComboBox(this,wxID_ANY,wxT("Combo!"),wxDefaultPosition, wxDefaultSize,0,NULL,0 );
s2->Add(cbBR, 0, wxALL, 5);
eDataBR = new wxDatePickerCtrl(this, wxID_ANY, wxDefaultDateTime, wxDefaultPosition,
wxDefaultSize, wxDP_DEFAULT|wxDP_DROPDOWN);
s2->Add(eDataBR, 0, wxALL, 5);
t5 = new wxStaticText(this, wxID_ANY, wxT("Supplier"), wxDefaultPosition, wxDefaultSize, 0);
t5->Wrap(-1); s2->Add(t5, 0, wxALL, 5);
SupplierBR=new wxTextCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxDefaultSize,0 );
s2->Add(eSupplierBR, 0, wxALL, 5);
bAddBR = new wxButton(this, wxID_ANY, wxT("Adaugă BR"), wxDefaultPosition,wxSize(-1,25),0);
s2->Add(bAddBR, 0,wxALL,5);s1->Add(s2,0, wxEXPAND, 5);
pBR = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxFULL_REPAINT_ON_RESIZE|wxRAISED_BORDER|wxTAB_TRAVERSAL);
wxBoxSizer* s3; s3 = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* s4; s4 = new wxBoxSizer(wxHORIZONTAL);
bDelBR = new wxButton(pBR, wxID_ANY, wxT("ŞTERGE BR"), wxDefaultPosition, wxSize(-1,25),0);
bDelBR->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
s4->Add(bDelBR, 0, wxALL, 5); s4->Add(0, 0, 1, wxEXPAND, 5);
bSavBR = new wxButton(pBR,wxID_ANY,wxT("SALVEAZĂ BR"),wxDefaultPosition,wxSize(-1,25),0);
s4->Add(bSavBR, 0, wxALL, 5);s3->Add(s4,0,wxEXPAND,5);
GD1 = new wxGrid(pBR, wxID_ANY, wxDefaultPosition, wxSize(600,400), wxALWAYS_SHOW_SB) ;
GD1->CreateGrid(0,0);GD1->EnableEditing(true);
GD1->EnableGridLines(true);
GD1->EnableDragGridSize(false); GD1->SetMargins(0, 0);
GD1->AutoSizeColumns();GD1->EnableDragColMove(false);
GD1->EnableDragColSize(true); GD1->SetColLabelSize(30);
GD1->SetColLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
GD1->AutoSizeRows();GD1->EnableDragRowSize(true);
GD1->SetRowLabelSize(80);
GD1->SetRowLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
GD1->SetDefaultCellAlignment(wxALIGN_LEFT, wxALIGN_TOP);
s3->Add(GD1, 1, wxALL|wxEXPAND, 5);
wxBoxSizer* s5; s5 = new wxBoxSizer(wxHORIZONTAL);
bValidBR=new wxButton(pBR,wxID_ANY,wxT("VALIDEAZĂ BR"),wxDefaultPosition,wxSize(-1,25),0);
s5->Add(bValidBR, 0, wxALL, 5);
bAddLin =new wxButton(pBR, wxID_ANY,wxT("ADAUGĂ LINIE"),wxDefaultPosition,wxSize(-1,25),0);
s5->Add(bAddLin, 0, wxALL, 5);
bDelLin =new wxButton(pBR, wxID_ANY,wxT("ŞTERGE LINIE"),wxDefaultPosition, wxSize(-1,25),0);
s5->Add(bDelLin, 0, wxALL, 5); s5->Add(0, 0, 1, wxEXPAND, 5);
B_selNom = new wxButton(pBR,wxID_ANY,wxT("SELECŢIE NOMENCLATOR"), wxDefaultPosition,
wxSize(-1,25),0);
s5->Add(B_selNom, 0, wxALL, 5); s3->Add(s5, 0, wxEXPAND, 5);
pBR->SetSizer(s3);pBR->Layout();
s3->Fit(pBR);s1->Add(pBR,1,wxEXPAND,5);
this->SetSizer(s1); this->Layout();
cbBR->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(iFEI::afis_BR), NULL, this);
bAddBR->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::add_BR), NULL, this);
bDelBR->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::del_BR), NULL, this);
bSavBR->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::sav_BR), NULL, this);

235
ANEXE

GD1->Connect(wxEVT_GRID_CELL_CHANGE, wxGridEventHandler(iFEI::GD1OnGridCellChange), NULL, this);


GD1->Connect(wxEVT_GRID_EDITOR_SHOWN, wxGridEventHandler(iFEI::GD1OnGridEditorShown), NULL, this);
bValidBR->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::valid_BR), NULL, this);
bAddLin->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::add_Lin), NULL, this);
bDelLin->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::del_Lin), NULL, this);
B_selNom->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::sel_nom), NULL, this);
pBR->Hide();
cx_nrbr = new lxComboBox(cbBR,_("SELECT DISTINCT nrbr::text FROM
logx.br ORDER BY nrbr;"));
cx_supplier=new lxEditT(eSupplierBR); cx_datain=new lxDPC(eDataBR);
const char*
ach[]={"tcu","IDCode","RefCom","RefTeh","Descr","PO","Status",
"SerialNo","DepInit","Ampl.Init","Qty"};
wxArrayString a; for(int i=0;i<11;i++) a.Add(ach[i]);
cx_grid=new lxGrid(GD1,_("itmp_br"),a);
GD1->SetColMinimalAcceptableWidth(0);
//adăugare control combobox în grid pentru STATUS
wxGridCellAttr *A_col= new wxGridCellAttr();
wxString sel[] = { wxT("Funcţional"), wxT("Defect"), wxT("Rebut"),
wxT("Incert"), wxT("Casare") };
A_col->SetEditor(new wxGridCellChoiceEditor(5, sel));
GD1->SetColAttr(_COLSTATUS_, A_col);
//adaugare controale combobox pentru depozite şi amplasamente, lista
pentru amplasament va cuprinde selecţia pentru primul depozit afişat */
lxCtrl::lista(_("DROP TABLE IF EXISTS tmpacc; CREATE TEMP TABLE tmpacc AS
SELECT dep[s] FROM (select acces.*, generate_subscripts(dep,1) as s from
logx.acces where ('#'||alias||'#') = CURRENT_USER)par; SELECT DISTINCT
loc.dep FROM logx.loc,tmpacc WHERE loc.dep=tmpacc.dep ORDER BY
loc.dep;"),a);
A_col= new wxGridCellAttr();
A_col->SetEditor(new wxGridCellChoiceEditor(a));
GD1->SetColAttr(_COLDEP_ ,A_col);
/*obiectul de tip combobox pentru amplasament este pus şi aici doar
pentru agenerarea evenimentelor de afişare control în celulă */
lxCtrl::lista(_("SELECT DISTINCT ampl FROM logx.loc WHERE dep=(SELECT
DISTINCT dep FROM tmpacc ORDER BY dep LIMIT 1) ORDER BY ampl;"),a);
A_col= new wxGridCellAttr();
A_col->SetEditor(new wxGridCellChoiceEditor(a));
GD1->SetColAttr(_COLAMPL_,A_col);
}
//----------------------------------------------------------------------
----
iFEI::~iFEI() {
cbBR->Disconnect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(iFEI::afis_BR), NULL, this);
bAddBR->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::add_BR), NULL, this);
bDelBR->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::del_BR), NULL, this);
bSavBR->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::sav_BR), NULL, this);
GD1->Disconnect(wxEVT_GRID_CELL_CHANGE, wxGridEventHandler(iFEI::GD1OnGridCellChange), NULL, this);
GD1->Disconnect(wxEVT_GRID_EDITOR_SHOWN, wxGridEventHandler(iFEI::GD1OnGridEditorShown), NULL, this);
bValidBR->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::valid_BR), NULL, this);
bAddLin->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::add_Lin), NULL, this);
bDelLin->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::del_Lin), NULL, this);

236
6.Codul sursă pentru aplicaţia LogX

B_selNom->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFEI::sel_nom), NULL, this);


}
//-------------------------------------------------------------------
void iFEI::afis_BR(wxCommandEvent& event)
{ cx_supplier->rdSQL(_("SELECT supplier::text FROM logx.br WHERE nrbr=")
+cx_nrbr->getSel()+_(" LIMIT 1;"));
cx_datain->rdSQL(_("SELECT datain::date FROM logx.br WHERE nrbr=")
+cx_nrbr->getSel()+_(" LIMIT 1;"));
lxCtrl::cda(_("DROP TABLE IF EXISTS itmp_br; CREATE TEMPORARY TABLE
itmp_br AS SELECT (br.nrbr::text||'->'||br.idx::text) AS tcu,
br.idcode,refcom,refteh,descr,po,status,serialno,depinit,amplinit,qty
FROM logx.br,logx.nom WHERE nom.idcode=br.idcode AND nrbr=")+cx_nrbr-
>getSel()+_(";"));
//tcu contor unic pentru a realiza legaătura între cele două tabele, în
grid va fi în prima coloana care va fi setată pe invizibil
cx_grid->preluareVarColRow(); GD1->SetColSize(0,0); pBR->Show();
}
//---------------------------------------------------------------------
void iFEI::sav_BR(wxCommandEvent& event)
{ wxString achQ;
cx_grid->salvare(); // copiere din celule gridului în tabelul temporar
//şterge borderoul curent pentru numărul selectat şi adaugă în acesta
noua informaţie
if(!lxCtrl::cda(_("DELETE FROM logx.br USING itmp_br WHERE
itmp_br.tcu=(br.nrbr::text||'->'||br.idx); \
INSERT INTO
logx.br(nrbr,datain,supplier,idcode,po,status,serialno,depinit,
amplinit,qty) \
SELECT ")+cx_nrbr->getSel()+_(",")+cx_datain->getValFormatStr()+_(",")
+cx_supplier->getValFormatStr()+
_(" ,idcode,po,status,serialno,depinit,amplinit,qty FROM itmp_br; ")));
wxMessageBox(_("Salvat!"));
}
//---------------------------------------------------------------------
void iFEI::add_BR(wxCommandEvent& event)
{lxCtrl::cda(_("select logx.br_nou();"));
cx_nrbr->rdLista(_("SELECT DISTINCT nrbr::text FROM logx.br ORDER BY
nrbr;"));
cbBR->SetSelection(cbBR->GetCount()-1);
afis_BR(event);
}
//---------------------------------------------------------------------
void iFEI::add_Lin(wxCommandEvent& event)
{ GD1->AppendRows(); GD1->Fit();
}
//--------------------------------------------------------------------
void iFEI::del_Lin(wxCommandEvent& event)
{ wxMessageDialog *x =
new wxMessageDialog(this, wxT("Doriţi ştergerea liniei curente?"),
_(""),wxYES_NO|wxICON_QUESTION);
x->SetYesNoLabels(_("&Nu"),_("&Da"));

237
ANEXE

if(x->ShowModal() == wxID_NO) //DA=ID_NO


{// ştergere linia unde valoarea tpu = cu expresia formată în BR
lxCtrl::cda(_("DELETE FROM logx.br WHERE '") +
GD1->GetCellValue(GD1->GetSelectedRows()[0],0) +
("'=(br.nrbr::text||'->'||br.idx)"));
afis_BR(event);
}
}
//--------------------------------------------------------------------
void iFEI::del_BR(wxCommandEvent& event)
{ wxMessageDialog *x =
new wxMessageDialog(this,wxT("Doriţi ştergerea BPORDEROULUI
curent?"),_(""),wxYES_NO|wxICON_QUESTION);
x->SetYesNoLabels(_("&Nu"),_("&Da"));
if(x->ShowModal() == wxID_NO) //&Nu corespunde lui wxID_YES
lxCtrl::cda(_("DELETE FROM logx.br USING itmp_br WHERE
itmp_br.tcu=(br.nrbr::text||'->'||br.idx);"));
pBR->Hide(); cx_nrbr->refreshlx();
}
//-------------------------------------------------------------------
void iFEI::sel_nom(wxCommandEvent& event)
{ if(!GD1->IsSelection())
{wxMessageBox(wxT("Selectaţi linia!")); return;}
wxArrayString* rez=new wxArrayString;
iFNOM* x; x=new iFNOM(this,rez); x->ShowModal();
if(rez->GetCount()<1)
{wxMessageBox("Nu a fost selectat nimic >>>>>!"); return;}
for(int j=0; j<4; j++)
GD1->SetCellValue(GD1->GetSelectedRows()[0],j+1,rez->Item(j));
}
//--------------------------------------------------------------------
void iFEI::valid_BR(wxCommandEvent& event)
{ wxString achQ, achR;
sav_BR(event);
if((achR=lxCtrl::cda(_("SELECT logx.valid_br(")+cx_nrbr->getSel()
+");"))==_("OK"))
wxMessageBox(_("Validat!"));
else wxMessageBox(achR);
pBR->Hide(); cx_nrbr->refreshlx();
}
//--------------------------------------------------------------------
void iFEI::GD1OnGridEditorShown(wxGridEvent& event)
{if(event.GetCol()!=_COLAMPL_) return;
wxArrayString a;
lxCtrl::lista(_("SELECT DISTINCT ampl FROM logx.loc WHERE dep='") +
GD1->GetCellValue(event.GetRow(),_COLDEP_) + _("' ORDER BY ampl;"),a);
wxGridCellAttr *A_col= new wxGridCellAttr();
A_col->SetEditor(new wxGridCellChoiceEditor(a));
GD1->SetColAttr(_COLAMPL_,A_col);
}
//----------------------------------------------------------------------

238
6.Codul sursă pentru aplicaţia LogX

//modifică culuarea de scriere în roşu pentru celulele cu conţinut modificat


void iFEI::GD1OnGridCellChange(wxGridEvent& event)
{ GD1->SetCellTextColour(event.GetRow(),event.GetCol(),wxColour(wxT("RED")));
}

☑ Fişierul pentru clasa iFNOM


#include <wx/valgen.h>
#include "fnom.h"
iFNOM::iFNOM(wxWindow* parent, wxArrayString* rez1, wxWindowID id, const
wxString& title, const wxPoint& pos, const wxSize& size, long style) :
wxDialog(parent, id, title, pos, size, style)
{this->SetSizeHints(wxDefaultSize, wxDefaultSize);
wxBoxSizer* s1; s1 = new wxBoxSizer(wxVERTICAL);
wxString rOrdChoices[] = { wxT("RefCom"), wxT("RefTeh"),
wxT("Descriere"), wxT("idCode") };
int rOrdNChoices = sizeof(rOrdChoices) / sizeof(wxString);
rOrd = new wxRadioBox(this, wxID_ANY, wxT("Ordonare după:"), wxDefaultPosition, wxDefaultSize,
rOrdNChoices, rOrdChoices, 1, wxRA_SPECIFY_ROWS ); rOrd->SetSelection(2);
rOrd->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
rOrd->SetValidator(wxGenericValidator(&nr_ord));s1->Add(rOrd,0,wxALL,5);
GD1 = new wxGrid(this, wxID_ANY, wxDefaultPosition, wxSize(300,200), 0);
GD1->CreateGrid(0,0); GD1->EnableEditing(true);
GD1->EnableGridLines(true);
GD1->EnableDragGridSize(false); GD1->SetMargins(0, 0);
GD1->EnableDragColMove(false); GD1->EnableDragColSize(true);
GD1->SetColLabelSize(30);
GD1->SetColLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
GD1->EnableDragRowSize(true);GD1->SetRowLabelSize(80);
GD1->SetRowLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
GD1->SetDefaultCellAlignment(wxALIGN_LEFT, wxALIGN_TOP);
s1->Add(GD1, 1, wxALL|wxEXPAND, 5);
pEdit = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL) ;
wxBoxSizer* s2Nom; s2Nom = new wxBoxSizer(wxHORIZONTAL);
bAdd = new wxButton(pEdit, wxID_ANY, wxT("ADAUGĂ"), wxDefaultPosition, wxDefaultSize, 0 );
s2Nom->Add(bAdd, 0, wxALL|wxEXPAND, 5);
bDel = new wxButton(pEdit, wxID_ANY, wxT("ŞTERGE"), wxDefaultPosition, wxDefaultSize, 0) ;
s2Nom->Add(bDel, 0, wxALL|wxEXPAND, 5);
bSav = new wxButton(pEdit, wxID_ANY, wxT("SALVEAZĂ"), wxDefaultPosition, wxDefaultSize, 0) ;
s2Nom->Add(bSav, 0, wxALL|wxEXPAND, 5);
bRenEdit=new wxButton(pEdit, wxID_ANY, wxT("RENUNŢĂ"), wxDefaultPosition, wxDefaultSize, 0);
s2Nom->Add(bRenEdit, 0, wxALL|wxEXPAND, 5);
pEdit->SetSizer(s2Nom); pEdit->Layout(); s2Nom->Fit(pEdit);
s1->Add(pEdit, 0, wxALIGN_BOTTOM|wxEXPAND, 5);
wxBoxSizer* s2; s2 = new wxBoxSizer(wxHORIZONTAL);
bEdit = new wxButton(this, wxID_ANY, wxT("EDITARE"), wxDefaultPosition, wxDefaultSize, 0);
s2->Add(bEdit, 0, wxALL, 5);
bSel = new wxButton(this, wxID_ANY, wxT("SELECTEAZĂ"), wxDefaultPosition, wxDefaultSize, 0) ;
bSel->SetDefault();
bSel->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString));
s2->Add(bSel, 0, wxALL, 5);

239
ANEXE

bRenNom = new wxButton(this, wxID_ANY, wxT("RENUNŢĂ"), wxDefaultPosition, wxDefaultSize, 0) ;


s2->Add(bRenNom, 0, wxALL, 5); s1->Add(s2, 0, wxEXPAND, 5);
this->SetSizer(s1);this->Layout();
s1->Fit(this);this->Centre(wxBOTH);
// Evenumente de conectare
rOrd->Connect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(iFNOM::ordoneaza), NULL, this);
bAdd->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::add_Lin), NULL, this);
bDel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::del_Lin), NULL, this);
bSav->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::salveaza), NULL, this);
bRenEdit->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::ren_Edit), NULL, this);
bEdit->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::editare), NULL, this);
bSel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::selecteaza), NULL, this);
bRenNom->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::ren_Nom), NULL, this);
const char* ach[]={"ID Code","RefCom","RefTeh","Descriere"};
const char* ord1[]={"refcom","refteh","descr","idcode"};
wxArrayString a; nr_ord=2;
for(int i=0;i<4;i++) {a.Add(ach[i]); ord.Add(ord1[i]);}
if(rez1) {rez=rez1; rez->Clear();} else bSel->Hide();
cx_grid=new lxGrid(GD1,_("itmp_nom"),a);
pEdit->Hide(); GD1->EnableEditing(false);
GD1->SetSelectionMode(wxGrid::wxGridSelectRows);
afis_NOM();
}
//----------------------------------------------------------------------
----
iFNOM::~iFNOM() {
rOrd->Disconnect(wxEVT_COMMAND_RADIOBOX_SELECTED,wxCommandEventHandler(iFNOM::ordoneaza),NULL,this);
bAdd->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::add_Lin), NULL, this);
bDel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::del_Lin), NULL, this);
bSav->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::salveaza), NULL, this);
bRenEdit->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED,wxCommandEventHandler(iFNOM::ren_Edit),NULL,this);
bEdit->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::editare), NULL, this);
bSel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFNOM::selecteaza), NULL, this);
bRenNom->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED,wxCommandEventHandler(iFNOM::ren_Nom),NULL,this);
}
//------------------------------------------------------------------
void iFNOM::afis_NOM()
{ nr_ord=rOrd->GetSelection();
lxCtrl::cda(_("DROP TABLE IF EXISTS itmp_nom; CREATE TEMPORARY TABLE
itmp_nom AS SELECT idcode,refcom,refteh,descr FROM logx.nom ORDER BY ") +
ord.Item(nr_ord)+_(";"));
cx_grid->preluareVarColRow();
}
//-------------------------------------------------------------------
void iFNOM::salveaza(wxCommandEvent& event)
{ lxCtrl::cda(_("DROP TABLE IF EXISTS itmp_nom; CREATE TEMPORARY TABLE
itmp_nom AS SELECT idcode,refcom,refteh,descr FROM logx.nom;"));
wxString achQ;
cx_grid->salvare();
if(!lxCtrl::cda(_("UPDATE logx.nom SET
refcom=itmp_nom.refcom,refteh=itmp_nom.refteh,descr=itmp_nom.descr \
FROM itmp_nom WHERE itmp_nom.idcode=nom.idcode; \

240
6.Codul sursă pentru aplicaţia LogX

INSERT INTO logx.nom SELECT par.* FROM (SELECT


idcode,refcom,refteh,descr FROM itmp_nom EXCEPT SELECT
idcode,refcom,refteh,descr FROM logx.nom)par;")))
wxMessageBox(_("Salvat!"));
}
//--------------------------------------------------------------------
void iFNOM::editare(wxCommandEvent& event)
{ pEdit->Show();GD1->EnableEditing(true); this->Fit();
GD1->SetSelectionMode(wxGrid::wxGridSelectCells);
}
//------------------------------------------------------------------
void iFNOM::ren_Edit(wxCommandEvent& event)
{ pEdit->Hide();GD1->EnableEditing(false);
GD1->SetSelectionMode(wxGrid::wxGridSelectRows);
}
//------------------------------------------------------------------
void iFNOM::selecteaza(wxCommandEvent& event)
{ if(!GD1->IsSelection()) {wxMessageBox(wxT("Selectaţi linia!"));
return;} //dacă există selecţie în cadrul gridului
for(int i=0;i<4;i++)
rez->Add(GD1->GetCellValue(GD1->GetSelectedRows()[0],i));
this->Close();
}
//--------------------------------------------------------------------
void iFNOM::ren_Nom(wxCommandEvent& event)
{ this->Close();
}
//-------------------------------------------------------------------
void iFNOM::add_Lin(wxCommandEvent& event)
{ GD1->AppendRows(); GD1->Fit();
}
//------------------------------------------------------------------
void iFNOM::del_Lin(wxCommandEvent& event)
{ wxMessageDialog *x =
new wxMessageDialog(this,wxT("Doriţi ştergerea liniei curente?"),
_(""),wxYES_NO|wxICON_QUESTION); x->SetYesNoLabels(_("&Nu"),_("&Da"));
if(x->ShowModal() == wxID_NO)
{GD1->DeleteRows(GD1->GetGridCursorRow(),1);GD1->Fit(); }
}
//--------------------------------------------------------------
void iFNOM::ordoneaza(wxCommandEvent& event)
{ afis_NOM();
}

☑ Fişierul pentru clasa iFSELST


#include <wx/clipbrd.h> //fişierul selst.cpp
#include "selst.h"
#include "fnom.h"
iFSELST::iFSELST(wxWindow* parent, wxString achBP1, wxWindowID id, const
wxString& title, const wxPoint& pos, const wxSize& size, long style) :

241
ANEXE

wxDialog(parent, id, title, pos, size, style)


{this->SetBackgroundColour(wxColour(228, 243, 235));//setare culoare
fundal
wxBoxSizer* s1; s1 = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* s2; s2 = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer* s3; s3 = new wxBoxSizer(wxVERTICAL);
t8 = new
wxStaticText(this,wxID_ANY,wxT("AFIŞARE"),wxDefaultPosition,wxDefaultSize,wxALIGN_CENTRE );
t8->Wrap(-1); s3->Add(t8, 0, wxALIGN_CENTER|wxALL, 5);
wxString cblAChoices[] = { wxT("ID Code"), wxT("Ref Com"), wxT("Ref
Teh."), wxT("Descriere"), wxT("Furnizor"), wxT("Data IN"),
wxT("SerialNo"), wxT("Status"), wxT("Comandă (PO)"), wxT("Depozit"),
wxT("Amplasament"), wxT("ID") };
int cblANChoices = sizeof(cblAChoices) / sizeof(wxString);
cblA=new wxCheckListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, cblANChoices,
cblAChoices,0);
cblA->SetBackgroundColour(wxColour(252, 253, 223));
s3->Add(cblA, 0, wxALL, 5); s2->Add(s3, 0, wxEXPAND, 5);
wxBoxSizer* s4; s4 = new wxBoxSizer(wxVERTICAL);
t9 = new wxStaticText(this, wxID_ANY, wxT("SELECŢIE"), wxDefaultPosition, wxDefaultSize,0);
t9->Wrap(-1); s4->Add(t9, 0, wxALIGN_CENTER|wxALL, 5);
wxString cblSChoices[] = { wxT("ID Code"), wxT("Ref Com"), wxT("Ref
Teh."), wxT("Descriere"), wxT("Furnizor"), wxT("Data IN"),
wxT("SerialNo"), wxT("Staus"), wxT("Comandă (PO)"), wxT("Depozit"),
wxT("Amplasament"), wxT("ID") };
int cblSNChoices = sizeof(cblSChoices) / sizeof(wxString);
cblS=new wxCheckListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, cblSNChoices,
cblSChoices,0);
cblS->SetBackgroundColour(wxColour(231, 238, 254));
s4->Add(cblS, 1, wxALL|wxEXPAND, 5); s2->Add(s4, 1, wxEXPAND, 5);
wxBoxSizer* s5; s5 = new wxBoxSizer(wxVERTICAL);
CBP = new wxChoicebook(this,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxCHB_DEFAULT);
CBP->SetBackgroundColour(wxColour(217, 234, 215));
p1 = new wxPanel(CBP, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
p1->SetBackgroundColour(wxColour(231, 245, 226));
wxBoxSizer* s6; s6 = new wxBoxSizer(wxVERTICAL);
bNom =new wxButton(p1, wxID_ANY, wxT("NOMENCLATOR"), wxDefaultPosition, wxDefaultSize, 0);
s6->Add(bNom, 0,wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL,5);
p1->SetSizer(s6); p1->Layout(); s6->Fit(p1);
CBP->AddPage(p1, wxT("ID Code/ RefCom/ RefTeh/ Descriere"), false);
p2 = new wxPanel(CBP, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* s7; s7 = new wxBoxSizer(wxHORIZONTAL);
t1 = new wxStaticText(p2, wxID_ANY, wxT("="), wxDefaultPosition, wxDefaultSize, 0);
t1->Wrap(-1); s7->Add(t1, 0, wxALL, 5);
eSupplier=new wxTextCtrl(p2, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
s7->Add(eSupplier, 1, wxALL, 5);
bRetFurnizor=new wxButton(p2, wxID_ANY, wxT("Reţine"),wxDefaultPosition,wxSize(60,-1),0);
s7->Add(bRetFurnizor, 0, wxALL, 5);
p2->SetSizer(s7); p2->Layout(); s7->Fit(p2);
CBP->AddPage(p2, wxT("Furnizor"), false);

242
6.Codul sursă pentru aplicaţia LogX

p3 = new wxPanel(CBP, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );


p3->SetBackgroundColour(wxColour(217, 244, 225));
wxBoxSizer* s8; s8 = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* s9; s9 = new wxBoxSizer(wxHORIZONTAL);
eData1= new wxDatePickerCtrl(p3, wxID_ANY, wxDefaultDateTime, wxDefaultPosition,
wxDefaultSize, 0);
s9->Add(eData1, 0, wxALL, 5);
t2 = new wxStaticText(p3, wxID_ANY, wxT(">->"), wxDefaultPosition, wxDefaultSize, 0);
t2->Wrap(-1); s9->Add(t2, 0, wxALL, 5);
eData2=new wxDatePickerCtrl(p3, wxID_ANY, wxDefaultDateTime, wxDefaultPosition,
wxDefaultSize,0);
s9->Add(eData2, 0, wxALL, 5); s8->Add(s9, 0, wxEXPAND, 5);
bRetData = new wxButton(p3, wxID_ANY, wxT("Reţine"), wxDefaultPosition, wxSize(60,-1), 0);
s8->Add(bRetData, 0, wxALIGN_CENTER|wxALL, 5);
p3->SetSizer(s8); p3->Layout(); s8->Fit(p3);
CBP->AddPage(p3, wxT("Data IN"), false);
p4 = new wxPanel(CBP, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxFlexGridSizer* s10; s10 = new wxFlexGridSizer(0, 2, 0, 0);
s10->SetFlexibleDirection(wxBOTH);
s10->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
t3 = new wxStaticText(p4, wxID_ANY, wxT("Depozit"), wxDefaultPosition, wxDefaultSize, 0);
t3->Wrap(-1); s10->Add(t3, 0, wxALL, 5);
cbDep=new wxComboBox(p4, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,0, NULL,0 );
s10->Add(cbDep, 0, wxALL, 5);
t4=new wxStaticText(p4, wxID_ANY, wxT("Amplasament"), wxDefaultPosition, wxDefaultSize, 0);
t4->Wrap(-1); s10->Add(t4, 0, wxALL, 5);
cbAmpl=new wxComboBox(p4, wxID_ANY, wxEmptyString,wxDefaultPosition,wxDefaultSize,0,NULL,0) ;
s10->Add(cbAmpl,0,wxALL,5); s10->Add(0, 0, 1, wxEXPAND, 5);
bRetDepAmp=new wxButton(p4, wxID_ANY, wxT("Reţine"), wxDefaultPosition, wxDefaultSize, 0);
s10->Add(bRetDepAmp,0,wxALL,5); p4->SetSizer(s10);
p4->Layout(); s10->Fit(p4);
CBP->AddPage(p4, wxT("Depozit/ Amplasament"), false);
p5 = new wxPanel(CBP, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* s11; s11 = new wxBoxSizer(wxHORIZONTAL);
t5 = new wxStaticText(p5, wxID_ANY, wxT("="), wxDefaultPosition, wxDefaultSize, 0);
t5->Wrap(-1); s11->Add(t5, 0, wxALL, 5);
ePO = new wxTextCtrl(p5, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
s11->Add(ePO, 1, wxALL, 5);
bRetPO=new wxButton(p5, wxID_ANY, wxT("Reţine"), wxDefaultPosition, wxSize(60,-1), 0);
s11->Add(bRetPO,0,wxALL,5); p5->SetSizer(s11);
p5->Layout(); s11->Fit(p5);
CBP->AddPage(p5, wxT("Comandă (PO)"), false);
p6 = new wxPanel(CBP, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* s12; s12 = new wxBoxSizer(wxHORIZONTAL);
t6 = new wxStaticText(p6, wxID_ANY, wxT("="), wxDefaultPosition, wxDefaultSize,0);
t6->Wrap(-1); s12->Add(t6, 0, wxALL, 5);
wxString cbStatusChoices[] = { wxT("Funcţional"), wxT("Defect"),
wxT("Rebut"), wxT("Incert"), wxT("Casare") };
int cbStatusNChoices = sizeof(cbStatusChoices) / sizeof(wxString);
cbStatus=new

243
ANEXE

wxChoice(p6,wxID_ANY,wxDefaultPosition,wxDefaultSize,cbStatusNChoices,cbStatusChoices, 0);
cbStatus->SetSelection(2); s12->Add(cbStatus, 0, wxALL, 5);
bRetStatus=new wxButton(p6, wxID_ANY, wxT("Reţine"), wxDefaultPosition, wxSize(60,-1), 0);
s12->Add(bRetStatus,0,wxALL,5);p6->SetSizer(s12);
p6->Layout();s12->Fit(p6);
CBP->AddPage(p6, wxT("STATUS"), true);
p7 = new wxPanel(CBP, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* s13; s13 = new wxBoxSizer(wxHORIZONTAL);
t7 = new wxStaticText(p7, wxID_ANY, wxT("="), wxDefaultPosition, wxDefaultSize, 0);
t7->Wrap(-1); s13->Add(t7, 0, wxALL, 5);
eSerialNo=new wxTextCtrl(p7, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
s13->Add(eSerialNo, 1, wxALL, 5);
bRetSerialNo=new wxButton(p7, wxID_ANY, wxT("Reţine"), wxDefaultPosition, wxSize(60,-1),0);
s13->Add(bRetSerialNo, 0, wxALL, 5);
p7->SetSizer(s13); p7->Layout(); s13->Fit(p7);
CBP->AddPage(p7, wxT("SerialNo"), false);
p10 = new wxPanel(CBP, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* s16; s16 = new wxBoxSizer(wxHORIZONTAL);
t10 = new wxStaticText(p10, wxID_ANY, wxT("="), wxDefaultPosition, wxDefaultSize, 0);
t10->Wrap(-1); s16->Add(t10, 0, wxALL, 5);
eID = new wxTextCtrl(p10, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
s16->Add(eID, 1, wxALL, 5);
bRetID = new wxButton(p10, wxID_ANY, wxT("Reţine"), wxDefaultPosition, wxSize(60,-1), 0);
s16->Add(bRetID, 0, wxALL, 5);
p10->SetSizer(s16); p10->Layout();s16->Fit(p10);
CBP->AddPage(p10, wxT("ID"), false);
s5->Add(CBP, 1, wxEXPAND | wxALL, 5);
wxBoxSizer* s35; s35 = new wxBoxSizer(wxHORIZONTAL);
t13=new wxStaticText(this, wxID_ANY, wxT("Sortare:"), wxDefaultPosition,wxDefaultSize, 0 );
t13->Wrap(-1); s35->Add(t13, 0, wxALL, 5);
wxString cbOrdChoices[] = { wxT("ID Code"), wxT("Ref Com"), wxT("Ref
Teh."), wxT("Descriere"), wxT("Furnizor"), wxT("Data IN"),
wxT("SerialNo"), wxT("Depozit"), wxT("Status"), wxT("Amplasament"),
wxT("Comandă (PO)"), wxT("ID"), wxT("Neordonat") };
int cbOrdNChoices = sizeof(cbOrdChoices) / sizeof(wxString);
cbOrd = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, cbOrdNChoices,
cbOrdChoices, 0);
cbOrd->SetSelection(12); s35->Add(cbOrd, 1, wxALL, 5);
bCopiaza = new wxButton(this,wxID_ANY,wxT("Copiază"),wxDefaultPosition, wxSize(-1,-
1),wxBU_EXACTFIT);
s35->Add(bCopiaza, 0, wxALL, 5); s5->Add(s35, 0, wxEXPAND, 5);
bQ =new wxButton(this, wxID_ANY,wxT("INTEROGARE STOC"), wxDefaultPosition,wxDefaultSize, 0);
bQ->SetFont(wxFont(13, 70, 90, 92, false, wxEmptyString));
bQ->SetForegroundColour(wxColour(109, 143, 71));
s5->Add(bQ, 0, wxALL|wxEXPAND, 5); s2->Add(s5, 0, wxEXPAND, 5);
s1->Add(s2, 0, wxEXPAND, 5);
GD1 = new wxGrid(this, wxID_ANY, wxDefaultPosition, wxSize(-1,150),
wxALWAYS_SHOW_SB|wxFULL_REPAINT_ON_RESIZE);
GD1->CreateGrid(0,0); GD1->EnableEditing(true);

244
6.Codul sursă pentru aplicaţia LogX

GD1->EnableGridLines(true);
GD1->EnableDragGridSize(false); GD1->SetMargins(0, 0);
GD1->AutoSizeColumns(); GD1->EnableDragColMove(false);
GD1->EnableDragColSize(true); GD1->SetColLabelSize(20);
GD1->SetColLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
GD1->AutoSizeRows(); GD1->EnableDragRowSize(true);
GD1->SetRowLabelSize(80);
GD1->SetRowLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
GD1->SetDefaultCellBackgroundColour(wxColour(226, 249, 250));
GD1->SetDefaultCellAlignment(wxALIGN_LEFT, wxALIGN_TOP);
GD1->SetBackgroundColour(wxColour(236, 250, 251));
s1->Add(GD1, 1, wxALL|wxEXPAND, 5);
pSelBP = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL) ;
wxBoxSizer* s17; s17 = new wxBoxSizer(wxVERTICAL);
P_QtyPrep = new wxPanel(pSelBP,wxID_ANY, wxPoint(-1,-1), wxSize(-1,40), wxTAB_TRAVERSAL) ;
P_QtyPrep->SetBackgroundColour(wxColour(247, 244, 232));
P_QtyPrep->SetMinSize(wxSize(-1,40));
wxBoxSizer* s14; s14 = new wxBoxSizer(wxHORIZONTAL);
s14->Add(0, 0, 1, wxALL|wxEXPAND, 5);
t12 = new wxStaticText(P_QtyPrep,wxID_ANY,wxT("Qty Preparată:"),wxDefaultPosition,
wxDefaultSize,0); t12->Wrap(-1); s14->Add(t12, 0, wxALL, 5);
eQtyPrep = new wxTextCtrl(P_QtyPrep, wxID_ANY, wxT("1"), wxDefaultPosition, wxSize(30,-1), 0);
s14->Add(eQtyPrep, 0, wxALL, 5);
bRezerva = new wxButton(P_QtyPrep,wxID_ANY,wxT("Rezervă !"),wxDefaultPosition,
wxSize(-1,-1),0);
s14->Add(bRezerva, 0, wxALL, 5); s14->Add(0,0,1,wxALL|wxEXPAND,5);
P_QtyPrep->SetSizer(s14);P_QtyPrep->Layout();
s17->Add(P_QtyPrep,0,wxALL|wxEXPAND,5);
GD2 = new wxGrid(pSelBP,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxALWAYS_SHOW_SB|
wxFULL_REPAINT_ON_RESIZE);
GD2->CreateGrid(0,0); GD2->EnableEditing(true);
GD2->EnableGridLines(true);
GD2->EnableDragGridSize(false);GD2->SetMargins(0, 0);
GD2->AutoSizeColumns(); GD2->EnableDragColMove(false);
GD2->EnableDragColSize(true); GD2->SetColLabelSize(20);
GD2->SetColLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
GD2->AutoSizeRows(); GD2->EnableDragRowSize(true);
GD2->SetRowLabelSize(80);
GD2->SetRowLabelAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
GD2->SetDefaultCellBackgroundColour(wxColour(247, 224, 223));
GD2->SetDefaultCellAlignment(wxALIGN_LEFT, wxALIGN_TOP);
GD2->SetMinSize(wxSize(-1,200)); s17->Add(GD2,1,wxALL|wxEXPAND,5);
p9 = new wxPanel(pSelBP,wxID_ANY,wxPoint(-1,-1),wxSize(-1,40),wxTAB_TRAVERSAL);
p9->SetBackgroundColour(wxColour(243, 245, 220));
wxBoxSizer* s15; s15 = new wxBoxSizer(wxHORIZONTAL);
bDelLin=new wxButton(p9, wxID_ANY, wxT("Şterge linia"), wxDefaultPosition, wxDefaultSize,0);
s15->Add(bDelLin, 0, wxALL, 5); s15->Add(0, 0, 1, wxEXPAND, 5);
bBP = new wxButton(p9,wxID_ANY,wxT("OK"),wxDefaultPosition,wxDefaultSize,0);
bBP->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(),70,90,92,false,
wxEmptyString));

245
ANEXE

bBP->SetForegroundColour(wxColour(128, 49, 72));


bBP->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
s15->Add(bBP, 0, wxALL, 5); p9->SetSizer(s15); p9->Layout();
s17->Add(p9, 0, wxALL|wxEXPAND, 5); pSelBP->SetSizer(s17);
pSelBP->Layout(); s17->Fit(pSelBP); s1->Add(pSelBP,1,wxEXPAND|wxALL,5);
this->SetSizer(s1);this->Layout();this->Centre(wxBOTH);
bNom->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::selNom), NULL, this);
bRetFurnizor->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineFurnizor), NULL,
this);
bRetData->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineData), NULL, this);
cbDep->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(iFSELST::selDep), NULL, this);
bRetDepAmp->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineDepAmp), NULL,
this);
bRetPO->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retinePO), NULL, this);
bRetStatus->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineSTATUS),NULL,this);
bRetSerialNo->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineSerialNo), NULL,
this);
bRetID->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineID), NULL, this);
bCopiaza->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::copiaza), NULL, this);
bQ->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::qST), NULL, this);
bRezerva->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::rezervaQtySelST), NULL,
this);
bDelLin->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::delLinSelST), NULL, this);
bBP->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::transferBP), NULL, this);
const char* achN[] = {"inx.idcode", "refcom", "refteh", "descr",
"supplier", "datain", "serialno","status","po",
"stx.dep","stx.ampl","stx.id"};
const char* achT[] = {"C","C","C","A","C","D2","C","C","C","C","C","C"};
wxArrayString a; achBP=achBP1;
for(unsigned int i=0;i<cblS->GetCount();i++)
{listaN.Add(achN[i]); listaT.Add(achT[i]);}
aListaInit=cblS->GetStrings();
for(unsigned int j=0;j<cblS->GetCount();j++)
{ val1.Add("NULL"); val2.Add("NULL");}
cx_grid1=new lxGrid(GD1,_("q_tmp_st"),a);
if(achBP != _("#")) cx_grid2=new lxGrid(GD2,_("tmpselst"),a);
else pSelBP->Hide();
cx_dep = new lxComboBox(cbDep, _("DROP TABLE IF EXISTS tmpacc; CREATE
TEMP TABLE tmpacc AS SELECT dep[s] FROM (select acces.*,
generate_subscripts(dep,1) as s from logx.acces)par; SELECT DISTINCT
loc.dep FROM logx.loc,tmpacc WHERE loc.dep=tmpacc.dep ORDER BY
loc.dep;"));
//lista ptr.amplasament va cuprinde selecţia pentru primul depozit
cx_ampl = new lxComboBox(cbAmpl,_("SELECT DISTINCT ampl FROM logx.loc
WHERE dep = (SELECT DISTINCT dep FROM logx.loc ORDER BY dep LIMIT 1)
ORDER BY ampl;"));
}
//-------------------------------------------------------------------
iFSELST::~iFSELST() {
bNom->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::selNom), NULL, this);
bRetFurnizor->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineFurnizor), NULL,
this);

246
6.Codul sursă pentru aplicaţia LogX

bRetData->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineData), NULL, this);


cbDep->Disconnect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(iFSELST::selDep), NULL, this);
bRetDepAmp->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineDepAmp),
NULL, this);
bRetPO->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retinePO), NULL, this);
bRetStatus->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineSTATUS), NULL,
this);
bRetSerialNo->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineSerialNo),
NULL, this);
bRetID->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::retineID), NULL, this);
bCopiaza->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::copiaza), NULL, this);
bQ->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::qST), NULL, this);
bRezerva->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::rezervaQtySelST), NULL,
this);
bDelLin->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::delLinSelST), NULL, this);
bBP->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFSELST::transferBP), NULL, this);
}
//-----------------------------------------------------------------
//FUNCŢIA DE INTEROGARE A STOCULUI
void iFSELST::qST(wxCommandEvent& event)
{wxString achListaCampuri, achTipCampuri, achAfisare, achSelectie,
achVal1, achVal2, achOrd, achR, ach;
wxArrayInt b;
//selecţie obligatorie ptr. Dep,Ampl,ID
if(achBP!=_("#"))
for(int j=0;j<3;j++) cblA->Check(cblA->GetCount()-1-j);
cblA->GetCheckedItems(b);
for(unsigned int j=0;j<12;j++)
{if(!achListaCampuri.IsEmpty()) achListaCampuri=achListaCampuri+_(",");
achListaCampuri=achListaCampuri+_("'")+listaN[j]+_("'");
if(!achTipCampuri.IsEmpty()) achTipCampuri = achTipCampuri+_(",");
achTipCampuri=achTipCampuri+_("'")+listaT[j]+_("'");
if(!achAfisare.IsEmpty()) achAfisare=achAfisare+_(",");
if(cblA->IsChecked(j)) achAfisare=achAfisare+_("'t'");
else achAfisare=achAfisare+_("'f'");
if(!achSelectie.IsEmpty()) achSelectie=achSelectie+_(",");
if(cblS->IsChecked(j)) achSelectie=achSelectie+_("'t'");
else achSelectie=achSelectie+_("'f'");
if(j) achVal1=achVal1+_(","); achVal1=achVal1+val1[j];
if(j) achVal2=achVal2+_(","); achVal2=achVal2+val2[j];
if(cbOrd->GetSelection()<12) achOrd=listaN[cbOrd->GetSelection()];
else achOrd=_("---");
}//endfor
if(achBP==_("#")) ach = _("ST"); else ach = _("STL");
//dacă interogarea este pentru rezervare (STL -stoc cu qtzdisp > 0
if((achR = lxCtrl::cda(_("SELECT logx.q_x('")+ach+_("'::text,
12::smallint, ARRAY[") + achListaCampuri+_("]::text[], ARRAY[") +
achTipCampuri + _("]::text[], ARRAY[") + achAfisare + _("]::boolean[],
ARRAY[") + achSelectie +_("]::boolean[], ARRAY[") + achVal1+_("]::text[],
ARRAY[") + achVal2+_("]::text[], '")+achOrd+_("'::text);")))!=_("OK"))
wxMessageBox(achR);
GD1->SetRowLabelSize(20);

247
ANEXE

cx_grid1->preluareVarColRow();
if(achBP != _("#")) //dacă este selecţie pentru BP
lxCtrl::cda(_("DROP TABLE IF EXISTS tmpselst;CREATE TEMPORARY TABLE
tmpselst as SELECT _bp.nrbp, _bp.qtyrez, inx.idcode, nom.descr, _bp.id,
_bp.dep, _bp.ampl FROM logx.inx, logx.nom, logx._bp WHERE
inx.idcode=nom.idcode AND _bp.id= inx.id AND _bp.nrbp=")+achBP+_(" ;"));
}
//------------------------------------------------------------------
// AFIŞARE PANOU SELECŢIE --FILTRE
void iFSELST::afis_sel()//afişare panou selecţie - filtrare
{wxString ach;
wxArrayString aLista;
aLista=aListaInit; cblS->Clear();
for(unsigned int j=0;j<aLista.GetCount();j++)
{ach = aLista[j];
if(val1[j]!=_("NULL")) //diferit de ŞIRUL ce conţine caracterele N U
L L!
{if(val2[j]!=_("NULL") && (listaT[j]==_("N2") ||
listaT[j]==_("D2")))
ach = val1[j] + wxT(" ≤ ") + aLista[j] + wxT(" ≤ ") +val2[j];
if(val2[j]==_("NULL") && (listaT[j]==_("N1") ||
listaT[j]==_("D1")))
ach= aLista[j] + _(" = ") + val1[j];
if(val2[j]==_("NULL") && listaT[j]==_("C"))
ach= aLista[j] + wxT(" ≡ ") + val1[j];
if(val2[j]==_("NULL") && listaT[j]==_("A"))
ach= val1[j] +wxT(" є ") + aLista[j] ;
if(val2[j]==_("NULL") && listaT[j]==_("B"))
ach= aLista[j] + _(" : ") + val1[j];
} //endif
cblS->Append(ach);
} //endfor
}
//------------------------------------------------------------------
//Selecţie NOMENCLATOR
void iFSELST::selNom(wxCommandEvent& event)
{wxArrayString* rez=new wxArrayString;
iFNOM* x; x=new iFNOM(this,rez); x->ShowModal();
if(rez->GetCount()<1)
{wxMessageBox("Nu a fost selectat nimic !"); return;}
for(int j=0; j<4; j++)
val1[j]=_("'")+rez->Item(j)+_("'");
afis_sel();}
//Şterge linia selectată
void iFSELST::delLinSelST(wxCommandEvent& event)
{ wxArrayString achS;
//dacă există selecţie în cadrul gridului
if(!GD2->IsSelection())
{wxMessageBox(wxT("Selectaţi linia!")); return;}
for(int j=0; j<GD2->GetNumberCols(); j++)
achS.Add(GD2->GetCellValue(GD2->GetSelectedRows()[0],j));

248
6.Codul sursă pentru aplicaţia LogX

wxString ach = lxCtrl::cda(_("SELECT logx.del_bp_id(")+ achBP+


_("::int,'")+achS[achS.GetCount()-1]+_("'::text,'")+achS[achS.GetCount()-
3]+_("'::text,'")+achS[achS.GetCount()-2]+_("'::text,")+eQtyPrep-
>GetValue()+_("::int);"));
if(ach != _("OK")) { wxMessageBox(ach); return;}
if(achBP != _("#")) //dacă este selecţie pentru BP
lxCtrl::cda(_("DROP TABLE IF EXISTS tmpselst; CREATE TEMPORARY TABLE
tmpselst as SELECT _bp.nrbp, _bp.qtyrez, inx.idcode, nom.descr, _bp.id,
_bp.dep, _bp.ampl FROM logx.inx, logx.nom, logx._bp WHERE
inx.idcode=nom.idcode AND _bp.id= inx.id AND _bp.nrbp=")+achBP+_(" ;"));
cx_grid2->preluareVarColRow(); GD2->Refresh();
}
//--------------------------------------------------------------------
//Rezervă cantitate din stoc
void iFSELST::rezervaQtySelST(wxCommandEvent& event)
{wxArrayString achS;
if(!GD1->IsSelection())
{wxMessageBox(wxT("Selectaţi linia!")); return;} //dacă există
selecţie în cadrul gridului
for(int j=0; j<GD1->GetNumberCols(); j++)
achS.Add(GD1->GetCellValue(GD1->GetSelectedRows()[0],j));
if(eQtyPrep->IsEmpty())
{wxMessageBox(wxT("Completaţi cantitatea!")); return;}
wxString ach = lxCtrl::cda(_("SELECT logx.add_bp_id(")+ achBP+
_("::int,'")+achS[achS.GetCount()-1]+_("'::text,'")+achS[achS.GetCount()-
3]+_("'::text,'")+achS[achS.GetCount()-2]+_("'::text,")+eQtyPrep-
>GetValue()+_("::int);"));
if(ach != _("OK"))
{ wxMessageBox(ach); return;}
if(achBP != _("#")) //dacă este selecţie pentru BP
lxCtrl::cda(_("DROP TABLE IF EXISTS tmpselst; CREATE TEMPORARY TABLE
tmpselst as SELECT _bp.nrbp, _bp.qtyrez, inx.idcode, nom.descr, _bp.id,
_bp.dep, _bp.ampl FROM logx.inx, logx.nom, logx._bp WHERE inx.idcode =
nom.idcode AND _bp.id = inx.id AND _bp.nrbp =")+achBP+_(" ;"));
GD1->DeleteRows(GD1->GetSelectedRows()[0]);
cx_grid2->preluareVarColRow(); GD1->Refresh(); GD2->Refresh();
}
//-------------------------------------------------------------------
//Închide fereastra de selecţie din stoc
void iFSELST::transferBP(wxCommandEvent& event)
{ lxCtrl::cda(_("DELETE FROM tmpselst USING logx._bp WHERE
_bp.nrbp=tmpselst.nrbp AND _bp.id=tmpselst.id AND _bp.dep=tmpselst.dep
AND _bp.ampl=tmpselst.ampl;"));
this->Close();
}
//---------------------------------------------------------------
//Schimbă lista cu amplasamentele în funcţie de depozitul selectat
void iFSELST::selDep(wxCommandEvent& event)
{cx_ampl->rdLista(_("SELECT DISTINCT ampl::text FROM logx.loc WHERE
dep='")+cx_dep->getSel()+("' ORDER BY ampl;"));
}

249
ANEXE

//------------------------------------------------------------------
//selectează depozitul şi amplasamentul curent
void iFSELST::retineDepAmp(wxCommandEvent& event)
{ for(unsigned int j=0;j<listaN.GetCount();j++)
{ if(listaN[j]==_("stx.dep")) val1[j]=_("'")+cx_dep->getSel()+_("'");
if(listaN[j]==_("stx.ampl")) val1[j]=_("'")+cx_ampl->getSel()
+_("'");
}
afis_sel();
}
//--------------------------------------------------------------------
//selectează PO
void iFSELST::retinePO(wxCommandEvent& event)
{ for(unsigned int j=0;j<listaN.GetCount();j++)
{ if(listaN[j]==_("po") && !ePO->GetValue().IsEmpty())
val1[j]=_("'")+ePO->GetValue()+_("'");
}
afis_sel();
}
//----------------------------------------------------------------
//selectează furnizor
void iFSELST::retineFurnizor(wxCommandEvent& event)
{ for(unsigned int j=0;j<listaN.GetCount();j++)
{ if(listaN[j]==_("supplier") && !eSupplier->GetValue().IsEmpty())
val1[j]=_("'")+eSupplier->GetValue()+_("'");
}
afis_sel();
}
//------------------------------------------------------------------
//selectează STATUS
void iFSELST::retineSTATUS(wxCommandEvent& event)
{ for(unsigned int j=0;j<listaN.GetCount();j++)
{ if(listaN[j]==_("status") && !cbStatus-
>GetStringSelection().IsEmpty())
val1[j]=_("'")+cbStatus->GetStringSelection()+_("'");
}
afis_sel();
}
//-----------------------------------------------------------------
//selectează SerialNo
void iFSELST::retineSerialNo(wxCommandEvent& event)
{ for(unsigned int j=0;j<listaN.GetCount();j++)
{ if(listaN[j]==_("serialno") && !eSerialNo->GetValue().IsEmpty())
val1[j]=_("'")+eSerialNo->GetValue()+_("'");
}
afis_sel();
}
//-------------------------------------------------------------------
//selectează ID
void iFSELST::retineID(wxCommandEvent& event)
{ for(unsigned int j=0;j<listaN.GetCount();j++)

250
6.Codul sursă pentru aplicaţia LogX

{ if(listaN[j]==_("stx.id") && !eID->GetValue().IsEmpty())


val1[j]=_("'")+eID->GetValue()+_("'");
}
afis_sel();
}
//-------------------------------------------------------------------
//selectează filtru data calendaristică de intrare
void iFSELST::retineData(wxCommandEvent& event)
{ for(unsigned int j=0;j<listaN.GetCount();j++)
{if(listaN[j]==_("datain"))
val1[j]=_("'")+eData1->GetValue().FormatISODate()+_("'");
if(listaN[j]==_("datain"))
val2[j]=_("'")+eData2->GetValue().FormatISODate()+_("'");
}
afis_sel();
}
//-------------------------------------------------------------------
//copiază datele din GRID ÎN CLIPBOARD în vederea exportului
void iFSELST::copiaza(wxCommandEvent& event)
{ wxString ach,dx;
for(int j=0;j<GD1->GetNumberCols();j++)
{ if(j) dx=dx+_(",");
dx = dx + GD1->GetColLabelValue(j);
}
for(int i=0;i<GD1->GetNumberRows();i++)
{ dx=dx+_("\n");
for(int j=0;j<GD1->GetNumberCols();j++)
{ if(j) dx=dx+_(",");
dx=dx+GD1->GetCellValue(i,j);
}
}
if (wxTheClipboard->Open())
{wxTheClipboard->SetData(new wxTextDataObject(dx));
wxTheClipboard->Close();
wxMessageBox(_("Au fost copiate "+ach.FromDouble(GD1->GetNumberRows()))
+ _(" linii !"));
}
/*funcţia se poate extinde prin testarea existenţei virgulei în şir şi
punerea între ghilimele a acestor şiruri etc*/
}

☑ Fişierul pentru clasele iFPB şi LogXApp (main.cpp)


#include "main.h"
#include "fei.h"
#include "fnom.h"
#include "selst.h"
#include "febp.h"
#include "facc.h"
#include "lx.h"
PGconn *lxCtrl::conexiune0;

251
ANEXE

IMPLEMENT_APP(LogXApp);
bool iFACC::accesX=false;
//-------------------------------------------------------------------
bool LogXApp::OnInit()
{ lxCtrl::conexiune0=NULL;
/*conexiunea iniţială pentru verificarea parolei de acces - se
conectează cu parola la nivel de aplicaţie*/
lxCtrl::conectare(_("picdb"),_("logx_sw"),_("lilo"),_("localhost"),_("543
5"));
/* Prin "return true" se asigură menţinerea în aplicaţie
în buclele de citire a evenimentelor şi de tratare a acestora*/
(new iFACC(NULL))->ShowModal();
if(iFACC::accesX)
{(new iFPB(NULL))->Show(); return true;}
//este lansată fereastra cu panoul cu butoane, nu este fereastră modală,
else return false;
}
//-------------------------------------------------------------------
iFPB::iFPB(wxWindow* parent, wxWindowID id, const wxString& title, const
wxPoint& pos, const wxSize& size, long style): wxFrame(parent, id, title,
pos, size, style)
{ this->SetSizeHints(wxDefaultSize, wxDefaultSize);
this->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(),70,90,90,false, wxEmptyString));
this->SetBackgroundColour(wxColour(244, 244, 244));
wxBoxSizer* s1; s1 = new wxBoxSizer(wxVERTICAL);
t1 = new wxStaticText(this, wxID_ANY, wxT("LogX"), wxDefaultPosition, wxDefaultSize,
wxALIGN_CENTRE);
t1->Wrap(-1); t1->SetFont(wxFont(18,72,90,92,false,wxEmptyString));
t1->SetHelpText(wxT("Exemplu pentru o aplicaţie simplă de gestiune a stocurilor."));
s1->Add(t1, 0, wxALIGN_CENTER|wxALL, 5);
wxBoxSizer* s2; s2 = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer* s3; s3 = new wxBoxSizer(wxVERTICAL);
t2 = new wxStaticText(this,wxID_ANY,wxT("EDITARE"), wxDefaultPosition, wxDefaultSize,
wxALIGN_CENTRE);
t2->Wrap(-1); t2->SetFont(wxFont(12,75,90,92,false,wxEmptyString));
s3->Add(t2, 0, wxALIGN_CENTER|wxALL, 5);
be1 = new wxButton(this, wxID_ANY, wxT("INTRĂRI"), wxDefaultPosition, wxDefaultSize, 0);
be1->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(),70,90,90,false, wxEmptyString));
s3->Add(be1, 0, wxALL|wxEXPAND, 5);
be2 = new wxButton(this, wxID_ANY, wxT("LIVRĂRI"), wxDefaultPosition, wxDefaultSize, 0);
be2->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(),70,90,90,false, wxEmptyString));
s3->Add(be2, 0, wxALL|wxEXPAND, 5);
be3 = new wxButton(this, wxID_ANY, wxT("TRANSFER"), wxDefaultPosition, wxDefaultSize, 0);
be3->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(),70,90,90,false, wxEmptyString));
be3->Enable(false); s3->Add(be3, 0, wxALL|wxEXPAND, 5);
be4 = new wxButton(this,wxID_ANY,wxT("NOMENCLATOR"),wxDefaultPosition, wxDefaultSize, 0);
s3->Add(be4, 0, wxALL|wxEXPAND, 5); s2->Add(s3, 1, wxEXPAND, 5);
wxBoxSizer* s4; s4 = new wxBoxSizer(wxVERTICAL);
t3 = new wxStaticText(this,wxID_ANY,wxT("RAPOARTE"),wxDefaultPosition, wxDefaultSize,

252
6.Codul sursă pentru aplicaţia LogX

wxALIGN_CENTRE);
t3->Wrap(-1); t3->SetFont(wxFont(12,75,90,92,false,wxEmptyString));
s4->Add(t3, 0, wxALIGN_CENTER|wxALL, 5);
br1 = new wxButton(this, wxID_ANY, wxT("INTRĂRI"), wxDefaultPosition, wxDefaultSize, 0);
br1->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString));
br1->Enable(false); s4->Add(br1, 0, wxALL|wxEXPAND, 5);
br2=new wxButton(this, wxID_ANY, wxT("LIVRĂRI"), wxDefaultPosition, wxDefaultSize, 0);
br2->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString));
br2->Enable(false); s4->Add(br2, 0, wxALL|wxEXPAND, 5);
br3=new wxButton(this, wxID_ANY, wxT("TRANSFER"), wxDefaultPosition, wxDefaultSize, 0);
br3->Enable(false); s4->Add(br3, 0, wxALL|wxEXPAND, 5);
br4 = new wxButton(this, wxID_ANY, wxT("STOC"), wxDefaultPosition, wxDefaultSize, 0);
s4->Add(br4, 0, wxALL|wxEXPAND, 5); s2->Add(s4, 1, wxEXPAND, 5);
s1->Add(s2, 1, wxEXPAND, 5); this->SetSizer(s1);
this->Layout();
// Evenimente pentru conectare
be1->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFPB::apasa_be1), NULL, this);
be2->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFPB::apasa_be2), NULL, this);
be4->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFPB::apasa_be4), NULL, this);
br4->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFPB::apasa_br4), NULL, this);
}
//-----------------------------------------------------------------
iFPB::~iFPB()
{ lxCtrl::deconectare();
be1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFPB::apasa_be1), NULL, this);
be2->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFPB::apasa_be2), NULL, this);
be4->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFPB::apasa_be4), NULL, this);
br4->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(iFPB::apasa_br4), NULL, this);
}
//------------------------------------------------------------------
void iFPB::apasa_be1(wxCommandEvent& event)
{ (new iFEI(this))->ShowModal();
}
//------------------------------------------------------------------
void iFPB::apasa_be4(wxCommandEvent& event)
{ (new iFNOM(this,NULL))->ShowModal();
}
//-----------------------------------------------------------------
void iFPB::apasa_br4(wxCommandEvent& event)
{ (new iFSELST(this,_("#")))->ShowModal();
}
//--------------------------------------------------------------------
void iFPB::apasa_be2(wxCommandEvent& event)
{(new iFEBP(this))->ShowModal();
}

253
Bibliografie

Bibliografie

[1] Kacsó, A., Kacsó, D, Turbo C, Tehnici de programare, Editura


MicroInformatica, Cluj, 1992.

[2] Somnea, D., Turturea, D., Introducere în C++. Programarea


orientată pe obiecte, Editura Tehnica, Bucureşti, 1993.

[3] www.postgresql.org.

[4] http://en.cppreference.com/w/

[5] http://www.delorie.com/djgpp/doc/libc/

[6] http://codelite.org/

[7] http://libharu.org/

[8] http://sourceforge.net/projects/wxformbuilder/

254

S-ar putea să vă placă și