Un mic experiment de SQL făcut de la zero, în C și Rust.
Am construit o bază de date pentru o facultate (studenți, materii, înscrieri) și un interpretor care înțelege comenzi,
de exemplu SELECT, UPDATE și DELETE.
Pe deasupra, am adăugat și criptare cu un algoritm simplificat de tip CBC (Cipher Block Chaining), ca să protejez datele.
Practic, am vrut să văd cât de greu e să faci un "mini–Postgres" în C și Rust.
Răspunsul meu: greu, dar extrem de fun.
| Lang. | Directory |
|---|---|
| C | C-method/ |
| Rust | rust-method/ |
Trei tabele principale:
- studenti: informații despre fiecare student (nume, an, medie, statut)
- materii: denumire + profesor titular
- inrolari: legăturile dintre studenți și materii, cu notele aferente
Datele sunt ținute în structuri C și gestionate dinamic (cu malloc, realloc, free).
-
Citirea bazei de date
- Pornesc de la un fișier text (o combinație de TOML + CSV).
- Îl parsez linie cu linie și încarc structurile în memorie.
- Folosesc un flag pentru a ști din ce tabel fac parte liniile citite.
-
Interpretor SQL
- Comenzile se citesc de la tastatură (
stdin). - Sunt implementate:
SELECT: afișare, cu suport pentruWHEREUPDATE: modificare condiționatăDELETE: ștergere (inclusiv relațiile aferente din alte tabele)
Am scris un parser simplu care sparge query-urile și le aplică pe datele din memorie. Aici am simțit cel mai mult ce înseamnă să scrii un interpretor "de mână".
- Comenzile se citesc de la tastatură (
-
Criptare
- Vectorul de studenți poate fi criptat.
- Am folosit un CBC simplificat:
XOR, permutări (P-Box) și unIV. - Totul se face byte cu byte: cu
unsigned charîn C, respectivu8în Rust.
Pentru a proteja datele, vectorul de studenți este criptat cu o variantă simplificată de CBC (Cipher Block Chaining).
Pașii algoritmului CBC:
- Transform vectorul de studenți într-o succesiune de octeți
- Împart vectorul în 4 blocuri de lungimi egale (ultimul bloc completat cu padding de
0x00dacă e nevoie) - Criptez primul bloc:
- 3.1.
XORîntreblock[0]șiiv(initialization vector) - 3.2. S-Box (
XOR) cu cheia de criptare - 3.3. P-Box (permutare) pe
block[0]
- 3.1.
- Criptez celelalte blocuri:
- 4.1.
XORîntreblock[i]șiblock[i-1] - 4.2. S-Box (
XOR) cu cheia - 4.3. P-Box (permutare)
- 4.1.
- Blocurile rezultate sunt scrise byte cu byte în fișierul de ieșire
Idei generale:
- Cum să parsezi text și să construiești un interpretor minimal
- Cât de complicat (și interesant) e să implementezi corect ceva ce pare banal în SQL
Implementarea în C:
- Gestionarea memoriei dinamice în C și evitarea memory leak-urilor
- De ce merită să folosești funcții sigure (
snprintf,strncpy) și să eviți clasiculstrcpy
Implementarea în Rust:
- Cum să structurez un proiect Rust modular, pe foldere și fișiere
- Propagarea erorilor într-un mod elegant folosind pattern matching, operatorul
?și.unwrap() - Scrierea de teste unitare table-based, reutilizând aceeași funcție generică alături de framework-ul rstest
- Folosirea funcțiilor generice pentru a elimina cod duplicat prin polimorfism
Am plecat de la niște structuri C/Rust și am ajuns cu un mini–SQL engine care:
- știe să ruleze interogări,
- respectă relații între tabele,
- și poate chiar să cripteze datele.
Un fel de bază de date în miniatură, dar care m-a făcut să înțeleg mai bine ce se află în spatele unui SGBD real.