Skip to content

SQLite ​

Scrii cod SQL simplu SQLite în unul sau mai multe fișiere .sql. Platforma rulează o instanță SQLite în browser prin intermediul build-ului oficial @sqlite.org/sqlite-wasm — fiecare fișier .sql care nu este de testare se execută automat la salvare pe aceeași bază de date în memorie (in-memory), în ordine lexicografică.

Proiectul inițial (seed) vine cu un fișier script.sql care creează un tabel greetings, inserează rândul (1, 'Hello, World!') și îl selectează înapoi — extinde-l adăugând mai multe elemente de schemă, date de test (seed) și interogări.

Fișiere de intrare ​

Fișierele soluției se află în rădăcina proiectului:

  • script.sql — fișier de soluție editabil. Proiectul de pornire conține un ghid TODO pentru crearea tabelului greetings și selectarea mesajului.
  • Poți redenumi script.sql în orice dorești și poți crea fișiere .sql suplimentare (le poți organiza în submape dacă vrei). Instrumentul de rulare automată (auto-runner) descoperă fiecare fișier .sql care nu este de test din arborele de fișiere.

Fișierele rulează în ordine lexicografică după calea completă. Pune un prefix numelor de fișiere (01-schema.sql, 02-data.sql, 03-queries.sql) atunci când ordinea de execuție contează.

Database playground ​

Panoul din dreapta este o filă live Database care comunică cu aceeași instanță SQLite în memorie:

  • Rezultatul rulării automate (Auto-run result) — fiecare SELECT (sau orice altă instrucțiune care returnează rânduri) din fișierele tale .sql se randează aici, grupat după fișierul sursă, la fiecare salvare.
  • Tabele (Tables) — bara laterală a schemei populată din sqlite_master care listează fiecare tabel, plus coloanele și tipurile sale (prin PRAGMA table_info). Fă clic pe Preview pe un tabel pentru a rula instantaneu SELECT * FROM <table> LIMIT 20 în fila de interogări ad-hoc.
  • Interogare ad-hoc (Ad-hoc query) — editor SQL bazat pe Monaco pentru interogări ocazionale pe baza de date live. Ctrl/⌘ + Enter rulează interogarea fără a modifica fișierele sursă.

Baza de date este în memorie: reîmprospătarea previzualizării o șterge. Pentru a curăța fără reîncărcare, rulează DROP TABLE … în orice fișier sursă sau în fila de interogări ad-hoc.

Versiune ​

Rulează sqlite-wasm v3.46.1-build3 (SQLite WASM) cu Vitest v3.2.4 pe Node v22.

Limbaje suportate ​

SQL (dialectul SQLite)

Cadru de testare ​

Vitest cu utilitarul @dojocode/sql-test-helpers.

Memento-uri speciale și detalii de implementare ​

  • Dialect SQLite standard. Folosește INTEGER PRIMARY KEY AUTOINCREMENT (NU SERIAL din PostgreSQL), INSERT OR IGNORE / INSERT OR REPLACE pentru operațiuni de tip upsert.
  • Introspecția schemei folosește sqlite_master (SELECT name FROM sqlite_master WHERE type = 'table') și PRAGMA table_info(<table>) pentru metadatele coloanelor.
  • Fișierele care se potrivesc cu modelul *.test.sql sunt ignorate de rularea automată — fiind rezervate pentru un viitor rulător de teste SQL brute.
  • Testele sunt scrise în TypeScript (*.test.ts) folosind utilitarul partajat createSqliteTestDb(import.meta.url). Acesta pornește o instanță nouă de sqlite-wasm (cu avertismentul de inițializare OPFS dezactivat) și încarcă automat fiecare fișier .sql care nu este de test împreună cu testul, înainte de rularea aserțiunilor.
  • query() și exec() sunt sincrone aici — spre deosebire de utilitarul PGlite, nu este necesar await pentru apelurile individuale (utilitarul în sine este asincron doar pentru că inițializarea modulului sqlite-wasm de la bază returnează un Promise).

Exemplu cu Vitest: ​

typescript
import { describe, it, expect, beforeAll } from 'vitest';
import { createSqliteTestDb, type SqliteTestDb } from '@dojocode/sql-test-helpers/sqlite';

let db: SqliteTestDb;

beforeAll(async () => {
  db = await createSqliteTestDb(import.meta.url);
});

describe('greetings', () => {
  it("contains the row with message 'Hello, World!'", () => {
    const rows = db.query<{ message: string }>(
      'SELECT message FROM greetings WHERE id = 1'
    );
    expect(rows[0]?.message).toBe('Hello, World!');
  });

  it('has exactly one row', () => {
    const rows = db.query<{ n: number }>('SELECT COUNT(*) AS n FROM greetings');
    expect(rows[0].n).toBe(1);
  });
});

Utilitarul returnează { query<T>(sql): T[], exec(sql): void, raw: sqlite3.oo1.DB }. Atât query, cât și exec sunt sincrone — returnează rezultatele sau modifică baza de date imediat. Folosește raw doar pentru cazuri avansate (instrucțiuni pregătite cu parametri asociați, tranzacții, familia wasm.poke*).

Biblioteci incluse ​

Cum se depanează ​

Trei moduri de a verifica ce face codul tău SQL:

1. Fila de rezultate Auto-run (Auto-run result) ​

Fiecare rezultat SELECT este randat acolo cu eticheta fișierului său sursă. Dacă o interogare nu returnează nimic, secțiunea este omisă — acesta fiind în sine un semnal util că un rând nu a fost inserat sau că un filtru WHERE este prea restrictiv.

2. Fila Tabele (Tables) ​

Confirmă ce tabele există și tipurile coloanelor lor în acest moment. Dacă întâmpini erori de tip "no such table", înseamnă că tabelul nu a fost creat — de obicei din cauza unei erori de sintaxă într-o instrucțiune CREATE anterioară.

3. Fila de interogări ad-hoc (Ad-hoc query) ​

Rulează instrucțiuni SELECT punctuale pe baza de date live, fără a modifica fișierele sursă:

sql
SELECT name FROM sqlite_master WHERE type = 'table';
PRAGMA table_info(greetings);
SELECT * FROM greetings WHERE id = 1;

Greșeli frecvente ​

  • Utilizarea sintaxei PostgreSQL — SERIAL nu există în SQLite; folosește INTEGER PRIMARY KEY AUTOINCREMENT. ON CONFLICT (col) DO NOTHING funcționează de asemenea, dar INSERT OR IGNORE este mai idiomatic în SQLite.
  • Omiterea INSERT OR IGNORE — rularea repetată a scriptului va eșua cu erori de încălcare a constrângerilor de unicitate dacă o rulare anterioară a inserat deja aceleași rânduri.
  • Ordinea de execuție între directoare — fișierele sunt sortate după calea lor completă, deci a/02-init.sql rulează ÎNAINTE de b/01-init.sql. Pune prefixe și directoarelor atunci când ordinea contează între directoare diferite.