Da dove partire
PrismDB è un motore di database relazionale embedded: vive dentro un file
(app.prismdb) e si usa con il dialetto SQL di SQLite 3.50. Le query, i
tipi, i messaggi di errore e i PRAGMA sono quelli di SQLite; il formato del
file, il motore di esecuzione e la gestione della concorrenza sono di PrismDB.
Tutto quello che serve si scarica dalla sezione Download. Scegli da qui cosa ti serve:
| Voglio... | Scarico | Leggo |
|---|---|---|
| provare l'SQL senza installare niente | niente: c'è il playground | — |
| usare un database da riga di comando o da script | Motore (Windows o Linux) | La shell prism |
| usarlo da un'app .NET (C#, F#, ASP.NET, EF Core) | Pacchetti NuGet + Gateway | .NET |
| usarlo da Node.js (Next, Nuxt, Astro, Express...) | Pacchetto Node | Node.js |
| usarlo da Java/Kotlin o da DBeaver | Driver JDBC + Gateway | JDBC e DBeaver |
| usarlo da C/C++ o da un linguaggio con FFI | Motore | API C |
| guardare e modificare i dati dall'IDE | l'estensione per VS Code, JetBrains o Visual Studio | Estensioni |
| mettere un database in rete | Gateway | Il gateway |
Piattaforme: Windows 10/11 x64 e Linux x86-64 (requisiti di glibc nelle singole sezioni). Non ci sono build per macOS o per ARM.
Prima di cominciare leggi I concetti: cinque minuti che evitano quasi tutti i problemi.
I concetti
Il file e i suoi compagni
Un database è un file, per convenzione con estensione .prismdb. Accanto,
mentre è in uso, compaiono altri file con lo stesso nome e un suffisso:
| File | Cos'è |
|---|---|
app.prismdb |
il database |
app.prismdb-wal |
il log delle transazioni non ancora riportate nel file. Sparisce alla chiusura pulita. Non cancellarlo mai: contiene dati confermati |
app.prismdb-lock |
il lock del processo che possiede il file. Può restare dopo la chiusura: è innocuo |
app.prismdb-endpoint |
indirizzo e token con cui gli altri processi raggiungono il proprietario (vedi sotto) |
Copiare un database
Non copiare app.prismdb mentre è aperto: la copia potrebbe non
contenere le ultime transazioni (stanno nel -wal). Per una copia
sicura usa VACUUM INTO da qualsiasi client, oppure chiudi
tutti i programmi che lo usano e copia il file.
Il formato è di PrismDB, non di SQLite: PrismDB non apre un file
.sqlite/.db di SQLite (risponde invalid prismdb header) e SQLite non
apre un .prismdb. Per portare un database da SQLite a PrismDB c'è una
procedura in tre comandi: Migrare da SQLite.
Un proprietario, tanti processi
Il primo processo che apre un file ne diventa il proprietario. Gli altri
processi che usano il motore (la shell prism, la libreria C, un altro
gateway) si collegano automaticamente al proprietario
attraverso l'-endpoint, in locale: leggono e scrivono gli stessi dati, con
le stesse regole di transazione. Se il proprietario termina, il primo
processo che fa una richiesta prende il suo posto. Non devi fare niente: è
trasparente.
Il gateway
Java, .NET, Node.js e gli IDE non caricano il motore dentro il loro processo:
gli parlano attraverso il gateway (prismdb-gateway), un piccolo programma
che apre il file e risponde su una porta TCP. Ci sono due modi di usarlo:
- incorporato: il client (il pacchetto Node, il provider .NET, le
estensioni degli IDE) lancia da solo il gateway per un file, su una porta
libera di
127.0.0.1con un token casuale, e lo chiude quando l'app termina. È il modo più semplice e quello consigliato per un'app che usa un suo file; - separato: avvii tu il gateway (
prismdb-gateway --db app.prismdb) e i client si collegano ahost:porta. Serve per DBeaver, per più applicazioni sullo stesso database o per l'accesso da un'altra macchina.
Dettagli e opzioni: Il gateway.
Transazioni e conflitti
In PrismDB più transazioni di scrittura lavorano insieme: ognuna vede una
fotografia coerente del database (snapshot). Se due transazioni toccano la
stessa riga (o una ha letto una riga che l'altra ha cambiato), la prima che
fa COMMIT vince e la seconda riceve un errore di conflitto: SQLITE_BUSY
(codice 5) nei client, 40001 come SQLState in JDBC, busy: row conflict, retry
dalla libreria C.
Il dato non è mai rovinato: basta rifare la transazione. Le istruzioni in
autocommit (senza BEGIN) riprovano da sole; per le transazioni esplicite i
client hanno un'opzione di riprova (Node: retries; vedi le singole sezioni).
Cifratura
Un database può essere cifrato con AES-256-GCM: ogni pagina, il WAL e i backup. La chiave è di 32 byte, scritta come 64 caratteri esadecimali. Per generarne una:
# Linux
openssl rand -hex 32
# Windows PowerShell
$b = New-Object byte[] 32; [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b); ($b | ForEach-Object { $_.ToString('x2') }) -join ''
Un file creato con una chiave si apre solo con quella chiave (con una chiave
sbagliata: page authentication failed; senza chiave: database is
encrypted). Se perdi la chiave, perdi i dati: non esiste un modo di
recuperarli. I file cifrati si aprono attraverso il gateway (--key-env) e
quindi da .NET, Node, Java e dagli IDE; la shell prism e la libreria C
aprono solo file non cifrati.
Il dialetto SQL
PrismDB esegue l'SQL di SQLite 3.50: JOIN di ogni tipo, sottoquery, CTE e
WITH RECURSIVE, window function, UPSERT, RETURNING, row value, trigger
(anche INSTEAD OF), viste, indici parziali e su espressioni, colonne
generate, tabelle STRICT e WITHOUT ROWID, chiavi esterne, JSON e JSONB,
FTS5, FTS3/FTS4, R-tree, generate_series, date e ora, printf, ATTACH,
PRAGMA, ANALYZE, EXPLAIN QUERY PLAN, VACUUM INTO. La documentazione
di riferimento della sintassi è quella di SQLite:
sqlite.org/lang.html.
Le differenze da conoscere:
PRAGMA foreign_keysè attivo di default (in SQLite è spento): le chiavi esterne sono controllate da subito. Per disattivarle in una connessione:PRAGMA foreign_keys = OFF;.- Procedure (estensione di PrismDB, SQLite non le ha): vedi Procedure.
- Non esistono:
load_extension, la tabellasqlite_sequence, l'EXPLAINin bytecode (c'è soloEXPLAIN QUERY PLAN). Nelle righe diEXPLAIN QUERY PLANla colonnaidè numerata da PrismDB (in SQLite è un indirizzo della sua macchina virtuale);parentedetailcoincidono. - Un valore (testo o blob) può arrivare a 10^9 byte, come in SQLite; attraverso il gateway una richiesta (SQL più parametri) arriva al massimo a 16 MiB.
Procedure
Le procedure raccolgono più istruzioni sotto un nome, con parametri tipizzati:
CREATE PROCEDURE registra(nome TEXT, importo INTEGER) BEGIN
INSERT INTO movimenti (chi, quanto, quando) VALUES (@nome, @importo, datetime('now'));
UPDATE conti SET saldo = saldo + @importo WHERE titolare = @nome;
END;
CALL registra('Ada', 100);
- Dentro il corpo i parametri si scrivono con
@(@nome). CALLesegue il corpo come un'unica istruzione: tutto o niente.- Se l'ultima istruzione del corpo è una
SELECT,CALLrestituisce le sue righe. - I parametri non accettano
NULL. CREATE PROCEDURE IF NOT EXISTS,DROP PROCEDURE [IF EXISTS] nomeeSHOW PROCEDURES(nome, parametri e testo) completano il quadro.- Procedure, tabelle, viste e trigger condividono lo stesso spazio di nomi.
Il motore e la shell prism
Lo zip del Motore contiene la shell prism (l'equivalente di sqlite3)
e la libreria C.
Installazione
Windows (prismdb-engine-2.0.0-windows-x64.zip):
- Estrai lo zip in una cartella, per esempio
C:\PrismDB. -
Facoltativo, per usare
prismda qualsiasi cartella: aggiungi la cartella alPATHdell'utente (poi apri un nuovo terminale):$p = [Environment]::GetEnvironmentVariable('Path', 'User') [Environment]::SetEnvironmentVariable('Path', "$p;C:\PrismDB", 'User') -
Prova:
prism -versionstampaPrismDB version 0.2.1(la versione del motore; i pacchetti sono 2.0.0).
Linux (prismdb-engine-2.0.0-linux-x64.zip). La shell richiede glibc
2.39 o successiva (Ubuntu 24.04+, Debian 13+, Fedora 40+):
mkdir -p ~/prismdb && cd ~/prismdb
unzip ~/Scaricati/prismdb-engine-2.0.0-linux-x64.zip
chmod +x prism # se il programma di estrazione non ha conservato i permessi
sudo install -m 755 prism /usr/local/bin/prism
prism -version
Uso
prism app.prismdb # apre (o crea) il file e aspetta comandi
prism # senza file: database in memoria
prism app.prismdb "SELECT count(*) FROM clienti" # esegue ed esce
prism app.prismdb < script.sql # esegue uno script
prism -header -column app.prismdb "SELECT * FROM clienti"
Nella shell interattiva ogni istruzione SQL termina con ;. I comandi che
iniziano con il punto sono della shell:
| Comando | Cosa fa |
|---|---|
.tables |
elenca tabelle e viste |
.schema [nome] |
mostra i CREATE |
.mode MODO |
formato dell'output: list, column, box, table, markdown, csv, json, line, html, insert, quote, tabs, ascii |
.headers on\|off |
intestazioni delle colonne |
.import --csv FILE TABELLA |
importa un CSV (se la tabella non esiste, la prima riga dà i nomi delle colonne) |
.once FILE / .output FILE |
manda il risultato della prossima istruzione / di tutte in un file |
.dump [oggetti] |
l'intero database (o solo alcuni oggetti) come script SQL |
.read FILE |
esegue uno script |
.parameter set :nome valore |
imposta un parametro per le query (:nome, @nome, $nome, ?1) |
.eqp on |
mostra il piano di esecuzione di ogni query |
.timer on |
tempo di ogni istruzione |
.bail on |
si ferma al primo errore |
.databases |
database collegati (main, temp, ATTACH) |
.open FILE |
chiude il database e ne apre un altro |
.help |
tutti i comandi |
.quit |
esce |
Esempi frequenti:
# esportare una tabella in CSV
prism app.prismdb ".headers on" ".mode csv" ".once clienti.csv" "SELECT * FROM clienti"
# importare un CSV in una tabella nuova
prism app.prismdb ".import --csv clienti.csv clienti"
# backup come script SQL e ripristino in un file nuovo
prism app.prismdb ".once backup.sql" ".dump"
prism ripristino.prismdb ".read backup.sql"
Opzioni della riga di comando: -header/-noheader, -column, -box,
-table, -markdown, -csv, -json, -line, -list, -html, -quote,
-tabs, -ascii, -separator SEP, -newline SEP, -nullvalue TESTO,
-cmd COMANDO (eseguito prima del resto), -init FILE, -bail, -echo,
-batch, -interactive, -version, -help. All'avvio legge ~/.prismrc
se esiste. In modalità batch un errore dà il messaggio Parse error near line
N o Runtime error near line N e il codice di uscita 1.
La shell funziona anche su un file aperto da un gateway o da un'altra app: si collega al proprietario (vedi Un proprietario, tanti processi).
Il gateway
prismdb-gateway apre un file .prismdb e lo serve su TCP ai client: JDBC,
.NET, Node.js, gli IDE. Il pacchetto Node e le estensioni degli IDE lo
contengono già; per .NET e Java si scarica a parte (Gateway nella sezione
Download): prismdb-gateway.exe per Windows, prismdb-gateway per Linux
(richiede glibc 2.34 o successiva: Ubuntu 22.04+, Debian 12+, RHEL 9+; dopo
il download: chmod +x prismdb-gateway).
Avvio
prismdb-gateway --db C:\dati\app.prismdb
Stampa PrismDB 2.0.0 in ascolto su 127.0.0.1:15432 e resta acceso finché
non premi Ctrl+C (chiusura pulita: il WAL viene riportato nel file). Se
il file non esiste viene creato. Un gateway serve un solo file: per due
database avvia due gateway su due porte.
| Opzione | Significato |
|---|---|
--db FILE |
il database (obbligatorio) |
--bind IP:PORTA |
dove ascoltare. Default 127.0.0.1:15432: solo da questo computer. 0.0.0.0:15432 = da tutta la rete (richiede un token) |
--create |
crea il file se manca (lo fa comunque: l'opzione rende esplicita l'intenzione) |
--token-env VAR |
il token di accesso, letto dalla variabile d'ambiente VAR |
--token-file FILE |
il token di accesso, letto da un file |
--key-env VAR |
la chiave di cifratura (64 caratteri esadecimali) dalla variabile VAR |
--key CHIAVE |
la chiave sulla riga di comando (sconsigliato: resta visibile agli altri processi) |
--parent-stdin |
si chiude pulito quando il suo stdin si chiude, cioè quando termina il programma che l'ha lanciato |
--insecure-no-auth |
permette di ascoltare in rete senza token (sconsigliato) |
Token di accesso
Senza token, chiunque raggiunga la porta può usare il database: per questo
il gateway ascolta solo su 127.0.0.1 e si rifiuta di partire su un
indirizzo di rete senza token. Il token deve avere almeno 16 caratteri e non
va mai sulla riga di comando:
# Linux
export PRISMDB_TOKEN="$(openssl rand -hex 24)"
prismdb-gateway --db /srv/dati/app.prismdb --bind 0.0.0.0:15432 --token-env PRISMDB_TOKEN
# Windows PowerShell
$env:PRISMDB_TOKEN = "scegli-un-token-lungo-e-casuale"
prismdb-gateway.exe --db C:\dati\app.prismdb --token-env PRISMDB_TOKEN
I client passano il token così: JDBC proprietà token (o il campo password
di DBeaver), .NET Token= nella connection string, Node
prismdb://TOKEN@host:porta.
Il traffico non è cifrato
Il gateway parla TCP in chiaro, token compreso. Su una rete che non
controlli completamente non esporre la porta: usa un tunnel SSH. Sul
computer client: ssh -N -L 15432:127.0.0.1:15432 utente@server, poi
collegati a 127.0.0.1:15432 come se il gateway fosse locale. In
alternativa metti davanti un proxy TLS: il pacchetto Node e l'estensione
VS Code lo raggiungono con prismdbs://host:porta.
Database cifrati
export PRISMDB_KEY="$(openssl rand -hex 32)" # conservala in un posto sicuro!
prismdb-gateway --db segreto.prismdb --key-env PRISMDB_KEY
La prima apertura con una chiave crea il file cifrato; le successive devono usare la stessa chiave.
Come servizio su Linux (systemd)
# /etc/systemd/system/prismdb.service
[Unit]
Description=PrismDB gateway
After=network.target
[Service]
User=prismdb
ExecStart=/usr/local/bin/prismdb-gateway --db /srv/prismdb/app.prismdb --bind 0.0.0.0:15432 --token-file /etc/prismdb/token
Restart=on-failure
KillSignal=SIGINT
[Install]
WantedBy=multi-user.target
sudo useradd --system --home /srv/prismdb prismdb
sudo mkdir -p /srv/prismdb /etc/prismdb && sudo chown prismdb /srv/prismdb
openssl rand -hex 24 | sudo tee /etc/prismdb/token > /dev/null
sudo chown prismdb /etc/prismdb/token && sudo chmod 600 /etc/prismdb/token
sudo systemctl daemon-reload && sudo systemctl enable --now prismdb
journalctl -u prismdb -f # il log
Su Windows, per avviarlo all'accesso usa l'Utilità di pianificazione:
Crea attività → scheda Attivazione: All'accesso → scheda Azioni:
programma C:\PrismDB\prismdb-gateway.exe, argomenti
--db C:\dati\app.prismdb.
Il protocollo
Per scrivere un client in un altro linguaggio: ogni messaggio è una
lunghezza u32 big-endian seguita da JSON UTF-8. Se il gateway ha un token,
il primo messaggio deve essere {"op":"auth","token":"..."}.
{"op":"hello"} → {"op":"welcome","engine":"PrismDB","version":"2.0.0",...}
{"op":"sql","sql":"SELECT ?","params":[{"t":"int","v":1}]}
→ {"op":"rows","columns":["?"],"rows":[[{"t":"int","v":1}]],...}
{"op":"sql","sql":"INSERT ..."} → {"op":"done","rows_affected":1,"last_rowid":7}
{"op":"begin"} → {"op":"begun","session":2}
{"op":"sql","sql":"...","session":2} (dentro la transazione)
{"op":"commit","session":2} / {"op":"rollback","session":2}
qualsiasi errore → {"op":"err","why":"no such table: x"}
Celle: {"t":"null"}, {"t":"int","v":1}, {"t":"real","v":1.5},
{"t":"inf","neg":false}, {"t":"text","v":"ciao"},
{"t":"textb","v":"<base64>"} (testo non UTF-8), {"t":"blob","v":"<base64>"}.
.NET
Tre pacchetti NuGet (nello zip Pacchetti NuGet):
| Pacchetto | Per | Framework |
|---|---|---|
PrismDB.Ado |
ADO.NET (DbConnection, DbCommand...), Dapper |
.NET Standard 2.0 (anche .NET Framework 4.7.2+), .NET 8+ |
PrismDB.EntityFrameworkCore |
Entity Framework Core, PrismDb.Scaffold() |
.NET 8 (EF Core 8) e .NET 10 (EF Core 10) |
PrismDB.Scaffolding |
il generatore di codice (lo installa da solo il precedente) | .NET Standard 2.0 |
Installazione
I pacchetti non sono su nuget.org: si usano da una cartella.
- Estrai
prismdb-dotnet-2.0.0-nuget.zipin una cartella, per esempioC:\PrismDB\nuget. -
Nella cartella della soluzione crea (o completa)
nuget.config:<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <add key="PrismDB" value="C:\PrismDB\nuget" /> </packageSources> </configuration>(in alternativa, per tutti i progetti dell'utente:
dotnet nuget add source C:\PrismDB\nuget --name PrismDB). 3. Aggiungi il pacchetto:dotnet add package PrismDB.EntityFrameworkCore --version 2.0.0 # EF Core (include PrismDB.Ado) dotnet add package PrismDB.Ado --version 2.0.0 # solo ADO.NET -
Il gateway: copia
prismdb-gateway.exe(Windows) oprismdb-gateway(Linux) nella cartella del progetto e fallo copiare accanto all'eseguibile aggiungendo al.csproj:<ItemGroup> <None Include="prismdb-gateway.exe" CopyToOutputDirectory="PreserveNewest" /> </ItemGroup>Il provider cerca il gateway, in ordine:
GatewayPathnelle opzioni, la cartella dell'app, le sottocartelleBINS\windows,BINS\linuxegateway, poi la variabile d'ambientePRISMDB_GATEWAY(percorso completo del file).
Il primo programma
using Microsoft.EntityFrameworkCore;
using PrismDB.Ado;
using PrismDB.EntityFrameworkCore;
// Il gateway incorporato: processo figlio, porta libera, token casuale.
// Il percorso è relativo alla cartella di lavoro; la cartella viene creata.
using var gateway = PrismDbGateway.Start("data/negozio.prismdb");
// ADO.NET
using (var c = gateway.Connect())
{
using var cmd = c.CreateCommand();
cmd.CommandText = "CREATE TABLE IF NOT EXISTS Clienti (Id INTEGER PRIMARY KEY, Nome TEXT NOT NULL)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO Clienti (Nome) VALUES (@nome)";
cmd.Parameters.AddWithValue("@nome", "Ada");
cmd.ExecuteNonQuery();
}
// Entity Framework Core sullo stesso gateway
using var db = new Negozio(gateway.ConnectionString);
db.Clienti.Add(new Cliente { Nome = "Grace" });
db.SaveChanges();
foreach (var cl in db.Clienti.OrderBy(x => x.Id)) Console.WriteLine($"{cl.Id} {cl.Nome}");
class Cliente
{
public long Id { get; set; }
public string Nome { get; set; } = "";
}
class Negozio(string cs) : DbContext
{
public DbSet<Cliente> Clienti => Set<Cliente>();
protected override void OnConfiguring(DbContextOptionsBuilder o) => o.UsePrismDb(cs);
protected override void OnModelCreating(ModelBuilder m) => m.Entity<Cliente>().ToTable("Clienti");
}
dotnet run stampa 1 Ada e 2 Grace. Il gateway si chiude con
gateway.Dispose() (qui alla fine del programma) o comunque quando l'app
termina.
Connessione a un gateway già acceso
using var c = new PrismDbConnection("Host=127.0.0.1;Port=15432;Token=il-token");
c.Open();
| Chiave | Default | |
|---|---|---|
Host |
127.0.0.1 |
indirizzo del gateway |
Port |
15432 |
porta |
Token (o Password) |
vuoto | token del gateway |
Timeout |
10000 |
timeout di connessione in millisecondi |
Pooling |
true |
pool di connessioni |
Il gateway incorporato con opzioni:
using var gateway = PrismDbGateway.Start(new PrismDbGatewayOptions
{
DatabasePath = "data/app.prismdb", // obbligatorio
Key = Environment.GetEnvironmentVariable("PRISMDB_KEY"), // file cifrato (64 caratteri esadecimali)
Port = 15432, // 0 (default) = una porta libera
GatewayPath = @"C:\PrismDB\prismdb-gateway.exe", // default: ricerca automatica
});
Token (default: casuale a ogni avvio, già dentro gateway.ConnectionString;
"" = nessun token), CreateIfMissing (default true),
StartupTimeoutMs (default 15000).
ADO.NET
- Parametri
?,?NNN,@nome,:nome,$nome, legati dal motore (i valori non entrano mai nel testo SQL). Più istruzioni in un comando: si leggono conNextResult(). CommandType.StoredProcedureesegueCALL nome(...)con i parametri.- I valori arrivano con le classi di SQLite (
long,double,string,byte[],DBNull);GetFieldValue<T>converte inDateTime,DateOnly,TimeOnly,DateTimeOffset,TimeSpan,Guid,decimal,bool, enum. BeginTransaction()apre una transazione vera; i savepoint funzionano.- Ogni connessione ha la sua sessione: tabelle
TEMP,ATTACHePRAGMArestano suoi. - Errori:
PrismDbExceptioncon il messaggio di SQLite eSqliteErrorCode(19 vincolo, 5 conflitto: rifai la transazione). GetCatalog()/GetTable("nome"): tabelle, colonne, chiavi, FK, indici, viste, procedure, trigger.Backup("file"),Vacuum().
Entity Framework Core
// Program.cs (ASP.NET Core)
builder.Services.AddDbContext<AppDbContext>(o => o.UsePrismDb(builder.Configuration.GetConnectionString("PrismDB")));
UsePrismDb usa il provider SQLite ufficiale di EF Core con le connessioni
PrismDB: LINQ completo, Include, GroupBy, ExecuteUpdate/ExecuteDelete,
transazioni, FromSql, migrations, EnsureCreated. Non ci sono le funzioni
ef_* di Microsoft.Data.Sqlite (aritmetica decimal nel database e
Regex.IsMatch nelle query).
Scaffold dal database: PrismDb.Scaffold()
Genera le classi dal database e le tiene aggiornate a ogni avvio:
using var gateway = PrismDbGateway.Start("data/app.prismdb");
PrismDb.Scaffold(gateway); // oppure PrismDb.Scaffold("Host=...;Port=...;Token=...")
Nella cartella del .csproj compaiono Entities/ (una classe per tabella con
le navigation dalle chiavi esterne, e il DbContext), DTOs/, Views/ e
StoredProcedures/ (un metodo tipizzato per procedura). Regole:
- a ogni avvio confronta il database con i file e riscrive solo quelli cambiati; dopo un cambiamento ricompila (lo dice il log);
- i file generati iniziano con
// <auto-generated by PrismDB.Scaffold />: il tuo codice va in un altro file con la stessapartial class; togli quella riga e il file diventa tuo (non viene più toccato); - in un'app pubblicata (nessun
.csprojsopra l'eseguibile) non fa niente; - senza argomenti la connection string viene da
PRISMDB_CONNECTIONo daConnectionStrings:PrismDBinappsettings.json.
Tipi: INTEGER→long, REAL→double, TEXT→string, BLOB→byte[],
DECIMAL→decimal, DATE→DateOnly, TIME→TimeOnly,
DATETIME→DateTime, BOOLEAN→bool, GUID→Guid.
Node.js
Il pacchetto npm prismdb (TypeScript, solo ESM) con il gateway incluso.
Requisiti: Node.js 22.12 o successivo.
Installazione
- Estrai
prismdb-node-2.0.0.zip(tre file.tgz) in una cartella, per esempiovendor/dentro il progetto. -
Installa il pacchetto e il gateway della tua piattaforma:
npm install ./vendor/prismdb-2.0.0.tgz ./vendor/prismdb-gateway-win32-x64-2.0.0.tgz # Windows npm install ./vendor/prismdb-2.0.0.tgz ./vendor/prismdb-gateway-linux-x64-2.0.0.tgz # LinuxPer un progetto che gira su entrambi (sviluppo su Windows, server Linux) installa entrambi i pacchetti del gateway: ognuno si attiva sulla sua piattaforma. Tieni i
.tgznel repository (o in un registro npm privato):package.jsonli referenzia per percorso.
Il gateway viene cercato in quest'ordine: l'opzione gatewayPath, la
variabile PRISMDB_GATEWAY, il pacchetto @prismdb/gateway-<piattaforma>, il
PATH.
Il primo programma
// index.js (in package.json: "type": "module")
import { getClient } from 'prismdb';
const db = getClient('file:data/negozio.prismdb'); // avvia il gateway incorporato
await db.run('CREATE TABLE IF NOT EXISTS Clienti (Id INTEGER PRIMARY KEY, Nome TEXT NOT NULL)');
const { lastInsertRowid } = await db.run('INSERT INTO Clienti (Nome) VALUES (?)', ['Ada']);
console.log('inserito', lastInsertRowid);
console.log(await db.query('SELECT * FROM Clienti WHERE Nome = :nome', { nome: 'Ada' }));
console.log('totale', await db.value('SELECT count(*) FROM Clienti'));
await db.close();
node index.js stampa inserito 1, [ { Id: 1, Nome: 'Ada' } ], totale 1.
Configurazione
getClient(config) accetta:
| Forma | Esempio |
|---|---|
| un file (gateway incorporato) | 'file:data/app.prismdb', 'file:data/app.prismdb?key=<64 hex>', 'data/app.prismdb' |
| un gateway acceso | 'prismdb://TOKEN@127.0.0.1:15432' (?timeout=5000&max=10&int64=bigint) |
| un gateway dietro un proxy TLS | 'prismdbs://TOKEN@db.esempio.it:443' |
| un oggetto | { file: 'data/app.prismdb', key }, { host, port, token } |
| niente | legge PRISMDB_URL (anche da .env), PRISMDB_TOKEN, PRISMDB_KEY |
getClient() restituisce sempre lo stesso client per la stessa
configurazione (anche fra i ricaricamenti a caldo dei framework): usalo nelle
app web. createClient() ne crea uno nuovo.
Il client
const righe = await db.query('SELECT * FROM Ordini WHERE ClienteId = ?', [1]); // array di oggetti
const una = await db.get('SELECT * FROM Clienti WHERE Id = :id', { id: 1 }); // un oggetto o undefined
const n = await db.value('SELECT count(*) FROM Ordini'); // un valore
const { changes, lastInsertRowid } = await db.run('DELETE FROM Ordini WHERE Id = ?', [7]);
const tutti = await db.exec('SELECT 1; SELECT 2'); // tutti i risultati
import { sql } from 'prismdb';
await db.sql`SELECT * FROM Ordini WHERE ClienteId = ${1} AND Id IN ${[1, 2, 3]}`; // parametri legati
await db.transaction(async (tx) => {
await tx.run('UPDATE Conti SET Saldo = Saldo - ? WHERE Id = ?', [100, 1]);
await tx.run('UPDATE Conti SET Saldo = Saldo + ? WHERE Id = ?', [100, 2]);
}, { retries: 3 }); // COMMIT se finisce, ROLLBACK se lancia; riprova sui conflitti
await db.call('Registra', 'Ada', 100); // CALL di una procedura
await db.backup('backup/app.prismdb');
Tipi: number intero → INTEGER, decimale → REAL; bigint → INTEGER;
string → TEXT; Buffer/Uint8Array → BLOB; boolean → 1/0; Date →
testo UTC YYYY-MM-DD HH:MM:SS; oggetti e array → testo JSON. Un INTEGER oltre
2^53 di default è un errore (mai un numero sbagliato): con
?int64=bigint arriva come bigint. Gli errori sono PrismDbError con
message di SQLite, code (SQLITE_CONSTRAINT_UNIQUE, SQLITE_BUSY...) e
isTransient (conflitto: riprova).
Scaffold: npx prismdb generate
npx prismdb generate --url file:data/app.prismdb # una volta
npx prismdb generate --url file:data/app.prismdb --watch # e a ogni cambio di schema
Crea src/db/ (o db/ senza src/) con una interfaccia per tabella (con le
relazioni), le viste, le procedure tipizzate e createDb(client), un client
tipizzato in stile Prisma:
import { getClient } from 'prismdb';
import { createDb } from './db';
const db = createDb(getClient('file:data/app.prismdb'));
const ada = await db.Clienti.findFirst({ where: { Nome: 'Ada' }, include: { Ordini: true } });
await db.Ordini.create({ data: { ClienteId: ada.Id, Totale: 10 } });
I file generati iniziano con // <auto-generated by prismdb generate />:
togli la riga e il file diventa tuo. Con Kysely o Drizzle fra le dipendenze
genera anche kysely.ts / drizzle.ts (usali con prismdb/kysely e
prismdb/drizzle).
Framework
- Next.js: in
next.config.mjsimport { withPrismDB } from 'prismdb/next'; export default withPrismDB({ ... })(genera i tipi all'avvio dinext deve tieneprismdbfuori dal bundle). Il client va in un modulo solo server (import 'server-only'). - Nuxt, SvelteKit, Astro, Remix, Vite: il plugin
prismdb/vite(plugins: [prismdb()]) e il client nel codice server. - Express, Fastify, Hono, NestJS:
getClient()in un modulo. - Il browser (React, Angular, Vue in SPA) non apre connessioni TCP: monta
nel tuo server il ponte
prismdb/http(con controllo d'accesso obbligatorio, modalitàprocedures,readonlyofull) e usaprismdb/browsernel client. Lo stesso negli edge runtime (Vercel Edge, Cloudflare Workers).
Riga di comando
npx prismdb gateway data/app.prismdb --port 15432 # gateway separato; stampa PRISMDB_URL
npx prismdb query "SELECT * FROM Clienti" --json
npx prismdb shell # SQL interattivo: .tables, .schema, .quit
npx prismdb catalog --json
La CLI legge .env e .env.local del progetto (PRISMDB_URL=...).
JDBC e DBeaver
prismdb-jdbc-2.0.0.jar: un solo file, senza dipendenze, Java 8 o
successivo, uguale per tutti i sistemi operativi. Parla con il
gateway, che va avviato a parte.
DBeaver
- Avvia il gateway sul tuo file:
prismdb-gateway --db C:\dati\app.prismdb(lascia aperta la finestra). - In DBeaver: Database → Gestione driver → Nuovo.
- Scheda Librerie → Aggiungi file → scegli
prismdb-jdbc-2.0.0.jar. - Nome classe:
com.prismdb.jdbc.PrismDBDriver(o Trova classe). - Modello URL:
jdbc:prismdb://{host}:{port}/{database}, porta predefinita15432. Salva. - Nuova connessione → driver PrismDB → host
127.0.0.1, porta15432, databaseapp(è solo un'etichetta: il file è quello del gateway). Se il gateway ha un token, scrivilo nel campo Password (l'utente è ignorato). - Test connessione → Fine.
DBeaver vede schemi (main, temp, i database di ATTACH), tabelle, viste,
colonne, chiavi, relazioni (anche nel diagramma ER), indici, e modifica i
dati nella griglia. In modalità Manual commit Commit/Rollback aprono e
chiudono una transazione vera.
Dopo aver cambiato il JAR
DBeaver tiene in memoria le classi caricate: chiudilo del tutto e riaprilo, lasciando nelle Librerie solo il JAR nuovo.
Da Java
import java.sql.*;
import java.util.Properties;
public class Demo {
public static void main(String[] args) throws Exception {
Properties p = new Properties();
p.setProperty("token", System.getenv("PRISMDB_TOKEN")); // se il gateway ha un token
try (Connection c = DriverManager.getConnection("jdbc:prismdb://127.0.0.1:15432/app", p)) {
try (Statement s = c.createStatement()) {
s.execute("CREATE TABLE IF NOT EXISTS Clienti (Id INTEGER PRIMARY KEY, Nome TEXT NOT NULL)");
}
try (PreparedStatement ps = c.prepareStatement("INSERT INTO Clienti (Nome) VALUES (?)")) {
ps.setString(1, "Ada");
ps.executeUpdate();
}
try (Statement s = c.createStatement(); ResultSet r = s.executeQuery("SELECT Id, Nome FROM Clienti")) {
while (r.next()) System.out.println(r.getLong("Id") + " " + r.getString("Nome"));
}
}
}
}
javac -cp prismdb-jdbc-2.0.0.jar Demo.java
java -cp "prismdb-jdbc-2.0.0.jar;." Demo # Windows (su Linux il separatore è ":")
Con Maven o Gradle installa il JAR nel repository locale (mvn
install:install-file -Dfile=prismdb-jdbc-2.0.0.jar -DgroupId=com.prismdb
-DartifactId=prismdb-jdbc -Dversion=2.0.0 -Dpackaging=jar) o aggiungilo come
dipendenza da file (implementation files("libs/prismdb-jdbc-2.0.0.jar")).
Proprietà: token (o password), connectTimeout (ms, default 10000).
Errori: getErrorCode() è il codice di SQLite (19 vincolo, 5 conflitto),
getSQLState() 23000 (vincolo), 42S02 (tabella mancante), 42000
(sintassi), 40001 (conflitto: rifai la transazione). getObject() restituisce
Long, Double, String, byte[] o null. Transazioni con
setAutoCommit(false), commit(), rollback() e savepoint; batch e chiavi
generate funzionano.
Visual Studio Code
Scarica il pacchetto della tua piattaforma (prismdb-vscode-win32-x64-2.0.0.vsix
o ...-linux-x64-...; contiene il gateway). Serve VS Code 1.101 o successivo.
code --install-extension prismdb-vscode-win32-x64-2.0.0.vsix
(oppure in VS Code: Estensioni → menu … → Installa da VSIX...).
-
Connessioni (vista PrismDB nella barra attività): Apri file .prismdb..., Nuovo database..., Connetti a un gateway... (
host:portaoprismdb://TOKEN@host:porta). Token salvati nei segreti di VS Code. Connessioni condivise nel progetto, in.vscode/settings.json:"prismdb.connections": [ { "name": "App", "file": "data/app.prismdb" }, { "name": "Staging", "url": "prismdb://db.interno:15432" } ] -
Editor SQL: qualunque file
.sql. La connessione è quella scelta nella barra di stato o quella scritta in testa al file (-- prismdb: App). Ctrl+Invio esegue l'istruzione sotto il cursore (o la selezione), Ctrl+Maiusc+Invio tutto il file; Piano di esecuzione mostraEXPLAIN QUERY PLAN. I parametri (?,:nome...) si chiedono prima di eseguire. Ogni editor ha la sua sessione (BEGIN,TEMP,ATTACH). - Risultati: un tab per istruzione, ordinamento e filtro, copia, esportazione Markdown, CSV e JSON.
- Dati: dal menu di una tabella Mostra e modifica dati: modifica nelle celle, righe nuove ed eliminate, Salva applica tutto in una transazione.
- Tipi del progetto: PrismDB: Genera tipi nel progetto esegue
prismdb generate(serve il pacchetto Node nel progetto). - Database cifrati: avvia un gateway con
--key-enve usa Connetti a un gateway.
Impostazioni: prismdb.maxRows (righe mostrate, 1000), prismdb.pageSize
(righe per pagina nell'editor dei dati, 200), prismdb.gatewayPath (un altro
gateway), prismdb.queryTimeout (ms, 0 = nessuno).
IDE JetBrains
prismdb-jetbrains-2.0.0.zip è il plugin per tutti gli IDE JetBrains
2025.2 o successivi (IntelliJ IDEA, WebStorm, PyCharm, Rider, RustRover,
GoLand, PhpStorm, CLion, DataGrip...), su Windows x64 e Linux x64. Contiene il
gateway.
- Settings → Plugins → icona ⚙ → Install Plugin from Disk...
-
Scegli il file zip così com'è (non estrarlo) → OK → riavvia l'IDE.
-
Finestra PrismDB (a destra): Apri file .prismdb..., Nuovo database..., Connetti a un gateway.... Per un file cifrato l'IDE chiede la chiave. Token e chiavi stanno nel Password Safe dell'IDE.
- Albero: tabelle (colonne, chiavi, FK), viste, indici, trigger, procedure;
menu: dati, definizione, script, backup (
VACUUM INTO), elimina. - Console SQL: Tools → PrismDB → Nuova console SQL, o qualunque
.sql(connessione scritta in testa:-- prismdb: App). Ctrl+Alt+Invio esegue l'istruzione sotto il cursore, Ctrl+Alt+Maiusc+Invio il file. - Finestra PrismDB Risultati in basso; editor dei dati con salvataggio in transazione; completamento di tabelle, colonne e procedure.
- Negli IDE con Database Tools (IDEA Ultimate, WebStorm, Rider, RustRover, DataGrip...) compare anche il driver PrismDB nella finestra Database (+ → Data Source → PrismDB): si collega a un gateway acceso.
Visual Studio
PrismDB-2.0.0.vsix per Visual Studio 2022 e 2026: chiudi Visual Studio
e fai doppio clic sul file. Contiene il gateway.
- Strumenti → Apri PrismDB Explorer: Apri file .prismdb... oppure un gateway (host, porta, token). Albero di database, tabelle, viste, procedure, trigger; griglia modificabile salvata in una transazione; editor SQL (Ctrl+Invio, un tab per risultato, piano di esecuzione).
- Strumenti → PrismDB: Scaffold nel progetto... (anche dal menu del
progetto): genera nel progetto le classi di
PrismDb.Scaffold(). - Database cifrati: avvia un gateway con
--key-enve collegati a quello.
API C
Nello zip del Motore: prism.h più prism_c.dll e prism_c.lib
(Windows) o libprism_c.so (Linux, glibc 2.34+). È un'API propria (prism_*),
non quella di sqlite3.
#include <stdio.h>
#include "prism.h"
int main(void) {
prism_db_t *db = prism_open("esempio.prismdb");
if (!db) {
fprintf(stderr, "apertura fallita: %s\n", prism_open_errmsg());
return 1;
}
if (prism_exec(db, "CREATE TABLE IF NOT EXISTS t (a INTEGER, b TEXT);"
"INSERT INTO t VALUES (1, 'uno'), (2, 'due');") != PRISM_OK) {
fprintf(stderr, "errore: %s\n", prism_errmsg(db));
prism_close(db);
return 1;
}
prism_stmt_t *st = prism_prepare(db, "SELECT a, b FROM t WHERE a >= ?");
prism_bind_int64(st, 1, 2);
while (prism_step(st) == PRISM_ROW) {
printf("%lld %s\n", (long long)prism_column_int64(st, 0), prism_column_text(st, 1));
}
prism_finalize(st);
prism_close(db);
return 0;
}
Compilare (dalla cartella dello zip):
# Windows, "x64 Native Tools Command Prompt" di Visual Studio
cl esempio.c prism_c.lib
esempio.exe # prism_c.dll deve stare accanto all'exe
# Linux
cc esempio.c -I. -L. -lprism_c -o esempio
LD_LIBRARY_PATH=. ./esempio # oppure installa libprism_c.so in /usr/local/lib e lancia ldconfig
| Funzione | |
|---|---|
prism_open(path) |
apre o crea (":memory:" = in memoria); NULL se fallisce, motivo in prism_open_errmsg() |
prism_close(db) |
chiude |
prism_exec(db, sql) |
esegue una o più istruzioni separate da ;, scarta le righe |
prism_prepare(db, sql) |
prepara un'istruzione; NULL + prism_errmsg(db) se non è valida |
prism_bind_int64/double/text/blob/null(st, i, ...) |
lega il parametro i (da 1). text con len < 0 = terminata da NUL |
prism_step(st) |
PRISM_ROW per ogni riga, poi PRISM_DONE; PRISM_ERROR se fallisce |
prism_column_count/name/type(st, ...) |
colonne del risultato; tipi PRISM_INTEGER, PRISM_REAL, PRISM_TEXT, PRISM_BLOB, PRISM_NULL |
prism_column_int64/real/text/blob/bytes(st, col) |
valori della riga corrente (colonne da 0); i puntatori valgono fino al prossimo step |
prism_finalize(st) |
libera l'istruzione |
prism_last_insert_rowid(db), prism_changes(db) |
rowid dell'ultimo INSERT, righe toccate dall'ultima scrittura |
prism_errmsg(db) |
testo dell'ultimo errore su quel database |
Codici: PRISM_OK 0, PRISM_ERROR 1, PRISM_ROW 100, PRISM_DONE 101. Un
errore interno non fa mai crollare il programma: diventa PRISM_ERROR. I
nomi delle colonne arrivano in minuscolo.
Rust
Il motore è scritto in Rust, ma il codice sorgente non è pubblico e il crate non viene distribuito. Da un programma Rust PrismDB si usa come da qualsiasi altro linguaggio: con la libreria C del Motore (FFI), oppure attraverso il gateway con il suo protocollo.
Con la libreria C: estrai lo zip del motore in una cartella prismdb/
accanto a Cargo.toml e aggiungi un build.rs:
// build.rs
fn main() {
// La cartella con prism_c.lib (Windows) o libprism_c.so (Linux).
println!("cargo:rustc-link-search=native=prismdb");
println!("cargo:rustc-link-lib=dylib=prism_c");
}
// src/main.rs
use std::ffi::{c_char, c_int, CStr, CString};
#[repr(C)] struct PrismDb { _p: [u8; 0] }
#[repr(C)] struct PrismStmt { _p: [u8; 0] }
extern "C" {
fn prism_open(path: *const c_char) -> *mut PrismDb;
fn prism_open_errmsg() -> *const c_char;
fn prism_close(db: *mut PrismDb);
fn prism_exec(db: *mut PrismDb, sql: *const c_char) -> c_int;
fn prism_errmsg(db: *mut PrismDb) -> *const c_char;
fn prism_prepare(db: *mut PrismDb, sql: *const c_char) -> *mut PrismStmt;
fn prism_bind_int64(st: *mut PrismStmt, idx: c_int, v: i64) -> c_int;
fn prism_step(st: *mut PrismStmt) -> c_int;
fn prism_column_int64(st: *mut PrismStmt, col: c_int) -> i64;
fn prism_column_text(st: *mut PrismStmt, col: c_int) -> *const c_char;
fn prism_finalize(st: *mut PrismStmt) -> c_int;
}
const PRISM_OK: c_int = 0;
const PRISM_ROW: c_int = 100;
fn main() {
let c = |s: &str| CString::new(s).unwrap();
unsafe {
let db = prism_open(c("esempio.prismdb").as_ptr());
if db.is_null() {
panic!("{}", CStr::from_ptr(prism_open_errmsg()).to_string_lossy());
}
let sql = c("CREATE TABLE IF NOT EXISTS t (a INTEGER, b TEXT); INSERT INTO t VALUES (1, 'uno'), (2, 'due');");
if prism_exec(db, sql.as_ptr()) != PRISM_OK {
panic!("{}", CStr::from_ptr(prism_errmsg(db)).to_string_lossy());
}
let st = prism_prepare(db, c("SELECT a, b FROM t WHERE a >= ?").as_ptr());
prism_bind_int64(st, 1, 2);
while prism_step(st) == PRISM_ROW {
let b = CStr::from_ptr(prism_column_text(st, 1)).to_string_lossy();
println!("{} {}", prism_column_int64(st, 0), b);
}
prism_finalize(st);
prism_close(db);
}
}
cargo run stampa 2 due. Per eseguire il programma la libreria deve
stare accanto all'eseguibile (prism_c.dll in target/debug/ su Windows) o
nel percorso delle librerie (LD_LIBRARY_PATH, o /usr/local/lib con
ldconfig, su Linux). Le altre funzioni sono nella tabella dell'API C.
Backup, copie e manutenzione
-
Copia coerente a caldo: da qualsiasi client o dalla shell,
VACUUM INTO 'C:/backup/app-2026-10-19.prismdb';scrive una copia compatta del database (anche mentre altri lo usano). Il file di destinazione non deve esistere. Un database cifrato produce una copia cifrata con la stessa chiave. Nei client:
db.backup(...)(Node),connection.Backup(...)(.NET), Backup... negli IDE. * Script SQL:prism app.prismdb ".once backup.sql" ".dump"(solo file non cifrati). * Ripristino: chiudi tutti i programmi che usano il file (gateway compreso) e sostituisciapp.prismdbcon la copia. Se accanto al file vecchio è rimasto unapp.prismdb-wal(dopo un crash), appartiene al database vecchio: eliminalo insieme a lui, altrimenti alla prossima apertura verrebbe applicato alla copia. * Spazio:VACUUM;ricompatta il file. Statistiche:ANALYZE;. Controllo:PRAGMA integrity_check;deve rispondereok. * Chiusura brusca (crash, kill, mancanza di corrente): le transazioni confermate sono nel WAL e vengono riportate alla riapertura; una transazione a metà viene scartata per intero.
Migrare da SQLite
-
Con
sqlite3(lo strumento di SQLite) esporta il database come SQL:sqlite3 vecchio.db .dump > dump.sql -
Se il database usava
AUTOINCREMENT, togli dal file le righe susqlite_sequence(PrismDB non ha quella tabella; i contatori ripartono comunque dal valore più alto):grep -v sqlite_sequence dump.sql > dump-prism.sql # LinuxGet-Content dump.sql | Where-Object { $_ -notmatch 'sqlite_sequence' } | Set-Content -Encoding utf8 dump-prism.sql # Windows -
Importa in un file nuovo e controlla:
prism nuovo.prismdb ".read dump-prism.sql" prism nuovo.prismdb "PRAGMA integrity_check" "PRAGMA foreign_key_check"
Tabelle, dati, indici, viste e trigger passano così come sono.
Risoluzione dei problemi
| Sintomo | Causa e rimedio |
|---|---|
invalid prismdb header |
il file non è un database PrismDB (per esempio è un file SQLite): Migrare da SQLite |
database is encrypted |
il file è cifrato: serve la chiave (Cifratura) |
page authentication failed |
chiave sbagliata, o file danneggiato |
Connection refused / timeout di connessione |
il gateway non è acceso, o ascolta su un'altra porta o indirizzo |
autenticazione al gateway fallita (token errato?) |
token mancante o sbagliato nel client |
token troppo corto (minimo 16 caratteri) |
usa un token più lungo |
--bind ... è raggiungibile dalla rete: serve --token-env... |
per ascoltare in rete serve un token |
prismdb-gateway non trovato (.NET, Node) |
il gateway non è accanto all'app e PRISMDB_GATEWAY non è impostata: .NET, Node |
SQLITE_BUSY, SQLState 40001, busy: row conflict, retry |
un'altra transazione ha cambiato le stesse righe: rifai la transazione |
FOREIGN KEY constraint failed dove SQLite passava |
in PrismDB le chiavi esterne sono attive di default |
no such table: sqlite_sequence durante un import |
togli quelle righe dal dump (Migrare da SQLite) |
Linux: GLIBC_2.39 not found (o 2.34) |
sistema troppo vecchio: Ubuntu 24.04+ per la shell, 22.04+ per gateway e libreria |
Linux: Permission denied avviando prism o prismdb-gateway |
chmod +x prism prismdb-gateway |
| Windows: SmartScreen blocca il programma | i binari non sono firmati: Ulteriori informazioni → Esegui comunque, o Proprietà del file → Sblocca |